mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
+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>
|
||||||
|
|
||||||
|
|||||||
+183
-2
@@ -3,6 +3,185 @@ 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
|
||||||
|
|
||||||
|
### 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 +207,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 +777,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
|
||||||
|
|||||||
@@ -2,17 +2,19 @@
|
|||||||
|
|
||||||
* [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,8 +29,11 @@ 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
|
||||||
|
|||||||
+45
-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,6 @@ 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([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 +108,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 +157,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 +223,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 +252,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 +263,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)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
@@ -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)
|
||||||
+28
-39
@@ -18,32 +18,29 @@ 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
|
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
|
||||||
`.monitor=` in `svc_t` ... the script named as the basename of the
|
how close Finit3 still is to the original easy-to-use Finit0 :)
|
||||||
service it monitors + `.sh`.
|
|
||||||
* Implement `initctl stop|start|restart|reload|status <SVC>` and
|
|
||||||
`service <SVC> stop|start|restart|reload|status` on top
|
General
|
||||||
|
-------
|
||||||
|
|
||||||
|
* 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 +51,13 @@ 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).
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -109,21 +115,4 @@ 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
|
[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
|
||||||
+169
-49
@@ -2,6 +2,7 @@ Configuration
|
|||||||
=============
|
=============
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
|
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||||
* [Syntax](#syntax)
|
* [Syntax](#syntax)
|
||||||
* [Limitations](#limitations)
|
* [Limitations](#limitations)
|
||||||
* [/etc/finit.conf](#etcfinitconf)
|
* [/etc/finit.conf](#etcfinitconf)
|
||||||
@@ -13,10 +14,10 @@ Introduction
|
|||||||
|
|
||||||
Finit can be configured using only the original `/etc/finit.conf` file
|
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
|
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
|
system using only `/etc/finit.d/*.conf`, highly useful for package-based
|
||||||
Linux distributions -- each package can provide its own "script" file.
|
Linux distributions -- each package can provide its own "script" file.
|
||||||
|
|
||||||
- `/etc/finit.conf`: main configuration file, read only once at boot
|
- `/etc/finit.conf`: main configuration file
|
||||||
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
- `/etc/finit.d/*.conf`: snippets, usually one service per file
|
||||||
|
|
||||||
Not all configuration directives are available in `/etc/finit.d/*.conf`
|
Not all configuration directives are available in `/etc/finit.d/*.conf`
|
||||||
@@ -26,8 +27,15 @@ the section [Limitations](#limitations) below for details.
|
|||||||
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
|
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
|
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).
|
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
|
Finit monitors all known active `.conf` files, so if you want to force
|
||||||
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
|
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:
|
On `initctl reload` the following is checked for all services:
|
||||||
|
|
||||||
@@ -42,24 +50,63 @@ For more info on the different states of a service, see the separate
|
|||||||
document [Finit Services](service.md).
|
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
|
Syntax
|
||||||
------
|
------
|
||||||
|
|
||||||
* `module <MODULE>`
|
* `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
|
Load a kernel module, with optional arguments
|
||||||
|
|
||||||
* `network <PATH>`
|
* `network <PATH>`
|
||||||
Script or program to bring up networking, with optional arguments
|
Script or program to bring up networking, with optional arguments
|
||||||
|
|
||||||
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>`
|
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
|
||||||
Modify the specified resource's hard or soft limit to be the
|
limit for a resource, or both if that argument is omitted. `RESOURCE`
|
||||||
specifed limit. `RESOURCE` is a lower-case string matching the
|
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
|
||||||
constants in `setrlimit(2)` with the `RLIMIT_` prefix
|
without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||||
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
|
|
||||||
`cpu`. `LIMIT` is an integer whose unit depends on the resource
|
LIMIT is an integer that depends on the resource being modified, see
|
||||||
being modified, see `setrlimit(2)` for more information. The special
|
the man page, or the kernel `/proc/PID/limits` file, for details.
|
||||||
limit `infinity` means the there should be no limit on the resource,
|
Finit versions before v3.1 used `infinity` for `unlimited`, which is
|
||||||
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`.
|
still supported, albeit deprecated.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# No process is allowed more than 8MB of address space
|
# No process is allowed more than 8MB of address space
|
||||||
@@ -67,8 +114,14 @@ Syntax
|
|||||||
|
|
||||||
# Core dumps may be arbitrarily large
|
# Core dumps may be arbitrarily large
|
||||||
rlimit soft core infinity
|
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>`
|
* `runlevel <N>`
|
||||||
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
N is the runlevel number 1-9, where 6 is reserved for reboot.
|
||||||
Default is 2.
|
Default is 2.
|
||||||
@@ -107,8 +160,30 @@ Syntax
|
|||||||
Finit waits for another service, `/sbin/zebra`, to have created its
|
Finit waits for another service, `/sbin/zebra`, to have created its
|
||||||
PID file in `/var/run/zebra.pid` before starting `ospfd`.
|
PID file in `/var/run/zebra.pid` before starting `ospfd`.
|
||||||
|
|
||||||
For a detailed description of conditions, and how to debug them,
|
Some services do not maintain a PID file and rather than patching each
|
||||||
see the [Finit Conditions](conditions.md) document.
|
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.
|
||||||
|
|
||||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
||||||
Launch a daemon when a client initiates a connection on an Internet
|
Launch a daemon when a client initiates a connection on an Internet
|
||||||
@@ -140,31 +215,48 @@ Syntax
|
|||||||
* `runparts <DIR>`
|
* `runparts <DIR>`
|
||||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||||
files, or scripts, in the directory are called, in alphabetic order.
|
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
|
It can be beneficial to use `S01name`, `S02othername`, etc. if there
|
||||||
is a dependency order between the scripts. Symlinks to existing
|
is a dependency order between the scripts. Symlinks to existing
|
||||||
daemons can talso be used, but make sure they daemonize by default.
|
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
|
Similar to the `/etc/rc.local` shell script, make sure that all your
|
||||||
services and programs either terminate or start in the background or
|
services and programs either terminate or start in the background or
|
||||||
you will block Finit.
|
you will block Finit.
|
||||||
|
|
||||||
* `include <CONF>`
|
* `include <CONF>`
|
||||||
Include another configuration file. Absolute path required.
|
Include another configuration file. Absolute path required.
|
||||||
|
|
||||||
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [TERM]`
|
* `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]`
|
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
||||||
The first variant of this option uses the built-in getty on the given
|
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`.
|
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:**
|
**Example:**
|
||||||
```conf
|
```conf
|
||||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||||
```
|
```
|
||||||
|
|
||||||
The second variant is for using an external getty, like agetty or the
|
The second `tty` syntax variant is for using an external getty, like
|
||||||
BusyBox getty.
|
agetty or the BusyBox getty.
|
||||||
|
|
||||||
By default both variants *clear* the TTY and *wait* for the user to
|
By default both variants *clear* the TTY and *wait* for the user to
|
||||||
press enter before starting getty.
|
press enter before starting getty.
|
||||||
|
|
||||||
@@ -182,10 +274,28 @@ Syntax
|
|||||||
embedded systems running multiple unused getty wastes both memory
|
embedded systems running multiple unused getty wastes both memory
|
||||||
and CPU cycles, so `wait` is the preferred default.
|
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
|
Notice the ordering, the `TERM` option to the built-in getty must be
|
||||||
the last argument.
|
the last argument.
|
||||||
|
|
||||||
On really bare bones systems Finit offers a fallback shell:
|
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
|
```shell
|
||||||
configure --enable-fallback-shell
|
configure --enable-fallback-shell
|
||||||
@@ -197,16 +307,10 @@ Syntax
|
|||||||
service [12345] /bin/sh -l
|
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
|
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
|
||||||
be installed since system requirements differ too much. Try out the
|
be installed since system requirements differ too much. Try out the
|
||||||
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
||||||
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
|
is capable of service monitoring SSH, sysklogd, gdm and getty!
|
||||||
|
|
||||||
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
||||||
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
||||||
@@ -229,8 +333,22 @@ Without the `:ID` to the service the latter will overwrite the former
|
|||||||
and only the old web server would be started and supervised.
|
and only the old web server would be started and supervised.
|
||||||
|
|
||||||
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
|
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
|
||||||
`log` to redirect `stderr` and `stdout` of the application to syslog,
|
`log` to redirect `stderr` and `stdout` of the application to a file or
|
||||||
using `logger`.
|
syslog using the native `logit` tool. The full syntax is:
|
||||||
|
|
||||||
|
log:/path/to/file
|
||||||
|
log:prio:facility.level,tag:ident
|
||||||
|
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
|
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||||
detailed description, see the [Conditions](conditions.md) document.
|
detailed description, see the [Conditions](conditions.md) document.
|
||||||
@@ -241,40 +359,42 @@ Limitations
|
|||||||
|
|
||||||
To understand the limitations of `finit.conf` vs `finit.d` it is useful
|
To understand the limitations of `finit.conf` vs `finit.d` it is useful
|
||||||
to picture the different phases of the system: bootstrap, runtime, and
|
to picture the different phases of the system: bootstrap, runtime, and
|
||||||
shutdown. The `finit.conf` file can be used for all possible settings,
|
shutdown.
|
||||||
but `finit.d/*.conf` can only be used for runtime settings.
|
|
||||||
|
|
||||||
### /etc/finit.conf
|
### /etc/finit.conf
|
||||||
|
|
||||||
This file was the only way to set up and boot a system using Finit. It
|
This file used to be the only way to set up and boot a Finit system.
|
||||||
is used for pre-runtime settings like system hostname, network bringup
|
Today it is mainly used for pre-runtime settings like system hostname,
|
||||||
and shutdown:
|
network bringup and shutdown:
|
||||||
|
|
||||||
- `host`
|
- `host`, only at bootstrap, (runlevel `S`)
|
||||||
- `mkdod`
|
- `mknod`, only at bootstrap
|
||||||
- `network`
|
- `network`, only at bootstrap
|
||||||
- `runparts`
|
- `runparts`, only at bootstrap
|
||||||
- `include`
|
- `include`
|
||||||
- `shtudown`
|
- `log`, global setting
|
||||||
- `runlevel`
|
- `shutdown`
|
||||||
|
- `runlevel`, only at bootstrap
|
||||||
- ... and all configuration stanzas from `/etc/finit.d` below
|
- ... and all configuration stanzas from `/etc/finit.d` below
|
||||||
|
|
||||||
### /etc/finit.d
|
### /etc/finit.d
|
||||||
|
|
||||||
Support for partial `.conf` files in `/etc/finit.d` was added to handle
|
Support for per-service `.conf` files in `/etc/finit.d` was added in
|
||||||
changes of the system configuration at runtime. It does *not support*
|
v3.0 to handle changes of the system configuration at runtime. As of
|
||||||
the above `finit.conf` settings described above, only the following:
|
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 in runlevel `S`
|
- `module`, but only at bootstrap
|
||||||
- `service`
|
- `service`
|
||||||
- `task`
|
- `task`
|
||||||
- `run`
|
- `run`
|
||||||
- `inetd`
|
- `inetd`
|
||||||
- `rlimit`
|
- `rlimit`
|
||||||
- `tty`
|
- `tty`
|
||||||
- `console`
|
|
||||||
|
|
||||||
**NOTE:*** The `/etc/finit.d` directory was previously the default Finit
|
**NOTE:** The `/etc/finit.d` directory was previously the default Finit
|
||||||
`runparts` directory. Finit no longer has a default `runparts`, make
|
`runparts` directory. Finit no longer has a default `runparts`, make
|
||||||
sure to update your setup, or the finit configuration, accordingly.
|
sure to update your setup, or the finit configuration, accordingly.
|
||||||
|
|
||||||
@@ -47,9 +47,6 @@ For your convenience a set of *optional* plugins are available:
|
|||||||
|
|
||||||
* *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
|
|
||||||
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
|
||||||
for services that may want to be SIGHUP'ed on new default route or
|
for services that may want to be SIGHUP'ed on new default route or
|
||||||
@@ -102,19 +99,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
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
+1
-8
@@ -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
|
||||||
@@ -38,10 +39,6 @@ libplug_la_SOURCES += time.c
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_LOST_PLUGIN
|
|
||||||
libplug_la_SOURCES += lost.c
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_NETLINK_PLUGIN
|
if BUILD_NETLINK_PLUGIN
|
||||||
libplug_la_SOURCES += netlink.c
|
libplug_la_SOURCES += netlink.c
|
||||||
endif
|
endif
|
||||||
@@ -83,10 +80,6 @@ pkglib_LTLIBRARIES += time.la
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_LOST_PLUGIN
|
|
||||||
pkglib_LTLIBRARIES += lost.la
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_NETLINK_PLUGIN
|
if BUILD_NETLINK_PLUGIN
|
||||||
pkglib_LTLIBRARIES += netlink.la
|
pkglib_LTLIBRARIES += netlink.la
|
||||||
endif
|
endif
|
||||||
|
|||||||
+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);
|
||||||
|
|
||||||
|
|||||||
+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:
|
||||||
|
|||||||
+43
-42
@@ -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,23 +53,16 @@ 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)) {
|
||||||
svc_started(svc);
|
svc_started(svc);
|
||||||
@@ -87,44 +73,59 @@ 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;
|
||||||
|
|
||||||
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
|
/*
|
||||||
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
* 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, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
||||||
|
free(path);
|
||||||
|
|
||||||
if (ctx->wd < 0) {
|
if (ctx->wd < 0) {
|
||||||
_pe("inotify_add_watch()");
|
_pe("inotify_add_watch()");
|
||||||
close(ctx->fd);
|
close(ctx->fd);
|
||||||
@@ -148,7 +149,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)
|
||||||
|
|||||||
+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
|
||||||
|
|||||||
+35
-17
@@ -1,24 +1,34 @@
|
|||||||
|
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
|
||||||
|
if WATCHDOGD
|
||||||
|
pkglibexec_PROGRAMS = watchdogd
|
||||||
|
endif
|
||||||
|
|
||||||
|
logit_SOURCES = logit.c
|
||||||
|
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
|
|
||||||
finit_SOURCES = api.c \
|
finit_SOURCES = api.c \
|
||||||
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 +40,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 +64,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 +85,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 +108,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,30 +74,60 @@ 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, int id)
|
||||||
|
{
|
||||||
|
char buf[20];
|
||||||
|
char idstr[13] = "";
|
||||||
|
|
||||||
|
if (!job)
|
||||||
|
job = "";
|
||||||
|
if (id > 1)
|
||||||
|
snprintf(idstr, sizeof(idstr), ":%d", id);
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s%s ", job, idstr);
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
int id = 1;
|
int id = 1;
|
||||||
char *ptr, *input = sanitize(buf, len);
|
char *ptr, *input;
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
|
input = sanitize(buf, len);
|
||||||
if (!input)
|
if (!input)
|
||||||
return -1;
|
return NULL;
|
||||||
|
|
||||||
ptr = strchr(input, ':');
|
ptr = strchr(input, ':');
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
@@ -102,11 +135,20 @@ static int do_query_inetd(char *buf, size_t len)
|
|||||||
id = atonum(ptr);
|
id = atonum(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
svc = svc_find_by_jobid(atonum(input), id);
|
if (isdigit(input[0]))
|
||||||
if (!svc || !svc_is_inetd(svc)) {
|
return svc_find_by_jobid(atonum(input), id);
|
||||||
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", 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 1;
|
||||||
}
|
|
||||||
|
|
||||||
return inetd_filter_str(&svc->inetd, buf, len);
|
return inetd_filter_str(&svc->inetd, buf, len);
|
||||||
}
|
}
|
||||||
@@ -163,20 +205,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 +288,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 +335,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->protected) {
|
||||||
|
wdog->protected = 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 +397,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 +415,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 +431,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 +443,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+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(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 (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:
|
|
||||||
*/
|
|
||||||
+113
-27
@@ -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,30 +139,53 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
+36
-15
@@ -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)
|
||||||
|
|||||||
+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_ */
|
||||||
|
|
||||||
|
|||||||
+467
-130
@@ -25,8 +25,10 @@
|
|||||||
|
|
||||||
#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 "finit.h"
|
#include "finit.h"
|
||||||
@@ -34,35 +36,111 @@
|
|||||||
#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 uev_t w1, w2, w3;
|
||||||
|
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,59 +241,101 @@ 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");
|
||||||
@@ -226,13 +346,13 @@ 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 +362,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 +387,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;
|
||||||
|
|
||||||
@@ -289,10 +431,9 @@ static void parse_static(char *line)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +441,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, 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, 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, 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, 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 +475,13 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special TTY console powers */
|
|
||||||
if (MATCH_CMD(line, "console ", x)) {
|
|
||||||
if (console) free(console);
|
|
||||||
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, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,9 +497,10 @@ static void tabstospaces(char *line)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
static int parse_conf_dynamic(char *file)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
fp = fopen(file, "r");
|
fp = fopen(file, "r");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
@@ -382,7 +508,10 @@ 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));
|
||||||
|
|
||||||
|
_d("Parsing %s <<<<<<", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
char line[LINE_SIZE] = "";
|
char line[LINE_SIZE] = "";
|
||||||
|
|
||||||
@@ -393,7 +522,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 +534,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,29 +557,54 @@ 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;
|
int i, num;
|
||||||
char *dir = rcsd;
|
|
||||||
struct dirent **e;
|
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) {
|
||||||
|
int rc;
|
||||||
|
char line[80] = "tty [12345] @console noclear nologin";
|
||||||
|
|
||||||
|
/* If rescue.conf is missing, fall back to a root shell */
|
||||||
|
rc = parse_conf(RESCUE_CONF);
|
||||||
|
if (rc)
|
||||||
|
tty_register(line, global_rlimit, NULL);
|
||||||
|
|
||||||
|
print(rc, "Entering rescue mode");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First, read /etc/finit.conf */
|
||||||
|
parse_conf(FINIT_CONF);
|
||||||
|
|
||||||
|
/* Next, read all *.conf in /etc/finit.d/ */
|
||||||
|
num = scandir(rcsd, &e, NULL, alphasort);
|
||||||
if (num < 0) {
|
if (num < 0) {
|
||||||
_d("Skipping %s, no files found ...", dir);
|
_d("Skipping %s, no files found ...", rcsd);
|
||||||
return 1;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
@@ -473,13 +612,11 @@ int conf_reload_dynamic(void)
|
|||||||
char path[LINE_SIZE];
|
char path[LINE_SIZE];
|
||||||
size_t len;
|
size_t len;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct timeval mtime;
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
snprintf(path, sizeof(path), "%s/%s", rcsd, 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 +633,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 +647,231 @@ 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--)
|
while (num--)
|
||||||
free(e[num]);
|
free(e[num]);
|
||||||
free(e);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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 = -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_ */
|
||||||
|
|
||||||
|
|||||||
+121
-17
@@ -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;
|
||||||
@@ -351,14 +451,18 @@ int run_parts(char *dir, char *cmd)
|
|||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
int j = 0;
|
int j = 0;
|
||||||
pid_t pid = 0;
|
pid_t pid = 0;
|
||||||
mode_t mode;
|
struct stat st;
|
||||||
char *args[NUM_ARGS];
|
char *args[NUM_ARGS];
|
||||||
char *name = e[i]->d_name;
|
char *name = e[i]->d_name;
|
||||||
char path[LINE_SIZE];
|
char path[LINE_SIZE];
|
||||||
|
|
||||||
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));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||||
_d("Skipping %s ...", path);
|
_d("Skipping %s ...", path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+235
-189
@@ -36,6 +36,7 @@
|
|||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
@@ -46,20 +47,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 +84,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 +108,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 +127,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 +154,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 +190,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 +204,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,29 +328,17 @@ int main(int argc, char* argv[])
|
|||||||
if (getpid() != 1)
|
if (getpid() != 1)
|
||||||
return client(argc, argv);
|
return client(argc, argv);
|
||||||
|
|
||||||
/* Set up canvas */
|
|
||||||
screen_init();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In case of emergency.
|
|
||||||
*/
|
|
||||||
emergency_shell();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initial setup of signals, ignore all until we're up.
|
|
||||||
*/
|
|
||||||
sig_init();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initalize event context.
|
* Initalize event context.
|
||||||
*/
|
*/
|
||||||
uev_init(&loop);
|
uev_init1(&loop, 1);
|
||||||
ctx = &loop;
|
ctx = &loop;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the PATH early to something sane
|
* Set PATH and SHELL early to something sane
|
||||||
*/
|
*/
|
||||||
setenv("PATH", _PATH_STDPATH, 1);
|
setenv("PATH", _PATH_STDPATH, 1);
|
||||||
|
setenv("SHELL", _PATH_BSHELL, 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||||
@@ -290,32 +351,50 @@ int main(int argc, char* argv[])
|
|||||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse kernel parameters, including log_init()
|
* Parse kernel command line (debug, rescue, splash, etc.)
|
||||||
|
* Also calls log_init() to set correct log level
|
||||||
*/
|
*/
|
||||||
conf_parse_cmdline();
|
conf_parse_cmdline();
|
||||||
|
|
||||||
|
/* Set up canvas */
|
||||||
|
if (!rescue && !log_is_debug())
|
||||||
|
screen_init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In case of emergency.
|
||||||
|
*/
|
||||||
|
emergency_shell();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initial setup of signals, ignore all until we're up.
|
||||||
|
*/
|
||||||
|
sig_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 +408,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 +426,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, 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, 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, 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, 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_ */
|
||||||
|
|||||||
+63
-117
@@ -64,42 +64,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 +126,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))
|
||||||
@@ -244,72 +207,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 +226,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)
|
||||||
|
|||||||
+82
-22
@@ -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,17 @@ 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";
|
||||||
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;
|
||||||
@@ -184,11 +218,14 @@ 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));
|
||||||
|
|
||||||
task->stdin_fd = stdin;
|
task->stdin_fd = stdin;
|
||||||
service_step(task);
|
service_step(task);
|
||||||
@@ -202,10 +239,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 +345,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 +702,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);
|
||||||
|
|||||||
+107
-152
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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[10], 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:%d", 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
|
||||||
@@ -526,7 +466,7 @@ static int show_status(char *arg)
|
|||||||
else
|
else
|
||||||
name++;
|
name++;
|
||||||
|
|
||||||
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
|
printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +479,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 +488,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, ' ');
|
||||||
@@ -579,9 +519,10 @@ static int show_status(char *arg)
|
|||||||
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 +534,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 +547,8 @@ 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"
|
"\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 +568,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 +578,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 },
|
||||||
@@ -653,6 +599,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 +607,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 +623,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
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "finit.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
|
||||||
|
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libuEv callback wrapper
|
||||||
|
*/
|
||||||
|
static void cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
struct wq *work = (struct wq *)arg;
|
||||||
|
|
||||||
|
if (UEV_ERROR == events) {
|
||||||
|
uev_timer_start(w);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
work->cb(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Place work on event queue
|
||||||
|
*/
|
||||||
|
int schedule_work(struct wq *work)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -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_ */
|
||||||
+34
@@ -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>
|
||||||
@@ -130,6 +131,9 @@ 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;
|
||||||
@@ -153,6 +157,9 @@ 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;
|
||||||
@@ -168,6 +175,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_ */
|
||||||
|
|
||||||
|
|||||||
+452
-167
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 *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");
|
||||||
|
|
||||||
|
|||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,11 +79,12 @@ static svc_t *__connect_shm(void)
|
|||||||
*/
|
*/
|
||||||
svc_t *svc_new(char *cmd, int id, int type)
|
svc_t *svc_new(char *cmd, int id, int type)
|
||||||
{
|
{
|
||||||
int i, job = -1;
|
int job = -1;
|
||||||
svc_t *svc, *list = __connect_shm();
|
char *desc;
|
||||||
|
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 +93,35 @@ 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;
|
||||||
|
svc->id = id;
|
||||||
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
|
||||||
memset(svc, 0, sizeof(*svc));
|
/* Default description, if missing */
|
||||||
svc->type = type;
|
desc = strrchr(cmd, '/');
|
||||||
svc->job = job;
|
if (desc)
|
||||||
svc->id = id;
|
desc++;
|
||||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
else
|
||||||
|
desc = cmd;
|
||||||
|
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||||
|
|
||||||
/* 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 +129,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,42 +188,22 @@ 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 = basename(svc->cmd);
|
||||||
|
|
||||||
if (!strncmp(name, cmd, strlen(name)))
|
if (!strncmp(name, cmd, strlen(name)))
|
||||||
@@ -229,19 +216,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 +242,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 +259,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 +284,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;
|
||||||
}
|
}
|
||||||
@@ -331,9 +303,9 @@ svc_t *svc_stop_completed(void)
|
|||||||
*/
|
*/
|
||||||
svc_t *svc_find(char *cmd, int id)
|
svc_t *svc_find(char *cmd, int 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 == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
@@ -350,9 +322,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;
|
||||||
}
|
}
|
||||||
@@ -370,9 +342,9 @@ 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, int 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 == id)
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
@@ -391,9 +363,9 @@ svc_t *svc_find_by_jobid(int job, int id)
|
|||||||
svc_t *svc_find_by_nameid(char *name, int id)
|
svc_t *svc_find_by_nameid(char *name, int id)
|
||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
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)) {
|
||||||
ptr = strrchr(svc->cmd, '/');
|
ptr = strrchr(svc->cmd, '/');
|
||||||
if (ptr)
|
if (ptr)
|
||||||
ptr++;
|
ptr++;
|
||||||
@@ -407,6 +379,27 @@ svc_t *svc_find_by_nameid(char *name, int id)
|
|||||||
return NULL;
|
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 NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
||||||
*
|
*
|
||||||
@@ -419,11 +412,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->protected)
|
||||||
|
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 +434,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 +443,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,85 +478,44 @@ 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)
|
|
||||||
{
|
|
||||||
if (svc_is_removed(svc))
|
|
||||||
return "removed";
|
|
||||||
if (svc_is_updated(svc))
|
|
||||||
return "updated";
|
|
||||||
if (svc_is_changed(svc))
|
|
||||||
return "UNKNOWN";
|
|
||||||
|
|
||||||
return "clean";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Same base service, return unique ID */
|
/* Same base service, return unique ID */
|
||||||
int svc_next_id(char *cmd)
|
int svc_next_id(char *cmd)
|
||||||
{
|
{
|
||||||
int id = 0;
|
int id = 0;
|
||||||
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 (!strcmp(svc->cmd, cmd) && id < svc->id)
|
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
||||||
id = svc->id;
|
id = svc->id;
|
||||||
}
|
}
|
||||||
@@ -583,12 +525,10 @@ int svc_next_id(char *cmd)
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -619,21 +559,21 @@ 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;
|
int id = 1;
|
||||||
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);
|
int job = atonum(token);
|
||||||
|
|
||||||
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, job);
|
||||||
|
|
||||||
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;
|
||||||
@@ -648,14 +588,14 @@ 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;
|
||||||
|
|||||||
@@ -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,16 @@ 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_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 +83,45 @@ 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, id; /* JOB:ID */
|
||||||
|
|
||||||
|
/* Limits and scoping */
|
||||||
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
/* 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 protected; /* 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];
|
||||||
|
|
||||||
/* 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 file[64];
|
||||||
|
char prio[20];
|
||||||
|
char ident[20];
|
||||||
|
} log;
|
||||||
|
|
||||||
/* Identity */
|
/* Identity */
|
||||||
char username[MAX_USER_LEN];
|
char username[MAX_USER_LEN];
|
||||||
@@ -122,25 +138,11 @@ 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;
|
|
||||||
|
|
||||||
/* 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);
|
svc_t *svc_new (char *cmd, int id, int type);
|
||||||
int svc_del (svc_t *svc);
|
int svc_del (svc_t *svc);
|
||||||
|
|
||||||
@@ -148,59 +150,128 @@ 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_find_by_jobid (int job, int id);
|
svc_t *svc_find_by_jobid (int job, int id);
|
||||||
svc_t *svc_find_by_nameid (char *name, int id);
|
svc_t *svc_find_by_nameid (char *name, int 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 (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 *, int));
|
||||||
|
|
||||||
|
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_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_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
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 int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||||
|
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) { svc->starting = 1; }
|
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
static inline int svc_is_starting (svc_t *svc) { return svc && 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_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_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_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_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||||
|
|
||||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
|
||||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
|
||||||
static inline void svc_unblock (svc_t *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:
|
||||||
|
*/
|
||||||
@@ -41,7 +41,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 +72,35 @@ 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
|
||||||
|
* @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 +118,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 *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 +143,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 +157,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 +204,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 +240,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 +257,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 +271,43 @@ 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));
|
||||||
|
|
||||||
|
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 +315,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 +330,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 +341,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 +379,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 +394,25 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_stop(finit_tty_t *tty)
|
void tty_stop(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty->pid)
|
if (!tty->pid)
|
||||||
return;
|
return;
|
||||||
@@ -406,7 +429,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 +477,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 +490,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 +499,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 +510,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 +535,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,48 @@
|
|||||||
#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. */
|
/* Set if modified => reloaded, or -1 when marked for removal */
|
||||||
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
|
int dirty;
|
||||||
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;
|
|
||||||
|
|
||||||
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 *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);
|
||||||
|
|||||||
+102
-1
@@ -23,8 +23,12 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <ctype.h> /* isprint() */
|
#include <ctype.h> /* isprint() */
|
||||||
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/sysinfo.h> /* sysinfo() */
|
||||||
|
#include <lite/lite.h> /* strlcat() */
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
int screen_rows = 24;
|
int screen_rows = 24;
|
||||||
@@ -42,12 +46,92 @@ char *progname(char *arg0)
|
|||||||
return prognm;
|
return prognm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int strtobytes(char *arg)
|
||||||
|
{
|
||||||
|
int mod = 0, bytes;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
if (!arg)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = strspn(arg, "0123456789");
|
||||||
|
if (arg[pos] != 0) {
|
||||||
|
if (arg[pos] == 'G')
|
||||||
|
mod = 3;
|
||||||
|
else if (arg[pos] == 'M')
|
||||||
|
mod = 2;
|
||||||
|
else if (arg[pos] == 'k')
|
||||||
|
mod = 1;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
arg[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = atoi(arg);
|
||||||
|
while (mod--)
|
||||||
|
bytes *= 1000;
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
void do_sleep(unsigned int sec)
|
void do_sleep(unsigned int sec)
|
||||||
{
|
{
|
||||||
while ((sec = sleep(sec)))
|
while ((sec = sleep(sec)))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Seconds since boot, from sysinfo() */
|
||||||
|
long jiffies(void)
|
||||||
|
{
|
||||||
|
struct sysinfo si;
|
||||||
|
|
||||||
|
if (!sysinfo(&si))
|
||||||
|
return si.uptime;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uptime(long secs, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
long mins, hours, days, years;
|
||||||
|
char y[20] = "", d[20] = "", h[20] = "", m[20] = "", s[20] = "";
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
years = secs / 31556926;
|
||||||
|
secs = secs % 31556926;
|
||||||
|
days = secs / 86400;
|
||||||
|
secs = secs % 86400;
|
||||||
|
hours = secs / 3600;
|
||||||
|
secs = secs % 3600;
|
||||||
|
mins = secs / 60;
|
||||||
|
secs = secs % 60;
|
||||||
|
|
||||||
|
if (years)
|
||||||
|
snprintf(y, sizeof(y), "%ld year", years);
|
||||||
|
if (days)
|
||||||
|
snprintf(d, sizeof(d), "%ld day", days);
|
||||||
|
if (hours)
|
||||||
|
snprintf(h, sizeof(h), "%ld hour", hours);
|
||||||
|
if (mins)
|
||||||
|
snprintf(m, sizeof(m), "%ld min", mins);
|
||||||
|
if (secs)
|
||||||
|
snprintf(s, sizeof(s), "%ld sec", secs);
|
||||||
|
|
||||||
|
snprintf(buf, len, "%s%s%s%s%s%s%s%s%s",
|
||||||
|
y, years ? " " : "",
|
||||||
|
d, days ? " " : "",
|
||||||
|
h, hours ? " " : "",
|
||||||
|
m, mins ? " " : "",
|
||||||
|
s);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allowed characters in job/id/name */
|
/* Allowed characters in job/id/name */
|
||||||
static int isallowed(int ch)
|
static int isallowed(int ch)
|
||||||
{
|
{
|
||||||
@@ -67,17 +151,34 @@ char *sanitize(char *arg, size_t len)
|
|||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 1 && arg[i] == 0)
|
if (i > 0 && arg[i] == 0)
|
||||||
return arg;
|
return arg;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called at boot, and shutdown, to (re)initialize the
|
||||||
|
* screen size for print() et al.
|
||||||
|
*/
|
||||||
void screen_init(void)
|
void screen_init(void)
|
||||||
{
|
{
|
||||||
|
if (!isatty(STDOUT_FILENO))
|
||||||
|
return;
|
||||||
|
|
||||||
initscr(&screen_rows, &screen_cols);
|
initscr(&screen_rows, &screen_cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when debug mode is enabled to revert back
|
||||||
|
* to old-school safe defaults.
|
||||||
|
*/
|
||||||
|
void screen_exit(void)
|
||||||
|
{
|
||||||
|
screen_rows = 24;
|
||||||
|
screen_cols = 80;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
@@ -39,10 +39,16 @@ extern char *prognm;
|
|||||||
#include <lite/conio.h>
|
#include <lite/conio.h>
|
||||||
|
|
||||||
char *progname (char *arg0);
|
char *progname (char *arg0);
|
||||||
|
int strtobytes (char *arg);
|
||||||
void do_sleep (unsigned int sec);
|
void do_sleep (unsigned int sec);
|
||||||
|
|
||||||
|
long jiffies (void);
|
||||||
|
char *uptime (long secs, char *buf, size_t len);
|
||||||
|
|
||||||
char *sanitize (char *arg, size_t len);
|
char *sanitize (char *arg, size_t len);
|
||||||
|
|
||||||
void screen_init (void);
|
void screen_init (void);
|
||||||
|
void screen_exit (void);
|
||||||
int screen_width (void);
|
int screen_width (void);
|
||||||
|
|
||||||
#endif /* FINIT_UTIL_H_ */
|
#endif /* FINIT_UTIL_H_ */
|
||||||
|
|||||||
+60
-68
@@ -1,6 +1,6 @@
|
|||||||
/* Built-in watchdog daemon
|
/* Built-in watchdog daemon
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2016-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
|
||||||
@@ -9,74 +9,47 @@
|
|||||||
* copies of the Software, and to permit persons to whom the Software is
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
* all copies or substantial portions of the Software.
|
* copies or substantial portions of the Software.
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
* 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
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* THE SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <paths.h>
|
#include <config.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/un.h>
|
#include <syslog.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
#include "finit.h"
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "watchdog.h"
|
#include "watchdog.h"
|
||||||
|
|
||||||
int running = 1;
|
int running = 1;
|
||||||
int handover = 0;
|
int handover = 0;
|
||||||
|
int shutdown = 0;
|
||||||
static int notify(void)
|
|
||||||
{
|
|
||||||
int sd, result = 255;
|
|
||||||
ssize_t len;
|
|
||||||
struct sockaddr_un sun = {
|
|
||||||
.sun_family = AF_UNIX,
|
|
||||||
.sun_path = INIT_SOCKET,
|
|
||||||
};
|
|
||||||
struct init_request rq;
|
|
||||||
|
|
||||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
||||||
if (-1 == sd)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
rq.cmd = INIT_CMD_WDOG_HELLO;
|
|
||||||
rq.magic = INIT_MAGIC;
|
|
||||||
rq.runlevel = getpid();
|
|
||||||
len = sizeof(rq);
|
|
||||||
if (write(sd, &rq, len) != len)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
if (read(sd, &rq, len) != len)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
result = 0;
|
|
||||||
exit:
|
|
||||||
close(sd);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sighandler(int signo)
|
static void sighandler(int signo)
|
||||||
{
|
{
|
||||||
if (signo == SIGTERM)
|
if (signo == SIGTERM)
|
||||||
handover = 1;
|
handover = 1;
|
||||||
|
if (signo == SIGPWR)
|
||||||
|
shutdown = 1;
|
||||||
|
|
||||||
running = 0;
|
running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int init(char *progname, char *devnode, int timeout)
|
static int init(char *progname, char *devnode)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
@@ -84,49 +57,68 @@ static int init(char *progname, char *devnode, int timeout)
|
|||||||
signal(SIGTERM, sighandler);
|
signal(SIGTERM, sighandler);
|
||||||
signal(SIGPWR, sighandler);
|
signal(SIGPWR, sighandler);
|
||||||
|
|
||||||
|
openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON);
|
||||||
|
syslog(LOG_INFO, "Finit v%s basic watchdogd starting ...", VERSION);
|
||||||
|
|
||||||
fd = open(devnode, O_WRONLY);
|
fd = open(devnode, O_WRONLY);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int loop(int fd)
|
static int loop(int fd, int timeout)
|
||||||
{
|
{
|
||||||
int done = 0;
|
int dummy = 0;
|
||||||
|
int period = timeout / 2;
|
||||||
|
|
||||||
|
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
do_sleep(1);
|
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||||
ioctl(fd, WDIOC_KEEPALIVE, &done);
|
sleep(period);
|
||||||
|
|
||||||
if (!done && !notify())
|
|
||||||
done = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handover)
|
/* System is going down, prepare to reboot system on TERM */
|
||||||
|
if (shutdown) {
|
||||||
|
timeout /= 3;
|
||||||
|
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* External watchdogd wants to take over ... */
|
||||||
|
if (handover) {
|
||||||
|
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
|
||||||
|
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||||
return !write(fd, "V", 1);
|
return !write(fd, "V", 1);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void watchdog(char *progname)
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (fork() == 0) {
|
int fd, ret;
|
||||||
int fd, ret;
|
|
||||||
|
|
||||||
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT);
|
fd = init(argv[0], WDT_DEVNODE);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
|
if (ENOENT != errno)
|
||||||
_exit(1);
|
syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||||
}
|
return 1;
|
||||||
|
|
||||||
ret = loop(fd);
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
_exit(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = loop(fd, WDT_TIMEOUT);
|
||||||
|
while (!handover) {
|
||||||
|
syslog(LOG_ALERT, "System going down ...");
|
||||||
|
|
||||||
|
/* Waiting for SIGTERM ... */
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
/* Set lowest possible timeout on SIGTERM */
|
||||||
|
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+3
-3
@@ -24,12 +24,12 @@
|
|||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
#define WDT_DEVNODE "/dev/watchdog"
|
#define WDT_DEVNODE "/dev/watchdog"
|
||||||
#define WDT_TIMEOUT 3
|
#define WDT_TIMEOUT 30
|
||||||
|
|
||||||
#ifdef BUILTIN_WATCHDOG
|
#ifdef BUILTIN_WATCHDOG
|
||||||
void watchdog(char *progname);
|
int watchdog(char *progname);
|
||||||
#else
|
#else
|
||||||
#define watchdog(progname)
|
#define watchdog(progname) 0
|
||||||
#endif /* BUILTIN_WATCHDOG */
|
#endif /* BUILTIN_WATCHDOG */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user