mirror of
https://github.com/troglobit/finit.git
synced 2026-10-14 10:19:36 +00:00
Compare commits
400
Commits
3.0
..
cgroup-config
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44137806e4 | ||
|
|
0848b9b07a | ||
|
|
c64188bfc5 | ||
|
|
5e789401d4 | ||
|
|
744fdd654f | ||
|
|
f51fde4d46 | ||
|
|
85261c9acd | ||
|
|
6a1082a947 | ||
|
|
2094d2a53a | ||
|
|
d7da177742 | ||
|
|
170e5aa3fd | ||
|
|
f8ef8a1616 | ||
|
|
f5f1a55747 | ||
|
|
a36aade7f4 | ||
|
|
76fc72770e | ||
|
|
b64dbbf8d0 | ||
|
|
d371b85c47 | ||
|
|
053304012f | ||
|
|
12d14aae84 | ||
|
|
6a7f115781 | ||
|
|
423c8d41a7 | ||
|
|
f2cab53e55 | ||
|
|
33fd6f0049 | ||
|
|
e83dcafb02 | ||
|
|
9a98bff23f | ||
|
|
1a2f7d162b | ||
|
|
5579ba16b1 | ||
|
|
510b36645b | ||
|
|
67cd88cd23 | ||
|
|
a04a6eda12 | ||
|
|
94525d6aae | ||
|
|
1d14806fd8 | ||
|
|
5e6f64eec9 | ||
|
|
df22628183 | ||
|
|
4870cab898 | ||
|
|
859c216347 | ||
|
|
931565c425 | ||
|
|
3006caf670 | ||
|
|
1269f082de | ||
|
|
7d444e07a5 | ||
|
|
6ec4dce5f9 | ||
|
|
8a0edbe801 | ||
|
|
46683f8eb8 | ||
|
|
fb16abd855 | ||
|
|
060de93b5b | ||
|
|
231b887b2a | ||
|
|
dddd45e3b5 | ||
|
|
cb9f6213dc | ||
|
|
f33ed869e4 | ||
|
|
4790344fbe | ||
|
|
a40858f162 | ||
|
|
682e80fed5 | ||
|
|
cd91e2c66c | ||
|
|
470549badb | ||
|
|
a85c3628b8 | ||
|
|
53ac0ffdfb | ||
|
|
eee6ffe95c | ||
|
|
a5491b3ded | ||
|
|
a1ea8ff564 | ||
|
|
0dffa731d2 | ||
|
|
9516c28a8a | ||
|
|
fa0a4e8579 | ||
|
|
a487a30814 | ||
|
|
40db4f4f58 | ||
|
|
3a1ad67e92 | ||
|
|
43942ab513 | ||
|
|
3b2f2690e7 | ||
|
|
5684b761da | ||
|
|
59f22c7d27 | ||
|
|
f522a26125 | ||
|
|
efb1d40ffd | ||
|
|
ea591c7580 | ||
|
|
af9a06466c | ||
|
|
7ed9f2ad05 | ||
|
|
65ccc407fa | ||
|
|
7fe9f3c4b6 | ||
|
|
24299ab5d9 | ||
|
|
e9670b87b3 | ||
|
|
406339e755 | ||
|
|
dc7039b8d8 | ||
|
|
90249e50ef | ||
|
|
b8e6954c46 | ||
|
|
4585aa0fb4 | ||
|
|
f2655d14c8 | ||
|
|
6d1d51b28c | ||
|
|
b9d233d19e | ||
|
|
2ec132c75a | ||
|
|
3092d55ec5 | ||
|
|
cd791afea5 | ||
|
|
829405f966 | ||
|
|
72526d9e88 | ||
|
|
f704eab6d7 | ||
|
|
a50bfc016e | ||
|
|
396c1b491f | ||
|
|
df89cc79b9 | ||
|
|
3429901d83 | ||
|
|
dedf885b92 | ||
|
|
93a21197ab | ||
|
|
8a0fffb00d | ||
|
|
7fdb280712 | ||
|
|
0a643953c0 | ||
|
|
fd837a4242 | ||
|
|
2759992dd2 | ||
|
|
d880cd75ae | ||
|
|
29fe8cc73d | ||
|
|
2b6f00f126 | ||
|
|
86e534095d | ||
|
|
865a3a7a4a | ||
|
|
2dc7de3aa2 | ||
|
|
06a5b43005 | ||
|
|
38939cc57e | ||
|
|
a3eadb72a7 | ||
|
|
6d41725387 | ||
|
|
ceb91f746c | ||
|
|
a8802ab0d2 | ||
|
|
ad98f69560 | ||
|
|
5953090688 | ||
|
|
22a69d8cca | ||
|
|
710a3d2c8f | ||
|
|
658fb9b037 | ||
|
|
a84bb9042f | ||
|
|
eeb13dba74 | ||
|
|
3d20237959 | ||
|
|
ddf1bce642 | ||
|
|
93cd4116db | ||
|
|
1df6a91a94 | ||
|
|
c64a71ff30 | ||
|
|
b2b6fd25d8 | ||
|
|
3ca548b681 | ||
|
|
ba2ab4b446 | ||
|
|
a8183b0559 | ||
|
|
227e4fa097 | ||
|
|
3d68ebbf54 | ||
|
|
6e017025aa | ||
|
|
5f818f917e | ||
|
|
0da1deb487 | ||
|
|
0337985422 | ||
|
|
f59d41cdec | ||
|
|
1cc998eb60 | ||
|
|
8c1d84305d | ||
|
|
7e4e5127c7 | ||
|
|
3b813bca68 | ||
|
|
a1f210bc70 | ||
|
|
0ecf46d7a9 | ||
|
|
268304927d | ||
|
|
fa02b59db5 | ||
|
|
2c904b5b44 | ||
|
|
4191abc79f | ||
|
|
abb928f60b | ||
|
|
3c52118a8e | ||
|
|
5354a57313 | ||
|
|
92b1d882e5 | ||
|
|
fd7a6c5020 | ||
|
|
a61551d15f | ||
|
|
7f62a69f74 | ||
|
|
c969bda62d | ||
|
|
af41fcd35e | ||
|
|
84e9569be6 | ||
|
|
a4ece0fe3f | ||
|
|
b2f66da1f9 | ||
|
|
abbc0a1d5c | ||
|
|
37e420bd35 | ||
|
|
d72eb45d6e | ||
|
|
30c593f64e | ||
|
|
f858d94d2a | ||
|
|
992bab2f8f | ||
|
|
72c9681ffb | ||
|
|
e439b79e82 | ||
|
|
36e001d636 | ||
|
|
75aa14466f | ||
|
|
7f899b7a91 | ||
|
|
1c177fd953 | ||
|
|
617ec7c147 | ||
|
|
192d3aafe7 | ||
|
|
f11ebb0046 | ||
|
|
b5e260b9e0 | ||
|
|
45c10edad6 | ||
|
|
b60d8ce4b6 | ||
|
|
d35a8bbda9 | ||
|
|
c616270ccb | ||
|
|
890ac2f4e1 | ||
|
|
8063b1bfe1 | ||
|
|
0bcfb464ce | ||
|
|
dc5b1a5980 | ||
|
|
14555d7a7b | ||
|
|
a06127b990 | ||
|
|
3b8e7d677f | ||
|
|
097fce4170 | ||
|
|
306bf0f191 | ||
|
|
f25390004b | ||
|
|
cbd4cc8d34 | ||
|
|
44591c6e51 | ||
|
|
0e51a1eed8 | ||
|
|
c315fb5637 | ||
|
|
1017e7b8e5 | ||
|
|
8ef216af62 | ||
|
|
a9b9631098 | ||
|
|
cc186334e0 | ||
|
|
e1b87d707f | ||
|
|
68e7bcba4c | ||
|
|
f9783b4b41 | ||
|
|
32d9afc7bb | ||
|
|
91b550662e | ||
|
|
d87af8270e | ||
|
|
366c23f5d1 | ||
|
|
0d4bc69584 | ||
|
|
cccf199280 | ||
|
|
a1c33dec21 | ||
|
|
df303c6243 | ||
|
|
6a6ae6fa3b | ||
|
|
4011b5a3b8 | ||
|
|
68ef6ec108 | ||
|
|
89a9d92d6c | ||
|
|
15cb576293 | ||
|
|
90734eee16 | ||
|
|
71915e0e14 | ||
|
|
112ab2c22f | ||
|
|
8f00ac2e90 | ||
|
|
6fb070c75c | ||
|
|
8fd431f3e9 | ||
|
|
50f128cd1d | ||
|
|
f6fa576270 | ||
|
|
b26d30757d | ||
|
|
dc9d0b6fca | ||
|
|
ff95ca92b1 | ||
|
|
c939aed87f | ||
|
|
71ea9e2ea3 | ||
|
|
b6bd10d960 | ||
|
|
87d8157175 | ||
|
|
cf6faa9eeb | ||
|
|
80a71aff07 | ||
|
|
04a4653e9b | ||
|
|
3aa9d9f451 | ||
|
|
c1afca9c33 | ||
|
|
205915d42f | ||
|
|
631f89b58c | ||
|
|
6d1c245985 | ||
|
|
fc08b0c65b | ||
|
|
83021b11d8 | ||
|
|
5309ece2f9 | ||
|
|
6480850ee2 | ||
|
|
eaa82377c4 | ||
|
|
5b3df3654c | ||
|
|
55cb50ef90 | ||
|
|
3f7e03e813 | ||
|
|
ab723d2f31 | ||
|
|
bc149d95fc | ||
|
|
c605874b26 | ||
|
|
c33ba6a477 | ||
|
|
63199d368f | ||
|
|
db3263c441 | ||
|
|
925212f7cf | ||
|
|
ad401280c2 | ||
|
|
f3d2586417 | ||
|
|
6fc1d27bcf | ||
|
|
f3305e75b7 | ||
|
|
e4c12314b1 | ||
|
|
c9a1a64ce5 | ||
|
|
3843f62aa8 | ||
|
|
ef54c4826b | ||
|
|
2fac65becb | ||
|
|
46b253dea6 | ||
|
|
b04983c3f3 | ||
|
|
f2d142ef57 | ||
|
|
33ce746e58 | ||
|
|
6ecb86b4a7 | ||
|
|
338810b886 | ||
|
|
c1344e89ff | ||
|
|
bc35b42bd2 | ||
|
|
dcf8fbe235 | ||
|
|
c82a2ce215 | ||
|
|
e1bb5e3396 | ||
|
|
8198582c10 | ||
|
|
0d19842aa1 | ||
|
|
6f665ab283 | ||
|
|
3f731aa5a3 | ||
|
|
9e8a01669e | ||
|
|
cbafbaea1d | ||
|
|
f534368b33 | ||
|
|
78fc9fccc5 | ||
|
|
3f7df3d35e | ||
|
|
9fc33418f9 | ||
|
|
72bc3b1a91 | ||
|
|
d6ea1c0cab | ||
|
|
523a270f22 | ||
|
|
3258edc246 | ||
|
|
32b7a14cb3 | ||
|
|
55479cc511 | ||
|
|
b702f7eeef | ||
|
|
0e37bb0924 | ||
|
|
360f3e72bc | ||
|
|
c1023c03c7 | ||
|
|
e94d2a8c08 | ||
|
|
f7c0366fa2 | ||
|
|
8992e7bf2b | ||
|
|
95518df62d | ||
|
|
85fe95835a | ||
|
|
baf9c4f61f | ||
|
|
b439ac670d | ||
|
|
bb3d9c8670 | ||
|
|
2f9b97b3a7 | ||
|
|
c59f7d23f4 | ||
|
|
b0663d04e4 | ||
|
|
6f26f2f9d0 | ||
|
|
e7cca05fe3 | ||
|
|
f3669c7537 | ||
|
|
a9456cec41 | ||
|
|
61a9926442 | ||
|
|
7784480abe | ||
|
|
db50c09ec5 | ||
|
|
0f1f51b5ad | ||
|
|
d7401b2a96 | ||
|
|
c0515d112e | ||
|
|
9a8b55737f | ||
|
|
d7d8b8487d | ||
|
|
7f04382eee | ||
|
|
bf004d9ec4 | ||
|
|
23e0928e5c | ||
|
|
d6b2ac94dd | ||
|
|
7362e6d00a | ||
|
|
d94d11e00a | ||
|
|
f06a0fcb71 | ||
|
|
cae61ad6df | ||
|
|
27e4af5ed8 | ||
|
|
f10a724503 | ||
|
|
33037b012a | ||
|
|
0c75e59b77 | ||
|
|
8e55a76732 | ||
|
|
32b9096e31 | ||
|
|
f51723e8b7 | ||
|
|
9e31bd8a23 | ||
|
|
e841984a4e | ||
|
|
594476c4fc | ||
|
|
9c423dfca4 | ||
|
|
77cd0f0243 | ||
|
|
5dc4e7c058 | ||
|
|
3c015f384d | ||
|
|
5117b723c2 | ||
|
|
b55a609834 | ||
|
|
60eccbac31 | ||
|
|
b182e6bcc5 | ||
|
|
b8bf26b107 | ||
|
|
14c0d80f63 | ||
|
|
1f55f677d1 | ||
|
|
40267a5f0f | ||
|
|
9f50b033d9 | ||
|
|
bbaa7a1403 | ||
|
|
b68c5c1291 | ||
|
|
44007c86f2 | ||
|
|
cd73c1c951 | ||
|
|
5406af2054 | ||
|
|
3c800f6508 | ||
|
|
e0fb2b036d | ||
|
|
0ee4657f1c | ||
|
|
ec2382b6d4 | ||
|
|
0fc2df1a65 | ||
|
|
6c563a2b22 | ||
|
|
6a2911223d | ||
|
|
7f499ed1d0 | ||
|
|
2d2807bc65 | ||
|
|
4af7974fb9 | ||
|
|
3fd45eac7c | ||
|
|
bb4578292f | ||
|
|
656fa3d0e7 | ||
|
|
9a75813350 | ||
|
|
5af1f05606 | ||
|
|
fcdb5c2965 | ||
|
|
af3e560d79 | ||
|
|
b6e695b656 | ||
|
|
123df18847 | ||
|
|
dae21d9d64 | ||
|
|
bf4a0abe5c | ||
|
|
6826d7ee3f | ||
|
|
168da2a5c6 | ||
|
|
c82dc54205 | ||
|
|
eb0a759371 | ||
|
|
830abc1971 | ||
|
|
4c3a11131f | ||
|
|
796701c854 | ||
|
|
ab760aa3ff | ||
|
|
2b9eecbef3 | ||
|
|
b8190c8a2a | ||
|
|
adb4670d2f | ||
|
|
d31575538c | ||
|
|
faf0c680b8 | ||
|
|
250e26f2f6 | ||
|
|
b45c3d5cb8 | ||
|
|
6b2678c2ed | ||
|
|
0d6f203e0b | ||
|
|
781ba66905 | ||
|
|
9a97ab29dc | ||
|
|
b2b17ea4fb | ||
|
|
bfa99266e7 | ||
|
|
c76a58ef7a | ||
|
|
51c5f3cbfa | ||
|
|
a6290da210 | ||
|
|
949f02eff4 | ||
|
|
7970e45f45 | ||
|
|
a36dafaa6c | ||
|
|
c092d22d48 |
No files matched your search
+8
-1
@@ -2,13 +2,15 @@
|
|||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
*.gz
|
*.gz
|
||||||
|
ID
|
||||||
GPATH
|
GPATH
|
||||||
GRTAGS
|
GRTAGS
|
||||||
GSYMS
|
GSYMS
|
||||||
GTAGS
|
GTAGS
|
||||||
|
|
||||||
# GNU configure and build system
|
# GNU configure and build system
|
||||||
/autom4te.cache/*
|
/.deps
|
||||||
|
/autom4te.cache
|
||||||
/aclocal.m4
|
/aclocal.m4
|
||||||
/ar-lib
|
/ar-lib
|
||||||
/compile
|
/compile
|
||||||
@@ -33,3 +35,8 @@ GTAGS
|
|||||||
/.config
|
/.config
|
||||||
/.stamp
|
/.stamp
|
||||||
/.unpacked
|
/.unpacked
|
||||||
|
|
||||||
|
# WeOS build system
|
||||||
|
/.autoconf
|
||||||
|
/.automake
|
||||||
|
/.built
|
||||||
+12
-9
@@ -9,9 +9,9 @@ sudo: false
|
|||||||
#sudo: true
|
#sudo: true
|
||||||
|
|
||||||
# Test build with both GCC and Clang (LLVM)
|
# Test build with both GCC and Clang (LLVM)
|
||||||
|
# - clang
|
||||||
compiler:
|
compiler:
|
||||||
- gcc
|
- gcc
|
||||||
- clang
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
@@ -33,20 +33,23 @@ addons:
|
|||||||
branch_pattern: dev
|
branch_pattern: dev
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- wget https://github.com/troglobit/libuev/releases/download/v1.5.2/libuev-1.5.2.tar.xz
|
- wget https://github.com/troglobit/libuev/releases/download/v2.2.0/libuev-2.2.0.tar.xz
|
||||||
- wget https://github.com/troglobit/libite/releases/download/v1.9.2/libite-1.9.2.tar.xz
|
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
|
||||||
- tar xf libuev-1.5.2.tar.xz
|
- tar xf libuev-2.2.0.tar.xz
|
||||||
- tar xf libite-1.9.2.tar.xz
|
- tar xf libite-2.0.1.tar.xz
|
||||||
- (cd libuev-1.5.2 && ./configure --prefix=/tmp && make && make install-strip)
|
- (cd libuev-2.2.0 && ./configure --prefix=/tmp && make && make install-strip)
|
||||||
- (cd libite-1.9.2 && ./configure --prefix=/tmp && make && make install-strip)
|
- (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip)
|
||||||
|
|
||||||
# Custom build script for the time being, no "make test" yet
|
# Custom build script for the time being, no "make test" yet
|
||||||
script:
|
script:
|
||||||
- ./autogen.sh
|
- ./autogen.sh
|
||||||
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp
|
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --disable-inetd --enable-static
|
||||||
|
- make clean
|
||||||
|
- make V=1 -j5
|
||||||
|
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig ./configure --prefix=/tmp --enable-inetd-echo-plugin --enable-inetd-time-plugin --enable-x11-common-plugin --enable-inetd-chargen-plugin --enable-fallback-shell --enable-watchdog
|
||||||
- cat config.log
|
- cat config.log
|
||||||
- make clean
|
- make clean
|
||||||
- make -j5 V=1
|
- make V=1 -j5
|
||||||
- make install-strip
|
- make install-strip
|
||||||
- tree /tmp
|
- tree /tmp
|
||||||
- ldd /tmp/sbin/finit
|
- ldd /tmp/sbin/finit
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
Current Maintainer(s):
|
Current Maintainer(s):
|
||||||
* Joachim Nilsson <troglobit@gmail.com>
|
* Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
|
||||||
|
Major Contributors:
|
||||||
|
* Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
|
* Mattias Walström <mattias.walstrom@westermo.se>
|
||||||
|
* Jonas Johansson <jonas.johansson@westermo.se>
|
||||||
|
|
||||||
Original Author(s):
|
Original Author(s):
|
||||||
* Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
|
||||||
+186
-2
@@ -3,6 +3,188 @@ Change Log
|
|||||||
|
|
||||||
All relevant changes are documented in this file.
|
All relevant changes are documented in this file.
|
||||||
|
|
||||||
|
|
||||||
|
[3.2][UNRELEASED] - 2018-10-xx
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Bug fix release.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
* Add support for `--disable-docs` and `--disable-contrib` to speed up
|
||||||
|
builds and work around issue with massively parallel builds
|
||||||
|
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
||||||
|
* Prefer udev to handle `/dev/` if mdev is also available
|
||||||
|
* Redirect dbus daemon output to syslog
|
||||||
|
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
|
||||||
|
* Finit no longer automatically reloads its `*.conf` files after running
|
||||||
|
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
|
||||||
|
* `initctl` without an argument or option now defaults to list services
|
||||||
|
* Converted built-in watchdog daemon to standalone mini watchdogd
|
||||||
|
* Improved watchdog hand-over, now based on `svc_t` and not PID
|
||||||
|
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
|
||||||
|
not allowed in the runtime runlevel are unconditionally stopped
|
||||||
|
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
|
||||||
|
than they were worth. Users are encouraged to use accounting instead
|
||||||
|
* Skip displaying "Restarting ..." progress for bootstrap processes
|
||||||
|
* Added a simple work queue mechanism to queue up work at boot + runtime
|
||||||
|
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
|
||||||
|
- As long as a stepped service changes state we queue another step all
|
||||||
|
event, because services may depend on each other
|
||||||
|
* Require new libuEv API: `uev_init1()` to reduce event cache so that
|
||||||
|
the kernel can invalidate deleted events before enqueing to userspace
|
||||||
|
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
|
||||||
|
from the `hwclock` tool. Note: the kernel can also be set to load
|
||||||
|
and store RTC to/from system clock at boot/halt as well.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix #96: Start udevd as a proper service
|
||||||
|
* Ensure we track run commands as well as task/service, once per runlevel
|
||||||
|
* Fix #98: FTBFS with `--disable-inetd`
|
||||||
|
* Make sure to unblock UDP inetd services when connection terminates.
|
||||||
|
Regression introduced in v3.1
|
||||||
|
* Ensure run/tasks also go to stopping state on exit, like services,
|
||||||
|
otherwise it is unnecessarily hard to restart them
|
||||||
|
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
|
||||||
|
by a PID. This caused a use after free issue crashing finit. Found
|
||||||
|
and fixed by Tobias Waldekranz, Westermo
|
||||||
|
* Fix missing OS/Finit title bug, adds leading newline before banner
|
||||||
|
* Remove "Failed connecting to watchdog ..." error message on systems
|
||||||
|
that do not have a watchdog
|
||||||
|
* Fix #100: Early condition handling may not work if `/var/run` does
|
||||||
|
not yet exist (symlink to `/run`). Added compat layer for access
|
||||||
|
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
|
||||||
|
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
|
||||||
|
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
|
||||||
|
Fixed by Jonas Johansson, Westermo
|
||||||
|
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
|
||||||
|
Fixed by Jonas Johansson, Westermo
|
||||||
|
* Fix #111: Only restart inetd services when necessary. E.g., if the
|
||||||
|
listening interface is changed. Only stop established connections
|
||||||
|
which are no longer allowed, i.e. do not touch already allowed
|
||||||
|
established connections. Fixed by Jonas Johansson, Westermo
|
||||||
|
* Fix: update inetd service args on config change. Found and fixed by
|
||||||
|
Petrus Hellgren, Westermo
|
||||||
|
* Fix `initctl [start | restart]`, should behave the same for services
|
||||||
|
that have crashed. Found by Mattias Walström, Westermo
|
||||||
|
* Wait for bootstrap phase to complete before cleaning out any bootstrap
|
||||||
|
processes that have stopped, they may be restarted again
|
||||||
|
* Reassert condition when an unmodified run/task/service goes from
|
||||||
|
WAITING back to RUNNING again after a reconfiguration event.
|
||||||
|
Found and fixed by Jonas Johansson, Westermo
|
||||||
|
* Remove service condition when service is deleted
|
||||||
|
|
||||||
|
|
||||||
|
[3.1][] - 2018-01-23
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Improvements to `netlink.so` plugin, per-service `rlimit` support,
|
||||||
|
improved integration with `watchdogd`, auto-detect TTY console. Much
|
||||||
|
improved debug, rescue and logging support. Also, many fixes to both
|
||||||
|
big and small issues, most notably in the condition handling, which no
|
||||||
|
longer is sensitive to time skips.
|
||||||
|
|
||||||
|
This version requires at least libuEv v2.1.0 and libite v2.0.1
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Support for more kernel command line settings:
|
||||||
|
- splash, enable boot progress
|
||||||
|
- debug, like `--debug` but also enable kernel debug
|
||||||
|
- single, single user mode (no network)
|
||||||
|
- rescue, new rescue mode
|
||||||
|
* Support for `IFF_RUNNING` to netlink plugin => `net/IFNAME/running`
|
||||||
|
* Support for restarting `initctl` API socket on `SIGHUP`
|
||||||
|
* Greatly updated `initctl status <JOB|NAME>` command
|
||||||
|
* Support for `rlimit` per service/run/task/inetd/tty, issue #45
|
||||||
|
* Support for setting `hard` and `soft` rlimit for a resource at once
|
||||||
|
* Support for auto-detecting serial console using Linux SysFS, the new
|
||||||
|
`tty @console` eliminates the need to keep track of different console
|
||||||
|
devices across embedded platforms: `/dev/ttyS0`, `/dev/ttyAMA0`, etc.
|
||||||
|
* Add TTY `nologin` option. Bypasses login and skips immediately to a
|
||||||
|
root shell. Useful during board bringup, in developer builds, etc.
|
||||||
|
* Support for calling run/tasks on Finit internal HOOK points, issue #18
|
||||||
|
* Removed support for long-since deprecated `console DEV` setting
|
||||||
|
* Cosmetic change to login, pressing enter at the `Press enter to ...`
|
||||||
|
prompt will now replace that line with the login issue text
|
||||||
|
* Calling `initctl` without any arguments or options now defaults to
|
||||||
|
show status of all enabled services, and run/task/inetd jobs
|
||||||
|
* Cosmetic change to boot messages, removed `Loading plugins ...`, start
|
||||||
|
of inetd services, and `Loading configuration ...`. No end user knows
|
||||||
|
what those plugins and configurations are, i.e. internal state+config
|
||||||
|
* Change kernel WDT timeout (3 --> 30 sec) for built-in watchdog daemon
|
||||||
|
* Advise watchdog dawmon on shutdown and reboot using `SIGPWR` and then
|
||||||
|
`SIGTERM`. It is recommended the daemon start a timer on the first
|
||||||
|
signal, in case the shutdown process somehow hangs.
|
||||||
|
* Handle `/etc/` OverlayFS, reload /etc/finit.d/*.conf after `mount -a`
|
||||||
|
* initctl: Add support for printing previous runlevel
|
||||||
|
* initctl: Support short forms of all commands
|
||||||
|
* initctl: Support for `initctl touch <CONF>` to be used with `reload`
|
||||||
|
* initctl: Improved output of `initctl show <SVC>`
|
||||||
|
* Support reloading `/etc/finit.conf`. The main finit.conf file
|
||||||
|
previously did not support reloading at runtime, as of v3.1 all
|
||||||
|
configuration directives supported in `/etc/finit.d/*.conf` are now
|
||||||
|
supported in `/etc/finit.conf`
|
||||||
|
* Change `.conf` dependency + reload handling. Finit no longer relies
|
||||||
|
on mtime of `.conf` files, instead an inotify handler tracks file
|
||||||
|
changes for time *insensitive* dependency tracking
|
||||||
|
* Change condition handling to not rely on mtime but a generation id.
|
||||||
|
* New configure script option `--enable-redirect` to automatically
|
||||||
|
redirect `stdout` and `stderr` of all applications to `/dev/null`
|
||||||
|
* New `pid` sub-option to services when a service does not create a PID
|
||||||
|
file, or when the PID file has another name. Issue #95
|
||||||
|
* Greatly improved `log` sub-option to service/run/tasks, selectively
|
||||||
|
redirect `stdout` and `stderr` using the new tool `logit` to either
|
||||||
|
syslog or a logfile. Issue #44
|
||||||
|
* Support for automatic log rotation of logfiles created by `log`
|
||||||
|
option. Use `configure --disable-logrotate` on systems with a
|
||||||
|
dedicated log rotation service. Issue #44
|
||||||
|
* Support for disabling service/run/task progress with empty ` --`
|
||||||
|
description. Note: no description separator gives a default desc.
|
||||||
|
* Create `/etc/mtab` symlink if missing on system (bootmisc plugin)
|
||||||
|
* New hook: `hook/mount/post` runs after `mount -a` but before the
|
||||||
|
`hook/mount/all`, where `bootmisc.so` runs. This provides the
|
||||||
|
possibility of running a second stage mount command before files in
|
||||||
|
`/run` and similar are created
|
||||||
|
* Skip `gdbserver` when unleashing the grim reaper at shutdown
|
||||||
|
* Distribute and install `docs/` and `contrib/` directories
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Reset TTY before restarting it. A program may manipulate the TTY in
|
||||||
|
various ways before the user logs out, Finit needs to reset the TTY to
|
||||||
|
a sane state before restarting it. Issue #84
|
||||||
|
* On .conf parse errors, do *not* default to set TTY speed 38400, reuse
|
||||||
|
current TTY speed instead
|
||||||
|
* Fix run/tasks, must be guaranteed to run once per declared runlevel.
|
||||||
|
All run/tasks on `[S]` with a condition `<...>` failed to run. Finit
|
||||||
|
now tracks run/tasks more carefully, waiting for them to finish before
|
||||||
|
switching to the configured runlevel at boot. Issue #86
|
||||||
|
* Allow inetd services to be registered with a unique ID, e.g. `:161`,
|
||||||
|
issue #87. Found by Westermo
|
||||||
|
* inetd: drop UDP packets from blocked interfaces, issue #88
|
||||||
|
* Handle obscure inter-plugin dependency issue by calling the netlink
|
||||||
|
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
|
||||||
|
* Handle event loop failure modes, issue found by Westermo
|
||||||
|
* Handle API socket errors more gracefully, restart socket
|
||||||
|
* Do not attempt to load kernel modules more than once at bootstrap
|
||||||
|
* Remove reboot symlinks properly on uninstall
|
||||||
|
* Fix regression in condition handling, reconf condition must be kept
|
||||||
|
as a reference point to previous reconfiguration, or bootstrap.
|
||||||
|
* Fix nasty race condition with internal service stop, abort kill if the
|
||||||
|
service has already terminated, otherwise we may do `kill(0, SIGKILL)`
|
||||||
|
* Fix reconfiguration issue with (very quick) systems that don't have
|
||||||
|
highres timers
|
||||||
|
* Fix formatting of runlevel string in `initctl show`
|
||||||
|
* Allow running `initctl` with `STDOUT` redirected
|
||||||
|
* Fix regression in `initctl start/stop <ID>`, using name worked not id
|
||||||
|
* Fix error handling in `initctl start/stop` without any argument
|
||||||
|
* Fix issue #81 properly, remove use of SYSV shm IPC completely. Finit
|
||||||
|
now use the API socket for all communication between PID 1 and initctl
|
||||||
|
* Fix segfault on x86_64 when started with kernel cmdline `--debug`
|
||||||
|
* Normalize condition paths on systems with `/run` instead of `/var/run`
|
||||||
|
|
||||||
|
|
||||||
[3.0][] - 2017-10-19
|
[3.0][] - 2017-10-19
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@@ -28,7 +210,7 @@ distributions.
|
|||||||
* Ensure plugins always have a default name, file name
|
* Ensure plugins always have a default name, file name
|
||||||
* Reorganization, move all source files to a `src/` sub-directory
|
* Reorganization, move all source files to a `src/` sub-directory
|
||||||
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
|
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
|
||||||
distributions. See [doc/distro.md](doc/distro.md) for details
|
distributions. See [docs/distro.md](docs/distro.md) for details
|
||||||
* Remove `UNUSED()` macro, mentioned here because it may have been used
|
* Remove `UNUSED()` macro, mentioned here because it may have been used
|
||||||
by external plugin developers. Set `-Wno-unused-parameter` instead
|
by external plugin developers. Set `-Wno-unused-parameter` instead
|
||||||
* New table headings in `initctl`, using `top` style inverted text
|
* New table headings in `initctl`, using `top` style inverted text
|
||||||
@@ -598,7 +780,9 @@ Major bug fix release.
|
|||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.0...HEAD
|
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
|
||||||
|
[3.2]: https://github.com/troglobit/finit/compare/3.1...3.2
|
||||||
|
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
|
||||||
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
||||||
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
|
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
|
||||||
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
|
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
|
||||||
|
|||||||
+8
-1
@@ -3,12 +3,19 @@ SUBDIRS = plugins src
|
|||||||
doc_DATA = README.md LICENSE contrib/finit.conf
|
doc_DATA = README.md LICENSE contrib/finit.conf
|
||||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||||
|
|
||||||
|
if CONTRIB
|
||||||
|
SUBDIRS += contrib
|
||||||
|
endif
|
||||||
|
if DOCS
|
||||||
|
SUBDIRS += docs
|
||||||
|
endif
|
||||||
|
|
||||||
# Compat hook
|
# Compat hook
|
||||||
install-dev:
|
install-dev:
|
||||||
@make -C src install-pkgincludeHEADERS
|
@make -C src install-pkgincludeHEADERS
|
||||||
|
|
||||||
# Target to run when building a release
|
# Target to run when building a release
|
||||||
release: dist
|
release: distcheck
|
||||||
@for file in $(DIST_ARCHIVES); do \
|
@for file in $(DIST_ARCHIVES); do \
|
||||||
md5sum $$file > ../$$file.md5; \
|
md5sum $$file > ../$$file.md5; \
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
<img align="right" src="images/finit3.png" alt="Finit: Fast Init">
|
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
* [Features](#features)
|
* [Features](#features)
|
||||||
* [Bootstrap](doc/bootstrap.md#bootstrap)
|
* [Bootstrap](docs/bootstrap.md#bootstrap)
|
||||||
* [Runlevels](#runlevels)
|
* [Runlevels](#runlevels)
|
||||||
* [Syntax](doc/config.md#syntax)
|
* [Syntax](docs/config.md#syntax)
|
||||||
* [Inetd](doc/inetd.md#inetd)
|
* [Inetd](docs/inetd.md#inetd)
|
||||||
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
||||||
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
|
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
|
||||||
* [Rebooting & Halting](#rebooting--halting)
|
* [Rebooting & Halting](#rebooting--halting)
|
||||||
* [Commands & Status](#commands--status)
|
* [Commands & Status](#commands--status)
|
||||||
* [Building](doc/build.md#building)
|
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
|
||||||
* [Running](doc/build.md#running)
|
* [Building](docs/build.md#building)
|
||||||
* [Debugging](doc/build.md#debugging)
|
* [Running](docs/build.md#running)
|
||||||
|
* [Recovery](docs/build.md#recovery)
|
||||||
|
* [Debugging](docs/build.md#debugging)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Origin & References](#origin--references)
|
* [Origin & References](#origin--references)
|
||||||
|
|
||||||
@@ -27,10 +29,13 @@ was reverse engineered from the [EeePC fastinit][] ten years ago by
|
|||||||
|
|
||||||
Focus is on small and embedded systems, although Finit is fully usable
|
Focus is on small and embedded systems, although Finit is fully usable
|
||||||
on server and desktop systems as well. For working examples, see the
|
on server and desktop systems as well. For working examples, see the
|
||||||
[contrib/](contrib/) section where both [Debian](contrib/debian/) and
|
[contrib/](contrib/) section with tutorials for:
|
||||||
up to date [Alpine Linux](contrib/alpine/) tutorials can be found.
|
|
||||||
|
|
||||||

|
* [Alpine Linux](contrib/alpine/),
|
||||||
|
* [Debian GNU/Linux](contrib/debian/), and
|
||||||
|
* [Void Linux](contrib/void/)
|
||||||
|
|
||||||
|

|
||||||
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
||||||
|
|
||||||
|
|
||||||
@@ -49,12 +54,23 @@ host default
|
|||||||
# Runlevel to start after bootstrap, 'S', default: 2
|
# Runlevel to start after bootstrap, 'S', default: 2
|
||||||
runlevel 2
|
runlevel 2
|
||||||
|
|
||||||
|
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||||
|
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||||
|
log size=100k count=4
|
||||||
|
|
||||||
# Services to be monitored and respawned as needed
|
# Services to be monitored and respawned as needed
|
||||||
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
|
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
|
||||||
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
|
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
|
||||||
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
||||||
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
||||||
|
|
||||||
|
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||||
|
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||||
|
# log file is rotated with the above settings. The condition declares
|
||||||
|
# a dependency on a system default route (gateway) to be set. A single
|
||||||
|
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||||
|
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
|
||||||
|
|
||||||
# For multiple instances of the same service, add :ID somewhere between
|
# For multiple instances of the same service, add :ID somewhere between
|
||||||
# the service/run/task keyword and the command.
|
# the service/run/task keyword and the command.
|
||||||
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
|
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
|
||||||
@@ -87,10 +103,13 @@ tty [12345] /sbin/getty -L 115200 /dev/tty3 linux
|
|||||||
# Use built-in getty for serial port and USB serial
|
# Use built-in getty for serial port and USB serial
|
||||||
tty [12345] /dev/ttyAMA0 115200 noclear nowait
|
tty [12345] /dev/ttyAMA0 115200 noclear nowait
|
||||||
tty [12345] /dev/ttyUSB0 115200 noclear
|
tty [12345] /dev/ttyUSB0 115200 noclear
|
||||||
|
|
||||||
|
# Just give me a shell, I need to debug this embedded system!
|
||||||
|
tty [12345] @console noclear nologin
|
||||||
```
|
```
|
||||||
|
|
||||||
The `service` stanza, as well as `task`, `run`, `inetd` and others are
|
The `service` stanza, as well as `task`, `run`, `inetd` and others are
|
||||||
described in full in [doc/config.md](doc/config.md). Here's a quick
|
described in full in [docs/config.md](docs/config.md). Here's a quick
|
||||||
overview of some of the most common components needed to start a UNIX
|
overview of some of the most common components needed to start a UNIX
|
||||||
daemon:
|
daemon:
|
||||||
|
|
||||||
@@ -111,7 +130,7 @@ advanced uses as well:
|
|||||||
|
|
||||||
service /usr/sbin/sshd -D
|
service /usr/sbin/sshd -D
|
||||||
|
|
||||||
Dependencies are handled using [conditions](doc/conditions.md). On of
|
Dependencies are handled using [conditions](docs/conditions.md). One of
|
||||||
the most common conditions is to wait for basic networking to become
|
the most common conditions is to wait for basic networking to become
|
||||||
available:
|
available:
|
||||||
|
|
||||||
@@ -124,6 +143,10 @@ to create its PID file, by default `/var/run/syslogd.pid`.
|
|||||||
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
|
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
|
||||||
service [S12345] /sbin/syslogd -n -- Syslog daemon
|
service [S12345] /sbin/syslogd -n -- Syslog daemon
|
||||||
|
|
||||||
|
Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
|
||||||
|
run in the foreground. With `log` Finit redirects `stdout` + `stderr`
|
||||||
|
to the system log daemon using the command line `logger(1)` tool.
|
||||||
|
|
||||||
A service, or task, can have multiple dependencies listed. Here we wait
|
A service, or task, can have multiple dependencies listed. Here we wait
|
||||||
for *both* `syslogd` to have started and basic networking to be up:
|
for *both* `syslogd` to have started and basic networking to be up:
|
||||||
|
|
||||||
@@ -148,7 +171,7 @@ Start, monitor and restart services should they fail.
|
|||||||
|
|
||||||
**Inetd**
|
**Inetd**
|
||||||
|
|
||||||
Finit comes with a built-in [inetd server](doc/inetd.md). No need to
|
Finit comes with a built-in [inetd server](docs/inetd.md). No need to
|
||||||
maintain a separate config file for services that you want to start on
|
maintain a separate config file for services that you want to start on
|
||||||
demand.
|
demand.
|
||||||
|
|
||||||
@@ -163,7 +186,7 @@ Built-in optional inetd services:
|
|||||||
- discard [RFC863][]
|
- discard [RFC863][]
|
||||||
- time (rdate) [RFC868][]
|
- time (rdate) [RFC868][]
|
||||||
|
|
||||||
For more information, see [doc/inetd.md](doc/inetd.md).
|
For more information, see [docs/inetd.md](docs/inetd.md).
|
||||||
|
|
||||||
|
|
||||||
**Getty**
|
**Getty**
|
||||||
@@ -179,7 +202,18 @@ tty [12345] /dev/ttyAMA0 115200 noclear vt100
|
|||||||
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, see [doc/config.md](doc/config.md#syntax).
|
Users of embedded systems may want to enable automatic serial console
|
||||||
|
with the special `@console` device. This works regardless weather the
|
||||||
|
system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
|
||||||
|
figures it out by querying sysfs: `/sys/class/tty/console/active`.
|
||||||
|
|
||||||
|
```conf
|
||||||
|
tty [12345] @console 115200 linux noclear
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
|
||||||
|
latter is for skipping the login process entirely. For more information,
|
||||||
|
see [docs/config.md](docs/config.md#syntax).
|
||||||
|
|
||||||
|
|
||||||
**Runlevels**
|
**Runlevels**
|
||||||
@@ -188,10 +222,18 @@ Support for SysV init-style [runlevels][5] is available, in the same
|
|||||||
minimal style as everything else in Finit. The `[2345]` syntax can be
|
minimal style as everything else in Finit. The `[2345]` syntax can be
|
||||||
applied to service, task, run, inetd, and TTY stanzas.
|
applied to service, task, run, inetd, and TTY stanzas.
|
||||||
|
|
||||||
All services in runlevel S(1) are started first, followed by the desired
|
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
|
||||||
run-time runlevel. Runlevel S can be started in sequence by using `run
|
SysV init. Runlevel 1 can be configured freely, but is recommended to
|
||||||
[S] cmd`. Changing runlevels at runtime is done like any other init,
|
be kept as the system single-user runlevel since Finit will not start
|
||||||
e.g. <kbd>init 4</kbd>, but also using the more advanced `intictl` tool.
|
networking here. The configured `runlevel NUM` from `/etc/finit.conf`
|
||||||
|
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
|
||||||
|
given on the kernel cmdline, in which case runlevel 1 is started.
|
||||||
|
|
||||||
|
All services in runlevel S(0) are started first, followed by the desired
|
||||||
|
run-time runlevel. Run tasks in runlevel S can be started in sequence
|
||||||
|
by using `run [S] cmd`. Changing runlevels at runtime is done like any
|
||||||
|
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
|
||||||
|
`intictl` tool.
|
||||||
|
|
||||||
|
|
||||||
**Plugins**
|
**Plugins**
|
||||||
@@ -216,25 +258,49 @@ Extensions and functionality not purely related to what an `/sbin/init`
|
|||||||
needs to start a system are available as a set of plugins that either
|
needs to start a system are available as a set of plugins that either
|
||||||
hook into the boot process or respond to various I/O.
|
hook into the boot process or respond to various I/O.
|
||||||
|
|
||||||
For more information, see [doc/plugins.md](doc/plugins.md).
|
For more information, see [docs/plugins.md](docs/plugins.md).
|
||||||
|
|
||||||
|
|
||||||
|
**Progress**
|
||||||
|
|
||||||
|
Finit prior to v3.0 showed the progress at boot: plugins and modules
|
||||||
|
being loaded, run/tasks/services being started, etc. By default Finit
|
||||||
|
is now silent, but progress can still be enabled at build-time using:
|
||||||
|
|
||||||
|
configure --enable-progress
|
||||||
|
|
||||||
|
or at boot from the kernel cmdline using the boot parameter `splash`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
append="init=/sbin/finit splash"
|
||||||
|
```
|
||||||
|
|
||||||
|
For a really nice boot, add `quiet` as well. This silences the output
|
||||||
|
from the kernel, leaving only warnings and errors. For other kernel
|
||||||
|
command line parameters, see [docs/build.md](docs/build.md#recovery).
|
||||||
|
|
||||||
|
|
||||||
Runparts & /etc/rc.local
|
Runparts & /etc/rc.local
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
At the end of the boot, when networking and all services are up, Finit
|
At the end of the boot, when all bootstrap (`S`) tasks and services have
|
||||||
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory,
|
started, but not networking, Finit calls its built-in [run-parts(8)][]
|
||||||
and `/etc/rc.local`, in that order if they exist.
|
command on any configured `runparts <DIR>` directory. This happens just
|
||||||
|
before changing to the configured runlevel (default 2). (Networking is
|
||||||
|
enabled just prior to changing from single user mode.)
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
runparts /etc/rc.d/
|
runparts /etc/rc.d/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Right after the runlevel change when all services have started properly,
|
||||||
|
`/etc/rc.local` is called.
|
||||||
|
|
||||||
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
||||||
If it exists and is an executable shell script, finit calls it at the
|
If it exists and is an executable shell script Finit calls it at the very
|
||||||
very end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on
|
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
|
||||||
hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system
|
in [docs/plugins.md](docs/plugins.md#hooks), and about the system bootstrap
|
||||||
bootstrap in [doc/bootstrap.md](doc/bootstrap.md).
|
in [docs/bootstrap.md](docs/bootstrap.md).
|
||||||
|
|
||||||
|
|
||||||
Runlevels
|
Runlevels
|
||||||
@@ -312,7 +378,7 @@ section.
|
|||||||
For compatibility reasons Finit listens to the same set of signals as
|
For compatibility reasons Finit listens to the same set of signals as
|
||||||
BusyBox init. This is not 100% compatible with SysV init, but clearly
|
BusyBox init. This is not 100% compatible with SysV init, but clearly
|
||||||
the more common combination for Finit. For more details, see
|
the more common combination for Finit. For more details, see
|
||||||
[doc/signals.md](doc/signals.md).
|
[docs/signals.md](docs/signals.md).
|
||||||
|
|
||||||
Finit also listens to the classic SysV init FIFO, used by `telinit`.
|
Finit also listens to the classic SysV init FIFO, used by `telinit`.
|
||||||
Support for this is implemented by the `initctl.so` plugin. Hence,
|
Support for this is implemented by the `initctl.so` plugin. Hence,
|
||||||
@@ -346,33 +412,46 @@ not return until the given command has fully completed.
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
~ $ initctl -h
|
~ $ initctl -h
|
||||||
Usage: initctl [OPTIONS] <COMMAND>
|
Usage: initctl [OPTIONS] [COMMAND]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-d, --debug Debug initctl (client)
|
|
||||||
-v, --verbose Verbose output
|
-v, --verbose Verbose output
|
||||||
-h, --help This help text
|
-h, --help This help text
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
debug Toggle Finit (daemon) debug
|
debug Toggle Finit (daemon) debug
|
||||||
help This help text
|
help This help text
|
||||||
reload Reload *.conf in /etc/finit.d/ and activate changes
|
version Show Finit version
|
||||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
|
||||||
status | show Show status of services
|
list List all .conf in /etc/finit.d/
|
||||||
|
enable <CONF> Enable .conf in /etc/finit.d/available/
|
||||||
|
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
|
||||||
|
touch <CONF> Mark .conf in /etc/finit.d/ for reload
|
||||||
|
reload Reload *.conf in /etc/finit.d/ (activates changes)
|
||||||
|
|
||||||
cond set <COND> Set (assert) condition => +COND
|
cond set <COND> Set (assert) condition => +COND
|
||||||
cond clear <COND> Clear (deassert) condition => -COND
|
cond clear <COND> Clear (deassert) condition => -COND
|
||||||
cond show Show condition status
|
cond show Show condition status
|
||||||
cond dump Dump all conditions and their status
|
cond dump Dump all conditions and their status
|
||||||
|
|
||||||
|
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||||
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
||||||
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
||||||
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
||||||
reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name
|
status <JOB|NAME>[:ID] Show service status, by job# or name
|
||||||
version Show Finit version
|
status | show Show status of services, default command
|
||||||
|
|
||||||
|
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||||
|
reboot Reboot system
|
||||||
|
halt Halt system
|
||||||
|
poweroff Halt and power off system
|
||||||
|
|
||||||
|
utmp show Raw dump of UTMP/WTMP db
|
||||||
```
|
```
|
||||||
|
|
||||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||||
file must be used to tell Finit to stop and start it on `reload` and
|
file must be used to tell Finit to stop and start it on `reload` and
|
||||||
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
|
`runlevel` changes. If `<>` holds more [conditions](docs/conditions.md),
|
||||||
these will also affect how a service is maintained.
|
these will also affect how a service is maintained.
|
||||||
|
|
||||||
**Note:** even though it is possible to start services not belonging in
|
**Note:** even though it is possible to start services not belonging in
|
||||||
@@ -398,11 +477,13 @@ Requirements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
Finit is capable of running on both desktop/server systems with udev and
|
Finit is capable of running on both desktop/server systems with udev and
|
||||||
embedded systems that usually come with BusyBox mdev. Finit probes for
|
embedded systems that usually come with BusyBox mdev. Some systems have
|
||||||
them at runtime and expects `/dev/` to be writable, usually `devtmpfs`.
|
systemd-udev or eudev today instead of the original udev, Finit probes
|
||||||
It is also possible to run on a statically set up `/dev` if needed. It
|
for all of them at runtime and expects `/dev/` to be a writable file
|
||||||
is however not a good idea to have both udev and mdev installed at the
|
system using `devtmpfs`. It is also possible to run on a statically set
|
||||||
same time, this will lead to unpredictable results.
|
up `/dev` if needed. It is however not a good idea to have both udev
|
||||||
|
and mdev installed at the same time, this will lead to unpredictable
|
||||||
|
results.
|
||||||
|
|
||||||
At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is
|
At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is
|
||||||
done slightly differently and on systems with udev you might want to add
|
done slightly differently and on systems with udev you might want to add
|
||||||
|
|||||||
+46
-10
@@ -1,10 +1,17 @@
|
|||||||
AC_INIT(Finit, 3.0, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
AC_INIT(Finit, 3.2-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
|
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
|
|
||||||
AC_CONFIG_SRCDIR([src/finit.c])
|
AC_CONFIG_SRCDIR([src/finit.c])
|
||||||
AC_CONFIG_HEADER([config.h])
|
AC_CONFIG_HEADER([config.h])
|
||||||
AC_CONFIG_FILES([Makefile plugins/Makefile src/Makefile])
|
AC_CONFIG_FILES([Makefile
|
||||||
|
contrib/Makefile
|
||||||
|
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
|
||||||
|
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
|
||||||
|
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
|
||||||
|
docs/Makefile
|
||||||
|
plugins/Makefile
|
||||||
|
src/Makefile])
|
||||||
|
|
||||||
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
|
||||||
#m4_include([m4/plugin.m4])
|
#m4_include([m4/plugin.m4])
|
||||||
@@ -21,6 +28,7 @@ LT_INIT([shared disable-static dlopen aix-soname=both disable-fast-install])
|
|||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
AC_PROG_LN_S
|
AC_PROG_LN_S
|
||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
|
AC_PROG_MKDIR_P
|
||||||
|
|
||||||
# Configuration.
|
# Configuration.
|
||||||
AC_HEADER_STDC
|
AC_HEADER_STDC
|
||||||
@@ -33,8 +41,8 @@ AC_TYPE_UINT16_T
|
|||||||
AC_TYPE_UINT32_T
|
AC_TYPE_UINT32_T
|
||||||
|
|
||||||
# Check for required libraries
|
# Check for required libraries
|
||||||
PKG_CHECK_MODULES([uev], [libuev >= 1.5.2])
|
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
||||||
PKG_CHECK_MODULES([lite], [libite >= 1.9.2])
|
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
|
||||||
|
|
||||||
# Check for configured Finit features
|
# Check for configured Finit features
|
||||||
AC_ARG_ENABLE(emergency_shell,
|
AC_ARG_ENABLE(emergency_shell,
|
||||||
@@ -58,6 +66,10 @@ AC_ARG_ENABLE(watchdog,
|
|||||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
||||||
enable_watchdog=no])
|
enable_watchdog=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(redirect,
|
||||||
|
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
|
||||||
|
enable_redirect_output=no])
|
||||||
|
|
||||||
AC_ARG_ENABLE(inetd,
|
AC_ARG_ENABLE(inetd,
|
||||||
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
|
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
|
||||||
enable_inetd=yes])
|
enable_inetd=yes])
|
||||||
@@ -66,6 +78,14 @@ AC_ARG_ENABLE(logrotate,
|
|||||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
||||||
enable_logrotate=yes])
|
enable_logrotate=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(docs,
|
||||||
|
AS_HELP_STRING([--disable-docs], [Disable build and install of docs section]),,[
|
||||||
|
enable_docs=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(contrib,
|
||||||
|
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
|
||||||
|
enable_contrib=yes])
|
||||||
|
|
||||||
# Check for extra plugins to enable
|
# Check for extra plugins to enable
|
||||||
enable_all_plugins=auto
|
enable_all_plugins=auto
|
||||||
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
||||||
@@ -75,7 +95,7 @@ AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
|
|||||||
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
|
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
|
||||||
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
|
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
|
||||||
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
|
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
|
||||||
AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!])
|
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
|
||||||
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
||||||
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
|
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
|
||||||
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
|
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
|
||||||
@@ -89,11 +109,6 @@ AC_ARG_WITH(fifo,
|
|||||||
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
|
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
|
||||||
[fifo=$withval], [fifo=$localstatedir/run/initctl])
|
[fifo=$withval], [fifo=$localstatedir/run/initctl])
|
||||||
|
|
||||||
AC_ARG_WITH(plugindir,
|
|
||||||
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
|
|
||||||
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
|
|
||||||
AC_SUBST([pkglibdir])
|
|
||||||
|
|
||||||
AC_ARG_WITH(config,
|
AC_ARG_WITH(config,
|
||||||
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
|
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
|
||||||
[conf=$withval], [conf=$sysconfdir/finit.conf])
|
[conf=$withval], [conf=$sysconfdir/finit.conf])
|
||||||
@@ -143,6 +158,9 @@ AS_IF([test "x$enable_progress" = "xyes"], [
|
|||||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
||||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
||||||
|
|
||||||
|
AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||||
|
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||||
|
|
||||||
### Disable features ###########################################################################
|
### Disable features ###########################################################################
|
||||||
AS_IF([test "x$enable_inetd" != "xno"], [
|
AS_IF([test "x$enable_inetd" != "xno"], [
|
||||||
enable_inetd="yes"
|
enable_inetd="yes"
|
||||||
@@ -206,6 +224,21 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
|||||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||||
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
||||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||||
|
AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"])
|
||||||
|
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||||
|
|
||||||
|
# Override default libdir, used for plugins and rescue.conf
|
||||||
|
pkglibdir=$libdir/finit
|
||||||
|
AC_SUBST(pkglibexecdir)
|
||||||
|
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
|
||||||
|
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
|
||||||
|
AC_SUBST(plugin_path)
|
||||||
|
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
|
||||||
|
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
||||||
|
|
||||||
# Generate all files
|
# Generate all files
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
@@ -220,6 +253,7 @@ cat <<EOF
|
|||||||
FIFO path.............: $fifo_path
|
FIFO path.............: $fifo_path
|
||||||
Finit config file.....: $conf_path
|
Finit config file.....: $conf_path
|
||||||
Finit config.d path...: $rcsd_path
|
Finit config.d path...: $rcsd_path
|
||||||
|
Finit plugin path.....: $plugin_path
|
||||||
Compat rc.local path..: $rclocal_path
|
Compat rc.local path..: $rclocal_path
|
||||||
Random seed path......: $random_path
|
Random seed path......: $random_path
|
||||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||||
@@ -230,6 +264,8 @@ Behavior:
|
|||||||
Plugins...............: $plugins
|
Plugins...............: $plugins
|
||||||
|
|
||||||
Optional features:
|
Optional features:
|
||||||
|
Install docs/.........: $enable_docs
|
||||||
|
Install contrib/......: $enable_contrib
|
||||||
Built-in inetd........: $enable_inetd
|
Built-in inetd........: $enable_inetd
|
||||||
Built-in watchdogd....: $enable_watchdog
|
Built-in watchdogd....: $enable_watchdog
|
||||||
Built-in logrotate....: $enable_logrotate
|
Built-in logrotate....: $enable_logrotate
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
SUBDIRS = alpine debian void
|
||||||
|
bin_SCRIPT = service
|
||||||
|
docsdir := @docdir@/contrib
|
||||||
|
docs_DATA = README.md finit.conf procmon.sh
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
SUBDIRS = finit.d
|
||||||
|
docsdir := @docdir@/contrib/alpine
|
||||||
|
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
SUBDIRS = available
|
||||||
|
docsdir := @docdir@/contrib/alpine/finit.d
|
||||||
|
d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
|
||||||
|
syslogd.conf
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ -L $$lnk ]; then \
|
||||||
|
echo "Skipping $$lnk exists."; \
|
||||||
|
else \
|
||||||
|
$(LN_S) $$src $$lnk; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
|
$(RM) $$lnk; \
|
||||||
|
done
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||||
|
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||||
|
lxdm.conf modules.conf ntpd.conf klogd.conf syslogd.conf
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SUBDIRS = finit.d grub.d
|
||||||
|
docsdir := @docdir@/contrib/debian
|
||||||
|
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
SUBDIRS = available
|
||||||
|
docsdir := @docdir@/contrib/debian/finit.d
|
||||||
|
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
|
||||||
|
modules.conf rsyslogd.conf sshd.conf
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ -L $$lnk ]; then \
|
||||||
|
echo "Skipping $$lnk exists."; \
|
||||||
|
else \
|
||||||
|
$(LN_S) $$src $$lnk; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
|
$(RM) $$lnk; \
|
||||||
|
done
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
docsdir := @docdir@/contrib/debian/finit.d/available
|
||||||
|
docs_DATA = acpid.conf console-setup.conf getty.conf \
|
||||||
|
keyboard-setup.conf lightdm.conf modules.conf \
|
||||||
|
ntpclient.conf rsyslogd.conf sshd.conf xdm.conf
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
docsdir := @docdir@/contrib/debian/grub.d
|
||||||
|
docs_DATA = 40_custom
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -17,6 +17,10 @@ runlevel 2
|
|||||||
# Network bringup script
|
# Network bringup script
|
||||||
network service networking start
|
network service networking start
|
||||||
|
|
||||||
|
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||||
|
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||||
|
log size=100k count=4
|
||||||
|
|
||||||
# Virtual consoles to start getty on
|
# Virtual consoles to start getty on
|
||||||
tty /dev/tty1
|
tty /dev/tty1
|
||||||
tty /dev/tty2
|
tty /dev/tty2
|
||||||
@@ -33,6 +37,13 @@ service [2345] /sbin/klogd -n -- Kernel logging server
|
|||||||
service [2345] /sbin/syslogd -n -- Syslog server
|
service [2345] /sbin/syslogd -n -- Syslog server
|
||||||
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
||||||
|
|
||||||
|
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||||
|
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||||
|
# log file is rotated with the above settings. The condition declares
|
||||||
|
# a dependency on a system default route (gateway) to be set. A single
|
||||||
|
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||||
|
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
|
||||||
|
|
||||||
# Run start scripts from this directory
|
# Run start scripts from this directory
|
||||||
# runparts /etc/start.d
|
# runparts /etc/start.d
|
||||||
|
|
||||||
|
|||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
USAGE="Usage: $0 processName"
|
||||||
|
LOG_FILE="/tmp/memusage.csv"
|
||||||
|
ELAPSED_TIME=0
|
||||||
|
PERIOD=1 # seconds
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo $USAGE
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "ElapsedTime,VmSize,VmRSS" > $LOG_FILE
|
||||||
|
|
||||||
|
# Monitor memory usage forever until script is killed
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
SUM_VM_SIZE=0
|
||||||
|
SUM_RSS_SIZE=0
|
||||||
|
# In case the monitored process has not yet started
|
||||||
|
# keep searching until its PID is found
|
||||||
|
PROCESS_PIDS=""
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
PROCESS_PIDS=`/bin/pidof $1`
|
||||||
|
if [ "$PROCESS_PIDS.X" != ".X" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for PID in ${PROCESS_PIDS} ; do
|
||||||
|
VM_SIZE=`awk '/VmSize/ {print $2}' < /proc/$PID/status`
|
||||||
|
if [ "$VM_SIZE.X" = ".X" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
#echo exprVM_ $SUM_VM_SIZE + $VM_SIZE
|
||||||
|
SUM_VM_SIZE=`expr $SUM_VM_SIZE + $VM_SIZE`
|
||||||
|
|
||||||
|
VM_RSS=`awk '/VmRSS/ {print $2}' < /proc/$PID/status`
|
||||||
|
if [ "$VM_RSS.X" = ".X" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
SUM_RSS_SIZE=`expr $SUM_RSS_SIZE + $VM_RSS`
|
||||||
|
done
|
||||||
|
echo "$ELAPSED_TIME sec, $SUM_VM_SIZE KB, $SUM_RSS_SIZE KB"
|
||||||
|
echo "$ELAPSED_TIME,$SUM_VM_SIZE,$SUM_RSS_SIZE" >> $LOG_FILE
|
||||||
|
sleep $PERIOD
|
||||||
|
VM_SIZE=""
|
||||||
|
VM_RSS=""
|
||||||
|
# Needs to get actual elapsed time instead of doing this
|
||||||
|
ELAPSED_TIME=`expr $ELAPSED_TIME + $PERIOD`
|
||||||
|
done
|
||||||
Executable
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
JOB=$1
|
||||||
|
CMD=$2
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: service [job# | name] <stop | start | restart | status | show>"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
initctl show
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $CMD in
|
||||||
|
stop)
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
;;
|
||||||
|
show)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
initctl $2 $1
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SUBDIRS = finit.d
|
||||||
|
docsdir := @docdir@/contrib/void
|
||||||
|
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
SUBDIRS = available
|
||||||
|
docsdir := @docdir@/contrib/void/finit.d
|
||||||
|
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ -L $$lnk ]; then \
|
||||||
|
echo "Skipping $$lnk exists."; \
|
||||||
|
else \
|
||||||
|
$(LN_S) $$src $$lnk; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
|
$(RM) $$lnk; \
|
||||||
|
done
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
docsdir := @docdir@/contrib/void/finit.d/available
|
||||||
|
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
||||||
|
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
|
||||||
|
sshd.conf syslogd.conf uuidd.conf
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
-281
@@ -1,281 +0,0 @@
|
|||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
|
||||||
* [Syntax](#syntax)
|
|
||||||
* [Limitations](#limitations)
|
|
||||||
* [/etc/finit.conf](#etcfinitconf)
|
|
||||||
* [/etc/finit.d](#etcfinitd)
|
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
Finit can be configured using only the original `/etc/finit.conf` file
|
|
||||||
or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a
|
|
||||||
system using only /etc/finit.d/*.conf`, highly useful for package-based
|
|
||||||
Linux distributions -- each package can provide its own "script" file.
|
|
||||||
|
|
||||||
- `/etc/finit.conf`: main configuration file, read only once at boot
|
|
||||||
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
|
||||||
|
|
||||||
Not all configuration directives are available in `/etc/finit.d/*.conf`
|
|
||||||
and some directives are only available at bootstrap, runlevel `S`, see
|
|
||||||
the section [Limitations](#limitations) below for details.
|
|
||||||
|
|
||||||
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
|
|
||||||
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
|
|
||||||
call `finit q`, but that has been deprecated with the `initctl` tool).
|
|
||||||
Any service read from this directory is flagged as a dynamic service, so
|
|
||||||
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
|
|
||||||
|
|
||||||
On `initctl reload` the following is checked for all services:
|
|
||||||
|
|
||||||
- If a service's `.conf` file has been removed, or its conditions are no
|
|
||||||
longer satisifed, the service is stopped.
|
|
||||||
- If the file is modified, or a service it depends on has been reloaded,
|
|
||||||
the service is reloaded (stopped and started).
|
|
||||||
- If a new service is added it is automatically started — respecting
|
|
||||||
runlevels and return values from any callbacks.
|
|
||||||
|
|
||||||
For more info on the different states of a service, see the separate
|
|
||||||
document [Finit Services](service.md).
|
|
||||||
|
|
||||||
|
|
||||||
Syntax
|
|
||||||
------
|
|
||||||
|
|
||||||
* `module <MODULE>`
|
|
||||||
Load a kernel module, with optional arguments
|
|
||||||
|
|
||||||
* `network <PATH>`
|
|
||||||
Script or program to bring up networking, with optional arguments
|
|
||||||
|
|
||||||
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>`
|
|
||||||
Modify the specified resource's hard or soft limit to be the
|
|
||||||
specifed limit. `RESOURCE` is a lower-case string matching the
|
|
||||||
constants in `setrlimit(2)` with the `RLIMIT_` prefix
|
|
||||||
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
|
|
||||||
`cpu`. `LIMIT` is an integer whose unit depends on the resource
|
|
||||||
being modified, see `setrlimit(2)` for more information. The special
|
|
||||||
limit `infinity` means the there should be no limit on the resource,
|
|
||||||
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# No process is allowed more than 8MB of address space
|
|
||||||
rlimit hard as 8388608
|
|
||||||
|
|
||||||
# Core dumps may be arbitrarily large
|
|
||||||
rlimit soft core infinity
|
|
||||||
```
|
|
||||||
|
|
||||||
* `runlevel <N>`
|
|
||||||
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
|
||||||
Default is 2.
|
|
||||||
|
|
||||||
* `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
|
||||||
One-shot command to run in sequence when entering a runlevel, with
|
|
||||||
optional arguments and description.
|
|
||||||
|
|
||||||
`run` commands are guaranteed to be completed before running the next
|
|
||||||
command. Highly useful if true serialization is needed.
|
|
||||||
|
|
||||||
* `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
|
||||||
One-shot like 'run', but starts in parallel with the next command.
|
|
||||||
|
|
||||||
Both `run` and `task` commands are run in a shell, so pipes and
|
|
||||||
redirects can be freely used:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
task [s] echo "foo" | cat >/tmp/bar
|
|
||||||
```
|
|
||||||
|
|
||||||
* `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
|
||||||
Service, or daemon, to be monitored and automatically restarted if it
|
|
||||||
exits prematurely. Please note that you often need to provide a
|
|
||||||
`--foreground` or `--no-background` argument to most daemons to
|
|
||||||
prevent them from forking off a sub-process in the background.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
|
|
||||||
```
|
|
||||||
|
|
||||||
The `[2345]` is the runlevels `ospfd` is allowed to run in, they are
|
|
||||||
optional and default to level 2-5 if left out.
|
|
||||||
|
|
||||||
The `<...>` is the condition for starting `ospfd`. In this example
|
|
||||||
Finit waits for another service, `/sbin/zebra`, to have created its
|
|
||||||
PID file in `/var/run/zebra.pid` before starting `ospfd`.
|
|
||||||
|
|
||||||
For a detailed description of conditions, and how to debug them,
|
|
||||||
see the [Finit Conditions](conditions.md) document.
|
|
||||||
|
|
||||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
|
||||||
Launch a daemon when a client initiates a connection on an Internet
|
|
||||||
port. Available services are listed in the UNIX `/etc/services` file.
|
|
||||||
Finit can filter access to from a list of interfaces, `@iflist`, per
|
|
||||||
inetd service as well as listen to custom ports.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
inetd ftp/tcp nowait @root /usr/sbin/uftpd -i -f
|
|
||||||
inetd tftp/udp wait @root /usr/sbin/uftpd -i -t
|
|
||||||
```
|
|
||||||
|
|
||||||
The following example listens to port 2323 for telnet connections and
|
|
||||||
only allows clients connecting from `eth0`:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
inetd 2323/tcp@eth0 nowait [2345] /sbin/telnetd -i -F
|
|
||||||
```
|
|
||||||
|
|
||||||
The interface list, `@iflist`, is of the format `@iface,!iface,iface`,
|
|
||||||
where a single `!` means to deny access. Notice how interfaces are
|
|
||||||
comma separated with no spaces.
|
|
||||||
|
|
||||||
The `inetd` directive can also have ` -- Optional Description`, only
|
|
||||||
Finit does not output this text on the console when launching inetd
|
|
||||||
services. Instead this text is sent to syslog and also shown by the
|
|
||||||
`initctl` tool. More on inetd below.
|
|
||||||
|
|
||||||
* `runparts <DIR>`
|
|
||||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
|
||||||
files, or scripts, in the directory are called, in alphabetic order.
|
|
||||||
|
|
||||||
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
|
||||||
is a dependency order between the scripts. Symlinks to existing
|
|
||||||
daemons can talso be used, but make sure they daemonize by default.
|
|
||||||
|
|
||||||
As the optional `/etc/rc.local` shell script, make sure that all your
|
|
||||||
services and programs either terminate or start in the background or
|
|
||||||
you will block Finit.
|
|
||||||
|
|
||||||
* `include <CONF>`
|
|
||||||
Include another configuration file. Absolute path required.
|
|
||||||
|
|
||||||
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [TERM]`
|
|
||||||
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
|
||||||
The first variant of this option uses the built-in getty on the given
|
|
||||||
TTY device DEV, in the given runlevels. Default baud rate is `38400`.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```conf
|
|
||||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
|
||||||
```
|
|
||||||
|
|
||||||
The second variant is for using an external getty, like agetty or the
|
|
||||||
BusyBox getty.
|
|
||||||
|
|
||||||
By default both variants *clear* the TTY and *wait* for the user to
|
|
||||||
press enter before starting getty.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```conf
|
|
||||||
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
|
||||||
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
|
|
||||||
```
|
|
||||||
|
|
||||||
The `noclear` option disables clearing the TTY after each session.
|
|
||||||
Clearing the TTY when a user logs out is usually preferable.
|
|
||||||
|
|
||||||
The `nowait` option disables the `press Enter to activate console`
|
|
||||||
message before actually starting the getty program. On small and
|
|
||||||
embedded systems running multiple unused getty wastes both memory
|
|
||||||
and CPU cycles, so `wait` is the preferred default.
|
|
||||||
|
|
||||||
Notice the ordering, the `TERM` option to the built-in getty must be
|
|
||||||
the last argument.
|
|
||||||
|
|
||||||
On really bare bones systems Finit offers a fallback shell:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
configure --enable-fallback-shell
|
|
||||||
```
|
|
||||||
|
|
||||||
One can also use the `service` stanza to start a stand-alone shell:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
service [12345] /bin/sh -l
|
|
||||||
```
|
|
||||||
|
|
||||||
* `console <DEV>`
|
|
||||||
Use this, along with a matching `tty DEV` line, to mark this TTY as a
|
|
||||||
special "console" port with `prctl()`. Useful in use-cases when
|
|
||||||
all logins except the console must be stopped, e.g. when performing a
|
|
||||||
flash upgrade on an embedded system.
|
|
||||||
|
|
||||||
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
|
|
||||||
be installed since system requirements differ too much. Try out the
|
|
||||||
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
|
||||||
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
|
|
||||||
|
|
||||||
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
|
||||||
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
|
||||||
`[@USR[:GRP]]` like this:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
run [2345] @joe:users /usr/bin/logger "Hello world"
|
|
||||||
```
|
|
||||||
|
|
||||||
For multiple instances of the same command, e.g. a DHCP client or
|
|
||||||
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
|
||||||
`service` keyword and the command, like this:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
service :1 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
|
|
||||||
service :2 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
|
|
||||||
```
|
|
||||||
|
|
||||||
Without the `:ID` to the service the latter will overwrite the former
|
|
||||||
and only the old web server would be started and supervised.
|
|
||||||
|
|
||||||
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
|
|
||||||
`log` to redirect `stderr` and `stdout` of the application to syslog,
|
|
||||||
using `logger`.
|
|
||||||
|
|
||||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
|
||||||
detailed description, see the [Conditions](conditions.md) document.
|
|
||||||
|
|
||||||
|
|
||||||
Limitations
|
|
||||||
-----------
|
|
||||||
|
|
||||||
To understand the limitations of `finit.conf` vs `finit.d` it is useful
|
|
||||||
to picture the different phases of the system: bootstrap, runtime, and
|
|
||||||
shutdown. The `finit.conf` file can be used for all possible settings,
|
|
||||||
but `finit.d/*.conf` can only be used for runtime settings.
|
|
||||||
|
|
||||||
### /etc/finit.conf
|
|
||||||
|
|
||||||
This file was the only way to set up and boot a system using Finit. It
|
|
||||||
is used for pre-runtime settings like system hostname, network bringup
|
|
||||||
and shutdown:
|
|
||||||
|
|
||||||
- `host`
|
|
||||||
- `mkdod`
|
|
||||||
- `network`
|
|
||||||
- `runparts`
|
|
||||||
- `include`
|
|
||||||
- `shtudown`
|
|
||||||
- `runlevel`
|
|
||||||
- ... and all configuration stanzas from `/etc/finit.d` below
|
|
||||||
|
|
||||||
### /etc/finit.d
|
|
||||||
|
|
||||||
Support for partial `.conf` files in `/etc/finit.d` was added to handle
|
|
||||||
changes of the system configuration at runtime. It does *not support*
|
|
||||||
the above `finit.conf` settings described above, only the following:
|
|
||||||
|
|
||||||
- `module`, but only in runlevel `S`
|
|
||||||
- `service`
|
|
||||||
- `task`
|
|
||||||
- `run`
|
|
||||||
- `inetd`
|
|
||||||
- `rlimit`
|
|
||||||
- `tty`
|
|
||||||
- `console`
|
|
||||||
|
|
||||||
**NOTE:*** The `/etc/finit.d` directory was previously the default Finit
|
|
||||||
`runparts` directory. Finit no longer has a default `runparts`, make
|
|
||||||
sure to update your setup, or the finit configuration, accordingly.
|
|
||||||
|
|
||||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,4 @@
|
|||||||
|
docsdir := @docdir@/docs
|
||||||
|
docs_DATA = config.md conditions.md distro.md inetd.md plugins.md \
|
||||||
|
service.md signals.md svc-machine.png cmdline.md
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
config.md
|
||||||
+28
-41
@@ -18,32 +18,27 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
|
|||||||
can also be used.
|
can also be used.
|
||||||
|
|
||||||
|
|
||||||
General
|
Near Future
|
||||||
-------
|
-----------
|
||||||
|
|
||||||
* Remove shm, let `initctl` call `api.c` for each `svc_t` instead of
|
* Merge UDP redirect service uredir as an UDP inetd variant
|
||||||
opening up the inner parts of PID 1 to other processes. Also, this
|
|
||||||
would prevent issues like #81 from happening.
|
|
||||||
* Add support for timed shutdown in Finit, including cancelled shutdown.
|
|
||||||
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
|
|
||||||
API restart to SIGHUP callback for the same functionality in Finit.
|
|
||||||
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
|
||||||
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
|
||||||
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
|
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
|
||||||
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
||||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||||
* Add `IFF_RUNNING` support to netlink plugin along `IFF_UP`
|
* Cron/At support, see below
|
||||||
* Allow scripts, instead of C code, as process callbacks
|
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||||
* Add support for a pipe/popen where we can listen for `STOP`, `START`
|
`pimd` man pages ...
|
||||||
and `RELOAD` on `STDOUT` from a script ... call it for example
|
|
||||||
`.monitor=` in `svc_t` ... the script named as the basename of the
|
|
||||||
service it monitors + `.sh`.
|
General
|
||||||
* Implement `initctl stop|start|restart|reload|status <SVC>` and
|
-------
|
||||||
`service <SVC> stop|start|restart|reload|status` on top
|
|
||||||
|
* Add support for timed shutdown in Finit, including cancelled shutdown,
|
||||||
|
possibly using an at-job, see [crond section](#Crond) below
|
||||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||||
`initctl show --json`
|
`initctl show --json`
|
||||||
* Improve `initctl show SVC` output. Add service uptime, last ten SVC
|
|
||||||
logs/history (historian), etc. Similar to `systemctl show SVC`
|
|
||||||
* [Solaris SMF][] (Service Management Facility) has some interesting
|
* [Solaris SMF][] (Service Management Facility) has some interesting
|
||||||
features that may be well worth looking into adopting:
|
features that may be well worth looking into adopting:
|
||||||
|
|
||||||
@@ -54,6 +49,15 @@ General
|
|||||||
* `initctl add /sbin/service -n -- Service description`, for details
|
* `initctl add /sbin/service -n -- Service description`, for details
|
||||||
see above [Solaris SMF][] featuer, issue #55 and issue #69,
|
see above [Solaris SMF][] featuer, issue #55 and issue #69,
|
||||||
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
|
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
|
||||||
|
* Changing the name of a pidfile, `pid:[/path/]file.pid`, after a
|
||||||
|
service has been started is not supported atm. The previous name will
|
||||||
|
not be (never) removed and the new name will not be created until the
|
||||||
|
process has been stopped and started again.
|
||||||
|
* PID files created in subdirectories to `/run` is not supported right
|
||||||
|
now inotify listeners should be automatically created (and removed)
|
||||||
|
when a subdirectory is added (or removed).
|
||||||
|
|
||||||
|
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
|
||||||
|
|
||||||
|
|
||||||
Init
|
Init
|
||||||
@@ -90,14 +94,16 @@ system-level timed periodic tasks:
|
|||||||
Proposed syntax:
|
Proposed syntax:
|
||||||
|
|
||||||
# Crontab
|
# Crontab
|
||||||
cron @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
cron @SPEC [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||||
|
|
||||||
# One-shot 'at' command
|
# One-shot 'at' command
|
||||||
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
|
||||||
|
|
||||||
Notice the ISO date in notation. The runlevels is extra filtering
|
Notice the ISO date in the `at` notation. The `cron @SPEC` could use
|
||||||
sugar. E.g., if cron service is only allowed in runlevels two or three
|
are mo loose notation, e.g. `@hourly`, `@midnight`, `@daily`, `@weekly`,
|
||||||
it will not start if system currently is in runlevel four.
|
`@reboot`, etc. The runlevels is extra filtering sugar. E.g., if cron
|
||||||
|
service is only allowed in runlevels two or three it will not start if
|
||||||
|
system currently is in runlevel four.
|
||||||
|
|
||||||
To run a command as another user the `.conf` file must have a that
|
To run a command as another user the `.conf` file must have a that
|
||||||
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
|
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
|
||||||
@@ -108,22 +114,3 @@ could support the basic functionality of the `at` command directly from
|
|||||||
the shell -- that way setting up one-time jobs would not entail
|
the shell -- that way setting up one-time jobs would not entail
|
||||||
re-reading `/etc/finit.conf`
|
re-reading `/etc/finit.conf`
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
|
||||||
`pimd` man pages ...
|
|
||||||
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
|
|
||||||
how close Finit3 still is to the original easy-to-use Finit0 :)
|
|
||||||
|
|
||||||
|
|
||||||
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
|
|
||||||
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
|
|
||||||
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
|
|
||||||
|
|
||||||
<!--
|
|
||||||
-- Local Variables:
|
|
||||||
-- mode: markdown
|
|
||||||
-- End:
|
|
||||||
-->
|
|
||||||
@@ -5,7 +5,7 @@ Bootstrap
|
|||||||
2. Set up initial signal handlers
|
2. Set up initial signal handlers
|
||||||
3. Set up default $PATH early
|
3. Set up default $PATH early
|
||||||
4. Mount `/proc` and `/sys`
|
4. Mount `/proc` and `/sys`
|
||||||
5. Check kernel command line for `--debug` to figure out log level
|
5. Check kernel command line for `debug` to figure out log level
|
||||||
6. Load all `.so` plugins
|
6. Load all `.so` plugins
|
||||||
7. Call `fsck` on file systems listed in `/etc/fstab`
|
7. Call `fsck` on file systems listed in `/etc/fstab`
|
||||||
8. Populate `/dev` using either udev or mdev, depending on system type
|
8. Populate `/dev` using either udev or mdev, depending on system type
|
||||||
@@ -25,13 +25,13 @@ Bootstrap
|
|||||||
20. Bring up loopback interface and all `/etc/network/interfaces`, if
|
20. Bring up loopback interface and all `/etc/network/interfaces`, if
|
||||||
the `.conf` setting `network <SCRIPT>` is set, it is called instead
|
the `.conf` setting `network <SCRIPT>` is set, it is called instead
|
||||||
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
||||||
22. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||||
|
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
||||||
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
||||||
(re)loaded and new services, tasks, and blocking run commands are
|
(re)loaded and new services, tasks, and blocking run commands are
|
||||||
started. Provided they are allowed in the new runlevel and all of
|
started. Provided they are allowed in the new runlevel and all of
|
||||||
their conditions, if any, are set.
|
their conditions, if any, are set.
|
||||||
23. Call 4th level hooks, `HOOK_SVC_UP`
|
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||||
24. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
|
||||||
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||||
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||||
27. Start all configured TTYs, or a fallback shell on `/dev/console`
|
27. Start all configured TTYs, or a fallback shell on `/dev/console`
|
||||||
@@ -26,6 +26,12 @@ optional plugins to enable. It depends on two external libraries:
|
|||||||
|
|
||||||
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
|
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
|
||||||
|
|
||||||
|
The configure script checks for all dependencies, including the correct
|
||||||
|
version of the above mentioned libraries. Currently requried versions:
|
||||||
|
|
||||||
|
- libite v2.0.1
|
||||||
|
- libuEv v2.1.0
|
||||||
|
|
||||||
|
|
||||||
Configure
|
Configure
|
||||||
---------
|
---------
|
||||||
@@ -47,8 +53,6 @@ Below are a few of the main switches to configure:
|
|||||||
|
|
||||||
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
|
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
|
||||||
|
|
||||||
* `--enable-lost-plugin`: Enable noisy example plugin for `HOOK_SVC_LOST`.
|
|
||||||
|
|
||||||
* `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
|
* `--enable-resolvconf-plugin`: Enable the `resolvconf.so` optional plugin.
|
||||||
|
|
||||||
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
|
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
|
||||||
@@ -118,14 +122,32 @@ append="init=/sbin/finit"
|
|||||||
Remember to also set up an initial `/etc/finit.conf` before rebooting!
|
Remember to also set up an initial `/etc/finit.conf` before rebooting!
|
||||||
|
|
||||||
|
|
||||||
|
Recovery
|
||||||
|
--------
|
||||||
|
|
||||||
|
To rescue a system with Finit, append the following to the kernel
|
||||||
|
command line:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
append="init=/sbin/finit rescue"
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells Finit to start in a very limited recovery mode, no services
|
||||||
|
are loaded, no filesystems are mounted or checked, and no networking is
|
||||||
|
enabled. The default Finit rescue mode configuration is installed into
|
||||||
|
`/lib/finit/rescue.conf`, which can be safely removed or changed.
|
||||||
|
|
||||||
|
By default the a root shell, without login, is started.
|
||||||
|
|
||||||
|
|
||||||
Debugging
|
Debugging
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Add `finit_debug`, or `--debug`, to the kernel command line to enable
|
Edit, or append to, the kernel command line: remove `quiet` to enable
|
||||||
debug messages.
|
kernel messages and add `debug` to enable Finit debug messages.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
append="init=/sbin/finit --debug"
|
append="init=/sbin/finit debug"
|
||||||
```
|
```
|
||||||
|
|
||||||
To debug startup issues, in particular issues with getty/login, try
|
To debug startup issues, in particular issues with getty/login, try
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
Tips & Tricks with the kernel cmdline
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
This document summarizes the different boot parameters that can be
|
||||||
|
passed on the Linux kernel command line. Not just limited to Finit.
|
||||||
|
|
||||||
|
* `debug`, or `--debug`
|
||||||
|
Enable debug mode. The former syntax also enable kernel debug, and
|
||||||
|
the latter only in Finit. Debug messages are printed to the console
|
||||||
|
until a syslog daemon has been started.
|
||||||
|
|
||||||
|
* `init=/bin/sh`
|
||||||
|
Bypass system default init and tell kernel to start a shell. Note,
|
||||||
|
this shell is very limited and does not support signals and has no
|
||||||
|
job control. Recommend using, and modifying, `rescue` mode instead.
|
||||||
|
|
||||||
|
* `panic=SEC`
|
||||||
|
By default the kernel does not reboot after a kernel panic. This
|
||||||
|
setting will cause a kernel reboot after SEC seconds.
|
||||||
|
|
||||||
|
* `quiet`
|
||||||
|
Suppress kernel logging to console, except for warnings and errors.
|
||||||
|
|
||||||
|
* `rescue`
|
||||||
|
Start Finit rescue mode; no network, no `fsck` of file systems in
|
||||||
|
`/etc/fstab`, no `/etc/rc.local`, no `runparts`. The configuration
|
||||||
|
is read from `/etc/rescue.conf`, can be modified by the operator.
|
||||||
|
|
||||||
|
* `single`, or `S`
|
||||||
|
Single user mode, runlevel 1, in Finit. Useful to debug problems with
|
||||||
|
the regular runlevel. All services and TTYs in `[1]` will be started,
|
||||||
|
so a `tty [1] @console nologin` configuration presents you with a
|
||||||
|
root console without login.
|
||||||
|
|
||||||
|
* `splash`
|
||||||
|
Display Finit splash screen with progress at bootstrap and shutdown.
|
||||||
|
|
||||||
|
For more on kernel boot paramaters, see the man page `bootparam(7)`.
|
||||||
@@ -95,10 +95,11 @@ Built-in conditions:
|
|||||||
- `net/route/default`
|
- `net/route/default`
|
||||||
- `net/<IFNAME>/exist`
|
- `net/<IFNAME>/exist`
|
||||||
- `net/<IFNAME>/up`
|
- `net/<IFNAME>/up`
|
||||||
|
- `net/<IFNAME>/running`
|
||||||
|
|
||||||
**Note:** `up` means administratively up, the interface flag `IFF_UP`,
|
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
|
||||||
not link up, `IFF_RUNNING`. This latter conditions is currently not
|
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
|
||||||
implemented by the `plugins/netlink.c` plugin.
|
difference is that `running` tells if the NIC has link.
|
||||||
|
|
||||||
|
|
||||||
Debugging
|
Debugging
|
||||||
+462
@@ -0,0 +1,462 @@
|
|||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||||
|
* [Syntax](#syntax)
|
||||||
|
* [Limitations](#limitations)
|
||||||
|
* [/etc/finit.conf](#etcfinitconf)
|
||||||
|
* [/etc/finit.d](#etcfinitd)
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
Finit can be configured using only the original `/etc/finit.conf` file
|
||||||
|
or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a
|
||||||
|
system using only `/etc/finit.d/*.conf`, highly useful for package-based
|
||||||
|
Linux distributions -- each package can provide its own "script" file.
|
||||||
|
|
||||||
|
- `/etc/finit.conf`: main configuration file
|
||||||
|
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
||||||
|
|
||||||
|
Not all configuration directives are available in `/etc/finit.d/*.conf`
|
||||||
|
and some directives are only available at bootstrap, runlevel `S`, see
|
||||||
|
the section [Limitations](#limitations) below for details.
|
||||||
|
|
||||||
|
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
|
||||||
|
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
|
||||||
|
call `finit q`, but that has been deprecated with the `initctl` tool).
|
||||||
|
Finit monitors all known active `.conf` files, so if you want to force
|
||||||
|
a restart of any service you can simply touch its corresponding `.conf`
|
||||||
|
file in `/etc/finit.d` and call `initctl reload`. Finit handle any
|
||||||
|
and all conditions and dependencies between services automatically.
|
||||||
|
|
||||||
|
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
|
||||||
|
and use `initctl enable` to enable a service `.conf` file. This may be
|
||||||
|
useful in particular to Linux distributions that may want to install all
|
||||||
|
files for a package and let the user decide when to enable a service.
|
||||||
|
|
||||||
|
On `initctl reload` the following is checked for all services:
|
||||||
|
|
||||||
|
- If a service's `.conf` file has been removed, or its conditions are no
|
||||||
|
longer satisifed, the service is stopped.
|
||||||
|
- If the file is modified, or a service it depends on has been reloaded,
|
||||||
|
the service is reloaded (stopped and started).
|
||||||
|
- If a new service is added it is automatically started — respecting
|
||||||
|
runlevels and return values from any callbacks.
|
||||||
|
|
||||||
|
For more info on the different states of a service, see the separate
|
||||||
|
document [Finit Services](service.md).
|
||||||
|
|
||||||
|
|
||||||
|
Service Wrapper Scripts
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
If your service requires to run additional commands, executed before the
|
||||||
|
service is actually started, like the systemd `ExecStartPre`, you can
|
||||||
|
use a wrapper shell script to start your service.
|
||||||
|
|
||||||
|
The Finit service `.conf` file can be put into `/etc/finit.d/available`,
|
||||||
|
so you can control the service using `initctl`. Then use the path to
|
||||||
|
the wrapper script in the Finit `.conf` service stanza. The following
|
||||||
|
example employs a wrapper script in `/etc/start.d`.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
* `/etc/finit.d/available/program.conf`:
|
||||||
|
```conf
|
||||||
|
service [235] <!> /etc/start.d/program -- Example Program
|
||||||
|
```
|
||||||
|
|
||||||
|
* `/etc/start.d/program:`
|
||||||
|
```shell
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Prepare the command line options
|
||||||
|
OPTIONS="-u $(cat /etc/username)"
|
||||||
|
|
||||||
|
# Execute the program
|
||||||
|
exec /usr/bin/program $OPTIONS
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:**, that the example sets `<!>` to denote that it doesn't support
|
||||||
|
`SIGHUP`. That way Finit will stop/start such the service instead of
|
||||||
|
sending SIGHUP at restart/reload.
|
||||||
|
|
||||||
|
|
||||||
|
Syntax
|
||||||
|
------
|
||||||
|
|
||||||
|
* `host <NAME>`, or `hostname <NAME>`
|
||||||
|
Set system hostname to NAME, unless `/etc/hostname` exists in which
|
||||||
|
case the contents of that file is used.
|
||||||
|
|
||||||
|
* `module <MODULE> [ARGS]`
|
||||||
|
Load a kernel module, with optional arguments
|
||||||
|
|
||||||
|
* `network <PATH>`
|
||||||
|
Script or program to bring up networking, with optional arguments
|
||||||
|
|
||||||
|
* `cgroup <NAME> cpuset:SPEC cpu:SPEC mem:SPEC`
|
||||||
|
Create a control group for resource limiting services using cgroups.
|
||||||
|
Unlike `rlimit` this setting defines a common group to be selected for
|
||||||
|
any set of run/task/services using the `group` directive (below).
|
||||||
|
|
||||||
|
A cpuset for a cgroup can be the traditional list of CPU cores to
|
||||||
|
assign to the group, or a range that Finit will use to dynamically
|
||||||
|
allocate cores based on the amount available. The range values can be
|
||||||
|
either in absolute form or percent.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup first cpuset:1
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a cgroup with one exclusive CPU:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup single cpuset:[1,1]
|
||||||
|
```
|
||||||
|
|
||||||
|
To create cgroup that uses at most two shared CPUs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup uptotwo cpuset:[,2]
|
||||||
|
```
|
||||||
|
|
||||||
|
To create cgroup with at least two shared CPUs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup dual cpuset:[2,]
|
||||||
|
```
|
||||||
|
|
||||||
|
To create cgroup with at least one CPU and at most half of all
|
||||||
|
available CPUs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup greedy cpuset:[1,50%]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* `group <NAME>`
|
||||||
|
All subsequent `service`, `task`, or `run` directives are executed in the
|
||||||
|
given control group `NAME`, which must be defined prior to this line. If
|
||||||
|
`NAME` does not yet exist the default group `default` is used.
|
||||||
|
|
||||||
|
When separate Finit `.conf` files are used for services the default group
|
||||||
|
is reset for each `.conf` file read.
|
||||||
|
|
||||||
|
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
|
||||||
|
limit for a resource, or both if that argument is omitted. `RESOURCE`
|
||||||
|
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
|
||||||
|
without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||||
|
|
||||||
|
LIMIT is an integer that depends on the resource being modified, see
|
||||||
|
the man page, or the kernel `/proc/PID/limits` file, for details.
|
||||||
|
Finit versions before v3.1 used `infinity` for `unlimited`, which is
|
||||||
|
still supported, albeit deprecated.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# No process is allowed more than 8MB of address space
|
||||||
|
rlimit hard as 8388608
|
||||||
|
|
||||||
|
# Core dumps may be arbitrarily large
|
||||||
|
rlimit soft core infinity
|
||||||
|
|
||||||
|
# CPU limit for all services, soft & hard = 10 sec
|
||||||
|
rlimit cpu 10
|
||||||
|
```
|
||||||
|
|
||||||
|
`rlimit` can be set globally, in `/etc/finit.conf`, or locally for
|
||||||
|
a set of task/run/services, in `/etc/finit.d/*.conf`.
|
||||||
|
|
||||||
|
* `runlevel <N>`
|
||||||
|
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
||||||
|
Default is 2.
|
||||||
|
|
||||||
|
* `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||||
|
One-shot command to run in sequence when entering a runlevel, with
|
||||||
|
optional arguments and description.
|
||||||
|
|
||||||
|
`run` commands are guaranteed to be completed before running the next
|
||||||
|
command. Highly useful if true serialization is needed.
|
||||||
|
|
||||||
|
* `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||||
|
One-shot like 'run', but starts in parallel with the next command.
|
||||||
|
|
||||||
|
Both `run` and `task` commands are run in a shell, so pipes and
|
||||||
|
redirects can be freely used:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
task [s] echo "foo" | cat >/tmp/bar
|
||||||
|
```
|
||||||
|
|
||||||
|
* `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
||||||
|
Service, or daemon, to be monitored and automatically restarted if it
|
||||||
|
exits prematurely. Please note that you often need to provide a
|
||||||
|
`--foreground` or `--no-background` argument to most daemons to
|
||||||
|
prevent them from forking off a sub-process in the background.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
service [2345] <svc/sbin/zebra> /sbin/ospfd -- OSPF daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
The `[2345]` is the runlevels `ospfd` is allowed to run in, they are
|
||||||
|
optional and default to level 2-5 if left out.
|
||||||
|
|
||||||
|
The `<...>` is the condition for starting `ospfd`. In this example
|
||||||
|
Finit waits for another service, `/sbin/zebra`, to have created its
|
||||||
|
PID file in `/var/run/zebra.pid` before starting `ospfd`.
|
||||||
|
|
||||||
|
Some services do not maintain a PID file and rather than patching each
|
||||||
|
application Finit provides a workaround. A `pid` keyword can be set
|
||||||
|
to have Finit automatically create (when starting) and later remove
|
||||||
|
(when stopping) the PID file. The file is created in the `/var/run`
|
||||||
|
directory using the `basename(1)` of the service. The default can be
|
||||||
|
modified with an optional argument:
|
||||||
|
|
||||||
|
pid[:[/path/to/]filename[.pid]]
|
||||||
|
|
||||||
|
For example, by adding `pid:/run/foo.pid` to the service `bar` that
|
||||||
|
PID file will, not only be created and removed automatically, but also
|
||||||
|
be used by the Finit condition subsystem. So another service/run/task
|
||||||
|
can depend on `<svc/foo>`.
|
||||||
|
|
||||||
|
If a service `bar` *does* maintain a PID file, but using `foo.pid`, we
|
||||||
|
can inform Finit of this by simplly prepending an `!` to the argument.
|
||||||
|
|
||||||
|
pid:!/run/foo.pid
|
||||||
|
|
||||||
|
Here Finit will *not* create/remove/touch the PID file, only use it
|
||||||
|
for the condition handling instead of the default PID file name.
|
||||||
|
|
||||||
|
For a detailed description of conditions, and how to debug them, see
|
||||||
|
the [Finit Conditions](conditions.md) document.
|
||||||
|
|
||||||
|
If a service should not be automatically started, it can be configured
|
||||||
|
as manual with the optional `manual` argument. The service can then be
|
||||||
|
started at any time by running `initctl start <service>`.
|
||||||
|
|
||||||
|
manual:yes
|
||||||
|
|
||||||
|
The name of a service, shown by the `initctl` tool, defaults to the
|
||||||
|
basename of the service executable. It can be changed with the
|
||||||
|
optional `name` argument:
|
||||||
|
|
||||||
|
name:<service-name>
|
||||||
|
|
||||||
|
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
||||||
|
Launch a daemon when a client initiates a connection on an Internet
|
||||||
|
port. Available services are listed in the UNIX `/etc/services` file.
|
||||||
|
Finit can filter access to from a list of interfaces, `@iflist`, per
|
||||||
|
inetd service as well as listen to custom ports.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
inetd ftp/tcp nowait @root /usr/sbin/uftpd -i -f
|
||||||
|
inetd tftp/udp wait @root /usr/sbin/uftpd -i -t
|
||||||
|
```
|
||||||
|
|
||||||
|
The following example listens to port 2323 for telnet connections and
|
||||||
|
only allows clients connecting from `eth0`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
inetd 2323/tcp@eth0 nowait [2345] /sbin/telnetd -i -F
|
||||||
|
```
|
||||||
|
|
||||||
|
The interface list, `@iflist`, is of the format `@iface,!iface,iface`,
|
||||||
|
where a single `!` means to deny access. Notice how interfaces are
|
||||||
|
comma separated with no spaces.
|
||||||
|
|
||||||
|
The `inetd` directive can also have ` -- Optional Description`, only
|
||||||
|
Finit does not output this text on the console when launching inetd
|
||||||
|
services. Instead this text is sent to syslog and also shown by the
|
||||||
|
`initctl` tool. More on inetd below.
|
||||||
|
|
||||||
|
* `runparts <DIR>`
|
||||||
|
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||||
|
files, or scripts, in the directory are called, in alphabetic order.
|
||||||
|
The scripts in this directory are executed at the very end of runlevel
|
||||||
|
`S`, bootstrap.
|
||||||
|
|
||||||
|
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
||||||
|
is a dependency order between the scripts. Symlinks to existing
|
||||||
|
daemons can talso be used, but make sure they daemonize by default.
|
||||||
|
|
||||||
|
Similar to the `/etc/rc.local` shell script, make sure that all your
|
||||||
|
services and programs either terminate or start in the background or
|
||||||
|
you will block Finit.
|
||||||
|
|
||||||
|
* `include <CONF>`
|
||||||
|
Include another configuration file. Absolute path required.
|
||||||
|
|
||||||
|
* `log size:200k count:5`
|
||||||
|
|
||||||
|
Log rotation for run/task/services using the `log` sub-option with
|
||||||
|
redirection to a log file. Global setting, applies to all services.
|
||||||
|
|
||||||
|
The size can be given as bytes, without a specifier, or in `k`, `M`,
|
||||||
|
or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables
|
||||||
|
log rotation. The default is `200k`.
|
||||||
|
|
||||||
|
The count value is recommended to be between 1-5, with a default 5.
|
||||||
|
Setting count to 0 means the logfile will be truncated when the MAX
|
||||||
|
size limit is reached.
|
||||||
|
|
||||||
|
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||||
|
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
||||||
|
The first variant of this option uses the built-in getty on the given
|
||||||
|
TTY device DEV, in the given runlevels. The DEV may be the special
|
||||||
|
keyword `@console`, which is very useful on embedded systems. Default
|
||||||
|
baud rate is `38400`.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```conf
|
||||||
|
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||||
|
```
|
||||||
|
|
||||||
|
The second `tty` syntax variant is for using an external getty, like
|
||||||
|
agetty or the BusyBox getty.
|
||||||
|
|
||||||
|
By default both variants *clear* the TTY and *wait* for the user to
|
||||||
|
press enter before starting getty.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```conf
|
||||||
|
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
||||||
|
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
|
||||||
|
```
|
||||||
|
|
||||||
|
The `noclear` option disables clearing the TTY after each session.
|
||||||
|
Clearing the TTY when a user logs out is usually preferable.
|
||||||
|
|
||||||
|
The `nowait` option disables the `press Enter to activate console`
|
||||||
|
message before actually starting the getty program. On small and
|
||||||
|
embedded systems running multiple unused getty wastes both memory
|
||||||
|
and CPU cycles, so `wait` is the preferred default.
|
||||||
|
|
||||||
|
The `nologin` option disables getty and `/bin/login`, and gives the
|
||||||
|
user a root (login) shell on the given TTY `<DEV>` immediately.
|
||||||
|
Needless to say, this is a rather insecure option, but can be very
|
||||||
|
useful for developer builds, during board bringup, or similar.
|
||||||
|
|
||||||
|
Notice the ordering, the `TERM` option to the built-in getty must be
|
||||||
|
the last argument.
|
||||||
|
|
||||||
|
Embedded systems may want to enable automatic `DEV` by supplying the
|
||||||
|
special `@console` device. This works regardless weather the system
|
||||||
|
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
|
||||||
|
it out by querying sysfs: `/sys/class/tty/console/active`.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```conf
|
||||||
|
tty [12345] @console 115200 noclear vt220
|
||||||
|
```
|
||||||
|
|
||||||
|
On really bare bones systems Finit offers a fallback shell, which
|
||||||
|
should not be enabled on production systems since. This because it
|
||||||
|
may give a user root access without having to log in. However, for
|
||||||
|
bringup and system debugging it can come in handy:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
configure --enable-fallback-shell
|
||||||
|
```
|
||||||
|
|
||||||
|
One can also use the `service` stanza to start a stand-alone shell:
|
||||||
|
|
||||||
|
```conf
|
||||||
|
service [12345] /bin/sh -l
|
||||||
|
```
|
||||||
|
|
||||||
|
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
|
||||||
|
be installed since system requirements differ too much. Try out the
|
||||||
|
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
||||||
|
is capable of service monitoring SSH, sysklogd, gdm and getty!
|
||||||
|
|
||||||
|
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
||||||
|
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
||||||
|
`[@USR[:GRP]]` like this:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
run [2345] @joe:users /usr/bin/logger "Hello world"
|
||||||
|
```
|
||||||
|
|
||||||
|
For multiple instances of the same command, e.g. a DHCP client or
|
||||||
|
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
||||||
|
`service` keyword and the command, like this:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
service :1 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
|
||||||
|
service :2 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
|
||||||
|
```
|
||||||
|
|
||||||
|
Without the `:ID` to the service the latter will overwrite the former
|
||||||
|
and only the old web server would be started and supervised.
|
||||||
|
|
||||||
|
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
|
||||||
|
`log` to redirect `stderr` and `stdout` of the application to a file or
|
||||||
|
syslog using the native `logit` tool. The full syntax is:
|
||||||
|
|
||||||
|
log:/path/to/file
|
||||||
|
log:prio:facility.level,tag:ident
|
||||||
|
log:console
|
||||||
|
log:null
|
||||||
|
log
|
||||||
|
|
||||||
|
Default `prio` is `daemon.info` and default `tag` is the basename of the
|
||||||
|
service or run/task command.
|
||||||
|
|
||||||
|
Log rotation is controlled using the global `log` setting.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||||
|
|
||||||
|
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||||
|
detailed description, see the [Conditions](conditions.md) document.
|
||||||
|
|
||||||
|
|
||||||
|
Limitations
|
||||||
|
-----------
|
||||||
|
|
||||||
|
To understand the limitations of `finit.conf` vs `finit.d` it is useful
|
||||||
|
to picture the different phases of the system: bootstrap, runtime, and
|
||||||
|
shutdown.
|
||||||
|
|
||||||
|
### /etc/finit.conf
|
||||||
|
|
||||||
|
This file used to be the only way to set up and boot a Finit system.
|
||||||
|
Today it is mainly used for pre-runtime settings like system hostname,
|
||||||
|
network bringup and shutdown:
|
||||||
|
|
||||||
|
- `host`, only at bootstrap, (runlevel `S`)
|
||||||
|
- `mknod`, only at bootstrap
|
||||||
|
- `network`, only at bootstrap
|
||||||
|
- `runparts`, only at bootstrap
|
||||||
|
- `include`
|
||||||
|
- `log`, global setting
|
||||||
|
- `shutdown`
|
||||||
|
- `runlevel`, only at bootstrap
|
||||||
|
- ... and all configuration stanzas from `/etc/finit.d` below
|
||||||
|
|
||||||
|
### /etc/finit.d
|
||||||
|
|
||||||
|
Support for per-service `.conf` files in `/etc/finit.d` was added in
|
||||||
|
v3.0 to handle changes of the system configuration at runtime. As of
|
||||||
|
v3.1 `finit.conf` is also handled at runtime, except of course for any
|
||||||
|
stanza that only runs at bootstrap. However, a `/etc/finit.d/*.conf`
|
||||||
|
does *not support* the above `finit.conf` settings described above, only
|
||||||
|
the following:
|
||||||
|
|
||||||
|
- `module`, but only at bootstrap
|
||||||
|
- `service`
|
||||||
|
- `task`
|
||||||
|
- `run`
|
||||||
|
- `inetd`
|
||||||
|
- `rlimit`
|
||||||
|
- `tty`
|
||||||
|
|
||||||
|
**NOTE:** The `/etc/finit.d` directory was previously the default Finit
|
||||||
|
`runparts` directory. Finit no longer has a default `runparts`, make
|
||||||
|
sure to update your setup, or the finit configuration, accordingly.
|
||||||
|
|
||||||
|
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||||
File renamed without changes.
File renamed without changes.
@@ -42,13 +42,11 @@ For your convenience a set of *optional* plugins are available:
|
|||||||
|
|
||||||
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
|
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
|
||||||
|
|
||||||
* *hwclock.so*: Restore and save system clock from/to RTC on
|
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
||||||
startup/shutdown.
|
|
||||||
|
|
||||||
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
||||||
|
|
||||||
* *lost.so*: Very simple `HOOK_SVC_LOST` example. Logs process ID and
|
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
|
||||||
name to syslog. _Optional plugin._
|
|
||||||
|
|
||||||
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
||||||
interfaces. These events are then sent to the Finit service monitor
|
interfaces. These events are then sent to the Finit service monitor
|
||||||
@@ -102,19 +100,6 @@ Hooks
|
|||||||
all modified/removed services have been stopped. When the hook has
|
all modified/removed services have been stopped. When the hook has
|
||||||
completed, Finit continues to start all modified and new services.
|
completed, Finit continues to start all modified and new services.
|
||||||
|
|
||||||
* `HOOK_SVC_LOST`: Called when a process is lost. When reconfiguring
|
|
||||||
services at runtime this hook may be called a lot. However, it may be
|
|
||||||
a quite useful hook to monitor a system post bootstrap when no, or
|
|
||||||
few, services are expected to exit. A default plugin `lost.so` is
|
|
||||||
available in the `plugins/` subdirectory as an example.
|
|
||||||
|
|
||||||
**NOTE:** This hook callback gets the lost PID as argument.
|
|
||||||
|
|
||||||
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
|
|
||||||
started. Same caveats apply.
|
|
||||||
|
|
||||||
**NOTE:** This hook callback gets the new PID as argument.
|
|
||||||
|
|
||||||
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
||||||
change. The hook is called when services not matching the new
|
change. The hook is called when services not matching the new
|
||||||
runlevel have been been stopped. When the hook has completed, Finit
|
runlevel have been been stopped. When the hook has completed, Finit
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
-6
@@ -1,3 +1,4 @@
|
|||||||
|
pkglibdir = $(plugin_path)
|
||||||
AM_LDFLAGS = -module -avoid-version -shared
|
AM_LDFLAGS = -module -avoid-version -shared
|
||||||
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
|
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
|
||||||
@@ -6,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
|||||||
|
|
||||||
if STATIC
|
if STATIC
|
||||||
noinst_LTLIBRARIES = libplug.la
|
noinst_LTLIBRARIES = libplug.la
|
||||||
libplug_la_SOURCES = bootmisc.c hwclock.c initctl.c pidfile.c procps.c tty.c urandom.c
|
libplug_la_SOURCES = bootmisc.c rtc.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||||
|
|
||||||
if BUILD_ALSA_UTILS_PLUGIN
|
if BUILD_ALSA_UTILS_PLUGIN
|
||||||
libplug_la_SOURCES += alsa-utils.c
|
libplug_la_SOURCES += alsa-utils.c
|
||||||
@@ -38,8 +39,8 @@ libplug_la_SOURCES += time.c
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_LOST_PLUGIN
|
if BUILD_MODULES_LOAD_PLUGIN
|
||||||
libplug_la_SOURCES += lost.c
|
libplug_la_SOURCES += modules-load.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_NETLINK_PLUGIN
|
if BUILD_NETLINK_PLUGIN
|
||||||
@@ -51,7 +52,7 @@ libplug_la_SOURCES += resolvconf.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
else
|
else
|
||||||
pkglib_LTLIBRARIES = bootmisc.la hwclock.la initctl.la pidfile.la procps.la tty.la urandom.la
|
pkglib_LTLIBRARIES = bootmisc.la rtc.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||||
|
|
||||||
if BUILD_ALSA_UTILS_PLUGIN
|
if BUILD_ALSA_UTILS_PLUGIN
|
||||||
pkglib_LTLIBRARIES += alsa-utils.la
|
pkglib_LTLIBRARIES += alsa-utils.la
|
||||||
@@ -83,8 +84,8 @@ pkglib_LTLIBRARIES += time.la
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_LOST_PLUGIN
|
if BUILD_MODULES_LOAD_PLUGIN
|
||||||
pkglib_LTLIBRARIES += lost.la
|
pkglib_LTLIBRARIES += modules-load.la
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_NETLINK_PLUGIN
|
if BUILD_NETLINK_PLUGIN
|
||||||
|
|||||||
+5
-1
@@ -106,9 +106,10 @@ static void setup(void *arg)
|
|||||||
_d("Setting up FHS structure in /var ...");
|
_d("Setting up FHS structure in /var ...");
|
||||||
umask(0);
|
umask(0);
|
||||||
makedir("/var/cache", 0755);
|
makedir("/var/cache", 0755);
|
||||||
|
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||||
makedir("/var/games", 0755);
|
makedir("/var/games", 0755);
|
||||||
makedir("/var/lib", 0755);
|
makedir("/var/lib", 0755);
|
||||||
makedir("/var/lib/misc", 0755);
|
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
|
||||||
makedir("/var/lib/alarm", 0755);
|
makedir("/var/lib/alarm", 0755);
|
||||||
makedir("/var/lib/urandom",0755);
|
makedir("/var/lib/urandom",0755);
|
||||||
if (fisdir("/run")) {
|
if (fisdir("/run")) {
|
||||||
@@ -169,6 +170,9 @@ static void setup(void *arg)
|
|||||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
||||||
chown("/dev/xconsole", 0, gid);
|
chown("/dev/xconsole", 0, gid);
|
||||||
|
|
||||||
|
if (!fexist("/etc/mtab"))
|
||||||
|
symlink("../proc/self/mounts", "/etc/mtab");
|
||||||
|
|
||||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||||
uid = getuser("uuidd", NULL);
|
uid = getuser("uuidd", NULL);
|
||||||
if (uid >= 0) {
|
if (uid >= 0) {
|
||||||
|
|||||||
+4
-3
@@ -29,6 +29,7 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
#define DAEMON "dbus-daemon"
|
#define DAEMON "dbus-daemon"
|
||||||
#define ARGS "--nofork --system"
|
#define ARGS "--nofork --system"
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
char *cmd;
|
char *cmd;
|
||||||
char line[80];
|
char line[256];
|
||||||
|
|
||||||
cmd = which(DAEMON);
|
cmd = which(DAEMON);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
@@ -57,8 +58,8 @@ static void setup(void *arg)
|
|||||||
erase("/var/run/dbus/pid");
|
erase("/var/run/dbus/pid");
|
||||||
|
|
||||||
/* Register service with Finit */
|
/* Register service with Finit */
|
||||||
snprintf(line, sizeof(line), "[S12345] %s %s -- %s", cmd, ARGS, DESC);
|
snprintf(line, sizeof(line), "[S12345] log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
|
||||||
if (service_register(SVC_TYPE_SERVICE, line, NULL))
|
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||||
_pe("Failed registering %s", DAEMON);
|
_pe("Failed registering %s", DAEMON);
|
||||||
free(cmd);
|
free(cmd);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/* Scans /etc/modules-load.d for modules to load.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Robert Andersson <robert.m.andersson@se.atlascopco.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lite/lite.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
|
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||||
|
#define SERVICE_LINE \
|
||||||
|
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
||||||
|
|
||||||
|
static void load(void *arg)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
DIR *dirp;
|
||||||
|
struct dirent *direntp;
|
||||||
|
char line[256];
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
|
||||||
|
|
||||||
|
dirp = opendir(MODULES_LOAD_PATH);
|
||||||
|
if (!dirp) return;
|
||||||
|
|
||||||
|
while ((direntp = readdir(dirp)))
|
||||||
|
{
|
||||||
|
char module_path[PATH_MAX];
|
||||||
|
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||||
|
strlcat(module_path, direntp->d_name, sizeof(module_path));
|
||||||
|
|
||||||
|
fp = fopen(module_path, "r");
|
||||||
|
if (!fp) continue;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
char cmd[CMD_SIZE], *mod, *args;
|
||||||
|
|
||||||
|
mod = strip_line(line);
|
||||||
|
if (!*mod) continue;
|
||||||
|
|
||||||
|
mod = chomp(mod);
|
||||||
|
if (!mod || !*mod) continue;
|
||||||
|
|
||||||
|
mod = strtok_r(mod, " ", &args);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
|
||||||
|
index, mod, mod, args, mod);
|
||||||
|
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dirp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.name = __FILE__,
|
||||||
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
|
.cb = load
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+20
-23
@@ -94,6 +94,17 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void net_cond_set(char *ifname, char *cond, int set)
|
||||||
|
{
|
||||||
|
char msg[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "net/%s/%s", ifname, cond);
|
||||||
|
if (set)
|
||||||
|
cond_set(msg);
|
||||||
|
else
|
||||||
|
cond_clear(msg);
|
||||||
|
}
|
||||||
|
|
||||||
static void nl_link(struct nlmsghdr *nlmsg)
|
static void nl_link(struct nlmsghdr *nlmsg)
|
||||||
{
|
{
|
||||||
int la;
|
int la;
|
||||||
@@ -112,39 +123,25 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
|||||||
|
|
||||||
while (RTA_OK(a, la)) {
|
while (RTA_OK(a, la)) {
|
||||||
if (a->rta_type == IFLA_IFNAME) {
|
if (a->rta_type == IFLA_IFNAME) {
|
||||||
char msg[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||||
switch (nlmsg->nlmsg_type) {
|
switch (nlmsg->nlmsg_type) {
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
/*
|
/*
|
||||||
* New interface has appearad, or interface flags has changed.
|
* New interface has appeared, or interface flags has changed.
|
||||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||||
*/
|
*/
|
||||||
_d("%s: New link, ifi_change 0x%x", ifname, i->ifi_change);
|
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||||
if (i->ifi_change & IFF_UP) {
|
net_cond_set(ifname, "exist", 1);
|
||||||
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
|
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||||
|
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||||
if (i->ifi_flags & IFF_UP)
|
|
||||||
cond_set(msg);
|
|
||||||
else
|
|
||||||
cond_clear(msg);
|
|
||||||
|
|
||||||
if (string_compare("lo", ifname)) {
|
|
||||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
|
||||||
cond_set(msg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
|
||||||
cond_set(msg);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
/* NOTE: Interface has dissapeared, not link down ... */
|
/* NOTE: Interface has disappeared, not link down ... */
|
||||||
_d("%s: Delete link", ifname);
|
_d("%s: Delete link", ifname);
|
||||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
net_cond_set(ifname, "exist", 0);
|
||||||
cond_clear(msg);
|
net_cond_set(ifname, "up", 0);
|
||||||
|
net_cond_set(ifname, "running", 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_NEWADDR:
|
case RTM_NEWADDR:
|
||||||
|
|||||||
+45
-43
@@ -37,12 +37,6 @@ struct context {
|
|||||||
int wd;
|
int wd;
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *mkcond(char *buf, size_t len, char *nm)
|
|
||||||
{
|
|
||||||
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pidfile_callback(void *arg, int fd, int events)
|
static void pidfile_callback(void *arg, int fd, int events)
|
||||||
{
|
{
|
||||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
@@ -50,7 +44,6 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
|
|
||||||
struct inotify_event *ev;
|
struct inotify_event *ev;
|
||||||
ssize_t sz, len;
|
ssize_t sz, len;
|
||||||
char *basename;
|
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
@@ -60,25 +53,18 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
|
for (ev = (void *)ev_buf;
|
||||||
|
sz > (ssize_t)sizeof(*ev);
|
||||||
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||||
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
|
|
||||||
if (!ev->mask || !strstr(ev->name, ".pid"))
|
if (!ev->mask || !strstr(ev->name, ".pid"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
basename = strtok(ev->name, ".");
|
svc = svc_find_by_pidfile(ev->name);
|
||||||
if (!basename)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
svc = svc_find_by_nameid(basename, 1);
|
|
||||||
if (!svc)
|
if (!svc)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
|
|
||||||
|
|
||||||
_d("%s: match %s", basename, svc->cmd);
|
|
||||||
mkcond(cond, sizeof(cond), svc->cmd);
|
mkcond(cond, sizeof(cond), svc->cmd);
|
||||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
|
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||||
svc_started(svc);
|
svc_started(svc);
|
||||||
cond_set(cond);
|
cond_set(cond);
|
||||||
} else if (ev->mask & IN_DELETE)
|
} else if (ev->mask & IN_DELETE)
|
||||||
@@ -87,44 +73,60 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assert condition only if the service is running, but not if it's
|
* This function is called after `initctl reload` to reassert conditions
|
||||||
* recently been changed or while it's starting up.
|
* for services that have not been changed.
|
||||||
*
|
*
|
||||||
* We must wait for the service to create/touch its pidfile.
|
* We reassert the run/task/service's condition only if it is running,
|
||||||
|
* but not if it's recently been changed or while it's starting up.
|
||||||
*/
|
*/
|
||||||
static void pidfile_reconf(void *_null)
|
static void pidfile_reconf(void *arg)
|
||||||
{
|
{
|
||||||
static char cond[MAX_COND_LEN];
|
static char cond[MAX_COND_LEN];
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
(void)(_null);
|
|
||||||
int restart = 0;
|
|
||||||
|
|
||||||
do {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
restart = 0;
|
if (svc->state != SVC_RUNNING_STATE)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
if (svc_is_changed(svc) || svc_is_starting(svc))
|
||||||
mkcond(cond, sizeof(cond), svc->cmd);
|
continue;
|
||||||
if (svc->state == SVC_RUNNING_STATE &&
|
|
||||||
!svc_is_changed(svc) &&
|
|
||||||
!svc_is_starting(svc) &&
|
|
||||||
cond_get(cond) != COND_ON) {
|
|
||||||
cond_set_path(cond_path(cond), COND_ON);
|
|
||||||
restart = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restart)
|
mkcond(cond, sizeof(cond), svc->cmd);
|
||||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
if (cond_get(cond) == COND_ON)
|
||||||
|
continue;
|
||||||
|
|
||||||
} while (restart);
|
cond_set_path(cond_path(cond), COND_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will call service_step(), which in turn will schedule itself
|
||||||
|
* as long as stepped services change state. Services going from
|
||||||
|
* WAITING to RUNNING will reassert their conditions in that loop,
|
||||||
|
* which in turn may unlock other services, and so on.
|
||||||
|
*/
|
||||||
|
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pidfile_init(void *arg)
|
static void pidfile_init(void *arg)
|
||||||
{
|
{
|
||||||
|
char *path;
|
||||||
struct context *ctx = arg;
|
struct context *ctx = arg;
|
||||||
|
uint32_t mask = IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The bootmisc plugin is responsible for setting up /var/run.
|
||||||
|
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
||||||
|
* so it's safe here to query realpath() and set up inotify.
|
||||||
|
*/
|
||||||
|
path = realpath(_PATH_VARRUN, NULL);
|
||||||
|
if (!path) {
|
||||||
|
_pe("Failed ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->wd = inotify_add_watch(ctx->fd, path, mask);
|
||||||
|
free(path);
|
||||||
|
|
||||||
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
|
|
||||||
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
|
||||||
if (ctx->wd < 0) {
|
if (ctx->wd < 0) {
|
||||||
_pe("inotify_add_watch()");
|
_pe("inotify_add_watch()");
|
||||||
close(ctx->fd);
|
close(ctx->fd);
|
||||||
@@ -148,7 +150,7 @@ static plugin_t plugin = {
|
|||||||
.cb = pidfile_callback,
|
.cb = pidfile_callback,
|
||||||
.flags = PLUGIN_IO_READ,
|
.flags = PLUGIN_IO_READ,
|
||||||
},
|
},
|
||||||
.depends = { "bootmisc", },
|
.depends = { "bootmisc", "netlink" },
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
/* Save and restore RTC using hwclock
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2019 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <lite/lite.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
|
/* Kernel RTC driver validates against this date for sanity check */
|
||||||
|
#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */
|
||||||
|
|
||||||
|
static int rtc_open(void)
|
||||||
|
{
|
||||||
|
char *alt[] = {
|
||||||
|
"/dev/rtc",
|
||||||
|
"/dev/rtc0",
|
||||||
|
"/dev/misc/rtc"
|
||||||
|
};
|
||||||
|
size_t i;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
for (i = 0; i < NELEMS(alt); i++) {
|
||||||
|
fd = open(alt[i], O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtc_save(void *arg)
|
||||||
|
{
|
||||||
|
struct timeval tv = { 0 };
|
||||||
|
struct tm tm = { 0 };
|
||||||
|
int fd, rc = 0;
|
||||||
|
|
||||||
|
fd = rtc_open();
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rc = gettimeofday(&tv, NULL);
|
||||||
|
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
||||||
|
print_desc(NULL, "System clock invalid, not saving to RTC");
|
||||||
|
invalid:
|
||||||
|
logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC");
|
||||||
|
rc = 2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_desc(NULL, "Saving system time (UTC) to RTC");
|
||||||
|
gmtime_r(&tv.tv_sec, &tm);
|
||||||
|
if (ioctl(fd, RTC_SET_TIME, &tm) < 0) {
|
||||||
|
if (EINVAL == errno)
|
||||||
|
goto invalid;
|
||||||
|
rc = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
print(rc, NULL);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtc_restore(void *arg)
|
||||||
|
{
|
||||||
|
struct timeval tv = { 0 };
|
||||||
|
struct tm tm = { 0 };
|
||||||
|
int fd, rc = 0;
|
||||||
|
|
||||||
|
fd = rtc_open();
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
|
||||||
|
char msg[120];
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
|
||||||
|
EINVAL == errno ? "RTC time is too old" :
|
||||||
|
ENOENT == errno ? "RTC has no saved time" : "see log for details");
|
||||||
|
print_desc(NULL, msg);
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
||||||
|
if (EINVAL == errno)
|
||||||
|
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
|
||||||
|
else if (ENOENT == errno)
|
||||||
|
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||||
|
else
|
||||||
|
logit(LOG_ERR, "RTC error code %d: %m", errno);
|
||||||
|
rc = 2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||||
|
tv.tv_sec = mktime(&tm);
|
||||||
|
if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settimeofday(&tv, NULL) == -1)
|
||||||
|
rc = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
print(rc, NULL);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.name = __FILE__,
|
||||||
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
|
.cb = rtc_restore
|
||||||
|
},
|
||||||
|
.hook[HOOK_SHUTDOWN] = {
|
||||||
|
.cb = rtc_save
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+6
-6
@@ -45,7 +45,7 @@ static plugin_t plugin = {
|
|||||||
|
|
||||||
static void setup(void)
|
static void setup(void)
|
||||||
{
|
{
|
||||||
if (plugin.io.fd)
|
if (plugin.io.fd > 0)
|
||||||
close(plugin.io.fd);
|
close(plugin.io.fd);
|
||||||
|
|
||||||
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
@@ -57,7 +57,7 @@ static void watcher(void *arg, int fd, int events)
|
|||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
char buf[EVENT_SIZE], name[42];
|
char buf[EVENT_SIZE], name[42];
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||||
|
|
||||||
while ((len = read(fd, buf, sizeof(buf)))) {
|
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||||
@@ -72,11 +72,11 @@ static void watcher(void *arg, int fd, int events)
|
|||||||
|
|
||||||
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||||
entry = tty_find(name);
|
entry = tty_find(name);
|
||||||
if (entry && tty_enabled(&entry->data)) {
|
if (entry && tty_enabled(entry)) {
|
||||||
if (notified->mask & IN_CREATE)
|
if (notified->mask & IN_CREATE)
|
||||||
tty_start(&entry->data);
|
tty_start(entry);
|
||||||
else if (entry->data.pid)
|
else if (entry->pid)
|
||||||
tty_stop(&entry->data);
|
tty_stop(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
.deps/*
|
.deps/*
|
||||||
finit
|
finit
|
||||||
initctl
|
initctl
|
||||||
|
logit
|
||||||
reboot
|
reboot
|
||||||
|
watchdogd
|
||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
+37
-18
@@ -1,24 +1,35 @@
|
|||||||
|
EXTRA_DIST = rescue.conf
|
||||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||||
if STATIC
|
if STATIC
|
||||||
AM_CPPFLAGS += -DENABLE_STATIC
|
AM_CPPFLAGS += -DENABLE_STATIC
|
||||||
AM_LDFLAGS = -static-libtool-libs
|
AM_LDFLAGS = -static-libtool-libs
|
||||||
else
|
else
|
||||||
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
|
|
||||||
AM_LDFLAGS = -export-dynamic
|
AM_LDFLAGS = -export-dynamic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
bin_PROGRAMS = logit
|
||||||
sbin_PROGRAMS = finit initctl reboot
|
sbin_PROGRAMS = finit initctl reboot
|
||||||
finit_SOURCES = api.c \
|
pkglibexec_SCRIPTS = cgreaper.sh
|
||||||
|
if WATCHDOGD
|
||||||
|
pkglibexec_PROGRAMS = watchdogd
|
||||||
|
endif
|
||||||
|
|
||||||
|
logit_SOURCES = logit.c
|
||||||
|
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
|
|
||||||
|
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||||
cond.c cond-w.c cond.h \
|
cond.c cond-w.c cond.h \
|
||||||
client.c \
|
telinit.c \
|
||||||
conf.c conf.h \
|
conf.c conf.h \
|
||||||
exec.c finit.c finit.h \
|
exec.c finit.c finit.h \
|
||||||
getty.c \
|
getty.c stty.c \
|
||||||
helpers.c helpers.h \
|
helpers.c helpers.h \
|
||||||
log.c log.h \
|
log.c log.h \
|
||||||
mdadm.c mount.c pid.c \
|
mdadm.c mount.c \
|
||||||
|
pid.c pid.h \
|
||||||
plugin.c plugin.h private.h \
|
plugin.c plugin.h private.h \
|
||||||
|
schedule.c schedule.h \
|
||||||
service.c service.h \
|
service.c service.h \
|
||||||
sig.c sig.h \
|
sig.c sig.h \
|
||||||
sm.c sm.h \
|
sm.c sm.h \
|
||||||
@@ -30,20 +41,18 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
|
|||||||
if INETD
|
if INETD
|
||||||
finit_SOURCES += inetd.c inetd.h
|
finit_SOURCES += inetd.c inetd.h
|
||||||
endif
|
endif
|
||||||
if WATCHDOGD
|
|
||||||
finit_SOURCES += watchdog.c watchdog.h
|
|
||||||
endif
|
|
||||||
|
|
||||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||||
if STATIC
|
if STATIC
|
||||||
finit_LDADD += plugins/libplug.la
|
finit_LDADD += ../plugins/libplug.la
|
||||||
else
|
else
|
||||||
finit_LDADD += -ldl
|
finit_LDADD += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
initctl_SOURCES = initctl.c serv.c serv.h svc.c svc.h \
|
initctl_SOURCES = initctl.c client.c client.h \
|
||||||
|
serv.c serv.h svc.h \
|
||||||
cond.c cond.h util.c util.h
|
cond.c cond.h util.c util.h
|
||||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
initctl_CFLAGS += $(lite_CFLAGS)
|
initctl_CFLAGS += $(lite_CFLAGS)
|
||||||
@@ -56,12 +65,19 @@ reboot_LDADD = $(lite_LIBS)
|
|||||||
INIT_LNKS = telinit init
|
INIT_LNKS = telinit init
|
||||||
REBOOT_LNKS = shutdown halt poweroff suspend
|
REBOOT_LNKS = shutdown halt poweroff suspend
|
||||||
|
|
||||||
|
if WATCHDOGD
|
||||||
|
watchdogd_SOURCES = watchdog.c watchdog.h
|
||||||
|
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
|
#log.c log.h
|
||||||
|
endif
|
||||||
|
|
||||||
# Hook in install to add finit and reboot symlink(s)
|
# Hook in install to add finit and reboot symlink(s)
|
||||||
install-exec-hook:
|
install-exec-hook:
|
||||||
|
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
|
||||||
@for file in $(INIT_LNKS); do \
|
@for file in $(INIT_LNKS); do \
|
||||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||||
if [ -e $$lnk ]; then \
|
if [ -e $$lnk ]; then \
|
||||||
echo "Skipping $$lnk exists already."; \
|
echo "Skipping $$lnk exists."; \
|
||||||
else \
|
else \
|
||||||
$(LN_S) finit $$lnk; \
|
$(LN_S) finit $$lnk; \
|
||||||
fi; \
|
fi; \
|
||||||
@@ -70,16 +86,17 @@ install-exec-hook:
|
|||||||
echo "Non-finit reboot, skipping install."; \
|
echo "Non-finit reboot, skipping install."; \
|
||||||
else \
|
else \
|
||||||
for file in $(REBOOT_LNKS); do \
|
for file in $(REBOOT_LNKS); do \
|
||||||
link=$(DESTDIR)$(sbindir)/$$file; \
|
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||||
if [ -e $$link ]; then \
|
if [ -e $$lnk ]; then \
|
||||||
echo "$$link exists, skipping.";\
|
echo "Skipping $$lnk exists."; \
|
||||||
else \
|
else \
|
||||||
$(LN_S) reboot $$link; \
|
$(LN_S) reboot $$lnk; \
|
||||||
fi; \
|
fi; \
|
||||||
done; \
|
done; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
uninstall-hook:
|
uninstall-hook:
|
||||||
|
@$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf
|
||||||
@for file in $(INIT_LNKS); do \
|
@for file in $(INIT_LNKS); do \
|
||||||
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
||||||
if [ "$$lnk" != "finit" ]; then \
|
if [ "$$lnk" != "finit" ]; then \
|
||||||
@@ -92,9 +109,11 @@ uninstall-hook:
|
|||||||
echo "Non-finit reboot, skipping uninstall."; \
|
echo "Non-finit reboot, skipping uninstall."; \
|
||||||
else \
|
else \
|
||||||
for file in $(REBOOT_LNKS); do \
|
for file in $(REBOOT_LNKS); do \
|
||||||
if [ "`readlink $$file`" != "reboot" ]; \
|
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||||
then continue; \
|
dst=`readlink $$lnk`; \
|
||||||
|
if [ "$$dst" != "reboot" ]; then \
|
||||||
|
continue; \
|
||||||
fi; \
|
fi; \
|
||||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
$(RM) $$lnk; \
|
||||||
done; \
|
done; \
|
||||||
fi
|
fi
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -38,18 +39,20 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
uev_t api_watcher;
|
extern svc_t *wdog;
|
||||||
|
static uev_t api_watcher;
|
||||||
|
|
||||||
static int call(int (*action)(svc_t *), char *buf, size_t len)
|
static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||||
{
|
{
|
||||||
return svc_parse_jobstr(buf, len, action, NULL);
|
return svc_parse_jobstr(buf, len, action, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int service_stop(svc_t *svc)
|
static int stop(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -60,7 +63,7 @@ static int service_stop(svc_t *svc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int service_start(svc_t *svc)
|
static int start(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -71,43 +74,99 @@ static int service_start(svc_t *svc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int service_restart(svc_t *svc)
|
static int restart(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if (svc_is_blocked(svc))
|
||||||
|
svc_start(svc);
|
||||||
|
|
||||||
svc_mark_dirty(svc);
|
svc_mark_dirty(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); }
|
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
|
||||||
static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); }
|
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
|
||||||
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
|
||||||
|
|
||||||
#ifdef INETD_ENABLED
|
static char query_buf[368];
|
||||||
static int do_query_inetd(char *buf, size_t len)
|
static int missing(char *job, char *id)
|
||||||
{
|
{
|
||||||
int id = 1;
|
char buf[20];
|
||||||
char *ptr, *input = sanitize(buf, len);
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
|
if (!job)
|
||||||
|
job = "";
|
||||||
|
if (!id)
|
||||||
|
id = "";
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s:%s ", job, id);
|
||||||
|
strlcat(query_buf, buf, sizeof(query_buf));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_query(struct init_request *rq, size_t len)
|
||||||
|
{
|
||||||
|
memset(query_buf, 0, sizeof(query_buf));
|
||||||
|
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) {
|
||||||
|
memcpy(rq->data, query_buf, sizeof(rq->data));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static svc_t *do_find(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *id = NULL;
|
||||||
|
char *ptr, *input;
|
||||||
|
svc_t *iter = NULL;
|
||||||
|
|
||||||
|
input = sanitize(buf, len);
|
||||||
if (!input)
|
if (!input)
|
||||||
return -1;
|
return NULL;
|
||||||
|
|
||||||
ptr = strchr(input, ':');
|
ptr = strchr(input, ':');
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
*ptr++ = 0;
|
*ptr++ = 0;
|
||||||
id = atonum(ptr);
|
id = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
svc = svc_find_by_jobid(atonum(input), id);
|
if (isdigit(input[0])) {
|
||||||
if (!svc || !svc_is_inetd(svc)) {
|
char *ep;
|
||||||
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
|
long job = 0;
|
||||||
return 1;
|
|
||||||
|
errno = 0;
|
||||||
|
job = strtol(input, &ep, 10);
|
||||||
|
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
|
||||||
|
(errno != 0 && job == 0) ||
|
||||||
|
(input == ep))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!id)
|
||||||
|
return svc_job_iterator(&iter, 1, job);
|
||||||
|
|
||||||
|
return svc_find_by_jobid(job, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!id)
|
||||||
|
return svc_named_iterator(&iter, 1, input);
|
||||||
|
|
||||||
|
return svc_find_by_nameid(input, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef INETD_ENABLED
|
||||||
|
static int do_query_inetd(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
svc = do_find(buf, len);
|
||||||
|
if (!svc || !svc_is_inetd(svc))
|
||||||
|
return 1;
|
||||||
|
|
||||||
return inetd_filter_str(&svc->inetd, buf, len);
|
return inetd_filter_str(&svc->inetd, buf, len);
|
||||||
}
|
}
|
||||||
#endif /* INETD_ENABLED */
|
#endif /* INETD_ENABLED */
|
||||||
@@ -163,20 +222,38 @@ static int do_handle_emit(char *buf, size_t len)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void send_svc(int sd, svc_t *svc)
|
||||||
|
{
|
||||||
|
svc_t empty;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (!svc) {
|
||||||
|
empty.pid = -1;
|
||||||
|
svc = ∅
|
||||||
|
}
|
||||||
|
|
||||||
|
len = write(sd, svc, sizeof(*svc));
|
||||||
|
if (len != sizeof(*svc))
|
||||||
|
_d("Failed sending svc_t to client");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In contrast to the SysV compat handling in plugins/initctl.c, when
|
* In contrast to the SysV compat handling in plugins/initctl.c, when
|
||||||
* `initctl runlevel 0` is issued we default to POWERDOWN the system
|
* `initctl runlevel 0` is issued we default to POWERDOWN the system
|
||||||
* instead of just halting.
|
* instead of just halting.
|
||||||
*/
|
*/
|
||||||
static void cb(uev_t *w, void *arg, int events)
|
static void api_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
int sd, lvl;
|
int sd, lvl;
|
||||||
|
svc_t *svc;
|
||||||
|
static svc_t *iter = NULL;
|
||||||
struct init_request rq;
|
struct init_request rq;
|
||||||
|
|
||||||
sd = accept(w->fd, NULL, NULL);
|
sd = accept(w->fd, NULL, NULL);
|
||||||
if (sd < 0) {
|
if (sd < 0) {
|
||||||
_pe("Failed serving API request");
|
_pe("Failed serving API request");
|
||||||
return;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -228,37 +305,46 @@ static void cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_DEBUG:
|
case INIT_CMD_DEBUG:
|
||||||
|
_d("debug");
|
||||||
log_debug();
|
log_debug();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||||
|
_d("reload");
|
||||||
service_reload_dynamic();
|
service_reload_dynamic();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_START_SVC:
|
case INIT_CMD_START_SVC:
|
||||||
|
_d("start %s", rq.data);
|
||||||
result = do_start(rq.data, sizeof(rq.data));
|
result = do_start(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_STOP_SVC:
|
case INIT_CMD_STOP_SVC:
|
||||||
|
_d("stop %s", rq.data);
|
||||||
result = do_stop(rq.data, sizeof(rq.data));
|
result = do_stop(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RESTART_SVC:
|
case INIT_CMD_RESTART_SVC:
|
||||||
|
_d("restart %s", rq.data);
|
||||||
result = do_restart(rq.data, sizeof(rq.data));
|
result = do_restart(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
case INIT_CMD_QUERY_INETD:
|
case INIT_CMD_QUERY_INETD:
|
||||||
|
_d("query inetd");
|
||||||
result = do_query_inetd(rq.data, sizeof(rq.data));
|
result = do_query_inetd(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case INIT_CMD_EMIT:
|
case INIT_CMD_EMIT:
|
||||||
|
_d("emit %s", rq.data);
|
||||||
result = do_handle_emit(rq.data, sizeof(rq.data));
|
result = do_handle_emit(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_GET_RUNLEVEL:
|
case INIT_CMD_GET_RUNLEVEL:
|
||||||
rq.runlevel = runlevel;
|
_d("get runlevel");
|
||||||
|
rq.runlevel = runlevel;
|
||||||
|
rq.sleeptime = prevlevel;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_ACK:
|
case INIT_CMD_ACK:
|
||||||
@@ -266,18 +352,52 @@ static void cb(uev_t *w, void *arg, int events)
|
|||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
case INIT_CMD_WDOG_HELLO:
|
case INIT_CMD_WDOG_HELLO:
|
||||||
|
_d("wdog hello");
|
||||||
if (rq.runlevel <= 0) {
|
if (rq.runlevel <= 0) {
|
||||||
result = 1;
|
result = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wdogpid > 0 && wdogpid != rq.runlevel) {
|
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||||
kill(wdogpid, SIGTERM);
|
svc = svc_find_by_pid(rq.runlevel);
|
||||||
do_sleep(1);
|
if (!svc) {
|
||||||
|
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
wdogpid = rq.runlevel;
|
|
||||||
|
/* Disable and allow Finit to collect bundled watchdog */
|
||||||
|
if (wdog) {
|
||||||
|
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
|
||||||
|
stop(wdog);
|
||||||
|
if (wdog->protect) {
|
||||||
|
wdog->protect = 0;
|
||||||
|
wdog->runlevels = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wdog = svc;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case INIT_CMD_SVC_ITER:
|
||||||
|
_d("svc iter, first: %d", rq.runlevel);
|
||||||
|
/*
|
||||||
|
* XXX: This severly limits the number of
|
||||||
|
* simultaneous client connections, but will
|
||||||
|
* have to do for now.
|
||||||
|
*/
|
||||||
|
svc = svc_iterator(&iter, rq.runlevel);
|
||||||
|
send_svc(sd, svc);
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
case INIT_CMD_SVC_QUERY:
|
||||||
|
_d("svc query: %s", rq.data);
|
||||||
|
result = do_query(&rq, len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INIT_CMD_SVC_FIND:
|
||||||
|
_d("svc find: %s", rq.data);
|
||||||
|
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
|
||||||
|
goto leave;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_d("Unsupported cmd: %d", rq.cmd);
|
_d("Unsupported cmd: %d", rq.cmd);
|
||||||
break;
|
break;
|
||||||
@@ -294,6 +414,13 @@ static void cb(uev_t *w, void *arg, int events)
|
|||||||
|
|
||||||
leave:
|
leave:
|
||||||
close(sd);
|
close(sd);
|
||||||
|
if (UEV_ERROR == events)
|
||||||
|
goto error;
|
||||||
|
return;
|
||||||
|
error:
|
||||||
|
api_exit();
|
||||||
|
if (api_init(w->ctx))
|
||||||
|
_e("Unrecoverable error on API socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
int api_init(uev_ctx_t *ctx)
|
int api_init(uev_ctx_t *ctx)
|
||||||
@@ -305,6 +432,7 @@ int api_init(uev_ctx_t *ctx)
|
|||||||
.sun_path = INIT_SOCKET,
|
.sun_path = INIT_SOCKET,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_d("Setting up external API socket ...");
|
||||||
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
if (-1 == sd) {
|
if (-1 == sd) {
|
||||||
_pe("Failed starting external API socket");
|
_pe("Failed starting external API socket");
|
||||||
@@ -320,7 +448,7 @@ int api_init(uev_ctx_t *ctx)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
umask(oldmask);
|
umask(oldmask);
|
||||||
if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ))
|
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@@ -332,8 +460,9 @@ error:
|
|||||||
|
|
||||||
int api_exit(void)
|
int api_exit(void)
|
||||||
{
|
{
|
||||||
return shutdown(api_watcher.fd, SHUT_RDWR) ||
|
uev_io_stop(&api_watcher);
|
||||||
close(api_watcher.fd);
|
|
||||||
|
return close(api_watcher.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Called by kernel when last child in cgroup exists
|
||||||
|
|
||||||
|
BASE=/sys/fs/cgroup/finit
|
||||||
|
PROC=`basename $1`
|
||||||
|
DIR="$BASE$1"
|
||||||
|
|
||||||
|
logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
||||||
|
rmdir $DIR
|
||||||
|
|
||||||
|
exit 0
|
||||||
+219
@@ -0,0 +1,219 @@
|
|||||||
|
/* Finit control group support functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <lite/lite.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int active;
|
||||||
|
char cgroup[16];
|
||||||
|
char path[32];
|
||||||
|
} cg_avail[200];
|
||||||
|
|
||||||
|
static int cg_init = 0;
|
||||||
|
static int cg_num = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by Finit at early boot to mount initial cgroups
|
||||||
|
*/
|
||||||
|
void cgroup_init(void)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char buf[80];
|
||||||
|
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||||
|
|
||||||
|
fp = fopen("/proc/cgroups", "r");
|
||||||
|
if (!fp) {
|
||||||
|
_d("No cgroup support");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
|
||||||
|
_d("Failed mounting cgroups");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip first line, header */
|
||||||
|
fgets(buf, sizeof(buf), fp);
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
char *cgroup;
|
||||||
|
char rc[80];
|
||||||
|
|
||||||
|
cgroup = strtok(buf, "\t ");
|
||||||
|
if (!cgroup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||||
|
mkdir(rc, 0755);
|
||||||
|
if (mount("cgroup", rc, "cgroup", opts, cgroup)) {
|
||||||
|
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cg_avail[cg_num].active = 1;
|
||||||
|
strlcpy(cg_avail[cg_num].cgroup, cgroup, sizeof(cg_avail[cg_num].cgroup));
|
||||||
|
strlcpy(cg_avail[cg_num].path, rc, sizeof(cg_avail[cg_num].path));
|
||||||
|
cg_num++;
|
||||||
|
|
||||||
|
/* Create default group in all available cgroups */
|
||||||
|
strlcat(rc, "/default", sizeof(rc));
|
||||||
|
mkdir(rc, 0755);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default cgroups for process monitoring */
|
||||||
|
mkdir("/sys/fs/cgroup/finit", 0755);
|
||||||
|
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
|
||||||
|
_pe("Failed mounting Finit cgroup hierarchy");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up reaper and enable callbacks */
|
||||||
|
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||||
|
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
|
||||||
|
|
||||||
|
/* Default groups, PID 1, services, and user/login processes */
|
||||||
|
mkdir("/sys/fs/cgroup/finit/init", 0755);
|
||||||
|
mkdir("/sys/fs/cgroup/finit/system", 0755);
|
||||||
|
mkdir("/sys/fs/cgroup/finit/user", 0755);
|
||||||
|
|
||||||
|
/* Move ourselves to init */
|
||||||
|
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
|
||||||
|
|
||||||
|
/* We have signal, main screen turn on! */
|
||||||
|
cg_init = 1;
|
||||||
|
fail:
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_add(char *name, void (*cb)(char *, void *), void *arg)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cg_num; i++) {
|
||||||
|
char path[80];
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", cg_avail[i].path, name);
|
||||||
|
if (mkdir(path, 0755) && EEXIST != errno) {
|
||||||
|
rc++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: Add to cache of added groups, for cgroup_find() */
|
||||||
|
if (cb)
|
||||||
|
cb(path, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: Temporary hackish implementation */
|
||||||
|
int cgroup_find(char *name, char *group, size_t len)
|
||||||
|
{
|
||||||
|
char path[80];
|
||||||
|
|
||||||
|
if (cg_num <= 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", cg_avail[0].path, name);
|
||||||
|
if (access(path, F_OK))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
strlcpy(group, name, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* int cgroup_cpuset_range(char *group, int min, int max) */
|
||||||
|
/* { */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
static int move_pid(char *group, char *name, int pid)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||||
|
if (mkdir(path, 0755) && errno != EEXIST)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
strlcat(path, "/cgroup.procs", sizeof(path));
|
||||||
|
|
||||||
|
return echo(path, 0, "%d", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_user(char *name)
|
||||||
|
{
|
||||||
|
if (!cg_init)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return move_pid("finit/user", name, getpid());
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_service(char *cmd, int pid)
|
||||||
|
{
|
||||||
|
char *nm;
|
||||||
|
|
||||||
|
if (!cg_init)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (pid <= 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm = strrchr(cmd, '/');
|
||||||
|
if (nm)
|
||||||
|
nm++;
|
||||||
|
else
|
||||||
|
nm = cmd;
|
||||||
|
|
||||||
|
return move_pid("finit/system", nm, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_assign(char *name, int pid)
|
||||||
|
{
|
||||||
|
char path[120];
|
||||||
|
int i, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < cg_num; i++) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/cgroup.procs",
|
||||||
|
cg_avail[i].path, name);
|
||||||
|
rc += echo(path, 0, "%d", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/* Finit control group support functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_CGROUP_H_
|
||||||
|
#define FINIT_CGROUP_H_
|
||||||
|
|
||||||
|
void cgroup_init (void);
|
||||||
|
|
||||||
|
int cgroup_add (char *name, void (*cb)(char *, void *), void *arg);
|
||||||
|
int cgroup_find (char *name, char *group, size_t len);
|
||||||
|
|
||||||
|
int cgroup_user (char *name);
|
||||||
|
int cgroup_service (char *cmd, int pid);
|
||||||
|
int cgroup_assign (char *name, int pid);
|
||||||
|
|
||||||
|
#endif /* FINIT_CGROUP_H_ */
|
||||||
+109
-103
@@ -1,7 +1,6 @@
|
|||||||
/* Simple telinit client
|
/* External client API, using UNIX domain socket.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2015-2017 Joachim Nilsson <troglobit@gmail.com>
|
||||||
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -22,133 +21,140 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* Generated by configure script */
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include "client.h"
|
||||||
#include <getopt.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <lite/lite.h>
|
|
||||||
|
|
||||||
#include "finit.h"
|
static int sd = -1;
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
static int do_send(struct init_request *rq)
|
int client_connect(void)
|
||||||
{
|
{
|
||||||
static int fd = -1;
|
struct sockaddr_un sun = {
|
||||||
int result = 255;
|
.sun_family = AF_UNIX,
|
||||||
ssize_t len;
|
.sun_path = INIT_SOCKET,
|
||||||
|
};
|
||||||
|
|
||||||
if (fd == -1) {
|
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (!fexist(FINIT_FIFO)) {
|
if (-1 == sd)
|
||||||
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
|
goto error;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = open(FINIT_FIFO, O_RDWR);
|
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||||
if (-1 == fd) {
|
close(sd);
|
||||||
perror("Failed opening " FINIT_FIFO);
|
goto error;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
len = write(fd, rq, sizeof(*rq));
|
return sd;
|
||||||
if (sizeof(*rq) == len)
|
error:
|
||||||
result = 0;
|
perror("Failed connecting to finit");
|
||||||
|
return -1;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_reload(void)
|
int client_disconnect(void)
|
||||||
{
|
{
|
||||||
struct init_request rq = {
|
int rc;
|
||||||
.magic = INIT_MAGIC,
|
|
||||||
.cmd = INIT_CMD_RELOAD,
|
|
||||||
};
|
|
||||||
|
|
||||||
return do_send(&rq);
|
if (sd < 0) {
|
||||||
}
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
static int set_env(int runlevel)
|
|
||||||
{
|
|
||||||
struct init_request rq = {
|
|
||||||
.magic = INIT_MAGIC,
|
|
||||||
.cmd = INIT_CMD_SETENV,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (runlevel == 0) {
|
|
||||||
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
|
|
||||||
return do_send(&rq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
rc = close(sd);
|
||||||
}
|
sd = -1;
|
||||||
|
|
||||||
static int set_runlevel(int runlevel)
|
|
||||||
{
|
|
||||||
struct init_request rq = {
|
|
||||||
.magic = INIT_MAGIC,
|
|
||||||
.cmd = INIT_CMD_RUNLVL,
|
|
||||||
.runlevel = runlevel,
|
|
||||||
};
|
|
||||||
|
|
||||||
return set_env(runlevel) || do_send(&rq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* telinit q | telinit <0-9> */
|
|
||||||
static int usage(int rc)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
|
||||||
"Options:\n"
|
|
||||||
" -h, --help This help text\n"
|
|
||||||
" -V, --version Show Finit version\n\n"
|
|
||||||
"Commands:\n"
|
|
||||||
" 0 Power-off the system, same as initctl poweroff\n"
|
|
||||||
" 6 Reboot the system, same as initctl reboot\n"
|
|
||||||
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
|
|
||||||
" services in prev. runlevel that are not allowed in new.\n"
|
|
||||||
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
|
|
||||||
" sending SIGHUP to PID 1\n"
|
|
||||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
|
||||||
"\n", prognm);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int client(int argc, char *argv[])
|
int client_send(struct init_request *rq, ssize_t len)
|
||||||
{
|
{
|
||||||
int c;
|
int sd, result = 255;
|
||||||
struct option long_options[] = {
|
|
||||||
{"help", 0, NULL, 'h'},
|
sd = client_connect();
|
||||||
{"version", 0, NULL, 'v'},
|
if (-1 == sd)
|
||||||
{NULL, 0, NULL, 0}
|
return -1;
|
||||||
|
|
||||||
|
if (write(sd, rq, len) != len)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (read(sd, rq, len) != len)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
goto exit;
|
||||||
|
error:
|
||||||
|
perror("Failed communicating with finit");
|
||||||
|
exit:
|
||||||
|
client_disconnect();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_t *client_svc_iterator(int first)
|
||||||
|
{
|
||||||
|
int sd = -1;
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = INIT_CMD_SVC_ITER,
|
||||||
};
|
};
|
||||||
|
static svc_t svc;
|
||||||
|
|
||||||
progname(argv[0]);
|
sd = client_connect();
|
||||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
if (sd == -1)
|
||||||
switch(c) {
|
return NULL;
|
||||||
case 'v':
|
|
||||||
return puts("v" VERSION) == EOF;
|
|
||||||
|
|
||||||
case 'h':
|
if (first)
|
||||||
case '?':
|
rq.runlevel = 1;
|
||||||
return usage(0);
|
else
|
||||||
}
|
rq.runlevel = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (optind < argc) {
|
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||||
int req = (int)argv[optind][0];
|
goto error;
|
||||||
|
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
|
||||||
|
goto error;
|
||||||
|
|
||||||
if (isdigit(req))
|
client_disconnect();
|
||||||
return set_runlevel(req);
|
if (svc.pid < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (req == 'q' || req == 'Q')
|
return &svc;
|
||||||
return do_reload();
|
error:
|
||||||
|
perror("Failed communicating with finit");
|
||||||
|
client_disconnect();
|
||||||
|
sd = -1;
|
||||||
|
|
||||||
if (req == 's' || req == 'S')
|
return NULL;
|
||||||
return set_runlevel(1);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return usage(1);
|
svc_t *client_svc_find(const char *arg)
|
||||||
|
{
|
||||||
|
int sd = -1;
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = INIT_CMD_SVC_FIND,
|
||||||
|
};
|
||||||
|
static svc_t svc;
|
||||||
|
|
||||||
|
sd = client_connect();
|
||||||
|
if (sd == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
|
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||||
|
goto error;
|
||||||
|
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
client_disconnect();
|
||||||
|
if (svc.pid < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &svc;
|
||||||
|
error:
|
||||||
|
client_disconnect();
|
||||||
|
perror("Failed communicating with finit");
|
||||||
|
sd = -1;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is!
|
/* External client API, using UNIX domain socket.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2015-2017 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,40 +21,17 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_CLIENT_H_
|
||||||
|
#define FINIT_CLIENT_H_
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
#include "plugin.h"
|
|
||||||
|
|
||||||
static void lost_pid(void *arg)
|
int client_connect (void);
|
||||||
{
|
int client_disconnect (void);
|
||||||
pid_t pid = (uintptr_t)arg;
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
svc = svc_find_by_pid(pid);
|
int client_send (struct init_request *rq, ssize_t len);
|
||||||
if (!svc)
|
svc_t *client_svc_iterator (int first);
|
||||||
return;
|
svc_t *client_svc_find (const char *arg);
|
||||||
|
|
||||||
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd);
|
#endif /* FINIT_CLIENT_H_ */
|
||||||
}
|
|
||||||
|
|
||||||
static plugin_t plugin = {
|
|
||||||
.name = __FILE__,
|
|
||||||
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
|
|
||||||
};
|
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
|
||||||
{
|
|
||||||
plugin_register(&plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
PLUGIN_EXIT(plugin_exit)
|
|
||||||
{
|
|
||||||
plugin_unregister(&plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Variables:
|
|
||||||
* indent-tabs-mode: t
|
|
||||||
* c-file-style: "linux"
|
|
||||||
* End:
|
|
||||||
*/
|
|
||||||
+112
-26
@@ -1,6 +1,6 @@
|
|||||||
/* Condition engine (write)
|
/* Condition engine (write)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
* Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,33 +25,84 @@
|
|||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
|
#include "pid.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
|
||||||
|
static int cond_set_gen(const char *file, unsigned int gen)
|
||||||
|
{
|
||||||
|
char *ptr, path[256];
|
||||||
|
FILE *fp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* /var/run --> /run symlink may not exist (yet) */
|
||||||
|
ptr = pid_runpath(file, path, sizeof(path));
|
||||||
|
|
||||||
|
fp = fopen(ptr, "w");
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = fprintf(fp, "%u\n", gen);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return (ret > 0) ? 0 : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cond_bump_reconf(void)
|
||||||
|
{
|
||||||
|
unsigned int rgen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If %COND_RECONF does not exist, cond_get_gen() returns 0
|
||||||
|
* meaning that rgen++ is always what we want.
|
||||||
|
*/
|
||||||
|
rgen = cond_get_gen(COND_RECONF);
|
||||||
|
rgen++;
|
||||||
|
|
||||||
|
cond_set_gen(COND_RECONF, rgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cond_checkpath(const char *path)
|
||||||
|
{
|
||||||
|
char buf[MAX_ARG_LEN], *dir;
|
||||||
|
|
||||||
|
strlcpy(buf, path, sizeof(buf));
|
||||||
|
dir = dirname(buf);
|
||||||
|
if (!dir) {
|
||||||
|
_e("Invalid path '%s' for condition", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (makepath(dir) && errno != EEXIST) {
|
||||||
|
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int cond_set_path(const char *path, enum cond_state new)
|
int cond_set_path(const char *path, enum cond_state new)
|
||||||
{
|
{
|
||||||
char buf[MAX_ARG_LEN], *dir;
|
|
||||||
enum cond_state old;
|
enum cond_state old;
|
||||||
|
unsigned int rgen;
|
||||||
|
|
||||||
|
_d("%s", path);
|
||||||
|
|
||||||
|
rgen = cond_get_gen(COND_RECONF);
|
||||||
|
if (!rgen) {
|
||||||
|
_e("Unable to read configuration generation (%s)", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
old = cond_get_path(path);
|
old = cond_get_path(path);
|
||||||
|
|
||||||
switch (new) {
|
switch (new) {
|
||||||
case COND_ON:
|
case COND_ON:
|
||||||
strlcpy(buf, path, sizeof(buf));
|
if (cond_checkpath(path))
|
||||||
dir = dirname(buf);
|
return 0;
|
||||||
if (!dir) {
|
cond_set_gen(path, rgen);
|
||||||
_e("Invalid path '%s' for condition", path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (makepath(dir) && errno != EEXIST) {
|
|
||||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
touch(path);
|
|
||||||
utimensat(0, path, NULL, 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COND_OFF:
|
case COND_OFF:
|
||||||
@@ -88,14 +139,14 @@ static int svc_has_cond(svc_t *svc)
|
|||||||
|
|
||||||
static void cond_update(const char *name)
|
static void cond_update(const char *name)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("%s: match <%s> %s", name ?: "nil", svc->cond, svc->cmd);
|
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,15 +154,38 @@ static void cond_update(const char *name)
|
|||||||
void cond_set(const char *name)
|
void cond_set(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!cond_set_path(cond_path(name), COND_ON))
|
if (!cond_set_path(cond_path(name), COND_ON))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cond_update(name);
|
cond_update(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cond_set_oneshot(const char *name)
|
||||||
|
{
|
||||||
|
const char *path;
|
||||||
|
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = cond_path(name);
|
||||||
|
_d("s => %s", name, path);
|
||||||
|
|
||||||
|
if (cond_checkpath(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
symlink(COND_RECONF, path);
|
||||||
|
cond_update(name);
|
||||||
|
}
|
||||||
|
|
||||||
void cond_clear(const char *name)
|
void cond_clear(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -121,8 +195,8 @@ void cond_clear(const char *name)
|
|||||||
void cond_reload(void)
|
void cond_reload(void)
|
||||||
{
|
{
|
||||||
_d("");
|
_d("");
|
||||||
cond_set_path(COND_RECONF, COND_ON);
|
|
||||||
|
|
||||||
|
cond_bump_reconf();
|
||||||
cond_update(NULL);
|
cond_update(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +210,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
|||||||
if (tflg != FTW_F)
|
if (tflg != FTW_F)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
nm = (char *)fpath + sizeof(COND_PATH);
|
nm = strstr((char *)fpath, COND_DIR);
|
||||||
|
if (!nm) {
|
||||||
|
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm += sizeof(COND_DIR);
|
||||||
_d("Reasserting %s => %s", fpath, nm);
|
_d("Reasserting %s => %s", fpath, nm);
|
||||||
cond_set(nm);
|
cond_set(nm);
|
||||||
|
|
||||||
@@ -149,15 +229,21 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
|||||||
*/
|
*/
|
||||||
void cond_reassert(const char *type)
|
void cond_reassert(const char *type)
|
||||||
{
|
{
|
||||||
_d("%s", type ?: "reload done");
|
_d("%s", type);
|
||||||
if (!type) {
|
|
||||||
cond_set_path(COND_RECONF, COND_OFF);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cond_init(void)
|
||||||
|
{
|
||||||
|
char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
|
||||||
|
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_bump_reconf();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
|
|||||||
+38
-17
@@ -1,6 +1,6 @@
|
|||||||
/* Condition engine (read)
|
/* Condition engine (read)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
* Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -26,15 +26,14 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
|
#include "pid.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
|
||||||
static inline int timespec_newer(const struct timespec *a,
|
|
||||||
const struct timespec *b)
|
|
||||||
{
|
|
||||||
if (a->tv_sec != b->tv_sec)
|
|
||||||
return a->tv_sec > b->tv_sec;
|
|
||||||
|
|
||||||
return a->tv_nsec > b->tv_nsec;
|
char *mkcond(char *buf, size_t len, char *nm)
|
||||||
|
{
|
||||||
|
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *condstr(enum cond_state s)
|
const char *condstr(enum cond_state s)
|
||||||
@@ -50,25 +49,47 @@ const char *condstr(enum cond_state s)
|
|||||||
|
|
||||||
const char *cond_path(const char *name)
|
const char *cond_path(const char *name)
|
||||||
{
|
{
|
||||||
|
char tmp[MAX_ARG_LEN];
|
||||||
static char file[MAX_ARG_LEN];
|
static char file[MAX_ARG_LEN];
|
||||||
|
|
||||||
snprintf(file, sizeof(file), COND_PATH "/%s", name);
|
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
|
||||||
|
|
||||||
return file;
|
return pid_runpath(tmp, file, sizeof(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cond_get_gen(const char *file)
|
||||||
|
{
|
||||||
|
char *ptr, path[256];
|
||||||
|
unsigned int gen;
|
||||||
|
FILE *fp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* /var/run --> /run symlink may not exist (yet) */
|
||||||
|
ptr = pid_runpath(file, path, sizeof(path));
|
||||||
|
|
||||||
|
fp = fopen(ptr, "r");
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = fscanf(fp, "%u", &gen);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return (ret == 1) ? gen : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum cond_state cond_get_path(const char *path)
|
enum cond_state cond_get_path(const char *path)
|
||||||
{
|
{
|
||||||
struct stat st, st_reconf;
|
int cgen, rgen;
|
||||||
|
|
||||||
if (stat(path, &st))
|
rgen = cond_get_gen(COND_RECONF);
|
||||||
|
if (!rgen)
|
||||||
return COND_OFF;
|
return COND_OFF;
|
||||||
|
|
||||||
if (stat(COND_RECONF, &st_reconf) ||
|
cgen = cond_get_gen(path);
|
||||||
timespec_newer(&st.st_mtim, &st_reconf.st_mtim))
|
if (!cgen)
|
||||||
return COND_ON;
|
return COND_OFF;
|
||||||
|
|
||||||
return COND_FLUX;
|
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum cond_state cond_get(const char *name)
|
enum cond_state cond_get(const char *name)
|
||||||
@@ -80,7 +101,7 @@ enum cond_state cond_get_agg(const char *names)
|
|||||||
{
|
{
|
||||||
char *cond;
|
char *cond;
|
||||||
enum cond_state s = COND_ON;
|
enum cond_state s = COND_ON;
|
||||||
static char conds[MAX_ARG_LEN];
|
static char conds[MAX_COND_LEN];
|
||||||
|
|
||||||
if (!names)
|
if (!names)
|
||||||
return COND_ON;
|
return COND_ON;
|
||||||
@@ -95,7 +116,7 @@ enum cond_state cond_get_agg(const char *names)
|
|||||||
int cond_affects(const char *name, const char *names)
|
int cond_affects(const char *name, const char *names)
|
||||||
{
|
{
|
||||||
char *cond;
|
char *cond;
|
||||||
static char conds[MAX_ARG_LEN];
|
static char conds[MAX_COND_LEN];
|
||||||
|
|
||||||
if (!name || !names)
|
if (!name || !names)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+11
-7
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
|
|
||||||
#define COND_PATH _PATH_VARRUN "finit/cond"
|
#define COND_DIR "finit/cond"
|
||||||
#define COND_SVC_PATH COND_PATH "/svc/"
|
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||||
#define COND_RECONF COND_PATH "/reconf"
|
#define COND_RECONF COND_PATH "/reconf"
|
||||||
|
|
||||||
typedef enum cond_state {
|
typedef enum cond_state {
|
||||||
@@ -13,18 +13,22 @@ typedef enum cond_state {
|
|||||||
COND_ON
|
COND_ON
|
||||||
} cond_state_t;
|
} cond_state_t;
|
||||||
|
|
||||||
|
char *mkcond (char *buf, size_t len, char *nm);
|
||||||
const char *condstr (enum cond_state s);
|
const char *condstr (enum cond_state s);
|
||||||
const char *cond_path (const char *name);
|
const char *cond_path (const char *name);
|
||||||
|
unsigned int cond_get_gen (const char *path);
|
||||||
enum cond_state cond_get_path(const char *path);
|
enum cond_state cond_get_path(const char *path);
|
||||||
enum cond_state cond_get (const char *name);
|
enum cond_state cond_get (const char *name);
|
||||||
enum cond_state cond_get_agg (const char *names);
|
enum cond_state cond_get_agg (const char *names);
|
||||||
int cond_affects (const char *name, const char *names);
|
int cond_affects (const char *name, const char *names);
|
||||||
|
|
||||||
int cond_set_path(const char *path, enum cond_state new);
|
int cond_set_path (const char *path, enum cond_state new);
|
||||||
void cond_set (const char *name);
|
void cond_set (const char *name);
|
||||||
void cond_clear (const char *name);
|
void cond_set_oneshot (const char *name);
|
||||||
void cond_reload (void);
|
void cond_clear (const char *name);
|
||||||
void cond_reassert(const char *pat);
|
void cond_reload (void);
|
||||||
|
void cond_reassert (const char *pat);
|
||||||
|
void cond_init (void);
|
||||||
|
|
||||||
#endif /* FINIT_COND_H_ */
|
#endif /* FINIT_COND_H_ */
|
||||||
|
|
||||||
|
|||||||
+526
-137
@@ -25,44 +25,125 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <glob.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define BOOTSTRAP (runlevel == 0)
|
||||||
#define MATCH_CMD(l, c, x) \
|
#define MATCH_CMD(l, c, x) \
|
||||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||||
|
|
||||||
static int parse_conf(char *file);
|
int logfile_size_max = 200000; /* 200 kB */
|
||||||
|
int logfile_count_max = 5;
|
||||||
|
|
||||||
|
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
|
struct conf_change {
|
||||||
|
TAILQ_ENTRY(conf_change) link;
|
||||||
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char cgroup[32];
|
||||||
|
static uev_t w1, w2, w3, w4;
|
||||||
|
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||||
|
|
||||||
|
static int parse_conf(char *file);
|
||||||
|
static void drop_changes(void);
|
||||||
|
|
||||||
void conf_parse_cmdline(void)
|
void conf_parse_cmdline(void)
|
||||||
{
|
{
|
||||||
int dbg = 0;
|
int dbg = 0;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
char line[LINE_SIZE], *cmdline, *tok;
|
||||||
|
|
||||||
fp = fopen("/proc/cmdline", "r");
|
fp = fopen("/proc/cmdline", "r");
|
||||||
if (fp) {
|
if (!fp)
|
||||||
char line[LINE_SIZE];
|
goto done;
|
||||||
|
|
||||||
fgets(line, sizeof(line), fp);
|
|
||||||
chomp(line);
|
|
||||||
_d("%s", line);
|
|
||||||
|
|
||||||
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
|
|
||||||
dbg = 1;
|
|
||||||
|
|
||||||
|
if (!fgets(line, sizeof(line), fp)) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmdline = chomp(line);
|
||||||
|
_d("%s", cmdline);
|
||||||
|
|
||||||
|
while ((tok = strtok(cmdline, " \t"))) {
|
||||||
|
cmdline = NULL;
|
||||||
|
|
||||||
|
/* Catches finit_debug (deprecated), --debug, and debug */
|
||||||
|
if (strstr(tok, "debug"))
|
||||||
|
dbg = 1;
|
||||||
|
|
||||||
|
if (string_compare(tok, "rescue") || string_compare(tok, "recover"))
|
||||||
|
rescue = 1;
|
||||||
|
|
||||||
|
if (string_compare(tok, "single") || string_compare(tok, "S"))
|
||||||
|
single = 1;
|
||||||
|
|
||||||
|
if (string_compare(tok, "splash"))
|
||||||
|
splash = 1;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
done:
|
||||||
log_init(dbg);
|
log_init(dbg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int kmod_exists(char *mod)
|
||||||
|
{
|
||||||
|
int found = 0;
|
||||||
|
FILE *fp;
|
||||||
|
char buf[256];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for (len = 0; mod[len]; len++) {
|
||||||
|
if (mod[len] == ' ' || mod[len] == '\t')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen("/proc/modules", "r");
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (!found && (fgets(buf, sizeof(buf), fp))) {
|
||||||
|
if (!strncmp(buf, mod, len))
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kmod_load(char *line)
|
||||||
|
{
|
||||||
|
char *mod;
|
||||||
|
char cmd[CMD_SIZE];
|
||||||
|
|
||||||
|
if (runlevel != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mod = strip_line(line);
|
||||||
|
if (kmod_exists(mod))
|
||||||
|
return;
|
||||||
|
|
||||||
|
strcpy(cmd, "modprobe ");
|
||||||
|
strlcat(cmd, mod, sizeof(cmd));
|
||||||
|
|
||||||
|
run_interactive(cmd, "Loading kernel module %s", mod);
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||||
int conf_parse_runlevels(char *runlevels)
|
int conf_parse_runlevels(char *runlevels)
|
||||||
{
|
{
|
||||||
@@ -163,76 +244,157 @@ static const struct rlimit_name rlimit_names[] = {
|
|||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
void conf_parse_rlimit(char *line)
|
int str2rlim(char *str)
|
||||||
{
|
{
|
||||||
struct rlimit rlim;
|
const struct rlimit_name *rn;
|
||||||
rlim_t new, *set;
|
|
||||||
const struct rlimit_name *name;
|
|
||||||
int resource = -1;
|
|
||||||
|
|
||||||
char *tok = strtok(line, " \t");
|
for (rn = rlimit_names; rn->name; rn++) {
|
||||||
|
if (!strcmp(str, rn->name))
|
||||||
if (tok && !strcmp(tok, "soft"))
|
return rn->val;
|
||||||
set = &rlim.rlim_cur;
|
|
||||||
else if (tok && !strcmp(tok, "hard"))
|
|
||||||
set = &rlim.rlim_max;
|
|
||||||
else
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
tok = strtok(NULL, " \t");
|
|
||||||
if (!tok)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
for (name = rlimit_names; name->name; name++) {
|
|
||||||
if (!strcmp(tok, name->name))
|
|
||||||
resource = name->val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource < 0)
|
return -1;
|
||||||
goto fail;
|
}
|
||||||
|
|
||||||
tok = strtok(NULL, " \t");
|
char *rlim2str(int rlim)
|
||||||
if (!tok)
|
{
|
||||||
goto fail;
|
const struct rlimit_name *rn;
|
||||||
|
|
||||||
if (!strcmp(tok, "infinity")) {
|
for (rn = rlimit_names; rn->name; rn++) {
|
||||||
new = RLIM_INFINITY;
|
if (rn->val == rlim)
|
||||||
|
return rn->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *lim2str(struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
char tmp[25];
|
||||||
|
static char buf[42];
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||||
|
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||||
|
else
|
||||||
|
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
|
||||||
|
strlcat(buf, tmp, sizeof(buf));
|
||||||
|
|
||||||
|
if (RLIM_INFINITY == rlim->rlim_max)
|
||||||
|
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||||
|
else
|
||||||
|
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
|
||||||
|
strlcat(buf, tmp, sizeof(buf));
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First form: `rlimit <hard|soft> RESOURCE LIMIT` */
|
||||||
|
void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||||
|
{
|
||||||
|
char *level, *limit, *val;
|
||||||
|
int resource = -1;
|
||||||
|
rlim_t cfg;
|
||||||
|
|
||||||
|
level = strtok(line, " \t");
|
||||||
|
if (!level)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
limit = strtok(NULL, " \t");
|
||||||
|
if (!limit)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
val = strtok(NULL, " \t");
|
||||||
|
if (!val) {
|
||||||
|
/* Second form: `rlimit RESOURCE LIMIT` */
|
||||||
|
val = limit;
|
||||||
|
limit = level;
|
||||||
|
level = "both";
|
||||||
|
}
|
||||||
|
|
||||||
|
resource = str2rlim(limit);
|
||||||
|
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
||||||
|
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
||||||
|
cfg = RLIM_INFINITY;
|
||||||
} else {
|
} else {
|
||||||
const char *err = NULL;
|
const char *err = NULL;
|
||||||
|
|
||||||
new = strtonum(tok, 0, (long long)2 << 31, &err);
|
cfg = strtonum(val, 0, (long long)2 << 31, &err);
|
||||||
if (err)
|
if (err) {
|
||||||
goto fail;
|
logit(LOG_WARNING, "rlimit: invalid %s value: %s",
|
||||||
|
rlim2str(resource), val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getrlimit(resource, &rlim))
|
if (!strcmp(level, "soft"))
|
||||||
goto fail;
|
arr[resource].rlim_cur = cfg;
|
||||||
|
else if (!strcmp(level, "hard"))
|
||||||
|
arr[resource].rlim_max = cfg;
|
||||||
|
else if (!strcmp(level, "both"))
|
||||||
|
arr[resource].rlim_max = arr[resource].rlim_cur = cfg;
|
||||||
|
else
|
||||||
|
goto error;
|
||||||
|
|
||||||
*set = new;
|
|
||||||
if (setrlimit(resource, &rlim))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
|
|
||||||
return;
|
return;
|
||||||
error:
|
error:
|
||||||
logit(LOG_WARNING, "rlimit: parse error");
|
logit(LOG_WARNING, "rlimit: parse error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cpuset_cb(char *grp, void *arg)
|
||||||
|
{
|
||||||
|
int cpuset = *(int *)arg;
|
||||||
|
char path[128];
|
||||||
|
|
||||||
|
if (!strstr(grp, "cpuset"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Both .cpus and .mems are mandatory settings */
|
||||||
|
snprintf(path, sizeof(path), "%s/cpuset.cpus", grp);
|
||||||
|
echo(path, 0, "%d", cpuset);
|
||||||
|
snprintf(path, sizeof(path), "%s/cpuset.mems", grp);
|
||||||
|
echo(path, 0, "0");
|
||||||
|
|
||||||
|
/* XXX: HACK for now, fix proper CPU allocation */
|
||||||
|
snprintf(path, sizeof(path), "%s/cpuset.cpu_exclusive", grp);
|
||||||
|
echo(path, 0, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_cgroup(char *line)
|
||||||
|
{
|
||||||
|
char *group, *token;
|
||||||
|
int cpuset = -1;
|
||||||
|
|
||||||
|
group = strtok(line, " ");
|
||||||
|
if (!group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((token = strtok(NULL, " "))) {
|
||||||
|
if (!strncmp(token, "cpuset:", 7)) {
|
||||||
|
token += 7;
|
||||||
|
cpuset = atoi(token); /* XXX: fixme */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpuset >= 0)
|
||||||
|
cgroup_add(group, cpuset_cb, &cpuset);
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_static(char *line)
|
static void parse_static(char *line)
|
||||||
{
|
{
|
||||||
char *x;
|
char *x;
|
||||||
char cmd[CMD_SIZE];
|
char cmd[CMD_SIZE];
|
||||||
|
|
||||||
if (MATCH_CMD(line, "host ", x)) {
|
if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
|
||||||
if (hostname) free(hostname);
|
if (hostname) free(hostname);
|
||||||
hostname = strdup(strip_line(x));
|
hostname = strdup(strip_line(x));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "mknod ", x)) {
|
if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) {
|
||||||
char *dev = strip_line(x);
|
char *dev = strip_line(x);
|
||||||
|
|
||||||
strcpy(cmd, "mknod ");
|
strcpy(cmd, "mknod ");
|
||||||
@@ -242,13 +404,13 @@ static void parse_static(char *line)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "network ", x)) {
|
if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) {
|
||||||
if (network) free(network);
|
if (network) free(network);
|
||||||
network = strdup(strip_line(x));
|
network = strdup(strip_line(x));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "runparts ", x)) {
|
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
|
||||||
if (runparts) free(runparts);
|
if (runparts) free(runparts);
|
||||||
runparts = strdup(strip_line(x));
|
runparts = strdup(strip_line(x));
|
||||||
return;
|
return;
|
||||||
@@ -267,16 +429,38 @@ static void parse_static(char *line)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "log ", x)) {
|
||||||
|
char *tok;
|
||||||
|
static int size = 200000, count = 5;
|
||||||
|
|
||||||
|
tok = strtok(x, ":= ");
|
||||||
|
while (tok) {
|
||||||
|
if (!strncmp(tok, "size", 4))
|
||||||
|
size = strtobytes(strtok(NULL, ":= "));
|
||||||
|
else if (!strncmp(tok, "count", 5))
|
||||||
|
count = strtobytes(strtok(NULL, ":= "));
|
||||||
|
|
||||||
|
tok = strtok(NULL, ":= ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size >= 0)
|
||||||
|
logfile_size_max = size;
|
||||||
|
if (count >= 0)
|
||||||
|
logfile_count_max = count;
|
||||||
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||||
if (sdown) free(sdown);
|
if (sdown) free(sdown);
|
||||||
sdown = strdup(strip_line(x));
|
sdown = strdup(strip_line(x));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The desired runlevel to start when leaving bootstrap (S).
|
/*
|
||||||
|
* The desired runlevel to start when leaving bootstrap (S).
|
||||||
* Finit supports 1-9, but most systems only use 1-6, where
|
* Finit supports 1-9, but most systems only use 1-6, where
|
||||||
* 6 is reserved for reboot */
|
* 6 is reserved for reboot and 0 for halt/poweroff.
|
||||||
if (MATCH_CMD(line, "runlevel ", x)) {
|
*/
|
||||||
|
if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) {
|
||||||
char *token = strip_line(x);
|
char *token = strip_line(x);
|
||||||
const char *err = NULL;
|
const char *err = NULL;
|
||||||
|
|
||||||
@@ -287,12 +471,16 @@ static void parse_static(char *line)
|
|||||||
cfglevel = 2; /* Fallback */
|
cfglevel = 2; /* Fallback */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||||
|
parse_cgroup(strip_line(x));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_dynamic(char *line, struct timeval *mtime)
|
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||||
{
|
{
|
||||||
char *x;
|
char *x;
|
||||||
char cmd[CMD_SIZE];
|
|
||||||
|
|
||||||
/* Skip comments, i.e. lines beginning with # */
|
/* Skip comments, i.e. lines beginning with # */
|
||||||
if (MATCH_CMD(line, "#", x))
|
if (MATCH_CMD(line, "#", x))
|
||||||
@@ -300,41 +488,32 @@ static void parse_dynamic(char *line, struct timeval *mtime)
|
|||||||
|
|
||||||
/* Kernel module to load at bootstrap */
|
/* Kernel module to load at bootstrap */
|
||||||
if (MATCH_CMD(line, "module ", x)) {
|
if (MATCH_CMD(line, "module ", x)) {
|
||||||
char *mod;
|
kmod_load(x);
|
||||||
|
|
||||||
if (runlevel != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mod = strip_line(x);
|
|
||||||
strcpy(cmd, "modprobe ");
|
|
||||||
strlcat(cmd, mod, sizeof(cmd));
|
|
||||||
run_interactive(cmd, "Loading kernel module %s", mod);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Monitored daemon, will be respawned on exit */
|
/* Monitored daemon, will be respawned on exit */
|
||||||
if (MATCH_CMD(line, "service ", x)) {
|
if (MATCH_CMD(line, "service ", x)) {
|
||||||
service_register(SVC_TYPE_SERVICE, x, mtime);
|
service_register(SVC_TYPE_SERVICE, x, rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* One-shot task, will not be respawned */
|
/* One-shot task, will not be respawned */
|
||||||
if (MATCH_CMD(line, "task ", x)) {
|
if (MATCH_CMD(line, "task ", x)) {
|
||||||
service_register(SVC_TYPE_TASK, x, mtime);
|
service_register(SVC_TYPE_TASK, x, rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like task but waits for completion, useful w/ [S] */
|
/* Like task but waits for completion, useful w/ [S] */
|
||||||
if (MATCH_CMD(line, "run ", x)) {
|
if (MATCH_CMD(line, "run ", x)) {
|
||||||
service_register(SVC_TYPE_RUN, x, mtime);
|
service_register(SVC_TYPE_RUN, x, rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Classic inetd service */
|
/* Classic inetd service */
|
||||||
if (MATCH_CMD(line, "inetd ", x)) {
|
if (MATCH_CMD(line, "inetd ", x)) {
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
service_register(SVC_TYPE_INETD, x, mtime);
|
service_register(SVC_TYPE_INETD, x, rlimit, cgroup, file);
|
||||||
#else
|
#else
|
||||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||||
#endif
|
#endif
|
||||||
@@ -343,20 +522,19 @@ static void parse_dynamic(char *line, struct timeval *mtime)
|
|||||||
|
|
||||||
/* Read resource limits */
|
/* Read resource limits */
|
||||||
if (MATCH_CMD(line, "rlimit ", x)) {
|
if (MATCH_CMD(line, "rlimit ", x)) {
|
||||||
conf_parse_rlimit(x);
|
conf_parse_rlimit(x, rlimit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Special TTY console powers */
|
/* All subsequent run/task/service/tty/etc. directives to use this cgroup */
|
||||||
if (MATCH_CMD(line, "console ", x)) {
|
if (MATCH_CMD(line, "group ", x)) {
|
||||||
if (console) free(console);
|
cgroup_find(x, cgroup, sizeof(cgroup));
|
||||||
console = strdup(strip_line(x));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Regular or serial TTYs to run getty */
|
/* Regular or serial TTYs to run getty */
|
||||||
if (MATCH_CMD(line, "tty ", x)) {
|
if (MATCH_CMD(line, "tty ", x)) {
|
||||||
tty_register(strip_line(x), mtime);
|
tty_register(strip_line(x), rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,8 +550,9 @@ static void tabstospaces(char *line)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
static int parse_conf_dynamic(char *file)
|
||||||
{
|
{
|
||||||
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
fp = fopen(file, "r");
|
fp = fopen(file, "r");
|
||||||
@@ -382,7 +561,13 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Parsing %s", file);
|
/* Prepare default limits for each service */
|
||||||
|
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||||
|
|
||||||
|
/* Prepare default cgroup for each service */
|
||||||
|
strlcpy(cgroup, "default", sizeof(cgroup));
|
||||||
|
|
||||||
|
_d("Parsing %s <<<<<<", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
char line[LINE_SIZE] = "";
|
char line[LINE_SIZE] = "";
|
||||||
|
|
||||||
@@ -393,7 +578,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
|||||||
tabstospaces(line);
|
tabstospaces(line);
|
||||||
_d("%s", line);
|
_d("%s", line);
|
||||||
|
|
||||||
parse_dynamic(line, mtime);
|
parse_dynamic(line, rlimit, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -405,34 +590,19 @@ static int parse_conf(char *file)
|
|||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char line[LINE_SIZE] = "";
|
char line[LINE_SIZE] = "";
|
||||||
char *x;
|
|
||||||
|
/*
|
||||||
|
* Get current global limits, which may be overridden from both
|
||||||
|
* finit.conf, for Finit and its services like inetd+getty, and
|
||||||
|
* *.conf in finit.d/, for each service(s) listed there.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < RLIMIT_NLIMITS; i++)
|
||||||
|
getrlimit(i, &global_rlimit[i]);
|
||||||
|
|
||||||
fp = fopen(file, "r");
|
fp = fopen(file, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* If not standard finit.conf, then we want to show just the base name
|
|
||||||
* Loading configuration ............. vs
|
|
||||||
* Loading services configuration ....
|
|
||||||
*/
|
|
||||||
if (!string_match (file, FINIT_CONF)) {
|
|
||||||
/* Remove leading path */
|
|
||||||
x = strrchr(file, '/');
|
|
||||||
if (!x) x = file;
|
|
||||||
else x++;
|
|
||||||
|
|
||||||
/* Remove ending .conf */
|
|
||||||
strlcpy(line, x, sizeof(line));
|
|
||||||
x = strstr(line, ".conf");
|
|
||||||
if (x) *x = 0;
|
|
||||||
|
|
||||||
/* Add empty space. */
|
|
||||||
strcat(line, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
print(0, "Loading %sconfiguration", line);
|
|
||||||
|
|
||||||
_d("Parsing %s", file);
|
_d("Parsing %s", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
if (!fgets(line, sizeof(line), fp))
|
if (!fgets(line, sizeof(line), fp))
|
||||||
@@ -443,43 +613,60 @@ static int parse_conf(char *file)
|
|||||||
_d("%s", line);
|
_d("%s", line);
|
||||||
|
|
||||||
parse_static(line);
|
parse_static(line);
|
||||||
parse_dynamic(line, NULL);
|
parse_dynamic(line, global_rlimit, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
|
/* Set global limits */
|
||||||
|
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||||
|
if (setrlimit(i, &global_rlimit[i]) == -1)
|
||||||
|
logit(LOG_WARNING, "rlimit: Failed setting %s: %s",
|
||||||
|
rlim2str(i), lim2str(&global_rlimit[i]));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reload all *.conf in /etc/finit.d/ */
|
/*
|
||||||
int conf_reload_dynamic(void)
|
* Reload /etc/finit.conf and all *.conf in /etc/finit.d/
|
||||||
|
*/
|
||||||
|
int conf_reload(void)
|
||||||
{
|
{
|
||||||
int i, num;
|
size_t i;
|
||||||
char *dir = rcsd;
|
glob_t gl;
|
||||||
struct dirent **e;
|
|
||||||
|
|
||||||
/* Mark and sweep */
|
/* Mark and sweep */
|
||||||
svc_mark_dynamic();
|
svc_mark_dynamic();
|
||||||
tty_mark();
|
tty_mark();
|
||||||
|
|
||||||
num = scandir(dir, &e, NULL, alphasort);
|
if (rescue) {
|
||||||
if (num < 0) {
|
int rc;
|
||||||
_d("Skipping %s, no files found ...", dir);
|
char line[80] = "tty [12345] @console noclear nologin";
|
||||||
return 1;
|
|
||||||
|
/* If rescue.conf is missing, fall back to a root shell */
|
||||||
|
rc = parse_conf(RESCUE_CONF);
|
||||||
|
if (rc)
|
||||||
|
tty_register(line, global_rlimit, cgroup, NULL);
|
||||||
|
|
||||||
|
print(rc, "Entering rescue mode");
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
/* First, read /etc/finit.conf */
|
||||||
char *name = e[i]->d_name;
|
parse_conf(FINIT_CONF);
|
||||||
char path[LINE_SIZE];
|
|
||||||
|
/* Next, read all *.conf in /etc/finit.d/ */
|
||||||
|
glob("/etc/finit.d/*.conf", 0, NULL, &gl);
|
||||||
|
glob("/etc/finit.d/enabled/*.conf", GLOB_APPEND, NULL, &gl);
|
||||||
|
|
||||||
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
char *path = gl.gl_pathv[i];
|
||||||
size_t len;
|
size_t len;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct timeval mtime;
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
|
||||||
|
|
||||||
/* Check that it's an actual file ... beyond any symlinks */
|
/* Check that it's an actual file ... beyond any symlinks */
|
||||||
if (lstat(path, &st)) {
|
if (lstat(path, &st)) {
|
||||||
lost:
|
|
||||||
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -496,10 +683,6 @@ int conf_reload_dynamic(void)
|
|||||||
|
|
||||||
rp = realpath(path, NULL);
|
rp = realpath(path, NULL);
|
||||||
if (!rp) {
|
if (!rp) {
|
||||||
/*
|
|
||||||
* XXX: Prune from rcsd?
|
|
||||||
* XXX: Possibly temporary service from last boot
|
|
||||||
*/
|
|
||||||
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
|
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -514,27 +697,233 @@ int conf_reload_dynamic(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(path, &st))
|
parse_conf_dynamic(path);
|
||||||
goto lost; /* Weird, lost file between here and lstat() */
|
|
||||||
|
|
||||||
TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim);
|
|
||||||
parse_conf_dynamic(path, &mtime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (num--)
|
globfree(&gl);
|
||||||
free(e[num]);
|
|
||||||
free(e);
|
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* Drop record of all .conf changes */
|
||||||
|
drop_changes();
|
||||||
|
|
||||||
|
/* Override configured runlevel, user said 'S' on /proc/cmdline */
|
||||||
|
if (BOOTSTRAP && single)
|
||||||
|
cfglevel = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set host name, from %DEFHOST, *.conf or /etc/hostname. The
|
||||||
|
* latter wins, if neither exists we default to "noname"
|
||||||
|
*/
|
||||||
set_hostname(&hostname);
|
set_hostname(&hostname);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int conf_parse_config(void)
|
static struct conf_change *conf_find(char *file)
|
||||||
|
{
|
||||||
|
struct conf_change *node, *tmp;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) {
|
||||||
|
if (string_compare(node->name, file))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drop_change(struct conf_change *node)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&conf_change_list, node, link);
|
||||||
|
free(node->name);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void drop_changes(void)
|
||||||
|
{
|
||||||
|
struct conf_change *node, *tmp;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp)
|
||||||
|
drop_change(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_change(char *name, uint32_t mask)
|
||||||
|
{
|
||||||
|
struct conf_change *node;
|
||||||
|
|
||||||
|
node = conf_find(name);
|
||||||
|
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
|
||||||
|
drop_change(node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
_d("Event already registered for %s ...", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = malloc(sizeof(*node));
|
||||||
|
if (!node)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
node->name = strdup(name);
|
||||||
|
if (!node->name) {
|
||||||
|
free(node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Event registered for %s, mask 0x%x", name, mask);
|
||||||
|
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int conf_any_change(void)
|
||||||
|
{
|
||||||
|
if (TAILQ_EMPTY(&conf_change_list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int conf_changed(char *file)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((ptr = strrchr(file, '/')))
|
||||||
|
file = ++ptr;
|
||||||
|
|
||||||
|
if (conf_find(file))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void conf_cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
|
struct inotify_event *ev;
|
||||||
|
ssize_t sz, len;
|
||||||
|
|
||||||
|
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
|
if (sz <= 0) {
|
||||||
|
_pe("invalid inotify event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev_buf[sz] = 0;
|
||||||
|
ev = (struct inotify_event *)ev_buf;
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
do_change(arg, ev->mask);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
|
||||||
|
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||||
|
if (do_change(ev->name, ev->mask)) {
|
||||||
|
_pe("conf_monitor: Out of memory");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf_any_change())
|
||||||
|
service_reload_dynamic();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE;
|
||||||
|
char *arg = NULL;
|
||||||
|
int fd, wd;
|
||||||
|
|
||||||
|
if (!ctx)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (stat(path, &st)) {
|
||||||
|
_d("No such file or directory, skipping %s", path);
|
||||||
|
w->fd = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!S_ISDIR(st.st_mode)) {
|
||||||
|
arg = strrchr(path, '/');
|
||||||
|
if (!arg)
|
||||||
|
arg = path;
|
||||||
|
else
|
||||||
|
arg++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->fd >= 0)
|
||||||
|
close(w->fd);
|
||||||
|
|
||||||
|
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
_pe("Failed creating inotify descriptor");
|
||||||
|
w->fd = -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only forward error, don't report error,
|
||||||
|
* user may not have @path and that's OK
|
||||||
|
*/
|
||||||
|
wd = inotify_add_watch(fd, path, mask | opt);
|
||||||
|
if (wd < 0) {
|
||||||
|
w->fd = -1;
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uev_io_init(ctx, w, conf_cb, arg, fd, UEV_READ)) {
|
||||||
|
_pe("Failed setting up I/O callback for %s watcher", path);
|
||||||
|
w->fd = -1;
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up inotify watcher and load all *.conf in /etc/finit.d/
|
||||||
|
*/
|
||||||
|
int conf_monitor(uev_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Skip second run, when called from finit.c in rescue mode */
|
||||||
|
if (ctx && rescue)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If only one watcher fails, that's OK. A user may have only
|
||||||
|
* one of /etc/finit.conf or /etc/finit.d in use, and may also
|
||||||
|
* have or not have symlinks in place. We need to monitor for
|
||||||
|
* changes to either symlink or target.
|
||||||
|
*/
|
||||||
|
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
|
||||||
|
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available/", IN_DONT_FOLLOW);
|
||||||
|
rc += add_watcher(ctx, &w3, FINIT_RCSD "/enabled/", 0);
|
||||||
|
rc += add_watcher(ctx, &w4, FINIT_CONF, 0);
|
||||||
|
|
||||||
|
return rc + conf_reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare .conf parser and load all .conf files
|
||||||
|
*/
|
||||||
|
int conf_init(void)
|
||||||
{
|
{
|
||||||
hostname = strdup(DEFHOST);
|
hostname = strdup(DEFHOST);
|
||||||
|
w1.fd = w2.fd = w3.fd = w4.fd = -1;
|
||||||
|
|
||||||
return parse_conf(FINIT_CONF) || conf_reload_dynamic();
|
return conf_monitor(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+14
-2
@@ -26,11 +26,23 @@
|
|||||||
|
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
|
extern int logfile_size_max;
|
||||||
|
extern int logfile_count_max;
|
||||||
|
|
||||||
|
extern struct rlimit global_rlimit[];
|
||||||
|
|
||||||
|
int str2rlim(char *str);
|
||||||
|
char *rlim2str(int rlim);
|
||||||
|
|
||||||
|
int conf_init (void);
|
||||||
|
void conf_reload (void);
|
||||||
|
int conf_any_change (void);
|
||||||
|
int conf_changed (char *file);
|
||||||
|
int conf_monitor (uev_ctx_t *ctx);
|
||||||
|
|
||||||
void conf_parse_cmdline (void);
|
void conf_parse_cmdline (void);
|
||||||
int conf_parse_runlevels (char *runlevels);
|
int conf_parse_runlevels (char *runlevels);
|
||||||
void conf_parse_cond (svc_t *svc, char *cond);
|
void conf_parse_cond (svc_t *svc, char *cond);
|
||||||
int conf_parse_config (void);
|
|
||||||
void conf_reload_dynamic (void);
|
|
||||||
|
|
||||||
#endif /* FINIT_CONF_H_ */
|
#endif /* FINIT_CONF_H_ */
|
||||||
|
|
||||||
|
|||||||
+136
-35
@@ -30,17 +30,18 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||||
|
#include <termios.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
#define NUM_ARGS 16
|
#define NUM_ARGS 16
|
||||||
|
|
||||||
int getty(char *tty, char *baud, char *term, char *user);
|
|
||||||
|
|
||||||
|
|
||||||
/* Wait for process completion, returns status of waitpid(2) syscall */
|
/* Wait for process completion, returns status of waitpid(2) syscall */
|
||||||
int complete(char *cmd, int pid)
|
int complete(char *cmd, int pid)
|
||||||
@@ -234,30 +235,77 @@ int exec_runtask(char *cmd, char *args[])
|
|||||||
return execvp(_PATH_BSHELL, argv);
|
return execvp(_PATH_BSHELL, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepare_tty(char *tty, char *procname, int console)
|
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
|
||||||
{
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
struct termios term;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset to sane defaults in case of messup from prev. session
|
||||||
|
*/
|
||||||
|
stty(STDIN_FILENO, speed);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
|
||||||
|
* It is up to the getty process to allow them again.
|
||||||
|
*/
|
||||||
|
tcdrain(STDIN_FILENO);
|
||||||
|
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||||
|
term.c_lflag &= ~ISIG;
|
||||||
|
term.c_cc[VEOF] = _POSIX_VDISABLE;
|
||||||
|
term.c_cc[VINTR] = _POSIX_VDISABLE;
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset signal handlers that were set by the parent process */
|
/* Reset signal handlers that were set by the parent process */
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore a few signals, needed to prevent Ctrl-C at login:
|
||||||
|
* prompt and to prevent QUIT from dumping core.
|
||||||
|
*/
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
sa.sa_handler = SIG_IGN;
|
||||||
|
sigaction(SIGHUP, &sa, NULL);
|
||||||
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
sigaction(SIGQUIT, &sa, NULL);
|
||||||
|
|
||||||
|
/* Set configured limits */
|
||||||
|
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||||
|
if (setrlimit(i, &rlimit[i]) == -1)
|
||||||
|
logit(LOG_WARNING, "%s: rlimit: Failed setting %s", tty, rlim2str(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create new session and process group */
|
||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
/* Set INIT_PROCESS UTMP entry */
|
/* Finit is responsible for the UTMP INIT_PROCESS record */
|
||||||
utmp_set_init(tty, 0);
|
utmp_set_init(tty, 0);
|
||||||
|
|
||||||
if (console)
|
|
||||||
procname = "console";
|
|
||||||
prctl(PR_SET_NAME, procname, 0, 0, 0);
|
prctl(PR_SET_NAME, procname, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int activate_console(int noclear, int nowait)
|
static int activate_console(int noclear, int nowait)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct termios term;
|
||||||
|
|
||||||
if (!noclear)
|
if (!noclear)
|
||||||
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
||||||
|
|
||||||
if (nowait)
|
if (nowait)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
/* Disable ECHO, XON/OFF while waiting for <CR> */
|
||||||
|
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||||
|
term.c_iflag &= ~(IXON|IXOFF);
|
||||||
|
term.c_lflag &= ~ECHO;
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||||
|
}
|
||||||
|
|
||||||
while (!fexist(SYNC_SHUTDOWN)) {
|
while (!fexist(SYNC_SHUTDOWN)) {
|
||||||
char c;
|
char c;
|
||||||
|
static const char clr[] = "\r\e[2K";
|
||||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||||
|
|
||||||
if (fexist(SYNC_STOPPED)) {
|
if (fexist(SYNC_STOPPED)) {
|
||||||
@@ -265,26 +313,63 @@ static int activate_console(int noclear, int nowait)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)write(STDERR_FILENO, msg, sizeof(msg));
|
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||||
|
(void)write(STDERR_FILENO, msg, strlen(msg));
|
||||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fexist(SYNC_STOPPED))
|
if (fexist(SYNC_STOPPED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
return 1;
|
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
/* Restore ECHO, XON/OFF */
|
||||||
|
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||||
|
term.c_iflag |= IXON|IXOFF;
|
||||||
|
term.c_lflag |= ECHO;
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int console)
|
/*
|
||||||
|
* Start a getty on @tty
|
||||||
|
*
|
||||||
|
* At the login: prompt, no signals are allowed, both Ctrl-C and Ctrl-D
|
||||||
|
* should be disabled. Ctrl-S and Ctrl-Q are optional, but most getty
|
||||||
|
* allow them.
|
||||||
|
*
|
||||||
|
* Prior to getty is called and login: is printed, Finit may display the
|
||||||
|
* "Please press Enter ..." if @nowait is unset. This mode must be RAW,
|
||||||
|
* only accepting <CR> and not echoing anything, this also means no
|
||||||
|
* signals are allowed. For ease of implementation Finit will call the
|
||||||
|
* stty() function to reset the TTY and then force RAW mode until a <CR>
|
||||||
|
* has been received. This is handled in the same fashion for both this
|
||||||
|
* function and run_getty2(), which is used for external getty.
|
||||||
|
*
|
||||||
|
* When handing over to /bin/login, Ctrl-C and Ctrl-D must be enabled
|
||||||
|
* since /bin/login usually only disables ECHO until a password line has
|
||||||
|
* been entered. Upon starting the user's $SHELL the ISIG flag is reset
|
||||||
|
*/
|
||||||
|
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
prepare_tty(tty, "finit-getty", console);
|
speed_t speed = B38400;
|
||||||
|
|
||||||
|
if (baud && baud[0]) {
|
||||||
|
speed = stty_parse_speed(baud);
|
||||||
|
if (B0 == speed)
|
||||||
|
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_tty(tty, speed, "finit-getty", rlimit);
|
||||||
if (activate_console(noclear, nowait))
|
if (activate_console(noclear, nowait))
|
||||||
_exit(getty(tty, speed, term, NULL));
|
_exit(getty(tty, speed, term, NULL));
|
||||||
}
|
}
|
||||||
@@ -292,7 +377,7 @@ pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, int console)
|
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
@@ -321,7 +406,8 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
|
|||||||
dup2(fd, STDOUT_FILENO);
|
dup2(fd, STDOUT_FILENO);
|
||||||
dup2(fd, STDERR_FILENO);
|
dup2(fd, STDERR_FILENO);
|
||||||
|
|
||||||
prepare_tty(tty, "getty", console);
|
/* Dunno speed, tell stty() to not mess with it */
|
||||||
|
prepare_tty(tty, B0, "getty", rlimit);
|
||||||
|
|
||||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||||
_pe("Failed TIOCSCTTY");
|
_pe("Failed TIOCSCTTY");
|
||||||
@@ -337,6 +423,20 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (!pid) {
|
||||||
|
prepare_tty(tty, B0, "finit-sh", rlimit);
|
||||||
|
if (activate_console(noclear, nowait))
|
||||||
|
_exit(sh(tty));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
int run_parts(char *dir, char *cmd)
|
int run_parts(char *dir, char *cmd)
|
||||||
{
|
{
|
||||||
struct dirent **e;
|
struct dirent **e;
|
||||||
@@ -349,43 +449,44 @@ int run_parts(char *dir, char *cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
int j = 0;
|
struct stat st;
|
||||||
pid_t pid = 0;
|
char path[512];
|
||||||
mode_t mode;
|
char *argv[4] = {
|
||||||
char *args[NUM_ARGS];
|
"sh",
|
||||||
|
"-c",
|
||||||
|
path,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
char *name = e[i]->d_name;
|
char *name = e[i]->d_name;
|
||||||
char path[LINE_SIZE];
|
pid_t pid = 0;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
snprintf(path, sizeof(path), "%s/%s ", dir, name);
|
||||||
mode = fmode(path);
|
if (stat(path, &st)) {
|
||||||
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
|
_d("Failed stat(%s): %s", path, strerror(errno));
|
||||||
_d("Skipping %s ...", path);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in args[], starting with full path to executable */
|
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||||
args[j++] = path;
|
_d("Skipping %s ...", path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* If the callee didn't supply a run_parts() argument */
|
/* If the callee didn't supply a run_parts() argument */
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
_d("Checking if %s is a sysvinit startstop script ...", name);
|
||||||
if (name[0] == 'S' && isdigit(name[1])) {
|
if (name[0] == 'S' && isdigit(name[1]))
|
||||||
args[j++] = "start";
|
strlcat(path, "start", sizeof(path));
|
||||||
} else if (name[0] == 'K' && isdigit(name[1])) {
|
else if (name[0] == 'K' && isdigit(name[1]))
|
||||||
args[j++] = "stop";
|
strlcat(path, "stop", sizeof(path));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
args[j++] = cmd;
|
strlcat(path, cmd, sizeof(path));
|
||||||
}
|
}
|
||||||
args[j++] = NULL;
|
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
_d("Calling %s ...", path);
|
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
execv(path, args);
|
return execvp(_PATH_BSHELL, argv);
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(path, pid);
|
complete(path, pid);
|
||||||
|
|||||||
+254
-200
@@ -36,6 +36,8 @@
|
|||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
#include "cond.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
@@ -46,20 +48,25 @@
|
|||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
#include "watchdog.h"
|
#include "schedule.h"
|
||||||
|
|
||||||
int wdogpid = 0; /* No watchdog by default */
|
|
||||||
int runlevel = 0; /* Bootstrap 'S' */
|
int runlevel = 0; /* Bootstrap 'S' */
|
||||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||||
int prevlevel = -1;
|
int prevlevel = -1;
|
||||||
|
int rescue = 0; /* rescue mode from kernel cmdline */
|
||||||
|
int single = 0; /* single user mode from kernel cmdline */
|
||||||
|
int splash = 0; /* splash + progress enabled on kernel cmdline */
|
||||||
char *sdown = NULL;
|
char *sdown = NULL;
|
||||||
char *network = NULL;
|
char *network = NULL;
|
||||||
char *hostname = NULL;
|
char *hostname = NULL;
|
||||||
char *rcsd = FINIT_RCSD;
|
char *rcsd = FINIT_RCSD;
|
||||||
char *runparts = NULL;
|
char *runparts = NULL;
|
||||||
char *console = NULL;
|
|
||||||
|
|
||||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||||
|
svc_t *wdog = NULL; /* No watchdog by default */
|
||||||
|
|
||||||
|
static int udev = 0; /* Runtime detection of udev */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Show user configured banner before service bootstrap progress
|
* Show user configured banner before service bootstrap progress
|
||||||
@@ -78,17 +85,16 @@ static void banner(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
memset(separator, '=', sizeof(separator));
|
memset(separator, '=', sizeof(separator));
|
||||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Requires /proc to be mounted */
|
static int ismnt(char *file, char *dir)
|
||||||
static int fismnt(char *dir)
|
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
struct mntent *mnt;
|
struct mntent *mnt;
|
||||||
|
|
||||||
fp = setmntent("/proc/mounts", "r");
|
fp = setmntent(file, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 0; /* Dunno, maybe not */
|
return 0; /* Dunno, maybe not */
|
||||||
|
|
||||||
@@ -103,12 +109,18 @@ static int fismnt(char *dir)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Requires /proc to be mounted */
|
||||||
|
static int fismnt(char *dir)
|
||||||
|
{
|
||||||
|
return ismnt("/proc/mounts", dir);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check all filesystems in /etc/fstab with a fs_passno > 0
|
* Check all filesystems in /etc/fstab with a fs_passno > 0
|
||||||
*/
|
*/
|
||||||
static int fsck(int pass)
|
static int fsck(int pass)
|
||||||
{
|
{
|
||||||
int save;
|
// int save;
|
||||||
struct fstab *fs;
|
struct fstab *fs;
|
||||||
|
|
||||||
if (!setfsent()) {
|
if (!setfsent()) {
|
||||||
@@ -116,8 +128,8 @@ static int fsck(int pass)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((save = log_is_debug()))
|
// if ((save = log_is_debug()))
|
||||||
log_debug();
|
// log_debug();
|
||||||
|
|
||||||
while ((fs = getfsent())) {
|
while ((fs = getfsent())) {
|
||||||
char cmd[80];
|
char cmd[80];
|
||||||
@@ -143,61 +155,13 @@ static int fsck(int pass)
|
|||||||
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save)
|
// if (save)
|
||||||
log_debug();
|
// log_debug();
|
||||||
endfsent();
|
endfsent();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void networking(void)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
/* Run user network start script if enabled */
|
|
||||||
if (network) {
|
|
||||||
run_interactive(network, "Starting networking: %s", network);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Debian/Ubuntu/Busybox/RH/Suse */
|
|
||||||
if (!whichp("ifup"))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
fp = fopen("/etc/network/interfaces", "r");
|
|
||||||
if (fp) {
|
|
||||||
int i = 0;
|
|
||||||
char buf[160];
|
|
||||||
|
|
||||||
/* Bring up all 'auto' interfaces */
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
|
||||||
char cmd[80];
|
|
||||||
char *line, *ifname = NULL;
|
|
||||||
|
|
||||||
chomp(buf);
|
|
||||||
line = strip_line(buf);
|
|
||||||
|
|
||||||
if (!strncmp(line, "auto", 4))
|
|
||||||
ifname = &line[5];
|
|
||||||
if (!strncmp(line, "allow-hotplug", 13))
|
|
||||||
ifname = &line[14];
|
|
||||||
|
|
||||||
if (!ifname)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(cmd, 80, "ifup %s", ifname);
|
|
||||||
run_interactive(cmd, "Bringing up interface %s", ifname);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
/* Fall back to bring up at least loopback */
|
|
||||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If everything goes south we can use this to give the operator an
|
* If everything goes south we can use this to give the operator an
|
||||||
* emergency shell to debug the problem -- Finit should not crash!
|
* emergency shell to debug the problem -- Finit should not crash!
|
||||||
@@ -227,7 +191,7 @@ static void emergency_shell(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
||||||
fprintf(stderr, "=> To debug, add '--debug' to the kernel command line.\n\n");
|
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Become session leader and set controlling TTY
|
* Become session leader and set controlling TTY
|
||||||
@@ -241,12 +205,122 @@ static void emergency_shell(void)
|
|||||||
#endif /* EMERGENCY_SHELL */
|
#endif /* EMERGENCY_SHELL */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||||
|
*
|
||||||
|
* This is the final stage of bootstrap. It changes to the default
|
||||||
|
* (configured) runlevel, calls all external start scripts and final
|
||||||
|
* bootstrap hooks before bringing up TTYs.
|
||||||
|
*
|
||||||
|
* We must ensure that all declared `task [S]` and `run [S]` jobs in
|
||||||
|
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||||
|
* finalize the bootstrap process by calling this function.
|
||||||
|
*/
|
||||||
|
static void finalize(void)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Track bundled watchdogd in case a better one turns up
|
||||||
|
*/
|
||||||
|
svc = svc_find(FINIT_LIBPATH_ "/watchdogd", "1");
|
||||||
|
if (svc)
|
||||||
|
wdog = svc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run startup scripts in the runparts directory, if any.
|
||||||
|
*/
|
||||||
|
if (runparts && fisdir(runparts) && !rescue)
|
||||||
|
run_parts(runparts, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start all tasks/services in the configured runlevel
|
||||||
|
*/
|
||||||
|
_d("Change to default runlevel, start all services ...");
|
||||||
|
service_runlevel(cfglevel);
|
||||||
|
|
||||||
|
/* Clean up bootstrap-only tasks/services that never started */
|
||||||
|
_d("Clean up all bootstrap-only tasks/services ...");
|
||||||
|
svc_prune_bootstrap();
|
||||||
|
|
||||||
|
/* All services/tasks/inetd/etc. in configure runlevel have started */
|
||||||
|
_d("Running svc up hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_SVC_UP);
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
/* Convenient SysV compat for when you just don't care ... */
|
||||||
|
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
|
||||||
|
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||||
|
|
||||||
|
/* Hooks that should run at the very end */
|
||||||
|
_d("Calling all system up hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
/* Enable silent mode before starting TTYs */
|
||||||
|
_d("Going silent ...");
|
||||||
|
log_silent();
|
||||||
|
|
||||||
|
/* Delayed start of TTYs at bootstrap */
|
||||||
|
_d("Launching all getty services ...");
|
||||||
|
tty_runlevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start cranking the big state machine
|
||||||
|
*/
|
||||||
|
static void crank_worker(void *unused)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initalize state machine and start all bootstrap tasks
|
||||||
|
* NOTE: no network available!
|
||||||
|
*/
|
||||||
|
sm_init(&sm);
|
||||||
|
sm_step(&sm);
|
||||||
|
|
||||||
|
/* Debian has this little script to copy generated rules while the system was read-only */
|
||||||
|
if (udev && fexist("/lib/udev/udev-finish"))
|
||||||
|
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||||
|
* allowed to complete their work in [S], or timeout, before we call
|
||||||
|
* finalize(), should not take more than 120 sec.
|
||||||
|
*/
|
||||||
|
static void final_worker(void *work)
|
||||||
|
{
|
||||||
|
static int cnt = 120;
|
||||||
|
|
||||||
|
_d("Step all services ...");
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
if (cnt-- > 0 && !service_completed()) {
|
||||||
|
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt);
|
||||||
|
schedule_work(work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cnt > 0)
|
||||||
|
_d("All run/task have completed, resuming bootstrap.");
|
||||||
|
else
|
||||||
|
_d("Timeout, resuming bootstrap.");
|
||||||
|
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
struct wq crank = {
|
||||||
|
.cb = crank_worker
|
||||||
|
};
|
||||||
|
struct wq final = {
|
||||||
|
.cb = final_worker,
|
||||||
|
.delay = 1000
|
||||||
|
};
|
||||||
|
uev_ctx_t loop;
|
||||||
char *path;
|
char *path;
|
||||||
char cmd[256];
|
char cmd[256];
|
||||||
int ret, udev = 0;
|
|
||||||
uev_ctx_t loop;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* finit/init/telinit client tool uses /dev/initctl pipe
|
* finit/init/telinit client tool uses /dev/initctl pipe
|
||||||
@@ -255,8 +329,44 @@ int main(int argc, char* argv[])
|
|||||||
if (getpid() != 1)
|
if (getpid() != 1)
|
||||||
return client(argc, argv);
|
return client(argc, argv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initalize event context.
|
||||||
|
*/
|
||||||
|
uev_init1(&loop, 1);
|
||||||
|
ctx = &loop;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set PATH and SHELL early to something sane
|
||||||
|
*/
|
||||||
|
setenv("PATH", _PATH_STDPATH, 1);
|
||||||
|
setenv("SHELL", _PATH_BSHELL, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||||
|
*/
|
||||||
|
chdir("/");
|
||||||
|
umask(0);
|
||||||
|
if (mount("none", "/proc", "proc", 0, NULL))
|
||||||
|
_pe("Failed mounting /proc");
|
||||||
|
if (mount("none", "/sys", "sysfs", 0, NULL))
|
||||||
|
_pe("Failed mounting /sysfs");
|
||||||
|
if (fisdir("/proc/bus/usb"))
|
||||||
|
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize default control groups, if available
|
||||||
|
*/
|
||||||
|
cgroup_init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse kernel command line (debug, rescue, splash, etc.)
|
||||||
|
* Also calls log_init() to set correct log level
|
||||||
|
*/
|
||||||
|
conf_parse_cmdline();
|
||||||
|
|
||||||
/* Set up canvas */
|
/* Set up canvas */
|
||||||
screen_init();
|
if (!rescue && !log_is_debug())
|
||||||
|
screen_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of emergency.
|
* In case of emergency.
|
||||||
@@ -268,54 +378,31 @@ int main(int argc, char* argv[])
|
|||||||
*/
|
*/
|
||||||
sig_init();
|
sig_init();
|
||||||
|
|
||||||
/*
|
|
||||||
* Initalize event context.
|
|
||||||
*/
|
|
||||||
uev_init(&loop);
|
|
||||||
ctx = &loop;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the PATH early to something sane
|
|
||||||
*/
|
|
||||||
setenv("PATH", _PATH_STDPATH, 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
|
||||||
*/
|
|
||||||
chdir("/");
|
|
||||||
umask(0);
|
|
||||||
mount("none", "/proc", "proc", 0, NULL);
|
|
||||||
mount("none", "/sys", "sysfs", 0, NULL);
|
|
||||||
if (fisdir("/proc/bus/usb"))
|
|
||||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse kernel parameters, including log_init()
|
|
||||||
*/
|
|
||||||
conf_parse_cmdline();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load plugins early, finit.conf may contain references to
|
* Load plugins early, finit.conf may contain references to
|
||||||
* features implemented by plugins.
|
* features implemented by plugins.
|
||||||
*/
|
*/
|
||||||
ret = plugin_init(&loop, PLUGIN_PATH);
|
plugin_init(&loop);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hello world.
|
* Hello world.
|
||||||
*/
|
*/
|
||||||
banner();
|
banner();
|
||||||
|
|
||||||
/* Show results after banner() */
|
|
||||||
print(ret, "Loading plugins");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check file filesystems in /etc/fstab
|
* Check file filesystems in /etc/fstab
|
||||||
*/
|
*/
|
||||||
for (int pass = 1; pass < 10; pass++) {
|
for (int pass = 1; pass < 10 && !rescue; pass++) {
|
||||||
if (fsck(pass))
|
if (fsck(pass))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize .conf system and load static /etc/finit.conf
|
||||||
|
* Also initializes global_rlimit[] for udevd, below.
|
||||||
|
*/
|
||||||
|
conf_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some non-embedded systems without an initramfs may not have /dev mounted yet
|
* Some non-embedded systems without an initramfs may not have /dev mounted yet
|
||||||
* If they do, check if system has udevadm and perform cleanup from initramfs
|
* If they do, check if system has udevadm and perform cleanup from initramfs
|
||||||
@@ -329,8 +416,14 @@ int main(int argc, char* argv[])
|
|||||||
makedir("/dev/pts", 0755);
|
makedir("/dev/pts", 0755);
|
||||||
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some systems rely on us to both create /dev/shm and, to mount
|
||||||
|
* a tmpfs there. Any system with dbus needs shared memory, so
|
||||||
|
* mount it, unless its already mounted, but not if listed in
|
||||||
|
* the /etc/fstab file already.
|
||||||
|
*/
|
||||||
makedir("/dev/shm", 0755);
|
makedir("/dev/shm", 0755);
|
||||||
if (!fismnt("/dev/shm"))
|
if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
|
||||||
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -341,151 +434,112 @@ int main(int argc, char* argv[])
|
|||||||
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
|
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
|
||||||
umask(022);
|
umask(022);
|
||||||
|
|
||||||
|
/* Bootstrap conditions, needed for hooks */
|
||||||
|
cond_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populate /dev and prepare for runtime events from kernel.
|
* Populate /dev and prepare for runtime events from kernel.
|
||||||
|
* Prefer udev if mdev is also available on the system.
|
||||||
*/
|
*/
|
||||||
path = which("mdev");
|
path = which("udevd");
|
||||||
|
if (!path)
|
||||||
|
path = which("/lib/systemd/systemd-udevd");
|
||||||
if (path) {
|
if (path) {
|
||||||
/* Embedded Linux systems usually have BusyBox mdev */
|
|
||||||
if (log_is_debug())
|
|
||||||
touch("/dev/mdev.log");
|
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), "%s -s", path);
|
|
||||||
} else {
|
|
||||||
/* Desktop and server distros usually have a variant of udev */
|
/* Desktop and server distros usually have a variant of udev */
|
||||||
path = which("udevd");
|
udev = 1;
|
||||||
if (!path)
|
|
||||||
path = which("/lib/systemd/systemd-udevd");
|
|
||||||
if (path) {
|
|
||||||
udev = 1;
|
|
||||||
|
|
||||||
/* Register udevd as a monitored service, started much later */
|
/* Register udevd as a monitored service */
|
||||||
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path);
|
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
|
||||||
if (service_register(SVC_TYPE_SERVICE, cmd, NULL)) {
|
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, "default", NULL)) {
|
||||||
_pe("Failed registering %s", path);
|
_pe("Failed registering %s", path);
|
||||||
udev = 0;
|
udev = 0;
|
||||||
}
|
} else {
|
||||||
|
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
|
||||||
|
"udevadm trigger -c add -t devices "
|
||||||
|
"-- Requesting device events", path);
|
||||||
|
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||||
|
|
||||||
/* Start a temporary udevd instance to populate /dev */
|
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
|
||||||
snprintf(cmd, sizeof(cmd), "%s --daemon", path);
|
"udevadm trigger -c add -t subsystems "
|
||||||
|
"-- Requesting subsystem events", path);
|
||||||
|
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
free(path);
|
free(path);
|
||||||
|
} else {
|
||||||
|
path = which("mdev");
|
||||||
|
if (path) {
|
||||||
|
/* Embedded Linux systems usually have BusyBox mdev */
|
||||||
|
if (log_is_debug())
|
||||||
|
touch("/dev/mdev.log");
|
||||||
|
|
||||||
run_interactive(cmd, "Populating device tree");
|
snprintf(cmd, sizeof(cmd), "%s -s", path);
|
||||||
if (udev && whichp("udevadm")) {
|
free(path);
|
||||||
run("udevadm trigger --action=add --type=subsystems");
|
|
||||||
run("udevadm trigger --action=add --type=devices");
|
|
||||||
run("udevadm settle --timeout=120");
|
|
||||||
|
|
||||||
/* Tell temporary udevd to exit, we'll start a monitored instance later */
|
run_interactive(cmd, "Populating device tree");
|
||||||
run("udevadm control --exit");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse /etc/finit.conf and all *.conf in /etc/finit.d/ to
|
* Start bundled watchdogd as soon as possible, if enabled
|
||||||
* figure out how to bootstrap the system.
|
|
||||||
*/
|
*/
|
||||||
conf_parse_config();
|
if (which(FINIT_LIBPATH_ "/watchdogd"))
|
||||||
|
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, "default", NULL);
|
||||||
/*
|
|
||||||
* Start built-in watchdog as soon as possible, if enabled
|
|
||||||
*/
|
|
||||||
watchdog(argv[0]);
|
|
||||||
|
|
||||||
/* Set hostname as soon as possible, for syslog et al. */
|
|
||||||
set_hostname(&hostname);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mount filesystems
|
* Mount filesystems
|
||||||
*/
|
*/
|
||||||
|
if (!rescue) {
|
||||||
#ifdef REMOUNT_ROOTFS
|
#ifdef REMOUNT_ROOTFS
|
||||||
run("mount -n -o remount,rw /");
|
run("mount -n -o remount,rw /");
|
||||||
#endif
|
#endif
|
||||||
#ifdef SYSROOT
|
#ifdef SYSROOT
|
||||||
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* Debian has this little script to copy generated rules while the system was read-only */
|
if (!rescue) {
|
||||||
if (fexist("/lib/udev/udev-finish"))
|
_d("Root FS up, calling hooks ...");
|
||||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||||
|
|
||||||
_d("Root FS up, calling hooks ...");
|
umask(0);
|
||||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
if (run_interactive("mount -na", "Mounting filesystems"))
|
||||||
|
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||||
|
|
||||||
umask(0);
|
_d("Calling extra mount hook, after mount -a ...");
|
||||||
if (run_interactive("mount -na", "Mounting filesystems"))
|
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
|
||||||
|
|
||||||
run("swapon -ea");
|
run("swapon -ea");
|
||||||
umask(0022);
|
umask(0022);
|
||||||
|
}
|
||||||
|
|
||||||
/* Base FS up, enable standard SysV init signals */
|
/* Base FS up, enable standard SysV init signals */
|
||||||
sig_setup(&loop);
|
sig_setup(&loop);
|
||||||
|
|
||||||
_d("Base FS up, calling hooks ...");
|
if (!rescue) {
|
||||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
_d("Base FS up, calling hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initalize state machine and start all bootstrap tasks
|
* Set up inotify watcher for /etc/finit.d and read all .conf
|
||||||
* NOTE: no network available!
|
* files to figure out how to bootstrap the system.
|
||||||
*/
|
*/
|
||||||
sm_init(&sm);
|
conf_monitor(&loop);
|
||||||
sm_step(&sm);
|
|
||||||
|
|
||||||
/*
|
_d("Starting initctl API responder ...");
|
||||||
* Network stuff
|
api_init(&loop);
|
||||||
*/
|
|
||||||
networking();
|
|
||||||
umask(022);
|
umask(022);
|
||||||
|
|
||||||
/* Hooks that rely on loopback, or basic networking being up. */
|
_d("Starting the big state machine ...");
|
||||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
schedule_work(&crank);
|
||||||
|
|
||||||
|
_d("Starting bootstrap finalize timer ...");
|
||||||
|
schedule_work(&final);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start all tasks/services in the configured runlevel
|
* Enter main loop to monitor /dev/initctl and services
|
||||||
*/
|
|
||||||
service_runlevel(cfglevel);
|
|
||||||
|
|
||||||
/* Clean up bootstrap-only tasks/services that never started */
|
|
||||||
svc_prune_bootstrap();
|
|
||||||
|
|
||||||
_d("Running svc up hooks ...");
|
|
||||||
plugin_run_hooks(HOOK_SVC_UP);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Run startup scripts in the runparts directory, if any.
|
|
||||||
*/
|
|
||||||
if (runparts && fisdir(runparts)) {
|
|
||||||
_d("Running startup scripts in %s ...", runparts);
|
|
||||||
run_parts(runparts, NULL);
|
|
||||||
service_reload_dynamic();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convenient SysV compat for when you just don't care ... */
|
|
||||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
|
||||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
|
||||||
service_reload_dynamic();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hooks that should run at the very end */
|
|
||||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
|
||||||
|
|
||||||
/* Enable silent mode before starting TTYs */
|
|
||||||
log_silent();
|
|
||||||
|
|
||||||
/* Delayed start of TTYs at bootstrap */
|
|
||||||
tty_runlevel();
|
|
||||||
|
|
||||||
/* Start new initctl API responder */
|
|
||||||
api_init(&loop);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enter main loop to monior /dev/initctl and services
|
|
||||||
*/
|
*/
|
||||||
|
_d("Entering main loop ...");
|
||||||
return uev_run(&loop, 0);
|
return uev_run(&loop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -78,6 +78,9 @@
|
|||||||
#define INIT_CMD_EMIT 15
|
#define INIT_CMD_EMIT 15
|
||||||
#define INIT_CMD_GET_RUNLEVEL 16
|
#define INIT_CMD_GET_RUNLEVEL 16
|
||||||
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
|
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
|
||||||
|
#define INIT_CMD_SVC_ITER 129
|
||||||
|
#define INIT_CMD_SVC_QUERY 130
|
||||||
|
#define INIT_CMD_SVC_FIND 131
|
||||||
#define INIT_CMD_NACK 254
|
#define INIT_CMD_NACK 254
|
||||||
#define INIT_CMD_ACK 255
|
#define INIT_CMD_ACK 255
|
||||||
|
|
||||||
@@ -89,16 +92,17 @@ struct init_request {
|
|||||||
char data[368];
|
char data[368];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int wdogpid;
|
|
||||||
extern int runlevel;
|
extern int runlevel;
|
||||||
extern int cfglevel;
|
extern int cfglevel;
|
||||||
extern int prevlevel;
|
extern int prevlevel;
|
||||||
|
extern int rescue;
|
||||||
|
extern int single;
|
||||||
|
extern int splash;
|
||||||
extern char *rcsd;
|
extern char *rcsd;
|
||||||
extern char *sdown;
|
extern char *sdown;
|
||||||
extern char *network;
|
extern char *network;
|
||||||
extern char *hostname;
|
extern char *hostname;
|
||||||
extern char *runparts;
|
extern char *runparts;
|
||||||
extern char *console;
|
|
||||||
extern uev_ctx_t *ctx;
|
extern uev_ctx_t *ctx;
|
||||||
|
|
||||||
#endif /* FINIT_H_ */
|
#endif /* FINIT_H_ */
|
||||||
|
|||||||
+65
-117
@@ -32,6 +32,7 @@
|
|||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
@@ -64,42 +65,6 @@ static int readch(char *tty)
|
|||||||
return ch1 & 0xFF;
|
return ch1 & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stty(int fd, speed_t speed)
|
|
||||||
{
|
|
||||||
struct termios term;
|
|
||||||
|
|
||||||
tcdrain(fd);
|
|
||||||
if (tcgetattr(fd, &term))
|
|
||||||
return;
|
|
||||||
|
|
||||||
cfsetispeed(&term, speed);
|
|
||||||
cfsetospeed(&term, speed);
|
|
||||||
tcsetattr(fd, TCSAFLUSH, &term);
|
|
||||||
tcflush(fd, TCIOFLUSH);
|
|
||||||
|
|
||||||
/* Disable modem specific flags */
|
|
||||||
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
|
|
||||||
term.c_cflag &= ~CRTSCTS;
|
|
||||||
term.c_cflag |= CLOCAL;
|
|
||||||
|
|
||||||
/* Timeouts, minimum chars and default flags */
|
|
||||||
term.c_cc[VTIME] = 0;
|
|
||||||
term.c_cc[VMIN] = 1;
|
|
||||||
term.c_iflag = ICRNL|IXON|IXOFF;
|
|
||||||
term.c_oflag = OPOST|ONLCR;
|
|
||||||
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
|
||||||
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
|
|
||||||
|
|
||||||
/* Reset special characters to defaults */
|
|
||||||
term.c_cc[VINTR] = CTRL('C');
|
|
||||||
term.c_cc[VQUIT] = CTRL('\\');
|
|
||||||
term.c_cc[VEOF] = CTRL('D');
|
|
||||||
term.c_cc[VEOL] = '\n';
|
|
||||||
term.c_cc[VKILL] = CTRL('U');
|
|
||||||
term.c_cc[VERASE] = CERASE;
|
|
||||||
tcsetattr(fd, TCSANOW, &term);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse and display a line from /etc/issue
|
* Parse and display a line from /etc/issue
|
||||||
*/
|
*/
|
||||||
@@ -162,7 +127,6 @@ static void do_issue(char *tty)
|
|||||||
*/
|
*/
|
||||||
uname(&uts);
|
uname(&uts);
|
||||||
|
|
||||||
print("\n");
|
|
||||||
fp = fopen("/etc/issue", "r");
|
fp = fopen("/etc/issue", "r");
|
||||||
if (fp) {
|
if (fp) {
|
||||||
while (fgets(buf, sizeof(buf), fp))
|
while (fgets(buf, sizeof(buf), fp))
|
||||||
@@ -229,6 +193,7 @@ static int do_login(char *name)
|
|||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
cgroup_user(name);
|
||||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -244,72 +209,10 @@ static int do_login(char *name)
|
|||||||
return 1; /* We shouldn't get here ... */
|
return 1; /* We shouldn't get here ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
static speed_t do_parse_speed(char *baud)
|
int getty(char *tty, speed_t speed, char *term, char *user)
|
||||||
{
|
|
||||||
char *ptr;
|
|
||||||
size_t i;
|
|
||||||
unsigned long val;
|
|
||||||
struct { unsigned long val; speed_t speed; } v2s[] = {
|
|
||||||
{ 0, B0 },
|
|
||||||
{ 50, B50 },
|
|
||||||
{ 75, B75 },
|
|
||||||
{ 110, B110 },
|
|
||||||
{ 134, B134 },
|
|
||||||
{ 150, B150 },
|
|
||||||
{ 200, B200 },
|
|
||||||
{ 300, B300 },
|
|
||||||
{ 600, B600 },
|
|
||||||
{ 1200, B1200 },
|
|
||||||
{ 1800, B1800 },
|
|
||||||
{ 2400, B2400 },
|
|
||||||
{ 4800, B4800 },
|
|
||||||
{ 9600, B9600 },
|
|
||||||
{ 19200, B19200 },
|
|
||||||
{ 38400, B38400 },
|
|
||||||
{ 57600, B57600 },
|
|
||||||
{ 115200, B115200 },
|
|
||||||
{ 230400, B230400 },
|
|
||||||
{ 460800, B460800 },
|
|
||||||
{ 500000, B500000 },
|
|
||||||
{ 576000, B576000 },
|
|
||||||
{ 921600, B921600 },
|
|
||||||
{ 1000000, B1000000 },
|
|
||||||
{ 1152000, B1152000 },
|
|
||||||
{ 1500000, B1500000 },
|
|
||||||
{ 2000000, B2000000 },
|
|
||||||
{ 2500000, B2500000 },
|
|
||||||
{ 3000000, B3000000 },
|
|
||||||
{ 3500000, B3500000 },
|
|
||||||
{ 4000000, B4000000 },
|
|
||||||
};
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
val = strtoul(baud, &ptr, 10);
|
|
||||||
if (errno || ptr == baud)
|
|
||||||
return B0;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
|
|
||||||
if (v2s[i].val == val)
|
|
||||||
return v2s[i].speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getty(char *tty, char *baud, char *term, char *user)
|
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
char name[30];
|
char name[30];
|
||||||
speed_t speed = B38400;
|
|
||||||
struct sigaction sa;
|
|
||||||
|
|
||||||
if (baud) {
|
|
||||||
speed = do_parse_speed(baud);
|
|
||||||
if (speed == B0) {
|
|
||||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Detach from initial controlling TTY */
|
/* Detach from initial controlling TTY */
|
||||||
vhangup();
|
vhangup();
|
||||||
@@ -325,32 +228,77 @@ int getty(char *tty, char *baud, char *term, char *user)
|
|||||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||||
warn("Failed TIOCSCTTY");
|
warn("Failed TIOCSCTTY");
|
||||||
|
|
||||||
/*
|
/* The getty process is responsible for the UTMP login record */
|
||||||
* Ignore a few signals, needed to prevent Ctrl-C at login:
|
|
||||||
* prompt and to prevent QUIT from dumping core.
|
|
||||||
*/
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sa.sa_flags = SA_RESTART;
|
|
||||||
sa.sa_handler = SIG_IGN;
|
|
||||||
sigaction(SIGHUP, &sa, NULL);
|
|
||||||
sigaction(SIGINT, &sa, NULL);
|
|
||||||
sigaction(SIGQUIT, &sa, NULL);
|
|
||||||
|
|
||||||
/* Set up TTY */
|
|
||||||
stty(fd, speed);
|
|
||||||
close(fd);
|
|
||||||
utmp_set_login(tty, NULL);
|
utmp_set_login(tty, NULL);
|
||||||
if (term)
|
|
||||||
setenv("TERM", term, 1);
|
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
do_getty(tty, name, sizeof(name));
|
do_getty(tty, name, sizeof(name));
|
||||||
else
|
else
|
||||||
strlcpy(name, user, sizeof(name));
|
strlcpy(name, user, sizeof(name));
|
||||||
|
|
||||||
|
/* Set up TTY, re-enabling ISIG et al. */
|
||||||
|
stty(fd, speed);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (term && term[0])
|
||||||
|
setenv("TERM", term, 1);
|
||||||
|
|
||||||
return do_login(name);
|
return do_login(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sh(char *tty)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char *arg0;
|
||||||
|
char *args[2] = {
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
size_t len;
|
||||||
|
struct termios term;
|
||||||
|
|
||||||
|
/* Detach from initial controlling TTY */
|
||||||
|
vhangup();
|
||||||
|
|
||||||
|
fd = open(tty, O_RDWR);
|
||||||
|
if (fd < 0)
|
||||||
|
err(1, "Failed opening %s", tty);
|
||||||
|
|
||||||
|
dup2(fd, STDIN_FILENO);
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
dup2(fd, STDERR_FILENO);
|
||||||
|
|
||||||
|
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||||
|
warn("Failed TIOCSCTTY");
|
||||||
|
|
||||||
|
/* The getty process is usually responsible for the UTMP login record */
|
||||||
|
utmp_set_login(tty, NULL);
|
||||||
|
|
||||||
|
/* Set up TTY, re-enabling ISIG et al. */
|
||||||
|
stty(fd, B0);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
|
||||||
|
len = strlen(_PATH_BSHELL) + 2;
|
||||||
|
arg0 = malloc(len);
|
||||||
|
if (!arg0)
|
||||||
|
err(1, "Failed allocating memory");
|
||||||
|
snprintf(arg0, len, "-%s", _PATH_BSHELL);
|
||||||
|
args[0] = arg0;
|
||||||
|
|
||||||
|
/* Reenable Ctrl-D and Ctrl-C, and ... */
|
||||||
|
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||||
|
term.c_lflag |= ISIG;
|
||||||
|
term.c_cc[VEOF] = CTL('D');
|
||||||
|
term.c_cc[VINTR] = CTL('C');
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... unblock signals in general */
|
||||||
|
sig_unblock();
|
||||||
|
|
||||||
|
return execv(_PATH_BSHELL, args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ void set_hostname(char **hostname)
|
|||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
|
if (rescue)
|
||||||
|
goto done;
|
||||||
|
|
||||||
fp = fopen("/etc/hostname", "r");
|
fp = fopen("/etc/hostname", "r");
|
||||||
if (fp) {
|
if (fp) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -204,10 +207,86 @@ void set_hostname(char **hostname)
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
if (*hostname)
|
if (*hostname)
|
||||||
sethostname(*hostname, strlen(*hostname));
|
sethostname(*hostname, strlen(*hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ifup(char *ifname, int updown)
|
||||||
|
{
|
||||||
|
char cmd[80];
|
||||||
|
|
||||||
|
if (updown) {
|
||||||
|
snprintf(cmd, sizeof(cmd), "ifup %s", ifname);
|
||||||
|
run_interactive(cmd, "Bringing up interface %s", ifname);
|
||||||
|
} else {
|
||||||
|
snprintf(cmd, sizeof(cmd), "ifdown -f %s", ifname);
|
||||||
|
run_interactive(cmd, "Taking down interface %s", ifname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bring up networking, but only if not single-user or rescue mode
|
||||||
|
*/
|
||||||
|
void networking(int updown)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/* No need to report errors if network is already down */
|
||||||
|
if (!prevlevel && !updown)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (updown)
|
||||||
|
_d("Setting up networking ...");
|
||||||
|
else
|
||||||
|
_d("Taking down networking ...");
|
||||||
|
|
||||||
|
/* Run user network start script if enabled */
|
||||||
|
if (updown && network) {
|
||||||
|
run_interactive(network, "Starting networking: %s", network);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debian/Ubuntu/Busybox/RH/Suse */
|
||||||
|
if (!whichp("ifup"))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
fp = fopen("/etc/network/interfaces", "r");
|
||||||
|
if (fp) {
|
||||||
|
char buf[160];
|
||||||
|
|
||||||
|
/* Bring up, or down, all 'auto' interfaces */
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
char *line, *ifname = NULL;
|
||||||
|
|
||||||
|
chomp(buf);
|
||||||
|
line = strip_line(buf);
|
||||||
|
|
||||||
|
if (!strncmp(line, "auto", 4))
|
||||||
|
ifname = &line[5];
|
||||||
|
if (!strncmp(line, "allow-hotplug", 13))
|
||||||
|
ifname = &line[14];
|
||||||
|
|
||||||
|
if (!ifname)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ifup(ifname, updown);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* Fall back to bring up at least loopback */
|
||||||
|
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
|
||||||
|
|
||||||
|
/* Hooks that rely on loopback, or basic networking being up. */
|
||||||
|
if (updown) {
|
||||||
|
_d("Calling all network up hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef HAVE_GETFSENT
|
#ifndef HAVE_GETFSENT
|
||||||
static lfile_t *fstab = NULL;
|
static lfile_t *fstab = NULL;
|
||||||
|
|
||||||
|
|||||||
+12
-5
@@ -26,7 +26,10 @@
|
|||||||
#define FINIT_HELPERS_H_
|
#define FINIT_HELPERS_H_
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||||
|
#include <termios.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
@@ -48,25 +51,29 @@ struct fstab *getfsent (void);
|
|||||||
|
|
||||||
char *strip_line (char *line);
|
char *strip_line (char *line);
|
||||||
|
|
||||||
int pid_alive (pid_t pid);
|
int getty (char *tty, speed_t speed, char *term, char *user);
|
||||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
int sh (char *tty);
|
||||||
|
|
||||||
|
int stty (int fd, speed_t speed);
|
||||||
|
speed_t stty_parse_speed(char *baud);
|
||||||
|
|
||||||
void printv (const char *fmt, va_list ap);
|
void printv (const char *fmt, va_list ap);
|
||||||
void print (int action, const char *fmt, ...);
|
void print (int action, const char *fmt, ...);
|
||||||
void print_desc (char *action, char *desc);
|
void print_desc (char *action, char *desc);
|
||||||
int print_result (int fail);
|
int print_result (int fail);
|
||||||
|
|
||||||
int start_process (char *cmd, char *args[], int console);
|
|
||||||
int getuser (char *username, char **home);
|
int getuser (char *username, char **home);
|
||||||
int getgroup (char *group);
|
int getgroup (char *group);
|
||||||
void set_hostname (char **hostname);
|
void set_hostname (char **hostname);
|
||||||
|
void networking (int updown);
|
||||||
|
|
||||||
int complete (char *cmd, int pid);
|
int complete (char *cmd, int pid);
|
||||||
int run (char *cmd);
|
int run (char *cmd);
|
||||||
int run_interactive (char *cmd, char *fmt, ...);
|
int run_interactive (char *cmd, char *fmt, ...);
|
||||||
int exec_runtask (char *cmd, char *args[]);
|
int exec_runtask (char *cmd, char *args[]);
|
||||||
pid_t run_getty (char *tty, char *speed, char *term, int noclear, int nowait, int console);
|
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
|
||||||
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, int console);
|
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||||
|
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
|
||||||
int run_parts (char *dir, char *cmd);
|
int run_parts (char *dir, char *cmd);
|
||||||
|
|
||||||
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
|||||||
+86
-23
@@ -43,12 +43,13 @@
|
|||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||||
static int inetd_dgram_peek(int sd, char *ifname)
|
static int inetd_dgram_peek(int sd, char *ifname, size_t len)
|
||||||
{
|
{
|
||||||
char cmbuf[0x100];
|
|
||||||
struct msghdr msgh;
|
|
||||||
struct cmsghdr *cmsg;
|
struct cmsghdr *cmsg;
|
||||||
|
struct msghdr msgh;
|
||||||
|
char cmbuf[0x100];
|
||||||
|
|
||||||
|
memset(ifname, 0, len);
|
||||||
memset(&msgh, 0, sizeof(msgh));
|
memset(&msgh, 0, sizeof(msgh));
|
||||||
msgh.msg_control = cmbuf;
|
msgh.msg_control = cmbuf;
|
||||||
msgh.msg_controllen = sizeof(cmbuf);
|
msgh.msg_controllen = sizeof(cmbuf);
|
||||||
@@ -56,13 +57,15 @@ static int inetd_dgram_peek(int sd, char *ifname)
|
|||||||
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
|
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
|
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
|
||||||
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||||
|
char tmp[IF_NAMESIZE + 1] = { 0 };
|
||||||
|
|
||||||
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
|
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if_indextoname(ipi->ipi_ifindex, ifname);
|
if_indextoname(ipi->ipi_ifindex, tmp);
|
||||||
|
strlcpy(ifname, tmp, len);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -70,13 +73,31 @@ static int inetd_dgram_peek(int sd, char *ifname)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||||
|
static void inetd_dgram_drop(int sd, const char *ifname)
|
||||||
|
{
|
||||||
|
char pkt_interface[IF_NAMESIZE + 1];
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
|
||||||
|
if (string_compare(pkt_interface, ifname)) {
|
||||||
|
if (recv(sd, buf, sizeof(buf), 0) < 0)
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||||
static int inetd_stream_peek(int sd, char *ifname)
|
static int inetd_stream_peek(int sd, char *ifname, size_t ilen)
|
||||||
{
|
{
|
||||||
struct ifaddrs *ifaddr, *ifa;
|
struct ifaddrs *ifaddr, *ifa;
|
||||||
struct sockaddr_in sin;
|
struct sockaddr_in sin;
|
||||||
socklen_t len = sizeof(sin);
|
socklen_t len = sizeof(sin);
|
||||||
|
|
||||||
|
memset(ifname, 0, ilen);
|
||||||
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
|
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -95,7 +116,7 @@ static int inetd_stream_peek(int sd, char *ifname)
|
|||||||
|
|
||||||
iin = (struct sockaddr_in *)ifa->ifa_addr;
|
iin = (struct sockaddr_in *)ifa->ifa_addr;
|
||||||
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
|
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
|
||||||
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
|
strlcpy(ifname, ifa->ifa_name, ilen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,11 +126,12 @@ static int inetd_stream_peek(int sd, char *ifname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_stdin(svc_t *svc)
|
static int get_stdin(svc_t *svc, char *iifname, size_t len)
|
||||||
{
|
{
|
||||||
int stdin = svc->inetd.watcher.fd;
|
int stdin = svc->inetd.watcher.fd;
|
||||||
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
char ifname[IF_NAMESIZE + 1] = "UNKNOWN";
|
||||||
|
|
||||||
|
memset(iifname, 0, len);
|
||||||
if (svc->inetd.type == SOCK_STREAM) {
|
if (svc->inetd.type == SOCK_STREAM) {
|
||||||
/* Open new client socket from server socket */
|
/* Open new client socket from server socket */
|
||||||
stdin = accept(stdin, NULL, NULL);
|
stdin = accept(stdin, NULL, NULL);
|
||||||
@@ -120,19 +142,24 @@ static int get_stdin(svc_t *svc)
|
|||||||
|
|
||||||
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
|
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
|
||||||
|
|
||||||
inetd_stream_peek(stdin, ifname);
|
inetd_stream_peek(stdin, ifname, sizeof(ifname));
|
||||||
} else { /* SOCK_DGRAM */
|
} else { /* SOCK_DGRAM */
|
||||||
inetd_dgram_peek(stdin, ifname);
|
inetd_dgram_peek(stdin, ifname, sizeof(ifname));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||||
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
|
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
|
||||||
if (svc->inetd.type == SOCK_STREAM)
|
if (svc->inetd.type == SOCK_STREAM)
|
||||||
close(stdin);
|
close(stdin);
|
||||||
|
else
|
||||||
|
inetd_dgram_drop(stdin, ifname);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return ingress interface */
|
||||||
|
strlcpy(iifname, ifname, len);
|
||||||
|
|
||||||
return stdin;
|
return stdin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,10 +167,18 @@ static int get_stdin(svc_t *svc)
|
|||||||
static void socket_cb(uev_t *w, void *arg, int events)
|
static void socket_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
svc_t *svc = (svc_t *)arg, *task;
|
svc_t *svc = (svc_t *)arg, *task;
|
||||||
|
const char *conn = " connection";
|
||||||
|
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
|
||||||
|
char id[MAX_ID_LEN];
|
||||||
int stdin;
|
int stdin;
|
||||||
|
|
||||||
_d("Got event on %s socket ...", svc->cmd);
|
_d("%s: Got socket event ...", svc->cmd);
|
||||||
stdin = get_stdin(svc);
|
if (UEV_ERROR == events) {
|
||||||
|
logit(LOG_INFO, "%s: Socket error, aborting: %m", svc->cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdin = get_stdin(svc, iifname, sizeof(iifname));
|
||||||
if (stdin < 0) {
|
if (stdin < 0) {
|
||||||
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
|
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
|
||||||
return;
|
return;
|
||||||
@@ -160,7 +195,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN);
|
snprintf(id, sizeof(id), "%d", svc->inetd.next_id++);
|
||||||
|
task = svc_new(svc->cmd, id, SVC_TYPE_INETD_CONN);
|
||||||
if (!task) {
|
if (!task) {
|
||||||
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
|
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
|
||||||
if (svc->inetd.type == SOCK_STREAM)
|
if (svc->inetd.type == SOCK_STREAM)
|
||||||
@@ -184,11 +220,15 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
|||||||
task->inetd.cmd = svc->inetd.cmd;
|
task->inetd.cmd = svc->inetd.cmd;
|
||||||
task->inetd.type = svc->inetd.type;
|
task->inetd.type = svc->inetd.type;
|
||||||
|
|
||||||
|
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
|
||||||
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
||||||
memcpy(task->username, svc->username, sizeof(task->username));
|
memcpy(task->username, svc->username, sizeof(task->username));
|
||||||
memcpy(task->group, svc->group, sizeof(task->group));
|
memcpy(task->group, svc->group, sizeof(task->group));
|
||||||
memcpy(task->args, svc->args, sizeof(task->args));
|
memcpy(task->args, svc->args, sizeof(task->args));
|
||||||
snprintf(task->desc, sizeof(task->desc), "%52.52s connection", svc->desc);
|
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
|
||||||
|
strlcat(task->desc, conn, sizeof(task->desc));
|
||||||
|
strlcpy(task->iifname, iifname, sizeof(task->iifname));
|
||||||
|
strlcpy(task->name, svc->name, sizeof(task->name));
|
||||||
|
|
||||||
task->stdin_fd = stdin;
|
task->stdin_fd = stdin;
|
||||||
service_step(task);
|
service_step(task);
|
||||||
@@ -202,10 +242,10 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
|
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
char pname[NI_MAXHOST];
|
char pname[NI_MAXHOST];
|
||||||
|
|
||||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
|
||||||
inetd_t *i = &svc->inetd;
|
inetd_t *i = &svc->inetd;
|
||||||
|
|
||||||
if (!i->builtin || i->type != SOCK_DGRAM)
|
if (!i->builtin || i->type != SOCK_DGRAM)
|
||||||
@@ -308,20 +348,43 @@ int inetd_start(inetd_t *inetd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inetd_stop_children(inetd_t *inetd, int check_allowed)
|
||||||
|
{
|
||||||
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
svc = svc_job_iterator(&iter, 1, inetd->svc->job);
|
||||||
|
while (svc) {
|
||||||
|
if (!svc_is_inetd(svc)) {
|
||||||
|
if (!check_allowed || !inetd_is_allowed(inetd, svc->iifname)) {
|
||||||
|
svc_stop(svc);
|
||||||
|
service_step(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svc = svc_job_iterator(&iter, 0, inetd->svc->job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void inetd_stop(inetd_t *inetd)
|
void inetd_stop(inetd_t *inetd)
|
||||||
{
|
{
|
||||||
|
if (!inetd || !inetd->svc) {
|
||||||
|
_e("Invalid inetd, cannot stop it...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (inetd->watcher.fd != -1) {
|
if (inetd->watcher.fd != -1) {
|
||||||
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
||||||
uev_io_stop(&inetd->watcher);
|
uev_io_stop(&inetd->watcher);
|
||||||
|
|
||||||
/* For dgram inetd services we block the parent SVC
|
/*
|
||||||
* and halt the watcher, so don't close the socket! */
|
* For dgram inetd services we block the parent SVC
|
||||||
|
* and halt the watcher, so don't close the socket!
|
||||||
|
*/
|
||||||
if (!svc_is_busy(inetd->svc)) {
|
if (!svc_is_busy(inetd->svc)) {
|
||||||
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
||||||
if (inetd->type == SOCK_STREAM)
|
|
||||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
|
||||||
close(inetd->watcher.fd);
|
close(inetd->watcher.fd);
|
||||||
inetd->watcher.fd = -1;
|
inetd->watcher.fd = -1;
|
||||||
|
|
||||||
|
inetd_stop_children(inetd, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -642,9 +705,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
|||||||
|
|
||||||
svc_t *inetd_find_svc(char *path, char *service, char *proto)
|
svc_t *inetd_find_svc(char *path, char *service, char *proto)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
|
||||||
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
|
|||||||
|
|
||||||
int inetd_start (inetd_t *inetd);
|
int inetd_start (inetd_t *inetd);
|
||||||
void inetd_stop (inetd_t *inetd);
|
void inetd_stop (inetd_t *inetd);
|
||||||
|
void inetd_stop_children (inetd_t *inetd, int check_allowed);
|
||||||
|
|
||||||
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
|
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
|
||||||
int inetd_del (inetd_t *inetd);
|
int inetd_del (inetd_t *inetd);
|
||||||
|
|||||||
+178
-162
@@ -33,11 +33,9 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <utmp.h>
|
#include <utmp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "client.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "serv.h"
|
#include "serv.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
@@ -45,78 +43,30 @@
|
|||||||
|
|
||||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||||
|
|
||||||
typedef struct {
|
struct command {
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int (*cb)(char *arg);
|
int (*cb)(char *arg);
|
||||||
} command_t;
|
};
|
||||||
|
|
||||||
int verbose = 0;
|
int verbose = 0;
|
||||||
int runlevel = 0;
|
int runlevel = 0;
|
||||||
|
|
||||||
|
static int runlevel_get(int *prevlevel)
|
||||||
static int do_send(struct init_request *rq, ssize_t len)
|
|
||||||
{
|
{
|
||||||
int sd, result = 255;
|
int result;
|
||||||
struct sockaddr_un sun = {
|
|
||||||
.sun_family = AF_UNIX,
|
|
||||||
.sun_path = INIT_SOCKET,
|
|
||||||
};
|
|
||||||
|
|
||||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
||||||
if (-1 == sd)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (write(sd, rq, len) != len)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (read(sd, rq, len) != len)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
result = 0;
|
|
||||||
goto exit;
|
|
||||||
error:
|
|
||||||
perror("Failed communicating with finit");
|
|
||||||
exit:
|
|
||||||
close(sd);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int runlevel_get(void)
|
|
||||||
{
|
|
||||||
int sd, result = 255;
|
|
||||||
struct init_request rq;
|
struct init_request rq;
|
||||||
struct sockaddr_un sun = {
|
|
||||||
.sun_family = AF_UNIX,
|
|
||||||
.sun_path = INIT_SOCKET,
|
|
||||||
};
|
|
||||||
|
|
||||||
memset(&rq, 0, sizeof(rq));
|
memset(&rq, 0, sizeof(rq));
|
||||||
rq.cmd = INIT_CMD_GET_RUNLEVEL;
|
rq.cmd = INIT_CMD_GET_RUNLEVEL;
|
||||||
rq.magic = INIT_MAGIC;
|
rq.magic = INIT_MAGIC;
|
||||||
|
|
||||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
result = client_send(&rq, sizeof(rq));
|
||||||
if (-1 == sd)
|
if (!result) {
|
||||||
return -1;
|
result = rq.runlevel;
|
||||||
|
if (prevlevel)
|
||||||
|
*prevlevel = rq.sleeptime;
|
||||||
|
}
|
||||||
|
|
||||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (read(sd, &rq, sizeof(rq)) != sizeof(rq))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
result = rq.runlevel;
|
|
||||||
goto exit;
|
|
||||||
error:
|
|
||||||
perror("Failed communicating with finit");
|
|
||||||
result = -1;
|
|
||||||
exit:
|
|
||||||
close(sd);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +77,7 @@ static int toggle_debug(char *arg)
|
|||||||
.cmd = INIT_CMD_DEBUG,
|
.cmd = INIT_CMD_DEBUG,
|
||||||
};
|
};
|
||||||
|
|
||||||
return do_send(&rq, sizeof(rq));
|
return client_send(&rq, sizeof(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_log(char *svc)
|
static int do_log(char *svc)
|
||||||
@@ -146,6 +96,9 @@ static int do_log(char *svc)
|
|||||||
return system(cmd);
|
return system(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: Convert to read UTMP instead, like runlevel(8) does
|
||||||
|
*/
|
||||||
static int do_runlevel(char *arg)
|
static int do_runlevel(char *arg)
|
||||||
{
|
{
|
||||||
struct init_request rq = {
|
struct init_request rq = {
|
||||||
@@ -154,14 +107,25 @@ static int do_runlevel(char *arg)
|
|||||||
.runlevel = (int)arg[0],
|
.runlevel = (int)arg[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Not compatible with the SysV runlevel(8) command, it prints
|
|
||||||
* "PREVLEVEL RUNLEVEL", we just print the current runlevel. */
|
|
||||||
if (!rq.runlevel) {
|
if (!rq.runlevel) {
|
||||||
printf("%d\n", runlevel_get());
|
char prev;
|
||||||
|
int runlevel, prevlevel;
|
||||||
|
|
||||||
|
runlevel = runlevel_get(&prevlevel);
|
||||||
|
if (255 == runlevel) {
|
||||||
|
printf("unknown\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = prevlevel + '0';
|
||||||
|
if (prev <= '0' || prev > '9')
|
||||||
|
prev = 'N';
|
||||||
|
|
||||||
|
printf("%c %d\n", prev , runlevel);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return do_send(&rq, sizeof(rq));
|
return client_send(&rq, sizeof(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_svc(int cmd, char *arg)
|
static int do_svc(int cmd, char *arg)
|
||||||
@@ -173,27 +137,12 @@ static int do_svc(int cmd, char *arg)
|
|||||||
|
|
||||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
|
|
||||||
return do_send(&rq, sizeof(rq));
|
return client_send(&rq, sizeof(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
||||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
||||||
|
|
||||||
static int missing(char *job, int id)
|
|
||||||
{
|
|
||||||
char idstr[10] = "";
|
|
||||||
|
|
||||||
if (!job)
|
|
||||||
job = "";
|
|
||||||
if (id > 1)
|
|
||||||
snprintf(idstr, sizeof(idstr), ":%d", id);
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
fprintf(stderr, "No such service %s%s\n", job, idstr);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a wrapper for do_svc() that adds a simple sanity check of
|
* This is a wrapper for do_svc() that adds a simple sanity check of
|
||||||
* the service(s) provided as argument. If a service does not exist
|
* the service(s) provided as argument. If a service does not exist
|
||||||
@@ -201,28 +150,20 @@ static int missing(char *job, int id)
|
|||||||
*/
|
*/
|
||||||
static int do_startstop(int cmd, char *arg)
|
static int do_startstop(int cmd, char *arg)
|
||||||
{
|
{
|
||||||
int ret;
|
struct init_request rq = {
|
||||||
char *svcs;
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = INIT_CMD_SVC_QUERY
|
||||||
|
};
|
||||||
|
|
||||||
if (!arg) {
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]",
|
if (client_send(&rq, sizeof(rq))) {
|
||||||
|
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
|
||||||
|
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
|
||||||
cmd == INIT_CMD_START_SVC ? "start" :
|
cmd == INIT_CMD_START_SVC ? "start" :
|
||||||
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
|
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
svcs = strdup(arg);
|
|
||||||
if (!svcs) {
|
|
||||||
perror("Failed validating service(s) name");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Include \0 in len, needed by parser */
|
|
||||||
ret = svc_parse_jobstr(svcs, strlen(svcs) + 1, NULL, missing);
|
|
||||||
free(svcs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return do_svc(cmd, arg);
|
return do_svc(cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +173,7 @@ static int do_restart(char *arg) { return do_startstop(INIT_CMD_RESTART_SVC, arg
|
|||||||
|
|
||||||
static void show_cond_one(const char *_conds)
|
static void show_cond_one(const char *_conds)
|
||||||
{
|
{
|
||||||
static char conds[MAX_ARG_LEN];
|
static char conds[MAX_COND_LEN];
|
||||||
char *cond;
|
char *cond;
|
||||||
|
|
||||||
strlcpy(conds, _conds, sizeof(conds));
|
strlcpy(conds, _conds, sizeof(conds));
|
||||||
@@ -265,6 +206,9 @@ static int do_cond_magic(char op, char *cond)
|
|||||||
{
|
{
|
||||||
char event[368]; /* sizeof(init_request.data) */
|
char event[368]; /* sizeof(init_request.data) */
|
||||||
|
|
||||||
|
if (!cond || strlen(cond) < 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
event[0] = op;
|
event[0] = op;
|
||||||
strlcpy(&event[1], cond, sizeof(event) - 1);
|
strlcpy(&event[1], cond, sizeof(event) - 1);
|
||||||
|
|
||||||
@@ -281,7 +225,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
|||||||
if (tflag != FTW_F)
|
if (tflag != FTW_F)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!strcmp(fpath, _PATH_COND"reconf"))
|
if (!strcmp(fpath, _PATH_COND "reconf"))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
len = strlen(_PATH_COND);
|
len = strlen(_PATH_COND);
|
||||||
@@ -309,7 +253,7 @@ static int do_cond_show(char *arg)
|
|||||||
|
|
||||||
printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0);
|
printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0);
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||||
if (!svc->cond[0])
|
if (!svc->cond[0])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -333,10 +277,10 @@ static int do_cond(char *cmd)
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
char *arg;
|
char *arg;
|
||||||
command_t command[] = {
|
struct command command[] = {
|
||||||
|
{ "show", do_cond_show },
|
||||||
{ "set", do_cond_set },
|
{ "set", do_cond_set },
|
||||||
{ "clear", do_cond_clear },
|
{ "clear", do_cond_clear },
|
||||||
{ "show", do_cond_show },
|
|
||||||
{ "dump", do_cond_dump },
|
{ "dump", do_cond_dump },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
@@ -346,7 +290,7 @@ static int do_cond(char *cmd)
|
|||||||
*arg++ = 0;
|
*arg++ = 0;
|
||||||
|
|
||||||
for (c = 0; command[c].cmd; c++) {
|
for (c = 0; command[c].cmd; c++) {
|
||||||
if (!strcmp(command[c].cmd, cmd))
|
if (string_match(command[c].cmd, cmd))
|
||||||
return command[c].cb(arg);
|
return command[c].cb(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +375,7 @@ static int show_version(char *arg)
|
|||||||
char *runlevel_string(int runlevel, int levels)
|
char *runlevel_string(int runlevel, int levels)
|
||||||
{
|
{
|
||||||
int i, pos = 1;
|
int i, pos = 1;
|
||||||
static char lvl[20];
|
static char lvl[21];
|
||||||
|
|
||||||
memset(lvl, 0, sizeof(lvl));
|
memset(lvl, 0, sizeof(lvl));
|
||||||
lvl[0] = '[';
|
lvl[0] = '[';
|
||||||
@@ -459,53 +403,49 @@ char *runlevel_string(int runlevel, int levels)
|
|||||||
return lvl;
|
return lvl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In verbose mode we skip the header and each service description.
|
/*
|
||||||
|
* In verbose mode we skip the header and each service description.
|
||||||
* This in favor of having all info on one line so a machine can more
|
* This in favor of having all info on one line so a machine can more
|
||||||
* easily parse it. */
|
* easily parse it.
|
||||||
|
*/
|
||||||
static int show_status(char *arg)
|
static int show_status(char *arg)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
/* Fetch UTMP runlevel, needed for svc_status() call below */
|
/* Fetch UTMP runlevel, needed for svc_status() call below */
|
||||||
runlevel = runlevel_get();
|
runlevel = runlevel_get(NULL);
|
||||||
|
|
||||||
if (arg && arg[0]) {
|
if (arg && arg[0]) {
|
||||||
int id = 1;
|
long now = jiffies();
|
||||||
char *ptr, *next;
|
char buf[42] = "N/A";
|
||||||
|
|
||||||
ptr = strchr(arg, ':');
|
|
||||||
if (ptr) {
|
|
||||||
*ptr++ = 0;
|
|
||||||
next = strchr(ptr, ' ');
|
|
||||||
if (next)
|
|
||||||
*next = 0;
|
|
||||||
id = atonum(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isdigit(arg[0]))
|
|
||||||
svc = svc_find_by_jobid(atonum(arg), id);
|
|
||||||
else
|
|
||||||
svc = svc_find_by_nameid(arg, id);
|
|
||||||
|
|
||||||
|
svc = client_svc_find(arg);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
printf("%s\n", svc_status(svc));
|
printf("Service : %s\n", svc->cmd);
|
||||||
return 0;
|
printf("Description : %s\n", svc->desc);
|
||||||
|
printf("PID : %d\n", svc->pid);
|
||||||
|
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
|
||||||
|
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||||
|
printf("Status : %s\n", svc_status(svc));
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return do_log(svc->cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verbose)
|
if (!verbose)
|
||||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||||
char jobid[10], args[512] = "", *lvls;
|
char jobid[20], args[512] = "", *lvls;
|
||||||
|
|
||||||
if (svc_is_unique(svc))
|
// if (svc_is_unique(svc))
|
||||||
snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
// snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
||||||
else
|
// else
|
||||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
snprintf(jobid, sizeof(jobid), "%d:%s", svc->job, svc->id);
|
||||||
|
|
||||||
printf("%-5s %7s ", jobid, svc_status(svc));
|
printf("%-9s %7s ", jobid, svc_status(svc));
|
||||||
if (svc_is_inetd(svc))
|
if (svc_is_inetd(svc))
|
||||||
printf("inetd ");
|
printf("inetd ");
|
||||||
else
|
else
|
||||||
@@ -519,14 +459,7 @@ static int show_status(char *arg)
|
|||||||
|
|
||||||
if (!verbose) {
|
if (!verbose) {
|
||||||
int adj = screen_cols - 60;
|
int adj = screen_cols - 60;
|
||||||
char *name = strrchr(svc->cmd, '/');
|
printf("%-16.16s %-*.*s\n", svc->name, adj, adj, svc->desc);
|
||||||
|
|
||||||
if (!name)
|
|
||||||
name = svc->cmd;
|
|
||||||
else
|
|
||||||
name++;
|
|
||||||
|
|
||||||
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +472,7 @@ static int show_status(char *arg)
|
|||||||
};
|
};
|
||||||
|
|
||||||
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
|
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
|
||||||
if (do_send(&rq, sizeof(rq))) {
|
if (client_send(&rq, sizeof(rq))) {
|
||||||
snprintf(args, sizeof(args), "Unknown inetd");
|
snprintf(args, sizeof(args), "Unknown inetd");
|
||||||
info = args;
|
info = args;
|
||||||
} else {
|
} else {
|
||||||
@@ -548,7 +481,7 @@ static int show_status(char *arg)
|
|||||||
info = rq.data;
|
info = rq.data;
|
||||||
info[len - 1] = 0;
|
info[len - 1] = 0;
|
||||||
|
|
||||||
if (strcmp("internal", svc->cmd)) {
|
if (!string_match("internal", svc->cmd)) {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
ptr = strchr(info, ' ');
|
ptr = strchr(info, ' ');
|
||||||
@@ -576,12 +509,77 @@ static int show_status(char *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *group;
|
||||||
|
char path[256];
|
||||||
|
char buf[256];
|
||||||
|
int user = 0;
|
||||||
|
int num = 0;
|
||||||
|
|
||||||
|
if (tflag == FTW_F)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/cgroup.procs", fpath);
|
||||||
|
fp = fopen(path, "r");
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (strstr(fpath, "finit/user"))
|
||||||
|
user = 1;
|
||||||
|
|
||||||
|
group = strrchr(fpath, '/');
|
||||||
|
if (!group)
|
||||||
|
return 0;
|
||||||
|
group++;
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
FILE *cfp;
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
printf("%c :- %s/", user ? ' ' : '|', group);
|
||||||
|
num++;
|
||||||
|
|
||||||
|
pid = atoi(chomp(buf));
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||||
|
cfp = fopen(path, "r");
|
||||||
|
if (!cfp)
|
||||||
|
continue;
|
||||||
|
fgets(buf, sizeof(buf), cfp);
|
||||||
|
fclose(cfp);
|
||||||
|
|
||||||
|
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (num)
|
||||||
|
printf("\r%c\n", user ? ' ' : '|');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int show_cgroup(char *arg)
|
||||||
|
{
|
||||||
|
puts("finit/");
|
||||||
|
puts("|- init/");
|
||||||
|
nftw("/sys/fs/cgroup/finit/init", dump_cgroup, 20, 0);
|
||||||
|
puts("|- system/");
|
||||||
|
nftw("/sys/fs/cgroup/finit/system", dump_cgroup, 20, 0);
|
||||||
|
puts("`- user/");
|
||||||
|
nftw("/sys/fs/cgroup/finit/user", dump_cgroup, 20, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int usage(int rc)
|
static int usage(int rc)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: %s [OPTIONS] <COMMAND>\n"
|
"Usage: %s [OPTIONS] [COMMAND]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
|
" -b, --batch Batch mode, no screen size probing\n"
|
||||||
" -v, --verbose Verbose output\n"
|
" -v, --verbose Verbose output\n"
|
||||||
" -h, --help This help text\n"
|
" -h, --help This help text\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -593,7 +591,9 @@ static int usage(int rc)
|
|||||||
" list List all .conf in /etc/finit.d/\n"
|
" list List all .conf in /etc/finit.d/\n"
|
||||||
" enable <CONF> Enable .conf in /etc/finit.d/available/\n"
|
" enable <CONF> Enable .conf in /etc/finit.d/available/\n"
|
||||||
" disable <CONF> Disable .conf in /etc/finit.d/[enabled/]\n"
|
" disable <CONF> Disable .conf in /etc/finit.d/[enabled/]\n"
|
||||||
|
" touch <CONF> Mark .conf in /etc/finit.d/ for reload\n"
|
||||||
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
|
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
|
||||||
|
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
|
||||||
"\n"
|
"\n"
|
||||||
" cond set <COND> Set (assert) condition => +COND\n"
|
" cond set <COND> Set (assert) condition => +COND\n"
|
||||||
" cond clear <COND> Clear (deassert) condition => -COND\n"
|
" cond clear <COND> Clear (deassert) condition => -COND\n"
|
||||||
@@ -604,7 +604,10 @@ static int usage(int rc)
|
|||||||
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
|
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
|
||||||
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
|
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
|
||||||
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
||||||
" status | show Show status of services\n"
|
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
|
||||||
|
" status | show Show status of services, default command\n"
|
||||||
|
"\n"
|
||||||
|
" ps List processes based on cgroups\n"
|
||||||
"\n"
|
"\n"
|
||||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||||
" reboot Reboot system\n"
|
" reboot Reboot system\n"
|
||||||
@@ -624,8 +627,9 @@ static int do_help(char *arg)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c;
|
int interactive = 1, c;
|
||||||
command_t command[] = {
|
char *cmd, arg[120];
|
||||||
|
struct command command[] = {
|
||||||
{ "debug", toggle_debug },
|
{ "debug", toggle_debug },
|
||||||
{ "help", do_help },
|
{ "help", do_help },
|
||||||
{ "version", show_version },
|
{ "version", show_version },
|
||||||
@@ -633,6 +637,7 @@ int main(int argc, char *argv[])
|
|||||||
{ "list", serv_list },
|
{ "list", serv_list },
|
||||||
{ "enable", serv_enable },
|
{ "enable", serv_enable },
|
||||||
{ "disable", serv_disable },
|
{ "disable", serv_disable },
|
||||||
|
{ "touch", serv_touch },
|
||||||
{ "reload", do_reload },
|
{ "reload", do_reload },
|
||||||
|
|
||||||
{ "cond", do_cond },
|
{ "cond", do_cond },
|
||||||
@@ -644,6 +649,8 @@ int main(int argc, char *argv[])
|
|||||||
{ "status", show_status },
|
{ "status", show_status },
|
||||||
{ "show", show_status }, /* Convenience alias */
|
{ "show", show_status }, /* Convenience alias */
|
||||||
|
|
||||||
|
{ "ps", show_cgroup },
|
||||||
|
|
||||||
{ "runlevel", do_runlevel },
|
{ "runlevel", do_runlevel },
|
||||||
{ "reboot", do_reboot },
|
{ "reboot", do_reboot },
|
||||||
{ "halt", do_halt },
|
{ "halt", do_halt },
|
||||||
@@ -653,6 +660,7 @@ int main(int argc, char *argv[])
|
|||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
struct option long_options[] = {
|
struct option long_options[] = {
|
||||||
|
{"batch", 0, NULL, 'b'},
|
||||||
{"help", 0, NULL, 'h'},
|
{"help", 0, NULL, 'h'},
|
||||||
{"debug", 0, NULL, 'd'},
|
{"debug", 0, NULL, 'd'},
|
||||||
{"verbose", 0, NULL, 'v'},
|
{"verbose", 0, NULL, 'v'},
|
||||||
@@ -660,8 +668,12 @@ int main(int argc, char *argv[])
|
|||||||
};
|
};
|
||||||
|
|
||||||
progname(argv[0]);
|
progname(argv[0]);
|
||||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
while ((c = getopt_long(argc, argv, "bh?v", long_options, NULL)) != EOF) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
|
case 'b':
|
||||||
|
interactive = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
@@ -672,21 +684,25 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
screen_init();
|
if (interactive)
|
||||||
if (optind < argc) {
|
screen_init();
|
||||||
char *cmd = argv[optind++];
|
|
||||||
char arg[120] = "";
|
|
||||||
|
|
||||||
while (optind < argc) {
|
if (optind >= argc)
|
||||||
strlcat(arg, argv[optind++], sizeof(arg));
|
return show_status(NULL);
|
||||||
if (optind < argc)
|
|
||||||
strlcat(arg, " ", sizeof(arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (c = 0; command[c].cmd; c++) {
|
memset(arg, 0, sizeof(arg));
|
||||||
if (!strcmp(command[c].cmd, cmd))
|
cmd = argv[optind++];
|
||||||
return command[c].cb(arg);
|
while (optind < argc) {
|
||||||
}
|
strlcat(arg, argv[optind++], sizeof(arg));
|
||||||
|
if (optind < argc)
|
||||||
|
strlcat(arg, " ", sizeof(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (c = 0; command[c].cmd; c++) {
|
||||||
|
if (!string_match(command[c].cmd, cmd))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return command[c].cb(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return usage(1);
|
return usage(1);
|
||||||
|
|||||||
@@ -29,12 +29,22 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
static int up = 0;
|
static int up = 0;
|
||||||
static int debug = 0;
|
static int debug = 0;
|
||||||
static int silent = SILENT_MODE; /* Completely silent, including boot */
|
static int silent = SILENT_MODE; /* Completely silent, including boot */
|
||||||
static int loglevel = LOG_NOTICE;
|
static int loglevel = LOG_NOTICE;
|
||||||
|
|
||||||
|
static void muffler(void)
|
||||||
|
{
|
||||||
|
silent = SILENT_MODE;
|
||||||
|
|
||||||
|
/* User override from kernel cmdline */
|
||||||
|
if (splash)
|
||||||
|
silent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void log_init(int dbg)
|
void log_init(int dbg)
|
||||||
{
|
{
|
||||||
if (dbg)
|
if (dbg)
|
||||||
@@ -44,16 +54,27 @@ void log_init(int dbg)
|
|||||||
loglevel = LOG_DEBUG;
|
loglevel = LOG_DEBUG;
|
||||||
else
|
else
|
||||||
loglevel = LOG_NOTICE;
|
loglevel = LOG_NOTICE;
|
||||||
|
|
||||||
|
muffler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
|
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
|
||||||
void log_exit(void)
|
void log_exit(void)
|
||||||
{
|
{
|
||||||
silent = SILENT_MODE;
|
muffler();
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
sched_yield();
|
sched_yield();
|
||||||
fputs("\n", stderr);
|
fputs("\n", stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unless in debug mode at shutdown, Reinitialize screen,
|
||||||
|
* terminal may have been resized at runtime
|
||||||
|
*/
|
||||||
|
if (debug)
|
||||||
|
screen_exit();
|
||||||
|
else
|
||||||
|
screen_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_open(void)
|
void log_open(void)
|
||||||
@@ -63,7 +84,7 @@ void log_open(void)
|
|||||||
closelog();
|
closelog();
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
opts |= LOG_CONS | LOG_PERROR;
|
opts |= LOG_PERROR; /* LOG_CONS | */
|
||||||
openlog("finit", opts, LOG_DAEMON);
|
openlog("finit", opts, LOG_DAEMON);
|
||||||
setlogmask(LOG_UPTO(loglevel));
|
setlogmask(LOG_UPTO(loglevel));
|
||||||
|
|
||||||
@@ -91,12 +112,19 @@ void log_debug(void)
|
|||||||
if (debug) {
|
if (debug) {
|
||||||
silent = 0;
|
silent = 0;
|
||||||
loglevel = LOG_DEBUG;
|
loglevel = LOG_DEBUG;
|
||||||
|
|
||||||
|
screen_exit();
|
||||||
} else {
|
} else {
|
||||||
silent = SILENT_MODE;
|
silent = SILENT_MODE;
|
||||||
loglevel = LOG_NOTICE;
|
loglevel = LOG_NOTICE;
|
||||||
|
|
||||||
|
screen_init();
|
||||||
}
|
}
|
||||||
log_open();
|
log_open();
|
||||||
|
|
||||||
|
/* Also toggle silent mode (progress) */
|
||||||
|
log_silent();
|
||||||
|
|
||||||
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
|
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,45 +133,43 @@ int log_is_debug(void)
|
|||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void early_logit(int prio, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
if (!debug && prio > loglevel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fp = fopen("/dev/kmsg", "w");
|
|
||||||
if (fp) {
|
|
||||||
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
|
|
||||||
vfprintf(fp, fmt, ap);
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
} else {
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Log to /dev/kmsg until syslogd has started, then openlog()
|
* Log to /dev/kmsg until syslogd has started, then openlog()
|
||||||
* and continue logging as a regular daemon.
|
* and continue logging as a regular daemon.
|
||||||
*/
|
*/
|
||||||
void logit(int prio, const char *fmt, ...)
|
void logit(int prio, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
FILE *fp;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
if (!up) {
|
|
||||||
if (!fexist("/dev/log")) {
|
|
||||||
early_logit(prio, fmt, ap);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_open();
|
if (up || fexist("/dev/log")) {
|
||||||
|
if (!up)
|
||||||
|
log_open();
|
||||||
|
vsyslog(prio, fmt, ap);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!debug && prio > loglevel)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
fp = fopen("/dev/kmsg", "w");
|
||||||
|
if (!fp) {
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
|
||||||
|
vfprintf(fp, fmt, ap);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
va_end(ap);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
vsyslog(prio, fmt, ap);
|
|
||||||
done:
|
done:
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
* errors and debug messages. For all other user messages, see logit()
|
* errors and debug messages. For all other user messages, see logit()
|
||||||
*
|
*
|
||||||
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
|
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
|
||||||
* use `initctl debug` or add `--debug` to the kernel cmdline.
|
* use `initctl debug` or add `debug` to the kernel cmdline.
|
||||||
*/
|
*/
|
||||||
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
|
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
|
||||||
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args)
|
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args)
|
||||||
|
|||||||
+286
@@ -0,0 +1,286 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#define SYSLOG_NAMES
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
|
||||||
|
|
||||||
|
|
||||||
|
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||||
|
* At most @num old versions are kept and by default it starts gzipping
|
||||||
|
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||||
|
* files are kept uncompressed.
|
||||||
|
*/
|
||||||
|
static int logrotate(char *file, int num, off_t sz)
|
||||||
|
{
|
||||||
|
int cnt;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(file, &st))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||||
|
if (num > 0) {
|
||||||
|
size_t len = strlen(file) + 10 + 1;
|
||||||
|
char ofile[len];
|
||||||
|
char nfile[len];
|
||||||
|
|
||||||
|
/* First age zipped log files */
|
||||||
|
for (cnt = num; cnt > 2; cnt--) {
|
||||||
|
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||||
|
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||||
|
|
||||||
|
/* May fail because ofile doesn't exist yet, ignore. */
|
||||||
|
(void)rename(ofile, nfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cnt = num; cnt > 0; cnt--) {
|
||||||
|
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||||
|
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||||
|
|
||||||
|
/* May fail because ofile doesn't exist yet, ignore. */
|
||||||
|
(void)rename(ofile, nfile);
|
||||||
|
|
||||||
|
if (cnt == 2 && !access(nfile, F_OK)) {
|
||||||
|
size_t len = 5 + strlen(nfile) + 1;
|
||||||
|
char cmd[len];
|
||||||
|
|
||||||
|
snprintf(cmd, len, "gzip %s", nfile);
|
||||||
|
system(cmd);
|
||||||
|
|
||||||
|
remove(nfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rename(file, nfile))
|
||||||
|
(void)truncate(file, 0);
|
||||||
|
else
|
||||||
|
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||||
|
} else {
|
||||||
|
if (truncate(file, 0))
|
||||||
|
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int checksz(FILE *fp, off_t sz)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (sz <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!fstat(fileno(fp), &st) && st.st_size > sz) {
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flogit(char *logfile, int num, off_t sz, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
reopen:
|
||||||
|
fp = fopen(logfile, "a");
|
||||||
|
if (!fp) {
|
||||||
|
syslog(LOG_ERR | LOG_PERROR, "Failed opening %s: %s", logfile, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf[0]) {
|
||||||
|
fprintf(fp, "%s\n", buf);
|
||||||
|
fsync(fileno(fp));
|
||||||
|
if (checksz(fp, sz))
|
||||||
|
return logrotate(logfile, num, sz);
|
||||||
|
} else {
|
||||||
|
while ((fgets(buf, len, stdin))) {
|
||||||
|
fputs(buf, fp);
|
||||||
|
fsync(fileno(fp));
|
||||||
|
|
||||||
|
if (checksz(fp, sz)) {
|
||||||
|
logrotate(logfile, num, sz);
|
||||||
|
buf[0] = 0;
|
||||||
|
goto reopen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int logit(int level, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
if (buf[0]) {
|
||||||
|
syslog(level, "%s", buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((fgets(buf, len, stdin)))
|
||||||
|
syslog(level, "%s", buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_prio(char *arg, int *f, int *l)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
ptr = strchr(arg, '.');
|
||||||
|
if (ptr) {
|
||||||
|
*ptr++ = 0;
|
||||||
|
|
||||||
|
for (int i = 0; facilitynames[i].c_name; i++) {
|
||||||
|
if (!strcmp(facilitynames[i].c_name, arg)) {
|
||||||
|
*f = facilitynames[i].c_val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; prioritynames[i].c_name; i++) {
|
||||||
|
if (!strcmp(prioritynames[i].c_name, arg)) {
|
||||||
|
*l = prioritynames[i].c_val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usage(int code)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: logit [OPTIONS] [MESSAGE]\n"
|
||||||
|
"\n"
|
||||||
|
"Write MESSAGE (or stdin) to syslog, or file (with logrotate)\n"
|
||||||
|
"\n"
|
||||||
|
" -h This help text\n"
|
||||||
|
" -p PRIO Priority (numeric or facility.level pair)\n"
|
||||||
|
" -t TAG Log using the specified tag (defaults to user name)\n"
|
||||||
|
" -s Log to stderr as well as the system log\n"
|
||||||
|
"\n"
|
||||||
|
" -f FILE File to write log messages to, instead of syslog\n"
|
||||||
|
" -n SIZE Number of bytes before rotating, default: 200 kB\n"
|
||||||
|
" -r NUM Number of rotated files to keep, default: 5\n"
|
||||||
|
" -v Show program version\n"
|
||||||
|
"\n"
|
||||||
|
"This version of logit is distributed as part of Finit.\n"
|
||||||
|
"Bug report address: %s\n", PACKAGE_BUGREPORT);
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int c, rc, num = 5;
|
||||||
|
int facility = LOG_USER;
|
||||||
|
int level = LOG_INFO;
|
||||||
|
int log_opts = LOG_NOWAIT;
|
||||||
|
off_t size = 200 * 1024;
|
||||||
|
char *ident = NULL, *logfile = NULL;
|
||||||
|
char buf[512] = "";
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) {
|
||||||
|
switch (c) {
|
||||||
|
case 'f':
|
||||||
|
logfile = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
return usage(0);
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
size = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
if (parse_prio(optarg, &facility, &level))
|
||||||
|
return usage(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
num = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
log_opts |= LOG_PERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
ident = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'v': /* version */
|
||||||
|
fprintf(stderr, "%s\n", version_info);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return usage(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ident)
|
||||||
|
ident = getenv("LOGNAME") ?: getenv("USER");
|
||||||
|
|
||||||
|
if (optind < argc) {
|
||||||
|
size_t pos = 0, len = sizeof(buf);
|
||||||
|
|
||||||
|
while (optind < argc) {
|
||||||
|
size_t bytes;
|
||||||
|
|
||||||
|
bytes = snprintf(&buf[pos], len, "%s ", argv[optind++]);
|
||||||
|
pos += bytes;
|
||||||
|
len -= bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openlog(ident, log_opts, facility);
|
||||||
|
|
||||||
|
if (logfile)
|
||||||
|
rc = flogit(logfile, num, size, buf, sizeof(buf));
|
||||||
|
else
|
||||||
|
rc = logit(level, buf, sizeof(buf));
|
||||||
|
|
||||||
|
closelog();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
/* PID and PID file helpers
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2008-2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,11 +23,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <paths.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
|
#include "pid.h"
|
||||||
|
#include "svc.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -90,6 +93,117 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
|||||||
return pname;
|
return pname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *pid_file(svc_t *svc)
|
||||||
|
{
|
||||||
|
char fn[MAX_ARG_LEN];
|
||||||
|
static char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
if (svc->pidfile[0]) {
|
||||||
|
if (svc->pidfile[0] == '!')
|
||||||
|
return &svc->pidfile[1];
|
||||||
|
return svc->pidfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
||||||
|
|
||||||
|
return pid_runpath(fn, path, sizeof(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
int pid_file_create(svc_t *svc)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (!svc->pidfile[0] || svc->pidfile[0] == '!')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fp = fopen(svc->pidfile, "w");
|
||||||
|
if (!fp)
|
||||||
|
return 1;
|
||||||
|
fprintf(fp, "%d\n", svc->pid);
|
||||||
|
|
||||||
|
return fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pid_realpath(svc_t *svc, char *file)
|
||||||
|
{
|
||||||
|
int not = 0;
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
file = pid_file(svc);
|
||||||
|
|
||||||
|
if (file[0] == '!') {
|
||||||
|
not = 1;
|
||||||
|
file++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
|
||||||
|
if (not)
|
||||||
|
svc->pidfile[0] = '!';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function parses a PID file @arg for @svc.
|
||||||
|
*
|
||||||
|
* The logic is explained below. Please note that using the first form
|
||||||
|
* of the syntax not only creates and removes the PID file, but it also
|
||||||
|
* calls touch to update the mtime on service_restart(), only applicable
|
||||||
|
* to processes that accept SIGHUP. This to ensure dependant services
|
||||||
|
* are sent SIGCONT properly on reload, otherwise their condition would
|
||||||
|
* never be asserted again after `initctl reload`.
|
||||||
|
*
|
||||||
|
* pid --> /run/$(basename).pid
|
||||||
|
* pid:foo --> /run/foo.pid
|
||||||
|
* pid:foo.pid --> /run/foo.pid
|
||||||
|
* pid:foo.tla --> /run/foo.tla.pid
|
||||||
|
* pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!)
|
||||||
|
*
|
||||||
|
* An exclamation mark, or logial NOT, in the first character position
|
||||||
|
* indicates that Finit should *not* manage the PID file, and that the
|
||||||
|
* process uses a "non-standard" location. Eg. if service is 'bar'
|
||||||
|
* Finit would assume the PID file is /run/bar.pid and the below simply
|
||||||
|
* 'redirects' Finit to use the correct PID file.
|
||||||
|
*
|
||||||
|
* pid:!foo --> !/run/foo.pid
|
||||||
|
* pid:!/run/foo.pid --> !/run/foo.pid
|
||||||
|
*
|
||||||
|
* Note, nothing is created or removed by Finit in this latter form.
|
||||||
|
*/
|
||||||
|
int pid_file_parse(svc_t *svc, char *arg)
|
||||||
|
{
|
||||||
|
/* Sanity check ... */
|
||||||
|
if (!arg || !arg[0])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* 'pid:' implies argument following*/
|
||||||
|
if (!strncmp(arg, "pid:", 4)) {
|
||||||
|
int len, not = 0;
|
||||||
|
char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
arg += 4;
|
||||||
|
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
||||||
|
return pid_realpath(svc, arg);
|
||||||
|
|
||||||
|
if (arg[0] == '!') {
|
||||||
|
arg++;
|
||||||
|
not++;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = snprintf(path, sizeof(path), "%s%s%s", not ? "!" : "", _PATH_VARRUN, arg);
|
||||||
|
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
||||||
|
strlcat(path, ".pid", sizeof(path));
|
||||||
|
|
||||||
|
return pid_realpath(svc, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'pid' arg, no argument following */
|
||||||
|
if (!strcmp(arg, "pid"))
|
||||||
|
return pid_realpath(svc, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/* PID and PID file helpers
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_PID_H_
|
||||||
|
#define FINIT_PID_H_
|
||||||
|
|
||||||
|
#include "svc.h"
|
||||||
|
|
||||||
|
int pid_alive (pid_t pid);
|
||||||
|
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||||
|
|
||||||
|
char *pid_file (svc_t *svc);
|
||||||
|
int pid_file_create (svc_t *svc);
|
||||||
|
int pid_file_parse (svc_t *svc, char *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pid_runpath - Adjust /var/run --> /run path depending on system
|
||||||
|
* @file: Path to file in /run/path or /var/run/path
|
||||||
|
* @path: Pointer to buffer to write correct path
|
||||||
|
* @len: Length, in bytes, of @path buffer
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Always returns a valid pointer, which can be either @path with a /run
|
||||||
|
* or /var/run prefix to @file, or it may be @file if the prefix is OK.
|
||||||
|
*/
|
||||||
|
static inline char *pid_runpath(const char *file, char *path, size_t len)
|
||||||
|
{
|
||||||
|
static int unknown = 1;
|
||||||
|
static char *prefix = "/var/run";
|
||||||
|
|
||||||
|
if (unknown) {
|
||||||
|
if (fisdir("/run"))
|
||||||
|
prefix = "/run";
|
||||||
|
unknown = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(file, "/var/run/", 9))
|
||||||
|
file += 9;
|
||||||
|
else if (!strncmp(file, "/run/", 5))
|
||||||
|
file += 5;
|
||||||
|
|
||||||
|
snprintf(path, len, "%s/%s", prefix, file);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* FINIT_PID_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+27
-6
@@ -1,6 +1,6 @@
|
|||||||
/* Plugin based services architecture for finit
|
/* Plugin based services architecture for finit
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2012-2017 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,10 +30,12 @@
|
|||||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "cond.h"
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "private.h"
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "private.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
||||||
#define SEARCH_PLUGIN(str) \
|
#define SEARCH_PLUGIN(str) \
|
||||||
@@ -106,6 +108,9 @@ int plugin_register(plugin_t *plugin)
|
|||||||
/* Not called, at the moment plugins cannot be unloaded. */
|
/* Not called, at the moment plugins cannot be unloaded. */
|
||||||
int plugin_unregister(plugin_t *plugin)
|
int plugin_unregister(plugin_t *plugin)
|
||||||
{
|
{
|
||||||
|
if (is_io_plugin(plugin))
|
||||||
|
uev_io_stop(&plugin->watcher);
|
||||||
|
|
||||||
#ifndef ENABLE_STATIC
|
#ifndef ENABLE_STATIC
|
||||||
TAILQ_REMOVE(&plugins, plugin, link);
|
TAILQ_REMOVE(&plugins, plugin, link);
|
||||||
|
|
||||||
@@ -163,6 +168,15 @@ plugin_t *plugin_find(char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Private daemon API *******************************************************/
|
/* Private daemon API *******************************************************/
|
||||||
|
#define CHOOSE(x, y) y
|
||||||
|
static const char *hook_cond[] = HOOK_TYPES;
|
||||||
|
#undef CHOOSE
|
||||||
|
|
||||||
|
const char *plugin_hook_str(hook_point_t no)
|
||||||
|
{
|
||||||
|
return hook_cond[no];
|
||||||
|
}
|
||||||
|
|
||||||
int plugin_exists(hook_point_t no)
|
int plugin_exists(hook_point_t no)
|
||||||
{
|
{
|
||||||
plugin_t *p, *tmp;
|
plugin_t *p, *tmp;
|
||||||
@@ -175,7 +189,7 @@ int plugin_exists(hook_point_t no)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
|
/* Some hooks are called with a fixed argument */
|
||||||
void plugin_run_hook(hook_point_t no, void *arg)
|
void plugin_run_hook(hook_point_t no, void *arg)
|
||||||
{
|
{
|
||||||
plugin_t *p, *tmp;
|
plugin_t *p, *tmp;
|
||||||
@@ -186,6 +200,9 @@ void plugin_run_hook(hook_point_t no, void *arg)
|
|||||||
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cond_set_oneshot(hook_cond[no]);
|
||||||
|
service_step_all(SVC_TYPE_RUNTASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Regular hooks are called with the registered plugin's argument */
|
/* Regular hooks are called with the registered plugin's argument */
|
||||||
@@ -194,7 +211,11 @@ void plugin_run_hooks(hook_point_t no)
|
|||||||
plugin_run_hook(no, NULL);
|
plugin_run_hook(no, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
|
/*
|
||||||
|
* Generic libev I/O callback, looks up correct plugin and calls its
|
||||||
|
* callback. libuEv might return UEV_ERROR in events, it is up to the
|
||||||
|
* plugin callback to handle this.
|
||||||
|
*/
|
||||||
static void generic_io_cb(uev_t *w, void *arg, int events)
|
static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
plugin_t *p = (plugin_t *)arg;
|
plugin_t *p = (plugin_t *)arg;
|
||||||
@@ -349,11 +370,11 @@ static int load_plugins(char *path)
|
|||||||
}
|
}
|
||||||
#endif /* ENABLE_STATIC */
|
#endif /* ENABLE_STATIC */
|
||||||
|
|
||||||
int plugin_init(uev_ctx_t *ctx, char *path)
|
int plugin_init(uev_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
int fail = 1;
|
int fail = 1;
|
||||||
|
|
||||||
if (!load_plugins(path))
|
if (!load_plugins(PLUGIN_PATH))
|
||||||
fail = init_plugins(ctx);
|
fail = init_plugins(ctx);
|
||||||
|
|
||||||
return fail;
|
return fail;
|
||||||
|
|||||||
+44
-25
@@ -1,6 +1,6 @@
|
|||||||
/* Plugin based services architecture for finit
|
/* Plugin based services architecture for finit
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2012-2017 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,13 +29,17 @@
|
|||||||
|
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
|
|
||||||
#define PLUGIN_PATH "/dev/null"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PLUGIN_DEP_MAX 10
|
#define PLUGIN_DEP_MAX 10
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event flags for I/O plugins
|
||||||
|
*/
|
||||||
|
#define PLUGIN_IO_ERROR UEV_ERROR
|
||||||
#define PLUGIN_IO_READ UEV_READ
|
#define PLUGIN_IO_READ UEV_READ
|
||||||
#define PLUGIN_IO_WRITE UEV_WRITE
|
#define PLUGIN_IO_WRITE UEV_WRITE
|
||||||
|
#define PLUGIN_IO_PRI UEV_PRI
|
||||||
|
#define PLUGIN_IO_HUP UEV_HUP
|
||||||
|
#define PLUGIN_IO_RDHUP UEV_RDHUP
|
||||||
|
|
||||||
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
|
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
|
||||||
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
|
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
|
||||||
@@ -43,28 +47,43 @@
|
|||||||
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
|
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Predefined hook points for easier plugin debugging
|
* Predefined hook points and corresponding conditions in Finit,
|
||||||
|
* for use by plugins and scripts. Recommended to use the task
|
||||||
|
* or run stanzas: task <hook/mount/error> /bin/rescue.sh
|
||||||
|
*
|
||||||
|
* Some of the below hooks cannot (currently) be realized as conditions.
|
||||||
|
* The idea was to rename them action scripts, with optional argument,
|
||||||
|
* according to the following scheme:
|
||||||
|
*
|
||||||
|
* - HOOK_SVC_RECONF :: action/svc/reconf
|
||||||
|
* - HOOK_RUNLEVEL_CHANGE :: action/sys/runlevel
|
||||||
|
*
|
||||||
|
* However, the implementation did not turn out to be stable enough for
|
||||||
|
* general release, so it was pulled.
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
#define HOOK_TYPES { \
|
||||||
/* Bootstrap hooks */
|
/* Bootstrap hooks, runlevel [S] */ \
|
||||||
HOOK_BANNER = 0,
|
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
|
||||||
HOOK_ROOTFS_UP,
|
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
|
||||||
HOOK_MOUNT_ERROR,
|
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
||||||
HOOK_BASEFS_UP,
|
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
|
||||||
HOOK_NETWORK_UP,
|
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
||||||
HOOK_SVC_UP,
|
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
||||||
HOOK_SYSTEM_UP,
|
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
||||||
|
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
||||||
|
\
|
||||||
|
/* Runtime hooks, runlevel [S1-9] */ \
|
||||||
|
CHOOSE(HOOK_SVC_RECONF, "nop"), \
|
||||||
|
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
||||||
|
\
|
||||||
|
/* Shutdown hooks, runlevel [06] */ \
|
||||||
|
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
||||||
|
CHOOSE(HOOK_MAX_NUM, "nop") \
|
||||||
|
}
|
||||||
|
|
||||||
/* Runtime hooks */
|
#define CHOOSE(x, y) x
|
||||||
HOOK_SVC_RECONF,
|
typedef enum HOOK_TYPES hook_point_t;
|
||||||
HOOK_SVC_LOST,
|
#undef CHOOSE
|
||||||
HOOK_SVC_START,
|
|
||||||
HOOK_RUNLEVEL_CHANGE,
|
|
||||||
|
|
||||||
/* Shutdown hooks */
|
|
||||||
HOOK_SHUTDOWN,
|
|
||||||
HOOK_MAX_NUM
|
|
||||||
} hook_point_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* plugin_t - Finit &plugin_t object
|
* plugin_t - Finit &plugin_t object
|
||||||
|
|||||||
+2
-6
@@ -28,11 +28,6 @@
|
|||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* Main loop context
|
|
||||||
*/
|
|
||||||
uev_ctx_t *ctx;
|
|
||||||
|
|
||||||
int api_init (uev_ctx_t *ctx);
|
int api_init (uev_ctx_t *ctx);
|
||||||
int api_exit (void);
|
int api_exit (void);
|
||||||
|
|
||||||
@@ -40,11 +35,12 @@ int client (int argc, char *argv[]);
|
|||||||
|
|
||||||
void service_monitor (pid_t lost);
|
void service_monitor (pid_t lost);
|
||||||
|
|
||||||
|
const char *plugin_hook_str(hook_point_t no);
|
||||||
int plugin_exists (hook_point_t no);
|
int plugin_exists (hook_point_t no);
|
||||||
void plugin_run_hook (hook_point_t no, void *arg);
|
void plugin_run_hook (hook_point_t no, void *arg);
|
||||||
void plugin_run_hooks (hook_point_t no);
|
void plugin_run_hooks (hook_point_t no);
|
||||||
|
|
||||||
int plugin_init (uev_ctx_t *ctx, char *path);
|
int plugin_init (uev_ctx_t *ctx);
|
||||||
void plugin_exit (void);
|
void plugin_exit (void);
|
||||||
|
|
||||||
#endif /* FINIT_PRIVATE_H_ */
|
#endif /* FINIT_PRIVATE_H_ */
|
||||||
|
|||||||
+1
-1
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (c)
|
if (c)
|
||||||
err(1, "Failed signalling init to %s", msg);
|
err(1, "Failed signalling init to %s", msg);
|
||||||
do_sleep(2);
|
do_sleep(5);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
runlevel 1
|
||||||
|
hostname rescue
|
||||||
|
tty [12345] @console noclear nologin
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Save and restore RTC using hwclock
|
/* Work queue helper functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,44 +21,44 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <lite/lite.h>
|
#include "config.h"
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "schedule.h"
|
||||||
#include "plugin.h"
|
|
||||||
|
|
||||||
static void save(void *arg)
|
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libuEv callback wrapper
|
||||||
|
*/
|
||||||
|
static void cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("Saving system clock to RTC ...");
|
struct wq *work = (struct wq *)arg;
|
||||||
/* -w,--systohc, -u,--utc */
|
|
||||||
run_interactive("hwclock -w -u", "Saving system time (UTC) to RTC");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void restore(void *arg)
|
if (UEV_ERROR == events) {
|
||||||
{
|
uev_timer_start(w);
|
||||||
_d("Restoring system clock from RTC ...");
|
return;
|
||||||
/* -s,--hctosys, -u,--utc */
|
|
||||||
run_interactive("hwclock -s -u", "Restoring system clock (UTC) from RTC");
|
|
||||||
}
|
|
||||||
|
|
||||||
static plugin_t plugin = {
|
|
||||||
.name = __FILE__,
|
|
||||||
.hook[HOOK_BASEFS_UP] = {
|
|
||||||
.cb = restore
|
|
||||||
},
|
|
||||||
.hook[HOOK_SHUTDOWN] = {
|
|
||||||
.cb = save
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
work->cb(work);
|
||||||
{
|
|
||||||
plugin_register(&plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUGIN_EXIT(plugin_exit)
|
/*
|
||||||
|
* Place work on event queue
|
||||||
|
*/
|
||||||
|
int schedule_work(struct wq *work)
|
||||||
{
|
{
|
||||||
plugin_unregister(&plugin);
|
int msec;
|
||||||
|
|
||||||
|
if (!work)
|
||||||
|
return errno = EINVAL;
|
||||||
|
|
||||||
|
msec = work->delay;
|
||||||
|
if (work->init != SC_INIT) {
|
||||||
|
work->init = SC_INIT;
|
||||||
|
return uev_timer_init(ctx, &work->watcher, cb, work, msec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uev_timer_set(&work->watcher, msec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/* Work queue helper functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_SCHEDULE_H_
|
||||||
|
#define FINIT_SCHEDULE_H_
|
||||||
|
|
||||||
|
struct wq {
|
||||||
|
uev_t watcher;
|
||||||
|
int init;
|
||||||
|
int delay; /* msec delay before starting work */
|
||||||
|
void (*cb)(void *);
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
int schedule_work(struct wq *work);
|
||||||
|
|
||||||
|
#endif /* FINIT_SCHEDULE_H_ */
|
||||||
+40
-4
@@ -24,6 +24,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <fcntl.h> /* Definition of AT_* constants */
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
@@ -119,7 +120,7 @@ int serv_list(char *arg)
|
|||||||
if (fisdir(enabled))
|
if (fisdir(enabled))
|
||||||
do_list("ENABLED ", enabled);
|
do_list("ENABLED ", enabled);
|
||||||
if (fisdir(FINIT_RCSD))
|
if (fisdir(FINIT_RCSD))
|
||||||
do_list("ENABLED ", FINIT_RCSD);
|
do_list("STATIC ", FINIT_RCSD);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -130,17 +131,22 @@ int serv_enable(char *arg)
|
|||||||
char link[80];
|
char link[80];
|
||||||
char path[80];
|
char path[80];
|
||||||
|
|
||||||
|
if (!arg || !arg[0])
|
||||||
|
return serv_list(NULL);
|
||||||
|
|
||||||
if (!strstr(arg, ".conf")) {
|
if (!strstr(arg, ".conf")) {
|
||||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||||
arg = corr;
|
arg = corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushd(FINIT_RCSD);
|
pushd(FINIT_RCSD);
|
||||||
snprintf(path, sizeof(path), "available/%s", arg);
|
mkdir("enabled", 0755);
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", available, arg);
|
||||||
if (!fexist(path))
|
if (!fexist(path))
|
||||||
errx(1, "Cannot find %s", path);
|
errx(1, "Cannot find %s", path);
|
||||||
|
|
||||||
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
|
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
|
||||||
if (fexist(link))
|
if (fexist(link))
|
||||||
errx(1, "%s already exists", link);
|
errx(1, "%s already exists", link);
|
||||||
|
|
||||||
@@ -153,12 +159,15 @@ int serv_disable(char *arg)
|
|||||||
char link[80];
|
char link[80];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
if (!arg || !arg[0])
|
||||||
|
return serv_list(NULL);
|
||||||
|
|
||||||
if (!strstr(arg, ".conf")) {
|
if (!strstr(arg, ".conf")) {
|
||||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||||
arg = corr;
|
arg = corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
|
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
|
||||||
if (stat(link, &st))
|
if (stat(link, &st))
|
||||||
err(1, "Cannot find %s", link);
|
err(1, "Cannot find %s", link);
|
||||||
|
|
||||||
@@ -168,6 +177,33 @@ int serv_disable(char *arg)
|
|||||||
return remove(link) != 0;
|
return remove(link) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serv_touch(char *arg)
|
||||||
|
{
|
||||||
|
char corr[40];
|
||||||
|
|
||||||
|
if (!arg || !arg[0])
|
||||||
|
return serv_list(NULL);
|
||||||
|
|
||||||
|
if (!strstr(arg, ".conf")) {
|
||||||
|
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||||
|
arg = corr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushd(FINIT_RCSD);
|
||||||
|
if (!fexist(arg)) {
|
||||||
|
popd();
|
||||||
|
if (!strstr(arg, "finit.conf"))
|
||||||
|
errx(1, "Service %s is not enabled", arg);
|
||||||
|
arg = FINIT_CONF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* libite:touch() follows symlinks */
|
||||||
|
if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW))
|
||||||
|
err(1, "Failed marking %s for reload", arg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
int serv_list (char *arg);
|
int serv_list (char *arg);
|
||||||
int serv_enable (char *arg);
|
int serv_enable (char *arg);
|
||||||
int serv_disable (char *arg);
|
int serv_disable (char *arg);
|
||||||
|
int serv_touch (char *arg);
|
||||||
|
|
||||||
#endif /* FINIT_SERV_H_ */
|
#endif /* FINIT_SERV_H_ */
|
||||||
|
|
||||||
|
|||||||
+502
-165
File diff suppressed because it is too large.
Load diff
+6
-3
@@ -28,14 +28,17 @@
|
|||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
void service_runlevel (int newlevel);
|
void service_runlevel (int newlevel);
|
||||||
int service_register (int type, char *line, struct timeval *mtime);
|
int service_register (int type, char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||||
void service_unregister (svc_t *svc);
|
void service_unregister (svc_t *svc);
|
||||||
int service_enabled (svc_t *svc);
|
|
||||||
|
|
||||||
|
void service_runtask_clean (void);
|
||||||
void service_reload_dynamic (void);
|
void service_reload_dynamic (void);
|
||||||
|
|
||||||
void service_step (svc_t *svc);
|
int service_step (svc_t *svc);
|
||||||
void service_step_all (int types);
|
void service_step_all (int types);
|
||||||
|
void service_worker (void *unused);
|
||||||
|
|
||||||
|
int service_completed (void);
|
||||||
|
|
||||||
#endif /* FINIT_SERVICE_H_ */
|
#endif /* FINIT_SERVICE_H_ */
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
*
|
*
|
||||||
* SIGHUP
|
* SIGHUP
|
||||||
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
|
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
|
||||||
|
* Also restarts API (initctl) socket, like SysV init and systemd
|
||||||
|
* does on USR1 with their FIFO/D-Bus.
|
||||||
*
|
*
|
||||||
* SIGUSR1
|
* SIGUSR1
|
||||||
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
||||||
@@ -81,6 +83,8 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
|
extern svc_t *wdog;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
||||||
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
||||||
@@ -131,8 +135,12 @@ void do_kill(int signo)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fgets(file, sizeof(file), fp)) {
|
if (fgets(file, sizeof(file), fp)) {
|
||||||
if (file[0] != '@')
|
if (strstr(file, "gdbserver"))
|
||||||
|
_d("Skipping %s ...", file);
|
||||||
|
else if (file[0] != '@')
|
||||||
kill(pid, signo);
|
kill(pid, signo);
|
||||||
|
else
|
||||||
|
_d("Skipping %s ...", &file[1]);
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
@@ -179,6 +187,11 @@ void do_shutdown(shutop_t op)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wdog) {
|
||||||
|
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
|
||||||
|
do_sleep(2);
|
||||||
|
}
|
||||||
|
|
||||||
/* Unmount any tmpfs before unmounting swap ... */
|
/* Unmount any tmpfs before unmounting swap ... */
|
||||||
unmount_tmpfs();
|
unmount_tmpfs();
|
||||||
run("swapoff -e -a");
|
run("swapoff -e -a");
|
||||||
@@ -197,11 +210,11 @@ void do_shutdown(shutop_t op)
|
|||||||
|
|
||||||
/* Reboot via watchdog or kernel, or shutdown? */
|
/* Reboot via watchdog or kernel, or shutdown? */
|
||||||
if (op == SHUT_REBOOT) {
|
if (op == SHUT_REBOOT) {
|
||||||
if (wdogpid) {
|
if (wdog) {
|
||||||
int timeout = 10;
|
int timeout = 10;
|
||||||
|
|
||||||
/* Wait here until the WDT reboots, or timeout with fallback */
|
/* Wait here until the WDT reboots, or timeout with fallback */
|
||||||
print(kill(wdogpid, SIGPWR) == 1, "Pending watchdog reboot");
|
print(kill(wdog->pid, SIGTERM) == 1, "Pending watchdog reboot");
|
||||||
while (timeout--)
|
while (timeout--)
|
||||||
do_sleep(1);
|
do_sleep(1);
|
||||||
}
|
}
|
||||||
@@ -224,6 +237,15 @@ void do_shutdown(shutop_t op)
|
|||||||
static void sighup_cb(uev_t *w, void *arg, int events)
|
static void sighup_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
_d("...");
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restart initctl API domain socket, similar to systemd/SysV init */
|
||||||
|
api_exit();
|
||||||
|
api_init(w->ctx);
|
||||||
|
|
||||||
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
||||||
service_reload_dynamic();
|
service_reload_dynamic();
|
||||||
}
|
}
|
||||||
@@ -234,6 +256,11 @@ static void sighup_cb(uev_t *w, void *arg, int events)
|
|||||||
static void sigint_cb(uev_t *w, void *arg, int events)
|
static void sigint_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
_d("...");
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
halt = SHUT_REBOOT;
|
halt = SHUT_REBOOT;
|
||||||
service_runlevel(6);
|
service_runlevel(6);
|
||||||
}
|
}
|
||||||
@@ -244,6 +271,11 @@ static void sigint_cb(uev_t *w, void *arg, int events)
|
|||||||
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
_d("...");
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
halt = SHUT_HALT;
|
halt = SHUT_HALT;
|
||||||
service_runlevel(0);
|
service_runlevel(0);
|
||||||
}
|
}
|
||||||
@@ -254,6 +286,11 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
|
|||||||
static void sigusr2_cb(uev_t *w, void *arg, int events)
|
static void sigusr2_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
_d("...");
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
halt = SHUT_OFF;
|
halt = SHUT_OFF;
|
||||||
service_runlevel(0);
|
service_runlevel(0);
|
||||||
}
|
}
|
||||||
@@ -264,6 +301,11 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
|
|||||||
static void sigterm_cb(uev_t *w, void *arg, int events)
|
static void sigterm_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
_d("...");
|
_d("...");
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
halt = SHUT_REBOOT;
|
halt = SHUT_REBOOT;
|
||||||
service_runlevel(6);
|
service_runlevel(6);
|
||||||
}
|
}
|
||||||
@@ -275,6 +317,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
|||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reap all the children! */
|
/* Reap all the children! */
|
||||||
do {
|
do {
|
||||||
pid = waitpid(-1, NULL, WNOHANG);
|
pid = waitpid(-1, NULL, WNOHANG);
|
||||||
@@ -290,6 +337,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigstop_cb(uev_t *w, void *arg, int events)
|
static void sigstop_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
touch(SYNC_STOPPED);
|
touch(SYNC_STOPPED);
|
||||||
stopped++;
|
stopped++;
|
||||||
}
|
}
|
||||||
@@ -299,6 +351,11 @@ static void sigstop_cb(uev_t *w, void *arg, int events)
|
|||||||
*/
|
*/
|
||||||
static void sigcont_cb(uev_t *w, void *arg, int events)
|
static void sigcont_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
_e("Unrecoverable error in signal watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
stopped = 0;
|
stopped = 0;
|
||||||
erase(SYNC_STOPPED);
|
erase(SYNC_STOPPED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,22 @@ static char *sm_status(sm_state_t state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable login in single user mode and shutdown/reboot
|
||||||
|
*
|
||||||
|
* Re-enable only when going from these runlevels, this way a user can
|
||||||
|
* manage /etc/nologin manually within the other runlevels without us
|
||||||
|
* pulling the rug from under their feet.
|
||||||
|
*/
|
||||||
|
static void nologin(void)
|
||||||
|
{
|
||||||
|
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
||||||
|
touch("/etc/nologin");
|
||||||
|
|
||||||
|
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6)
|
||||||
|
erase("/etc/nologin");
|
||||||
|
}
|
||||||
|
|
||||||
void sm_set_runlevel(sm_t *sm, int newlevel)
|
void sm_set_runlevel(sm_t *sm, int newlevel)
|
||||||
{
|
{
|
||||||
sm->newlevel = newlevel;
|
sm->newlevel = newlevel;
|
||||||
@@ -95,7 +111,8 @@ void sm_step(sm_t *sm)
|
|||||||
restart:
|
restart:
|
||||||
old_state = sm->state;
|
old_state = sm->state;
|
||||||
|
|
||||||
_d("state: %s", sm_status(sm->state));
|
_d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d",
|
||||||
|
sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload);
|
||||||
|
|
||||||
switch (sm->state) {
|
switch (sm->state) {
|
||||||
case SM_BOOTSTRAP_STATE:
|
case SM_BOOTSTRAP_STATE:
|
||||||
@@ -136,16 +153,16 @@ restart:
|
|||||||
runlevel_set(prevlevel, runlevel);
|
runlevel_set(prevlevel, runlevel);
|
||||||
|
|
||||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||||
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
nologin();
|
||||||
touch("/etc/nologin");
|
|
||||||
else
|
|
||||||
erase("/etc/nologin");
|
|
||||||
|
|
||||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||||
if (runlevel != 0 && runlevel != 6)
|
if (conf_any_change())
|
||||||
conf_reload_dynamic();
|
conf_reload();
|
||||||
|
|
||||||
_d("Stopping services services not allowed in new runlevel ...");
|
/* Reset once flag of runtasks */
|
||||||
|
service_runtask_clean();
|
||||||
|
|
||||||
|
_d("Stopping services not allowed in new runlevel ...");
|
||||||
sm->in_teardown = 1;
|
sm->in_teardown = 1;
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
@@ -167,7 +184,7 @@ restart:
|
|||||||
_d("All services have been stoppped, calling runlevel change hooks ...");
|
_d("All services have been stoppped, calling runlevel change hooks ...");
|
||||||
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
||||||
|
|
||||||
_d("Starting services services new to this runlevel ...");
|
_d("Starting services new to this runlevel ...");
|
||||||
sm->in_teardown = 0;
|
sm->in_teardown = 0;
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
@@ -195,13 +212,13 @@ restart:
|
|||||||
|
|
||||||
case SM_RELOAD_CHANGE_STATE:
|
case SM_RELOAD_CHANGE_STATE:
|
||||||
/* First reload all *.conf in /etc/finit.d/ */
|
/* First reload all *.conf in /etc/finit.d/ */
|
||||||
conf_reload_dynamic();
|
conf_reload();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Then, mark all affected service conditions as in-flux and
|
* Then, mark all affected service conditions as in-flux and
|
||||||
* let all affected services move to WAITING/HALTED
|
* let all affected services move to WAITING/HALTED
|
||||||
*/
|
*/
|
||||||
_d("Stopping services services not allowed after reconf ...");
|
_d("Stopping services not allowed after reconf ...");
|
||||||
sm->in_teardown = 1;
|
sm->in_teardown = 1;
|
||||||
cond_reload();
|
cond_reload();
|
||||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||||
@@ -231,9 +248,6 @@ restart:
|
|||||||
_d("Calling reconf hooks ...");
|
_d("Calling reconf hooks ...");
|
||||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||||
|
|
||||||
/* Done, lift reconf condition */
|
|
||||||
cond_reassert(NULL);
|
|
||||||
|
|
||||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||||
_d("Reconfiguration done");
|
_d("Reconfiguration done");
|
||||||
|
|
||||||
|
|||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
/* Reset terminal line settings (stty sane)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2017 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h> /* _POSIX_VDISABLE */
|
||||||
|
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
speed_t stty_parse_speed(char *baud)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
size_t i;
|
||||||
|
unsigned long val;
|
||||||
|
struct { unsigned long val; speed_t speed; } v2s[] = {
|
||||||
|
{ 0, B0 },
|
||||||
|
{ 50, B50 },
|
||||||
|
{ 75, B75 },
|
||||||
|
{ 110, B110 },
|
||||||
|
{ 134, B134 },
|
||||||
|
{ 150, B150 },
|
||||||
|
{ 200, B200 },
|
||||||
|
{ 300, B300 },
|
||||||
|
{ 600, B600 },
|
||||||
|
{ 1200, B1200 },
|
||||||
|
{ 1800, B1800 },
|
||||||
|
{ 2400, B2400 },
|
||||||
|
{ 4800, B4800 },
|
||||||
|
{ 9600, B9600 },
|
||||||
|
{ 19200, B19200 },
|
||||||
|
{ 38400, B38400 },
|
||||||
|
{ 57600, B57600 },
|
||||||
|
{ 115200, B115200 },
|
||||||
|
{ 230400, B230400 },
|
||||||
|
{ 460800, B460800 },
|
||||||
|
{ 500000, B500000 },
|
||||||
|
{ 576000, B576000 },
|
||||||
|
{ 921600, B921600 },
|
||||||
|
{ 1000000, B1000000 },
|
||||||
|
{ 1152000, B1152000 },
|
||||||
|
{ 1500000, B1500000 },
|
||||||
|
{ 2000000, B2000000 },
|
||||||
|
{ 2500000, B2500000 },
|
||||||
|
{ 3000000, B3000000 },
|
||||||
|
{ 3500000, B3500000 },
|
||||||
|
{ 4000000, B4000000 },
|
||||||
|
};
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
val = strtoul(baud, &ptr, 10);
|
||||||
|
if (errno || ptr == baud)
|
||||||
|
return B0;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
|
||||||
|
if (v2s[i].val == val)
|
||||||
|
return v2s[i].speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stty(int fd, speed_t speed)
|
||||||
|
{
|
||||||
|
struct termios term;
|
||||||
|
|
||||||
|
tcdrain(fd);
|
||||||
|
if (tcgetattr(fd, &term))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (speed != B0) {
|
||||||
|
cfsetispeed(&term, speed);
|
||||||
|
cfsetospeed(&term, speed);
|
||||||
|
tcsetattr(fd, TCSAFLUSH, &term);
|
||||||
|
}
|
||||||
|
tcflush(fd, TCIOFLUSH);
|
||||||
|
|
||||||
|
/* Disable modem specific flags */
|
||||||
|
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
|
||||||
|
term.c_cflag &= ~CRTSCTS;
|
||||||
|
term.c_cflag |= CLOCAL;
|
||||||
|
|
||||||
|
/* Timeouts, minimum chars and default flags */
|
||||||
|
term.c_cc[VTIME] = 0;
|
||||||
|
term.c_cc[VMIN] = 1;
|
||||||
|
term.c_iflag = ICRNL|IXON|IXOFF;
|
||||||
|
term.c_oflag = OPOST|ONLCR;
|
||||||
|
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
||||||
|
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
|
||||||
|
|
||||||
|
/* Reset special characters to defaults */
|
||||||
|
term.c_cc[VINTR] = CTRL('C');
|
||||||
|
term.c_cc[VQUIT] = CTRL('\\');
|
||||||
|
term.c_cc[VEOF] = CTRL('D');
|
||||||
|
term.c_cc[VEOL] = '\n';
|
||||||
|
term.c_cc[VKILL] = CTRL('U');
|
||||||
|
term.c_cc[VERASE] = CERASE;
|
||||||
|
tcsetattr(fd, TCSANOW, &term);
|
||||||
|
|
||||||
|
/* Restore cursor if it was hidden previously */
|
||||||
|
write(fd, "\033[?25h", 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -24,48 +24,48 @@
|
|||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <ctype.h> /* isdigit() */
|
#include <ctype.h> /* isdigit() */
|
||||||
|
#include <time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "pid.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "cond.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
|
||||||
/* Each svc_t needs a unique job# */
|
/* Each svc_t needs a unique job# */
|
||||||
static int jobcounter = 1;
|
static int jobcounter = 1;
|
||||||
static svc_t *workaround = NULL;
|
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
||||||
|
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
|
||||||
|
|
||||||
static svc_t *__connect_shm(void)
|
static void svc_gc(void *arg)
|
||||||
{
|
{
|
||||||
svc_t *list;
|
struct timespec now;
|
||||||
|
struct wq *work = (struct wq *)arg;
|
||||||
|
svc_t *svc, *next;
|
||||||
|
|
||||||
list = finit_svc_connect();
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
||||||
if (!list) {
|
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
|
||||||
if (workaround)
|
char cond[MAX_COND_LEN];
|
||||||
return workaround;
|
int msec;
|
||||||
|
|
||||||
/* Linux not built with CONFIG_SYSVIPC, or libc does not support shmat()/shmget() */
|
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
|
||||||
if (ENOSYS == errno)
|
(now.tv_nsec - svc->gc.tv_nsec) / 1000000;
|
||||||
warn("Kernel does support SYSV shmat() IPC, error %d", errno);
|
if (msec < SVC_TERM_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Try to prevent PID 1 from aborting, issue #81 */
|
TAILQ_REMOVE(&gc_list, svc, link);
|
||||||
if (getpid() == 1) {
|
cond_clear(mkcond(cond, sizeof(cond), svc->cmd));
|
||||||
warnx("Implementing PID 1 workaround, initctl tool will not work ...");
|
free(svc);
|
||||||
if (!workaround)
|
|
||||||
workaround = calloc(MAX_NUM_SVC, sizeof(svc_t));
|
|
||||||
|
|
||||||
list = workaround;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) {
|
if (!TAILQ_EMPTY(&gc_list))
|
||||||
warn("Failed connecting to shared memory, error %d", errno);
|
schedule_work(work);
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,13 +77,13 @@ static svc_t *__connect_shm(void)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_new(char *cmd, int id, int type)
|
svc_t *svc_new(char *cmd, char *id, int type)
|
||||||
{
|
{
|
||||||
int i, job = -1;
|
int job = -1;
|
||||||
svc_t *svc, *list = __connect_shm();
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
/* Find first job n:o if registering multiple instances */
|
/* Find first job n:o if registering multiple instances */
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (!strcmp(svc->cmd, cmd)) {
|
if (!strcmp(svc->cmd, cmd)) {
|
||||||
job = svc->job;
|
job = svc->job;
|
||||||
break;
|
break;
|
||||||
@@ -92,36 +92,30 @@ svc_t *svc_new(char *cmd, int id, int type)
|
|||||||
if (job == -1)
|
if (job == -1)
|
||||||
job = jobcounter++;
|
job = jobcounter++;
|
||||||
|
|
||||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
svc = calloc(1, sizeof(*svc));
|
||||||
svc_t *svc = &list[i];
|
if (!svc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (svc->type == SVC_TYPE_FREE) {
|
svc->type = type;
|
||||||
char *desc;
|
svc->job = job;
|
||||||
|
strlcpy(svc->id, id, sizeof(svc->id));
|
||||||
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
|
||||||
memset(svc, 0, sizeof(*svc));
|
/* Default description, if missing */
|
||||||
svc->type = type;
|
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
||||||
svc->job = job;
|
|
||||||
svc->id = id;
|
|
||||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
|
||||||
|
|
||||||
/* Default description, if missing */
|
TAILQ_INSERT_TAIL(&svc_list, svc, link);
|
||||||
desc = rindex(cmd, '/');
|
|
||||||
if (desc)
|
|
||||||
desc++;
|
|
||||||
else
|
|
||||||
desc = cmd;
|
|
||||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
|
||||||
|
|
||||||
return svc;
|
return svc;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = ENOMEM;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct wq work = {
|
||||||
|
.cb = svc_gc,
|
||||||
|
.delay = SVC_TERM_TIMEOUT
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_del - Delete a service object
|
* svc_del - Mark a service object for deletion
|
||||||
* @svc: Pointer to an &svc_t object
|
* @svc: Pointer to an &svc_t object
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
@@ -129,50 +123,57 @@ svc_t *svc_new(char *cmd, int id, int type)
|
|||||||
*/
|
*/
|
||||||
int svc_del(svc_t *svc)
|
int svc_del(svc_t *svc)
|
||||||
{
|
{
|
||||||
svc->type = SVC_TYPE_FREE;
|
TAILQ_REMOVE(&svc_list, svc, link);
|
||||||
|
TAILQ_INSERT_TAIL(&gc_list, svc, link);
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
|
||||||
|
schedule_work(&work);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_iterator - Naive iterator over all registered services.
|
* svc_iterator - Naive iterator over all registered services.
|
||||||
* @first: Get first &svc_t object, or next until end.
|
* @iter: Iterator, must be a valid pointer
|
||||||
|
* @first: If set, get first &svc_t, otherwise get next
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* The first &svc_t when @first is set, otherwise the next &svc_t until
|
* An &svc_t pointer, or %NULL when no more entries can be found.
|
||||||
* the end when %NULL is returned.
|
|
||||||
*/
|
*/
|
||||||
svc_t *svc_iterator(int first)
|
svc_t *svc_iterator(svc_t **iter, int first)
|
||||||
{
|
|
||||||
static int i = 0;
|
|
||||||
svc_t *list = __connect_shm();
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
while (i < MAX_NUM_SVC) {
|
|
||||||
svc_t *svc = &list[i++];
|
|
||||||
|
|
||||||
if (svc->type != SVC_TYPE_FREE)
|
|
||||||
return svc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
|
||||||
* @first: Get first &svc_t object, or next until end.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* The first inetd &svc_t when @first is set, otherwise the next
|
|
||||||
* inetd &svc_t until the end when %NULL is returned.
|
|
||||||
*/
|
|
||||||
svc_t *svc_inetd_iterator(int first)
|
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
if (!iter) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
svc = TAILQ_FIRST(&svc_list);
|
||||||
|
else
|
||||||
|
svc = *iter;
|
||||||
|
|
||||||
|
if (svc)
|
||||||
|
*iter = TAILQ_NEXT(svc, link);
|
||||||
|
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
||||||
|
* @iter: Iterator, must be a valid pointer
|
||||||
|
* @first: If set, get first &svc_t, otherwise get next
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The first inetd &svc_t when %NULL is given as argument, otherwise the
|
||||||
|
* next inetd &svc_t until the end when %NULL is returned.
|
||||||
|
*/
|
||||||
|
svc_t *svc_inetd_iterator(svc_t **iter, int first)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||||
if (svc_is_inetd(svc))
|
if (svc_is_inetd(svc))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
@@ -181,43 +182,23 @@ svc_t *svc_inetd_iterator(int first)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
|
|
||||||
* @first: Get first &svc_t object, or next until end.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* The first dynamically loaded &svc_t when @first is set, otherwise the
|
|
||||||
* next dynamically loaded &svc_t until the end when %NULL is returned.
|
|
||||||
*/
|
|
||||||
svc_t *svc_dynamic_iterator(int first)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->mtime.tv_sec)
|
|
||||||
return svc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_named_iterator - Iterates over all instances of a service.
|
* svc_named_iterator - Iterates over all instances of a service.
|
||||||
* @first: Get first &svc_t object, or next until end.
|
* @iter: Iterator, must be a valid pointer
|
||||||
|
* @first: If set, get first &svc_t, otherwise get next
|
||||||
* @cmd: Service name to look for.
|
* @cmd: Service name to look for.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* The first matching &svc_t when @first is set, otherwise the next
|
* The first matching &svc_t when %NULL is given as argument, otherwise
|
||||||
* &svc_t instance with the same @cmd until the end when %NULL is
|
* the next &svc_t with the same @cmd name until the end when %NULL is
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_named_iterator(int first, char *cmd)
|
svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||||
char *name = basename(svc->cmd);
|
char *name = svc->name;
|
||||||
|
|
||||||
if (!strncmp(name, cmd, strlen(name)))
|
if (!strncmp(name, cmd, strlen(name)))
|
||||||
return svc;
|
return svc;
|
||||||
@@ -229,19 +210,20 @@ svc_t *svc_named_iterator(int first, char *cmd)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_job_iterator - Iterates over all instances of a service.
|
* svc_job_iterator - Iterates over all instances of a service.
|
||||||
* @first: Get first &svc_t object, or next until end.
|
* @iter: Iterator, must be a valid pointer
|
||||||
|
* @first: If set, get first &svc_t, otherwise get next
|
||||||
* @job: Job to look for.
|
* @job: Job to look for.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* The first matching &svc_t when @first is set, otherwise the next
|
* The first matching &svc_t when %NULL is given as argument, otherwise
|
||||||
* &svc_t instance with the same @job until the end when %NULL is
|
* the next &svc_t with the same @job ID until the end when %NULL is
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_job_iterator(int first, int job)
|
svc_t *svc_job_iterator(svc_t **iter, int first, int job)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||||
if (svc->job == job)
|
if (svc->job == job)
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
@@ -254,30 +236,14 @@ svc_t *svc_job_iterator(int first, int job)
|
|||||||
* svc_foreach - Run a callback for each registered service
|
* svc_foreach - Run a callback for each registered service
|
||||||
* @cb: Callback to run for each service
|
* @cb: Callback to run for each service
|
||||||
*/
|
*/
|
||||||
void svc_foreach(void (*cb)(svc_t *))
|
void svc_foreach(int (*cb)(svc_t *))
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
if (!cb)
|
if (!cb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
|
||||||
cb(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_foreach_dynamic - Run a callback for each registered dynamic service
|
|
||||||
* @cb: Callback to run for each dynamic service
|
|
||||||
*/
|
|
||||||
void svc_foreach_dynamic(void (*cb)(svc_t *))
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
if (!cb)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
|
|
||||||
cb(svc);
|
cb(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,14 +253,14 @@ void svc_foreach_dynamic(void (*cb)(svc_t *))
|
|||||||
* @types: Mask of service types
|
* @types: Mask of service types
|
||||||
* @cb: Callback to run for each matching type
|
* @cb: Callback to run for each matching type
|
||||||
*/
|
*/
|
||||||
void svc_foreach_type(int types, void (*cb)(svc_t *))
|
void svc_foreach_type(int types, int (*cb)(svc_t *))
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
if (!cb)
|
if (!cb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (!(svc->type & types))
|
if (!(svc->type & types))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -312,9 +278,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *))
|
|||||||
*/
|
*/
|
||||||
svc_t *svc_stop_completed(void)
|
svc_t *svc_stop_completed(void)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->state == SVC_STOPPING_STATE)
|
if (svc->state == SVC_STOPPING_STATE)
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
@@ -329,12 +295,12 @@ svc_t *svc_stop_completed(void)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to an &svc_t object, or %NULL if not found.
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_find(char *cmd, int id)
|
svc_t *svc_find(char *cmd, char *id)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
if (svc->id && !strcmp(svc->id, id) && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,9 +316,9 @@ svc_t *svc_find(char *cmd, int id)
|
|||||||
*/
|
*/
|
||||||
svc_t *svc_find_by_pid(pid_t pid)
|
svc_t *svc_find_by_pid(pid_t pid)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->pid == pid)
|
if (svc->pid == pid)
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
@@ -368,12 +334,12 @@ svc_t *svc_find_by_pid(pid_t pid)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to an &svc_t object, or %NULL if not found.
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_find_by_jobid(int job, int id)
|
svc_t *svc_find_by_jobid(int job, char *id)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->job == job && svc->id == id)
|
if (svc->job == job && svc->id && !strcmp(svc->id, id))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,19 +354,33 @@ svc_t *svc_find_by_jobid(int job, int id)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to an &svc_t object, or %NULL if not found.
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_find_by_nameid(char *name, int id)
|
svc_t *svc_find_by_nameid(char *name, char *id)
|
||||||
{
|
{
|
||||||
char *ptr;
|
svc_t *svc, *iter = NULL;
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
ptr = strrchr(svc->cmd, '/');
|
if (svc->id && !strcmp(svc->id, id) && !strcmp(name, svc->name))
|
||||||
if (ptr)
|
return svc;
|
||||||
ptr++;
|
}
|
||||||
else
|
|
||||||
ptr = svc->cmd;
|
|
||||||
|
|
||||||
if (svc->id == id && !strcmp(name, ptr))
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_find_by_plidfile - Find an service object by its PID file
|
||||||
|
* @fn: PID file, can be absolute path or relative to /run
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
|
*/
|
||||||
|
svc_t *svc_find_by_pidfile(char *fn)
|
||||||
|
{
|
||||||
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
if (string_compare(pid_runpath(fn, path, sizeof(path)), pid_file(svc)))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,11 +399,15 @@ svc_t *svc_find_by_nameid(char *name, int id)
|
|||||||
*/
|
*/
|
||||||
void svc_mark_dynamic(void)
|
void svc_mark_dynamic(void)
|
||||||
{
|
{
|
||||||
svc_t *svc = svc_dynamic_iterator(1);
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
if (svc->protect)
|
||||||
|
continue;
|
||||||
|
if (svc_is_inetd_conn(svc))
|
||||||
|
continue;
|
||||||
|
|
||||||
while (svc) {
|
|
||||||
*((int *)&svc->dirty) = -1;
|
*((int *)&svc->dirty) = -1;
|
||||||
svc = svc_dynamic_iterator(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,17 +421,6 @@ void svc_mark_clean(svc_t *svc)
|
|||||||
*((int *)&svc->dirty) = 0;
|
*((int *)&svc->dirty) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void svc_check_dirty(svc_t *svc, struct timeval *mtime)
|
|
||||||
{
|
|
||||||
if (mtime && timercmp(&svc->mtime, mtime, !=))
|
|
||||||
svc_mark_dirty(svc);
|
|
||||||
else
|
|
||||||
svc_mark_clean(svc);
|
|
||||||
|
|
||||||
svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
|
|
||||||
svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
|
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
|
||||||
* @cb: Callback to run for each stale service
|
* @cb: Callback to run for each stale service
|
||||||
@@ -457,14 +430,11 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime)
|
|||||||
*/
|
*/
|
||||||
void svc_clean_dynamic(void (*cb)(svc_t *))
|
void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||||
{
|
{
|
||||||
svc_t *svc = svc_dynamic_iterator(1);
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
while (svc) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->dirty == -1 && cb) {
|
if (svc->dirty == -1 && cb)
|
||||||
cb(svc);
|
cb(svc);
|
||||||
svc_mark_clean(svc);
|
|
||||||
}
|
|
||||||
svc = svc_dynamic_iterator(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,100 +465,63 @@ int svc_clean_bootstrap(svc_t *svc)
|
|||||||
*/
|
*/
|
||||||
void svc_prune_bootstrap(void)
|
void svc_prune_bootstrap(void)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (!svc->pid)
|
if (!svc->pid)
|
||||||
svc_clean_bootstrap(svc);
|
svc_clean_bootstrap(svc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *svc_status(svc_t *svc)
|
/**
|
||||||
|
* svc_enabled - Should the service run?
|
||||||
|
* @svc: Pointer to &svc_t object
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 1, if the service is allowed to run in the current runlevel and the
|
||||||
|
* user has not manually requested that this service should not run. 0
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
int svc_enabled(svc_t *svc)
|
||||||
{
|
{
|
||||||
switch (svc->state) {
|
if (!svc)
|
||||||
case SVC_HALTED_STATE:
|
return 0;
|
||||||
switch (svc->block) {
|
|
||||||
case SVC_BLOCK_NONE:
|
|
||||||
return "halted";
|
|
||||||
|
|
||||||
case SVC_BLOCK_MISSING:
|
if (!svc_in_runlevel(svc, runlevel))
|
||||||
return "missing";
|
return 0;
|
||||||
|
|
||||||
case SVC_BLOCK_CRASHING:
|
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||||
return "crashed";
|
return 0;
|
||||||
|
|
||||||
case SVC_BLOCK_USER:
|
return 1;
|
||||||
return "stopped";
|
|
||||||
|
|
||||||
case SVC_BLOCK_BUSY:
|
|
||||||
return "busy";
|
|
||||||
|
|
||||||
case SVC_BLOCK_RESTARTING:
|
|
||||||
return "restart";
|
|
||||||
}
|
|
||||||
|
|
||||||
case SVC_DONE_STATE:
|
|
||||||
return "done";
|
|
||||||
|
|
||||||
case SVC_STOPPING_STATE:
|
|
||||||
switch (svc->type) {
|
|
||||||
case SVC_TYPE_INETD_CONN:
|
|
||||||
case SVC_TYPE_RUN:
|
|
||||||
case SVC_TYPE_TASK:
|
|
||||||
return "active";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "stopping";
|
|
||||||
}
|
|
||||||
|
|
||||||
case SVC_WAITING_STATE:
|
|
||||||
return "waiting";
|
|
||||||
|
|
||||||
case SVC_READY_STATE:
|
|
||||||
return "ready";
|
|
||||||
|
|
||||||
case SVC_RUNNING_STATE:
|
|
||||||
return "running";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *svc_dirtystr(svc_t *svc)
|
/* Same base service, return unique ID as an integer */
|
||||||
|
int svc_next_id_int(char *cmd)
|
||||||
{
|
{
|
||||||
if (svc_is_removed(svc))
|
int n = 1;
|
||||||
return "removed";
|
svc_t *svc, *iter = NULL;
|
||||||
if (svc_is_updated(svc))
|
|
||||||
return "updated";
|
|
||||||
if (svc_is_changed(svc))
|
|
||||||
return "UNKNOWN";
|
|
||||||
|
|
||||||
return "clean";
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
}
|
char id[MAX_ID_LEN];
|
||||||
|
|
||||||
/* Same base service, return unique ID */
|
snprintf(id, sizeof(id), "%d", n);
|
||||||
int svc_next_id(char *cmd)
|
|
||||||
{
|
|
||||||
int id = 0;
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
if (!strcmp(svc->cmd, cmd) && svc->id && strcmp(svc->id, id))
|
||||||
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
return n;
|
||||||
id = svc->id;
|
|
||||||
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return id + 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int svc_is_unique(svc_t *svc)
|
int svc_is_unique(svc_t *svc)
|
||||||
{
|
{
|
||||||
svc_t *list = __connect_shm();
|
svc_t *s, *iter = NULL;
|
||||||
int i, unique = 1;
|
int unique = 1;
|
||||||
|
|
||||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
|
||||||
svc_t *s = &list[i];
|
|
||||||
|
|
||||||
|
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
|
||||||
if (svc->type == SVC_TYPE_FREE)
|
if (svc->type == SVC_TYPE_FREE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -607,7 +540,7 @@ int svc_is_unique(svc_t *svc)
|
|||||||
/*
|
/*
|
||||||
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
||||||
*/
|
*/
|
||||||
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int))
|
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *))
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
char *input, *token, *pos;
|
char *input, *token, *pos;
|
||||||
@@ -618,27 +551,36 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
|||||||
|
|
||||||
token = strtok_r(input, " ", &pos);
|
token = strtok_r(input, " ", &pos);
|
||||||
while (token) {
|
while (token) {
|
||||||
int id = 1;
|
char *id = NULL;
|
||||||
svc_t *svc;
|
svc_t *svc, *iter = NULL;
|
||||||
char *ptr = strchr(token, ':');
|
char *ptr = strchr(token, ':');
|
||||||
|
|
||||||
if (isdigit(token[0])) {
|
if (isdigit(token[0])) {
|
||||||
int job = atonum(token);
|
char *ep;
|
||||||
|
long job = 0;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
job = strtol(token, &ep, 10);
|
||||||
|
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
|
||||||
|
(errno != 0 && job == 0) ||
|
||||||
|
(token == ep)) {
|
||||||
|
result++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
svc = svc_job_iterator(1, job);
|
svc = svc_job_iterator(&iter, 1, job);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
result += not_found(NULL, job);
|
result += not_found(NULL, token);
|
||||||
|
|
||||||
while (svc) {
|
while (svc) {
|
||||||
if (found)
|
if (found)
|
||||||
result += found(svc);
|
result += found(svc);
|
||||||
svc = svc_job_iterator(0, job);
|
svc = svc_job_iterator(&iter, 0, job);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*ptr++ = 0;
|
*ptr++ = 0;
|
||||||
id = atonum(ptr);
|
id = ptr;
|
||||||
job = atonum(token);
|
|
||||||
|
|
||||||
svc = svc_find_by_jobid(job, id);
|
svc = svc_find_by_jobid(job, id);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
@@ -648,18 +590,18 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
svc = svc_named_iterator(1, token);
|
svc = svc_named_iterator(&iter, 1, token);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
result += not_found(token, id);
|
result += not_found(token, id);
|
||||||
|
|
||||||
while (svc) {
|
while (svc) {
|
||||||
if (found)
|
if (found)
|
||||||
result += found(svc);
|
result += found(svc);
|
||||||
svc = svc_named_iterator(0, token);
|
svc = svc_named_iterator(&iter, 0, token);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*ptr++ = 0;
|
*ptr++ = 0;
|
||||||
id = atonum(ptr);
|
id = ptr;
|
||||||
|
|
||||||
svc = svc_find_by_nameid(token, id);
|
svc = svc_find_by_nameid(token, id);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
|
|||||||
@@ -26,9 +26,10 @@
|
|||||||
#define FINIT_SVC_H_
|
#define FINIT_SVC_H_
|
||||||
|
|
||||||
#include <sys/ipc.h> /* IPC_CREAT */
|
#include <sys/ipc.h> /* IPC_CREAT */
|
||||||
#include <sys/shm.h> /* shmat() */
|
#include <sys/resource.h>
|
||||||
#include <sys/types.h> /* pid_t */
|
#include <sys/types.h> /* pid_t */
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
|
|
||||||
#include "inetd.h"
|
#include "inetd.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
@@ -65,15 +66,17 @@ typedef enum {
|
|||||||
SVC_BLOCK_RESTARTING,
|
SVC_BLOCK_RESTARTING,
|
||||||
} svc_block_t;
|
} svc_block_t;
|
||||||
|
|
||||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
#define MAX_ID_LEN 16
|
||||||
#define MAX_ARG_LEN 64
|
#define MAX_ARG_LEN 64
|
||||||
#define MAX_STR_LEN 64
|
#define MAX_STR_LEN 64
|
||||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||||
#define MAX_USER_LEN 16
|
#define MAX_USER_LEN 16
|
||||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||||
#define MAX_NUM_SVC 64 /* Enough? */
|
|
||||||
#define MAX_NUM_SVC_ARGS 32
|
#define MAX_NUM_SVC_ARGS 32
|
||||||
|
|
||||||
|
/* Time after SIGTERM that we SIGKILL stopping processes */
|
||||||
|
#define SVC_TERM_TIMEOUT 3000
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default enable for all services, can be stopped by means
|
* Default enable for all services, can be stopped by means
|
||||||
* of issuing an initctl call. E.g.
|
* of issuing an initctl call. E.g.
|
||||||
@@ -81,31 +84,51 @@ typedef enum {
|
|||||||
* initctl <stop|start|restart> service
|
* initctl <stop|start|restart> service
|
||||||
*/
|
*/
|
||||||
typedef struct svc {
|
typedef struct svc {
|
||||||
|
TAILQ_ENTRY(svc) link;
|
||||||
|
|
||||||
/* Instance specifics */
|
/* Instance specifics */
|
||||||
int job, id; /* JOB:ID */
|
int job; /* JOB: */
|
||||||
|
char id[MAX_ID_LEN]; /* :ID */
|
||||||
|
|
||||||
|
/* Limits and scoping */
|
||||||
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
|
/* Control group */
|
||||||
|
char cgroup[32];
|
||||||
|
|
||||||
/* Service details */
|
/* Service details */
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
char pidfile[MAX_ARG_LEN];
|
||||||
|
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||||
svc_type_t type;
|
svc_type_t type; /* Service, run, task, inetd, ... */
|
||||||
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
|
int protect; /* Services like dbus-daemon & udev by Finit */
|
||||||
const int dirty; /* Set if old mtime != new mtime => reloaded,
|
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
||||||
* or -1 when marked for removal */
|
|
||||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||||
int runlevels;
|
int runlevels;
|
||||||
int sighup; /* This service supports SIGHUP :) */
|
int sighup; /* This service supports SIGHUP :) */
|
||||||
svc_block_t block; /* Reason that this service is currently stopped */
|
svc_block_t block; /* Reason that this service is currently stopped */
|
||||||
char cond[MAX_COND_LEN];
|
char cond[MAX_COND_LEN];
|
||||||
|
char name[MAX_ARG_LEN];
|
||||||
|
|
||||||
/* Incremented for each restart by service monitor. */
|
/* Counters */
|
||||||
const unsigned int restart_counter;
|
char once; /* run/task, (at least) once per runlevel */
|
||||||
|
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
||||||
|
|
||||||
/* For inetd services */
|
/* For inetd services */
|
||||||
inetd_t inetd;
|
inetd_t inetd;
|
||||||
int stdin_fd;
|
int stdin_fd;
|
||||||
|
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
|
||||||
|
|
||||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||||
int log;
|
struct {
|
||||||
|
char enabled;
|
||||||
|
char null;
|
||||||
|
char console;
|
||||||
|
char file[64];
|
||||||
|
char prio[20];
|
||||||
|
char ident[20];
|
||||||
|
} log;
|
||||||
|
|
||||||
/* Identity */
|
/* Identity */
|
||||||
char username[MAX_USER_LEN];
|
char username[MAX_USER_LEN];
|
||||||
@@ -122,85 +145,140 @@ typedef struct svc {
|
|||||||
*/
|
*/
|
||||||
uev_t timer;
|
uev_t timer;
|
||||||
void (*timer_cb)(struct svc *svc);
|
void (*timer_cb)(struct svc *svc);
|
||||||
|
|
||||||
|
/* time at svc_del(), used by gc timer */
|
||||||
|
struct timespec gc;
|
||||||
} svc_t;
|
} svc_t;
|
||||||
|
|
||||||
typedef struct svc_map svc_map_t;
|
svc_t *svc_new (char *cmd, char *id, int type);
|
||||||
|
|
||||||
/* Put services array in shm */
|
|
||||||
static inline svc_t *finit_svc_connect(void)
|
|
||||||
{
|
|
||||||
static void *ptr = (void *)-1;
|
|
||||||
|
|
||||||
if ((void *)-1 == ptr) {
|
|
||||||
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
|
|
||||||
0600 | IPC_CREAT), NULL, 0);
|
|
||||||
if ((void *)-1 == ptr)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (svc_t *)ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
svc_t *svc_new (char *cmd, int id, int type);
|
|
||||||
int svc_del (svc_t *svc);
|
int svc_del (svc_t *svc);
|
||||||
|
|
||||||
svc_t *svc_find (char *cmd, int id);
|
svc_t *svc_find (char *cmd, char *id);
|
||||||
svc_t *svc_find_by_pid (pid_t pid);
|
svc_t *svc_find_by_pid (pid_t pid);
|
||||||
svc_t *svc_find_by_jobid (int job, int id);
|
svc_t *svc_find_by_jobid (int job, char *id);
|
||||||
svc_t *svc_find_by_nameid (char *name, int id);
|
svc_t *svc_find_by_nameid (char *name, char *id);
|
||||||
|
svc_t *svc_find_by_pidfile (char *fn);
|
||||||
|
|
||||||
svc_t *svc_iterator (int first);
|
svc_t *svc_iterator (svc_t **iter, int first);
|
||||||
svc_t *svc_inetd_iterator (int first);
|
svc_t *svc_inetd_iterator (svc_t **iter, int first);
|
||||||
svc_t *svc_dynamic_iterator (int first);
|
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
|
||||||
svc_t *svc_named_iterator (int first, char *cmd);
|
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
|
||||||
svc_t *svc_job_iterator (int first, int job);
|
|
||||||
|
|
||||||
void svc_foreach (void (*cb)(svc_t *));
|
void svc_foreach (int (*cb)(svc_t *));
|
||||||
void svc_foreach_dynamic (void (*cb)(svc_t *));
|
void svc_foreach_type (int types, int (*cb)(svc_t *));
|
||||||
void svc_foreach_type (int types, void (*cb)(svc_t *));
|
|
||||||
|
|
||||||
svc_t *svc_stop_completed (void);
|
svc_t *svc_stop_completed (void);
|
||||||
|
|
||||||
void svc_mark_dynamic (void);
|
void svc_mark_dynamic (void);
|
||||||
void svc_check_dirty (svc_t *svc, struct timeval *mtime);
|
|
||||||
void svc_mark_dirty (svc_t *svc);
|
void svc_mark_dirty (svc_t *svc);
|
||||||
void svc_mark_clean (svc_t *svc);
|
void svc_mark_clean (svc_t *svc);
|
||||||
void svc_clean_dynamic (void (*cb)(svc_t *));
|
void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||||
int svc_clean_bootstrap (svc_t *svc);
|
int svc_clean_bootstrap (svc_t *svc);
|
||||||
void svc_prune_bootstrap (void);
|
void svc_prune_bootstrap (void);
|
||||||
|
|
||||||
char *svc_status (svc_t *svc);
|
int svc_enabled (svc_t *svc);
|
||||||
const char *svc_dirtystr (svc_t *svc);
|
int svc_next_id_int (char *cmd);
|
||||||
int svc_next_id (char *cmd);
|
|
||||||
int svc_is_unique (svc_t *svc);
|
int svc_is_unique (svc_t *svc);
|
||||||
|
|
||||||
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
|
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
|
||||||
|
|
||||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
|
||||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
|
||||||
|
|
||||||
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
|
|
||||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
|
||||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
|
||||||
|
|
||||||
static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; }
|
|
||||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
|
||||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
|
||||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
|
||||||
|
|
||||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||||
|
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
||||||
|
|
||||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||||
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
|
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
||||||
|
|
||||||
|
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||||
|
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||||
|
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
||||||
|
|
||||||
|
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||||
|
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||||
|
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||||
|
|
||||||
|
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||||
|
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||||
|
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||||
#define svc_start(svc) svc_unblock(svc)
|
#define svc_start(svc) svc_unblock(svc)
|
||||||
static inline void svc_stop (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
|
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||||
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
|
static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_BUSY; }
|
||||||
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
|
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
|
||||||
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
|
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
||||||
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
|
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
||||||
|
|
||||||
|
static inline char *svc_status(svc_t *svc)
|
||||||
|
{
|
||||||
|
if (!svc)
|
||||||
|
return "Unknown";
|
||||||
|
|
||||||
|
switch (svc->state) {
|
||||||
|
case SVC_HALTED_STATE:
|
||||||
|
switch (svc->block) {
|
||||||
|
case SVC_BLOCK_NONE:
|
||||||
|
return "halted";
|
||||||
|
|
||||||
|
case SVC_BLOCK_MISSING:
|
||||||
|
return "missing";
|
||||||
|
|
||||||
|
case SVC_BLOCK_CRASHING:
|
||||||
|
return "crashed";
|
||||||
|
|
||||||
|
case SVC_BLOCK_USER:
|
||||||
|
return "stopped";
|
||||||
|
|
||||||
|
case SVC_BLOCK_BUSY:
|
||||||
|
return "busy";
|
||||||
|
|
||||||
|
case SVC_BLOCK_RESTARTING:
|
||||||
|
return "restart";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
|
||||||
|
case SVC_DONE_STATE:
|
||||||
|
return "done";
|
||||||
|
|
||||||
|
case SVC_STOPPING_STATE:
|
||||||
|
switch (svc->type) {
|
||||||
|
case SVC_TYPE_INETD_CONN:
|
||||||
|
case SVC_TYPE_RUN:
|
||||||
|
case SVC_TYPE_TASK:
|
||||||
|
return "active";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "stopping";
|
||||||
|
}
|
||||||
|
|
||||||
|
case SVC_WAITING_STATE:
|
||||||
|
return "waiting";
|
||||||
|
|
||||||
|
case SVC_READY_STATE:
|
||||||
|
return "ready";
|
||||||
|
|
||||||
|
case SVC_RUNNING_STATE:
|
||||||
|
return "running";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *svc_dirtystr(svc_t *svc)
|
||||||
|
{
|
||||||
|
if (!svc)
|
||||||
|
return "Unknown";
|
||||||
|
|
||||||
|
if (svc_is_removed(svc))
|
||||||
|
return "removed";
|
||||||
|
if (svc_is_updated(svc))
|
||||||
|
return "updated";
|
||||||
|
if (svc_is_changed(svc))
|
||||||
|
return "UNKNOWN";
|
||||||
|
|
||||||
|
return "clean";
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* FINIT_SVC_H_ */
|
#endif /* FINIT_SVC_H_ */
|
||||||
|
|
||||||
|
|||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
/* Simple telinit client
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <lite/lite.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static int do_send(struct init_request *rq)
|
||||||
|
{
|
||||||
|
static int fd = -1;
|
||||||
|
int result = 255;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
if (fd == -1) {
|
||||||
|
if (!fexist(FINIT_FIFO)) {
|
||||||
|
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(FINIT_FIFO, O_RDWR);
|
||||||
|
if (-1 == fd) {
|
||||||
|
perror("Failed opening " FINIT_FIFO);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len = write(fd, rq, sizeof(*rq));
|
||||||
|
if (sizeof(*rq) == len)
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_reload(void)
|
||||||
|
{
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = INIT_CMD_RELOAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
return do_send(&rq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_env(int runlevel)
|
||||||
|
{
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = INIT_CMD_SETENV,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (runlevel == 0) {
|
||||||
|
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
|
||||||
|
return do_send(&rq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_runlevel(int runlevel)
|
||||||
|
{
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = INIT_CMD_RUNLVL,
|
||||||
|
.runlevel = runlevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
return set_env(runlevel) || do_send(&rq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* telinit q | telinit <0-9> */
|
||||||
|
static int usage(int rc)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -h, --help This help text\n"
|
||||||
|
" -V, --version Show Finit version\n\n"
|
||||||
|
"Commands:\n"
|
||||||
|
" 0 Power-off the system, same as initctl poweroff\n"
|
||||||
|
" 6 Reboot the system, same as initctl reboot\n"
|
||||||
|
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
|
||||||
|
" services in prev. runlevel that are not allowed in new.\n"
|
||||||
|
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
|
||||||
|
" sending SIGHUP to PID 1\n"
|
||||||
|
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||||
|
"\n", prognm);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int client(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
struct option long_options[] = {
|
||||||
|
{"help", 0, NULL, 'h'},
|
||||||
|
{"version", 0, NULL, 'v'},
|
||||||
|
{NULL, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
progname(argv[0]);
|
||||||
|
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||||
|
switch(c) {
|
||||||
|
case 'v':
|
||||||
|
return puts("v" VERSION) == EOF;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
case '?':
|
||||||
|
return usage(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc) {
|
||||||
|
int req = (int)argv[optind][0];
|
||||||
|
|
||||||
|
if (isdigit(req))
|
||||||
|
return set_runlevel(req);
|
||||||
|
|
||||||
|
if (req == 'q' || req == 'Q')
|
||||||
|
return do_reload();
|
||||||
|
|
||||||
|
if (req == 's' || req == 'S')
|
||||||
|
return set_runlevel(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
#ifdef FALLBACK_SHELL
|
#ifdef FALLBACK_SHELL
|
||||||
static pid_t fallback = 0;
|
static pid_t fallback = 0;
|
||||||
#endif
|
#endif
|
||||||
static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
|
||||||
|
|
||||||
static char *canonicalize(char *tty)
|
static char *canonicalize(char *tty)
|
||||||
{
|
{
|
||||||
@@ -72,48 +73,36 @@ static char *canonicalize(char *tty)
|
|||||||
|
|
||||||
void tty_mark(void)
|
void tty_mark(void)
|
||||||
{
|
{
|
||||||
tty_node_t *tty;
|
struct tty *tty;
|
||||||
|
|
||||||
LIST_FOREACH(tty, &tty_list, link) {
|
LIST_FOREACH(tty, &tty_list, link)
|
||||||
if (tty->mtime.tv_sec)
|
tty->dirty = -1;
|
||||||
tty->dirty = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tty_check(tty_node_t *tty, struct timeval *mtime)
|
|
||||||
{
|
|
||||||
if (mtime && timercmp(&tty->mtime, mtime, !=))
|
|
||||||
tty->dirty = 1; /* Modified, restart */
|
|
||||||
else
|
|
||||||
tty->dirty = 0; /* Not modified */
|
|
||||||
|
|
||||||
/* Update mtime, if given */
|
|
||||||
tty->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
|
|
||||||
tty->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_sweep(void)
|
void tty_sweep(void)
|
||||||
{
|
{
|
||||||
tty_node_t *tty, *tmp;
|
struct tty *tty, *tmp;
|
||||||
|
|
||||||
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
|
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
|
||||||
if (tty->mtime.tv_sec && tty->dirty) {
|
if (!tty->dirty)
|
||||||
_d("TTY %s dirty, stopping ...", tty->data.name);
|
continue;
|
||||||
tty_stop(&tty->data);
|
|
||||||
|
|
||||||
if (tty->dirty == -1) {
|
_d("TTY %s dirty, stopping ...", tty->name);
|
||||||
_d("TTY %s removed, cleaning up.", tty->data.name);
|
tty_stop(tty);
|
||||||
LIST_REMOVE(tty, link);
|
|
||||||
tty_unregister(tty);
|
if (tty->dirty == -1) {
|
||||||
}
|
_d("TTY %s removed, cleaning up.", tty->name);
|
||||||
|
tty_unregister(tty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_register - Register a getty on a device
|
* tty_register - Register a getty on a device
|
||||||
* @line: Configuration, text after initial "tty"
|
* @line: Configuration, text after initial "tty"
|
||||||
* @mtime: Modification time, to propagate to lower layers
|
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||||
|
* @cgroup: Control group, may be default
|
||||||
|
* @file: The file name TTY was loaded from
|
||||||
*
|
*
|
||||||
* A Finit tty line can use the internal getty implementation or an
|
* A Finit tty line can use the internal getty implementation or an
|
||||||
* external one, like the BusyBox getty for instance. This function
|
* external one, like the BusyBox getty for instance. This function
|
||||||
@@ -131,14 +120,14 @@ void tty_sweep(void)
|
|||||||
* Different getty implementations prefer the TTY device argument in
|
* Different getty implementations prefer the TTY device argument in
|
||||||
* different order, so take care to investigate this first.
|
* different order, so take care to investigate this first.
|
||||||
*/
|
*/
|
||||||
int tty_register(char *line, struct timeval *mtime)
|
int tty_register(char *line, struct rlimit rlimit[], char *cgroup, char *file)
|
||||||
{
|
{
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
int insert = 0, noclear = 0, nowait = 0;
|
|
||||||
size_t i, num = 0;
|
size_t i, num = 0;
|
||||||
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
|
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256];
|
||||||
char *dev = NULL, *baud = NULL;
|
char *dev = NULL, *baud = NULL;
|
||||||
char *runlevels = NULL, *term = NULL;
|
char *runlevels = NULL, *term = NULL;
|
||||||
|
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
|
||||||
|
|
||||||
if (!line) {
|
if (!line) {
|
||||||
_e("Missing argument");
|
_e("Missing argument");
|
||||||
@@ -156,6 +145,8 @@ int tty_register(char *line, struct timeval *mtime)
|
|||||||
noclear = 1;
|
noclear = 1;
|
||||||
else if (!strcmp(tok, "nowait"))
|
else if (!strcmp(tok, "nowait"))
|
||||||
nowait = 1;
|
nowait = 1;
|
||||||
|
else if (!strcmp(tok, "nologin"))
|
||||||
|
nologin = 1;
|
||||||
else
|
else
|
||||||
args[num++] = tok;
|
args[num++] = tok;
|
||||||
|
|
||||||
@@ -168,6 +159,8 @@ int tty_register(char *line, struct timeval *mtime)
|
|||||||
if (!dev && !cmd) {
|
if (!dev && !cmd) {
|
||||||
if (args[i][0] == '[')
|
if (args[i][0] == '[')
|
||||||
runlevels = line;
|
runlevels = line;
|
||||||
|
if (!strcmp(args[i], "@console"))
|
||||||
|
dev = args[i];
|
||||||
if (!strncmp(args[i], "/dev", 4))
|
if (!strncmp(args[i], "/dev", 4))
|
||||||
dev = args[i];
|
dev = args[i];
|
||||||
if (!strncmp(args[i], "tty", 3))
|
if (!strncmp(args[i], "tty", 3))
|
||||||
@@ -213,6 +206,24 @@ int tty_register(char *line, struct timeval *mtime)
|
|||||||
return errno = EINVAL;
|
return errno = EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Auto-detect serial console, for embedded devices mostly */
|
||||||
|
if (!strcmp(dev, "@console")) {
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen("/sys/class/tty/console/active", "r");
|
||||||
|
if (!fp) {
|
||||||
|
_e("Cannot find system console, is sysfs not mounted?");
|
||||||
|
return errno = ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
atcon = 1;
|
||||||
|
if (fgets(buf, sizeof(buf), fp))
|
||||||
|
dev = strtok(chomp(buf), " \t");
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
again:
|
||||||
/* Ensure all getty (built-in + external) are registered with absolute path */
|
/* Ensure all getty (built-in + external) are registered with absolute path */
|
||||||
dev = canonicalize(dev);
|
dev = canonicalize(dev);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
@@ -231,12 +242,13 @@ int tty_register(char *line, struct timeval *mtime)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->data.name = dev;
|
strlcpy(entry->name, dev, sizeof(entry->name));
|
||||||
entry->data.baud = baud ? strdup(baud) : NULL;
|
strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud));
|
||||||
entry->data.term = term ? strdup(term) : NULL;
|
strlcpy(entry->term, term ? term : "", sizeof(entry->term));
|
||||||
entry->data.noclear = noclear;
|
entry->noclear = noclear;
|
||||||
entry->data.nowait = nowait;
|
entry->nowait = nowait;
|
||||||
entry->data.runlevels = conf_parse_runlevels(runlevels);
|
entry->nologin = nologin;
|
||||||
|
entry->runlevels = conf_parse_runlevels(runlevels);
|
||||||
|
|
||||||
/* External getty */
|
/* External getty */
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
@@ -247,12 +259,12 @@ int tty_register(char *line, struct timeval *mtime)
|
|||||||
tok = cmd;
|
tok = cmd;
|
||||||
else
|
else
|
||||||
tok++;
|
tok++;
|
||||||
entry->data.cmd = cmd;
|
entry->cmd = cmd;
|
||||||
args[1] = strdup(tok);
|
args[1] = strdup(tok);
|
||||||
|
|
||||||
for (i = 1; i < num; i++)
|
for (i = 1; i < num; i++)
|
||||||
entry->data.args[j++] = strdup(args[i]);
|
entry->args[j++] = strdup(args[i]);
|
||||||
entry->data.args[++j] = NULL;
|
entry->args[++j] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
|
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
|
||||||
@@ -261,33 +273,46 @@ int tty_register(char *line, struct timeval *mtime)
|
|||||||
if (insert)
|
if (insert)
|
||||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||||
|
|
||||||
tty_check(entry, mtime);
|
/* Register configured limits */
|
||||||
|
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
|
||||||
|
|
||||||
|
/* Register desired control group */
|
||||||
|
strlcpy(entry->cgroup, cgroup, sizeof(entry->cgroup));
|
||||||
|
|
||||||
|
if (file && conf_changed(file))
|
||||||
|
entry->dirty = 1; /* Modified, restart */
|
||||||
|
else
|
||||||
|
entry->dirty = 0; /* Not modified */
|
||||||
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
|
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
|
||||||
|
|
||||||
|
if (atcon) {
|
||||||
|
dev = strtok(NULL, " \t");
|
||||||
|
if (dev) {
|
||||||
|
_d("Found another @console: %s", dev);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tty_unregister(tty_node_t *tty)
|
int tty_unregister(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty) {
|
if (!tty) {
|
||||||
_e("Missing argument");
|
_e("Missing argument");
|
||||||
return errno = EINVAL;
|
return errno = EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tty->data.name)
|
LIST_REMOVE(tty, link);
|
||||||
free(tty->data.name);
|
|
||||||
if (tty->data.baud)
|
if (tty->cmd) {
|
||||||
free(tty->data.baud);
|
|
||||||
if (tty->data.term)
|
|
||||||
free(tty->data.term);
|
|
||||||
if (tty->data.cmd) {
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
free(tty->data.cmd);
|
free(tty->cmd);
|
||||||
for (i = 0; i < TTY_MAX_ARGS; i++) {
|
for (i = 0; i < TTY_MAX_ARGS; i++) {
|
||||||
if (tty->data.args[i])
|
if (tty->args[i])
|
||||||
free(tty->data.args[i]);
|
free(tty->args[i]);
|
||||||
tty->data.args[i] = NULL;
|
tty->args[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(tty);
|
free(tty);
|
||||||
@@ -295,12 +320,12 @@ int tty_unregister(tty_node_t *tty)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tty_node_t *tty_find(char *dev)
|
struct tty *tty_find(char *dev)
|
||||||
{
|
{
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
if (!strcmp(dev, entry->data.name))
|
if (!strcmp(dev, entry->name))
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +335,7 @@ tty_node_t *tty_find(char *dev)
|
|||||||
size_t tty_num(void)
|
size_t tty_num(void)
|
||||||
{
|
{
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link)
|
LIST_FOREACH(entry, &tty_list, link)
|
||||||
num++;
|
num++;
|
||||||
@@ -321,22 +346,22 @@ size_t tty_num(void)
|
|||||||
size_t tty_num_active(void)
|
size_t tty_num_active(void)
|
||||||
{
|
{
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
if (entry->data.pid)
|
if (entry->pid)
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
tty_node_t *tty_find_by_pid(pid_t pid)
|
struct tty *tty_find_by_pid(pid_t pid)
|
||||||
{
|
{
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
if (entry->data.pid == pid)
|
if (entry->pid == pid)
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,9 +384,8 @@ static int tty_exist(char *dev)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_start(finit_tty_t *tty)
|
void tty_start(struct tty *tty)
|
||||||
{
|
{
|
||||||
int is_console = 0;
|
|
||||||
char *dev;
|
char *dev;
|
||||||
|
|
||||||
if (tty->pid) {
|
if (tty->pid) {
|
||||||
@@ -375,21 +399,27 @@ void tty_start(finit_tty_t *tty)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (console && !strcmp(dev, console))
|
|
||||||
is_console = 1;
|
|
||||||
|
|
||||||
if (tty_exist(dev)) {
|
if (tty_exist(dev)) {
|
||||||
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
|
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tty->nologin) {
|
||||||
|
_d("%s: Starting /bin/sh ...", dev);
|
||||||
|
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
|
||||||
if (!tty->cmd)
|
if (!tty->cmd)
|
||||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, is_console);
|
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||||
else
|
else
|
||||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, is_console);
|
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
||||||
|
|
||||||
|
cgroup_assign(tty->cgroup, tty->pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_stop(finit_tty_t *tty)
|
void tty_stop(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty->pid)
|
if (!tty->pid)
|
||||||
return;
|
return;
|
||||||
@@ -406,7 +436,7 @@ void tty_stop(finit_tty_t *tty)
|
|||||||
tty->pid = 0;
|
tty->pid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tty_enabled(finit_tty_t *tty)
|
int tty_enabled(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty)
|
if (!tty)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -454,12 +484,12 @@ int tty_fallback(pid_t lost)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tty_action(tty_node_t *tty)
|
static void tty_action(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty_enabled(&tty->data))
|
if (!tty_enabled(tty))
|
||||||
tty_stop(&tty->data);
|
tty_stop(tty);
|
||||||
else
|
else
|
||||||
tty_start(&tty->data);
|
tty_start(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -467,7 +497,7 @@ static void tty_action(tty_node_t *tty)
|
|||||||
*/
|
*/
|
||||||
int tty_respawn(pid_t pid)
|
int tty_respawn(pid_t pid)
|
||||||
{
|
{
|
||||||
tty_node_t *tty = tty_find_by_pid(pid);
|
struct tty *tty = tty_find_by_pid(pid);
|
||||||
|
|
||||||
if (!tty)
|
if (!tty)
|
||||||
return tty_fallback(pid);
|
return tty_fallback(pid);
|
||||||
@@ -476,7 +506,7 @@ int tty_respawn(pid_t pid)
|
|||||||
utmp_set_dead(pid);
|
utmp_set_dead(pid);
|
||||||
|
|
||||||
/* Clear PID to be able to respawn it. */
|
/* Clear PID to be able to respawn it. */
|
||||||
tty->data.pid = 0;
|
tty->pid = 0;
|
||||||
tty_action(tty);
|
tty_action(tty);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -487,15 +517,17 @@ int tty_respawn(pid_t pid)
|
|||||||
*/
|
*/
|
||||||
void tty_reload(char *dev)
|
void tty_reload(char *dev)
|
||||||
{
|
{
|
||||||
tty_node_t *tty;
|
struct tty *tty;
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
tty = tty_find(dev);
|
tty = tty_find(dev);
|
||||||
if (!tty)
|
if (!tty) {
|
||||||
logit(LOG_WARNING, "No TTY registered for %s", dev);
|
logit(LOG_WARNING, "No TTY registered for %s", dev);
|
||||||
else
|
return;
|
||||||
tty_action(tty);
|
}
|
||||||
|
|
||||||
|
tty_action(tty);
|
||||||
|
tty->dirty = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +542,7 @@ void tty_reload(char *dev)
|
|||||||
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
||||||
void tty_runlevel(void)
|
void tty_runlevel(void)
|
||||||
{
|
{
|
||||||
tty_node_t *tty;
|
struct tty *tty;
|
||||||
|
|
||||||
LIST_FOREACH(tty, &tty_list, link)
|
LIST_FOREACH(tty, &tty_list, link)
|
||||||
tty_action(tty);
|
tty_action(tty);
|
||||||
|
|||||||
@@ -26,51 +26,51 @@
|
|||||||
#define FINIT_TTY_H_
|
#define FINIT_TTY_H_
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
|
|
||||||
#define TTY_MAX_ARGS 16
|
#define TTY_MAX_ARGS 16
|
||||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||||
|
|
||||||
typedef struct {
|
struct tty {
|
||||||
char *name;
|
LIST_ENTRY(tty) link;
|
||||||
char *baud;
|
|
||||||
char *term;
|
char name[42];
|
||||||
|
char baud[10];
|
||||||
|
char term[10];
|
||||||
int noclear;
|
int noclear;
|
||||||
int nowait;
|
int nowait;
|
||||||
|
int nologin;
|
||||||
int runlevels;
|
int runlevels;
|
||||||
|
|
||||||
char *cmd; /* NULL when running built-in getty */
|
char *cmd; /* NULL when running built-in getty */
|
||||||
char *args[TTY_MAX_ARGS];
|
char *args[TTY_MAX_ARGS];
|
||||||
|
|
||||||
int pid;
|
int pid;
|
||||||
} finit_tty_t;
|
|
||||||
|
|
||||||
typedef struct tty_node {
|
/* Limits and scoping */
|
||||||
LIST_ENTRY(tty_node) link;
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
finit_tty_t data;
|
|
||||||
|
|
||||||
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */
|
/* Control group */
|
||||||
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
|
char cgroup[32];
|
||||||
int dirty; /* Set if old mtime != new mtime => reloaded,
|
|
||||||
* or -1 when marked for removal */
|
|
||||||
} tty_node_t;
|
|
||||||
|
|
||||||
//extern LIST_HEAD(, tty_node) tty_list;
|
/* Set if modified => reloaded, or -1 when marked for removal */
|
||||||
|
int dirty;
|
||||||
|
};
|
||||||
|
|
||||||
void tty_mark (void);
|
void tty_mark (void);
|
||||||
void tty_check (tty_node_t *tty, struct timeval *mtime);
|
|
||||||
void tty_sweep (void);
|
void tty_sweep (void);
|
||||||
|
|
||||||
int tty_register (char *line, struct timeval *mtime);
|
int tty_register (char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||||
int tty_unregister (tty_node_t *tty);
|
int tty_unregister (struct tty *tty);
|
||||||
|
|
||||||
tty_node_t *tty_find (char *dev);
|
struct tty *tty_find (char *dev);
|
||||||
size_t tty_num (void);
|
size_t tty_num (void);
|
||||||
size_t tty_num_active (void);
|
size_t tty_num_active (void);
|
||||||
tty_node_t *tty_find_by_pid (pid_t pid);
|
struct tty *tty_find_by_pid (pid_t pid);
|
||||||
void tty_start (finit_tty_t *tty);
|
void tty_start (struct tty *tty);
|
||||||
void tty_stop (finit_tty_t *tty);
|
void tty_stop (struct tty *tty);
|
||||||
int tty_enabled (finit_tty_t *tty);
|
int tty_enabled (struct tty *tty);
|
||||||
int tty_respawn (pid_t pid);
|
int tty_respawn (pid_t pid);
|
||||||
void tty_reload (char *dev);
|
void tty_reload (char *dev);
|
||||||
void tty_runlevel (void);
|
void tty_runlevel (void);
|
||||||
|
|||||||
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user