mirror of
https://github.com/troglobit/finit.git
synced 2026-10-18 10:59:34 +00:00
Compare commits
287
Commits
3.1-rc1
...
cgroup-config
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44137806e4 | ||
|
|
0848b9b07a | ||
|
|
c64188bfc5 | ||
|
|
5e789401d4 | ||
|
|
744fdd654f | ||
|
|
f51fde4d46 | ||
|
|
85261c9acd | ||
|
|
6a1082a947 | ||
|
|
2094d2a53a | ||
|
|
d7da177742 | ||
|
|
170e5aa3fd | ||
|
|
f8ef8a1616 | ||
|
|
f5f1a55747 | ||
|
|
a36aade7f4 | ||
|
|
76fc72770e | ||
|
|
b64dbbf8d0 | ||
|
|
d371b85c47 | ||
|
|
053304012f | ||
|
|
12d14aae84 | ||
|
|
6a7f115781 | ||
|
|
423c8d41a7 | ||
|
|
f2cab53e55 | ||
|
|
33fd6f0049 | ||
|
|
e83dcafb02 | ||
|
|
9a98bff23f | ||
|
|
1a2f7d162b | ||
|
|
5579ba16b1 | ||
|
|
510b36645b | ||
|
|
67cd88cd23 | ||
|
|
a04a6eda12 | ||
|
|
94525d6aae | ||
|
|
1d14806fd8 | ||
|
|
5e6f64eec9 | ||
|
|
df22628183 | ||
|
|
4870cab898 | ||
|
|
859c216347 | ||
|
|
931565c425 | ||
|
|
3006caf670 | ||
|
|
1269f082de | ||
|
|
7d444e07a5 | ||
|
|
6ec4dce5f9 | ||
|
|
8a0edbe801 | ||
|
|
46683f8eb8 | ||
|
|
fb16abd855 | ||
|
|
060de93b5b | ||
|
|
231b887b2a | ||
|
|
dddd45e3b5 | ||
|
|
cb9f6213dc | ||
|
|
f33ed869e4 | ||
|
|
4790344fbe | ||
|
|
a40858f162 | ||
|
|
682e80fed5 | ||
|
|
cd91e2c66c | ||
|
|
470549badb | ||
|
|
a85c3628b8 | ||
|
|
53ac0ffdfb | ||
|
|
eee6ffe95c | ||
|
|
a5491b3ded | ||
|
|
a1ea8ff564 | ||
|
|
0dffa731d2 | ||
|
|
9516c28a8a | ||
|
|
fa0a4e8579 | ||
|
|
a487a30814 | ||
|
|
40db4f4f58 | ||
|
|
3a1ad67e92 | ||
|
|
43942ab513 | ||
|
|
3b2f2690e7 | ||
|
|
5684b761da | ||
|
|
59f22c7d27 | ||
|
|
f522a26125 | ||
|
|
efb1d40ffd | ||
|
|
ea591c7580 | ||
|
|
af9a06466c | ||
|
|
7ed9f2ad05 | ||
|
|
65ccc407fa | ||
|
|
7fe9f3c4b6 | ||
|
|
24299ab5d9 | ||
|
|
e9670b87b3 | ||
|
|
406339e755 | ||
|
|
dc7039b8d8 | ||
|
|
90249e50ef | ||
|
|
b8e6954c46 | ||
|
|
4585aa0fb4 | ||
|
|
f2655d14c8 | ||
|
|
6d1d51b28c | ||
|
|
b9d233d19e | ||
|
|
2ec132c75a | ||
|
|
3092d55ec5 | ||
|
|
cd791afea5 | ||
|
|
829405f966 | ||
|
|
72526d9e88 | ||
|
|
f704eab6d7 | ||
|
|
a50bfc016e | ||
|
|
396c1b491f | ||
|
|
df89cc79b9 | ||
|
|
3429901d83 | ||
|
|
dedf885b92 | ||
|
|
93a21197ab | ||
|
|
8a0fffb00d | ||
|
|
7fdb280712 | ||
|
|
0a643953c0 | ||
|
|
fd837a4242 | ||
|
|
2759992dd2 | ||
|
|
d880cd75ae | ||
|
|
29fe8cc73d | ||
|
|
2b6f00f126 | ||
|
|
86e534095d | ||
|
|
865a3a7a4a | ||
|
|
2dc7de3aa2 | ||
|
|
06a5b43005 | ||
|
|
38939cc57e | ||
|
|
a3eadb72a7 | ||
|
|
6d41725387 | ||
|
|
ceb91f746c | ||
|
|
a8802ab0d2 | ||
|
|
ad98f69560 | ||
|
|
5953090688 | ||
|
|
22a69d8cca | ||
|
|
710a3d2c8f | ||
|
|
658fb9b037 | ||
|
|
a84bb9042f | ||
|
|
eeb13dba74 | ||
|
|
3d20237959 | ||
|
|
ddf1bce642 | ||
|
|
93cd4116db | ||
|
|
1df6a91a94 | ||
|
|
c64a71ff30 | ||
|
|
b2b6fd25d8 | ||
|
|
3ca548b681 | ||
|
|
ba2ab4b446 | ||
|
|
a8183b0559 | ||
|
|
227e4fa097 | ||
|
|
3d68ebbf54 | ||
|
|
6e017025aa | ||
|
|
5f818f917e | ||
|
|
0da1deb487 | ||
|
|
0337985422 | ||
|
|
f59d41cdec | ||
|
|
1cc998eb60 | ||
|
|
8c1d84305d | ||
|
|
7e4e5127c7 | ||
|
|
3b813bca68 | ||
|
|
a1f210bc70 | ||
|
|
0ecf46d7a9 | ||
|
|
268304927d | ||
|
|
fa02b59db5 | ||
|
|
2c904b5b44 | ||
|
|
4191abc79f | ||
|
|
abb928f60b | ||
|
|
3c52118a8e | ||
|
|
5354a57313 | ||
|
|
92b1d882e5 | ||
|
|
fd7a6c5020 | ||
|
|
a61551d15f | ||
|
|
7f62a69f74 | ||
|
|
c969bda62d | ||
|
|
af41fcd35e | ||
|
|
84e9569be6 | ||
|
|
a4ece0fe3f | ||
|
|
b2f66da1f9 | ||
|
|
abbc0a1d5c | ||
|
|
37e420bd35 | ||
|
|
d72eb45d6e | ||
|
|
30c593f64e | ||
|
|
f858d94d2a | ||
|
|
992bab2f8f | ||
|
|
72c9681ffb | ||
|
|
e439b79e82 | ||
|
|
36e001d636 | ||
|
|
75aa14466f | ||
|
|
7f899b7a91 | ||
|
|
1c177fd953 | ||
|
|
617ec7c147 | ||
|
|
192d3aafe7 | ||
|
|
f11ebb0046 | ||
|
|
b5e260b9e0 | ||
|
|
45c10edad6 | ||
|
|
b60d8ce4b6 | ||
|
|
d35a8bbda9 | ||
|
|
c616270ccb | ||
|
|
890ac2f4e1 | ||
|
|
8063b1bfe1 | ||
|
|
0bcfb464ce | ||
|
|
dc5b1a5980 | ||
|
|
14555d7a7b | ||
|
|
a06127b990 | ||
|
|
3b8e7d677f | ||
|
|
097fce4170 | ||
|
|
306bf0f191 | ||
|
|
f25390004b | ||
|
|
cbd4cc8d34 | ||
|
|
44591c6e51 | ||
|
|
0e51a1eed8 | ||
|
|
c315fb5637 | ||
|
|
1017e7b8e5 | ||
|
|
8ef216af62 | ||
|
|
a9b9631098 | ||
|
|
cc186334e0 | ||
|
|
e1b87d707f | ||
|
|
68e7bcba4c | ||
|
|
f9783b4b41 | ||
|
|
32d9afc7bb | ||
|
|
91b550662e | ||
|
|
d87af8270e | ||
|
|
366c23f5d1 | ||
|
|
0d4bc69584 | ||
|
|
cccf199280 | ||
|
|
a1c33dec21 | ||
|
|
df303c6243 | ||
|
|
6a6ae6fa3b | ||
|
|
4011b5a3b8 | ||
|
|
68ef6ec108 | ||
|
|
89a9d92d6c | ||
|
|
15cb576293 | ||
|
|
90734eee16 | ||
|
|
71915e0e14 | ||
|
|
112ab2c22f | ||
|
|
8f00ac2e90 | ||
|
|
6fb070c75c | ||
|
|
8fd431f3e9 | ||
|
|
50f128cd1d | ||
|
|
f6fa576270 | ||
|
|
b26d30757d | ||
|
|
dc9d0b6fca | ||
|
|
ff95ca92b1 | ||
|
|
c939aed87f | ||
|
|
71ea9e2ea3 | ||
|
|
b6bd10d960 | ||
|
|
87d8157175 | ||
|
|
cf6faa9eeb | ||
|
|
80a71aff07 | ||
|
|
04a4653e9b | ||
|
|
3aa9d9f451 | ||
|
|
c1afca9c33 | ||
|
|
205915d42f | ||
|
|
631f89b58c | ||
|
|
6d1c245985 | ||
|
|
fc08b0c65b | ||
|
|
83021b11d8 | ||
|
|
5309ece2f9 | ||
|
|
6480850ee2 | ||
|
|
eaa82377c4 | ||
|
|
5b3df3654c | ||
|
|
55cb50ef90 | ||
|
|
3f7e03e813 | ||
|
|
ab723d2f31 | ||
|
|
bc149d95fc | ||
|
|
c605874b26 | ||
|
|
c33ba6a477 | ||
|
|
63199d368f | ||
|
|
db3263c441 | ||
|
|
925212f7cf | ||
|
|
ad401280c2 | ||
|
|
f3d2586417 | ||
|
|
6fc1d27bcf | ||
|
|
f3305e75b7 | ||
|
|
e4c12314b1 | ||
|
|
c9a1a64ce5 | ||
|
|
3843f62aa8 | ||
|
|
ef54c4826b | ||
|
|
2fac65becb | ||
|
|
46b253dea6 | ||
|
|
b04983c3f3 | ||
|
|
f2d142ef57 | ||
|
|
33ce746e58 | ||
|
|
6ecb86b4a7 | ||
|
|
338810b886 | ||
|
|
c1344e89ff | ||
|
|
bc35b42bd2 | ||
|
|
dcf8fbe235 | ||
|
|
c82a2ce215 | ||
|
|
e1bb5e3396 | ||
|
|
8198582c10 | ||
|
|
0d19842aa1 | ||
|
|
6f665ab283 | ||
|
|
3f731aa5a3 | ||
|
|
9e8a01669e | ||
|
|
cbafbaea1d | ||
|
|
f534368b33 | ||
|
|
78fc9fccc5 | ||
|
|
3f7df3d35e | ||
|
|
9fc33418f9 | ||
|
|
72bc3b1a91 | ||
|
|
d6ea1c0cab | ||
|
|
523a270f22 | ||
|
|
3258edc246 | ||
|
|
32b7a14cb3 |
No files matched your search
+8
-1
@@ -2,13 +2,15 @@
|
|||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
*.gz
|
*.gz
|
||||||
|
ID
|
||||||
GPATH
|
GPATH
|
||||||
GRTAGS
|
GRTAGS
|
||||||
GSYMS
|
GSYMS
|
||||||
GTAGS
|
GTAGS
|
||||||
|
|
||||||
# GNU configure and build system
|
# GNU configure and build system
|
||||||
/autom4te.cache/*
|
/.deps
|
||||||
|
/autom4te.cache
|
||||||
/aclocal.m4
|
/aclocal.m4
|
||||||
/ar-lib
|
/ar-lib
|
||||||
/compile
|
/compile
|
||||||
@@ -33,3 +35,8 @@ GTAGS
|
|||||||
/.config
|
/.config
|
||||||
/.stamp
|
/.stamp
|
||||||
/.unpacked
|
/.unpacked
|
||||||
|
|
||||||
|
# WeOS build system
|
||||||
|
/.autoconf
|
||||||
|
/.automake
|
||||||
|
/.built
|
||||||
+9
-6
@@ -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/v2.1.0/libuev-2.1.0.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/v2.0.1/libite-2.0.1.tar.xz
|
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
|
||||||
- tar xf libuev-2.1.0.tar.xz
|
- tar xf libuev-2.2.0.tar.xz
|
||||||
- tar xf libite-2.0.1.tar.xz
|
- tar xf libite-2.0.1.tar.xz
|
||||||
- (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip)
|
- (cd libuev-2.2.0 && ./configure --prefix=/tmp && make && make install-strip)
|
||||||
- (cd libite-2.0.1 && ./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>
|
||||||
|
|
||||||
+119
-4
@@ -4,17 +4,105 @@ Change Log
|
|||||||
All relevant changes are documented in this file.
|
All relevant changes are documented in this file.
|
||||||
|
|
||||||
|
|
||||||
[3.1][] - 2017-12-xx
|
[3.2][UNRELEASED] - 2018-10-xx
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Bug fix release.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
* Add support for `--disable-docs` and `--disable-contrib` to speed up
|
||||||
|
builds and work around issue with massively parallel builds
|
||||||
|
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
||||||
|
* Prefer udev to handle `/dev/` if mdev is also available
|
||||||
|
* Redirect dbus daemon output to syslog
|
||||||
|
* Set `$SHELL`, like `$PATH`, to a sane default value, needed by BusyBox
|
||||||
|
* Finit no longer automatically reloads its `*.conf` files after running
|
||||||
|
`/etc/rc.local` or run-parts. Use `initctl reload` instead.
|
||||||
|
* `initctl` without an argument or option now defaults to list services
|
||||||
|
* Converted built-in watchdog daemon to standalone mini watchdogd
|
||||||
|
* Improved watchdog hand-over, now based on `svc_t` and not PID
|
||||||
|
* Extended bootstrap, runlevel S, timeout: 10 --> 120 sec. before services
|
||||||
|
not allowed in the runtime runlevel are unconditionally stopped
|
||||||
|
* Removed `HOOK_SVC_START` and `HOOK_SVC_LOST`, caused more problems
|
||||||
|
than they were worth. Users are encouraged to use accounting instead
|
||||||
|
* Skip displaying "Restarting ..." progress for bootstrap processes
|
||||||
|
* Added a simple work queue mechanism to queue up work at boot + runtime
|
||||||
|
- Postpone deletion of `svc_t` until any `SIGKILL` timer has elapsed
|
||||||
|
- As long as a stepped service changes state we queue another step all
|
||||||
|
event, because services may depend on each other
|
||||||
|
* Require new libuEv API: `uev_init1()` to reduce event cache so that
|
||||||
|
the kernel can invalidate deleted events before enqueing to userspace
|
||||||
|
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
|
||||||
|
from the `hwclock` tool. Note: the kernel can also be set to load
|
||||||
|
and store RTC to/from system clock at boot/halt as well.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix #96: Start udevd as a proper service
|
||||||
|
* Ensure we track run commands as well as task/service, once per runlevel
|
||||||
|
* Fix #98: FTBFS with `--disable-inetd`
|
||||||
|
* Make sure to unblock UDP inetd services when connection terminates.
|
||||||
|
Regression introduced in v3.1
|
||||||
|
* Ensure run/tasks also go to stopping state on exit, like services,
|
||||||
|
otherwise it is unnecessarily hard to restart them
|
||||||
|
* Fix #99: Do not try to `SIGKILL` inetd services, they are not backed
|
||||||
|
by a PID. This caused a use after free issue crashing finit. Found
|
||||||
|
and fixed by Tobias Waldekranz, Westermo
|
||||||
|
* Fix missing OS/Finit title bug, adds leading newline before banner
|
||||||
|
* Remove "Failed connecting to watchdog ..." error message on systems
|
||||||
|
that do not have a watchdog
|
||||||
|
* Fix #100: Early condition handling may not work if `/var/run` does
|
||||||
|
not yet exist (symlink to `/run`). Added compat layer for access
|
||||||
|
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY
|
||||||
|
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
|
||||||
|
* Fix #106: Don't mark inetd connections for deletion at .conf reload.
|
||||||
|
Fixed by Jonas Johansson, Westermo
|
||||||
|
* Fix #107: Stop spawned inetd conncections when stopping inetd service.
|
||||||
|
Fixed by Jonas Johansson, Westermo
|
||||||
|
* Fix #111: Only restart inetd services when necessary. E.g., if the
|
||||||
|
listening interface is changed. Only stop established connections
|
||||||
|
which are no longer allowed, i.e. do not touch already allowed
|
||||||
|
established connections. Fixed by Jonas Johansson, Westermo
|
||||||
|
* Fix: update inetd service args on config change. Found and fixed by
|
||||||
|
Petrus Hellgren, Westermo
|
||||||
|
* Fix `initctl [start | restart]`, should behave the same for services
|
||||||
|
that have crashed. Found by Mattias Walström, Westermo
|
||||||
|
* Wait for bootstrap phase to complete before cleaning out any bootstrap
|
||||||
|
processes that have stopped, they may be restarted again
|
||||||
|
* Reassert condition when an unmodified run/task/service goes from
|
||||||
|
WAITING back to RUNNING again after a reconfiguration event.
|
||||||
|
Found and fixed by Jonas Johansson, Westermo
|
||||||
|
* Remove service condition when service is deleted
|
||||||
|
|
||||||
|
|
||||||
|
[3.1][] - 2018-01-23
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
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
|
### 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 `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
|
* Support for auto-detecting serial console using Linux SysFS, the new
|
||||||
`tty @console` eliminates the need to keep track of different console
|
`tty @console` eliminates the need to keep track of different console
|
||||||
devices across embedded platforms: `/dev/ttyS0`, `/dev/ttyAMA0`, etc.
|
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
|
* Support for calling run/tasks on Finit internal HOOK points, issue #18
|
||||||
* Removed support for long-since deprecated `console DEV` setting
|
* Removed support for long-since deprecated `console DEV` setting
|
||||||
* Cosmetic change to login, pressing enter at the `Press enter to ...`
|
* Cosmetic change to login, pressing enter at the `Press enter to ...`
|
||||||
@@ -25,6 +113,9 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
|||||||
of inetd services, and `Loading configuration ...`. No end user knows
|
of inetd services, and `Loading configuration ...`. No end user knows
|
||||||
what those plugins and configurations are, i.e. internal state+config
|
what those plugins and configurations are, i.e. internal state+config
|
||||||
* Change kernel WDT timeout (3 --> 30 sec) for built-in watchdog daemon
|
* 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`
|
* Handle `/etc/` OverlayFS, reload /etc/finit.d/*.conf after `mount -a`
|
||||||
* initctl: Add support for printing previous runlevel
|
* initctl: Add support for printing previous runlevel
|
||||||
* initctl: Support short forms of all commands
|
* initctl: Support short forms of all commands
|
||||||
@@ -38,6 +129,25 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
|||||||
on mtime of `.conf` files, instead an inotify handler tracks file
|
on mtime of `.conf` files, instead an inotify handler tracks file
|
||||||
changes for time *insensitive* dependency tracking
|
changes for time *insensitive* dependency tracking
|
||||||
* Change condition handling to not rely on mtime but a generation id.
|
* 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
|
### Fixes
|
||||||
|
|
||||||
@@ -57,6 +167,8 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
|||||||
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
|
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
|
||||||
* Handle event loop failure modes, issue found by Westermo
|
* Handle event loop failure modes, issue found by Westermo
|
||||||
* Handle API socket errors more gracefully, restart socket
|
* 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
|
* Fix regression in condition handling, reconf condition must be kept
|
||||||
as a reference point to previous reconfiguration, or bootstrap.
|
as a reference point to previous reconfiguration, or bootstrap.
|
||||||
* Fix nasty race condition with internal service stop, abort kill if the
|
* Fix nasty race condition with internal service stop, abort kill if the
|
||||||
@@ -70,6 +182,7 @@ Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
|||||||
* Fix issue #81 properly, remove use of SYSV shm IPC completely. Finit
|
* 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
|
now use the API socket for all communication between PID 1 and initctl
|
||||||
* Fix segfault on x86_64 when started with kernel cmdline `--debug`
|
* 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
|
||||||
@@ -97,7 +210,7 @@ distributions.
|
|||||||
* Ensure plugins always have a default name, file name
|
* Ensure plugins always have a default name, file name
|
||||||
* Reorganization, move all source files to a `src/` sub-directory
|
* Reorganization, move all source files to a `src/` sub-directory
|
||||||
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
|
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
|
||||||
distributions. See [doc/distro.md](doc/distro.md) for details
|
distributions. See [docs/distro.md](docs/distro.md) for details
|
||||||
* Remove `UNUSED()` macro, mentioned here because it may have been used
|
* Remove `UNUSED()` macro, mentioned here because it may have been used
|
||||||
by external plugin developers. Set `-Wno-unused-parameter` instead
|
by external plugin developers. Set `-Wno-unused-parameter` instead
|
||||||
* New table headings in `initctl`, using `top` style inverted text
|
* New table headings in `initctl`, using `top` style inverted text
|
||||||
@@ -667,7 +780,9 @@ Major bug fix release.
|
|||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.0...HEAD
|
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
|
||||||
|
[3.2]: https://github.com/troglobit/finit/compare/3.1...3.2
|
||||||
|
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
|
||||||
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
|
||||||
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
|
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
|
||||||
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
|
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
|
||||||
|
|||||||
+8
-1
@@ -3,12 +3,19 @@ SUBDIRS = plugins src
|
|||||||
doc_DATA = README.md LICENSE contrib/finit.conf
|
doc_DATA = README.md LICENSE contrib/finit.conf
|
||||||
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
|
||||||
|
|
||||||
|
if CONTRIB
|
||||||
|
SUBDIRS += contrib
|
||||||
|
endif
|
||||||
|
if DOCS
|
||||||
|
SUBDIRS += docs
|
||||||
|
endif
|
||||||
|
|
||||||
# Compat hook
|
# Compat hook
|
||||||
install-dev:
|
install-dev:
|
||||||
@make -C src install-pkgincludeHEADERS
|
@make -C src install-pkgincludeHEADERS
|
||||||
|
|
||||||
# Target to run when building a release
|
# Target to run when building a release
|
||||||
release: dist
|
release: distcheck
|
||||||
@for file in $(DIST_ARCHIVES); do \
|
@for file in $(DIST_ARCHIVES); do \
|
||||||
md5sum $$file > ../$$file.md5; \
|
md5sum $$file > ../$$file.md5; \
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
<img align="right" src="images/finit3.png" alt="Finit: Fast Init">
|
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
* [Features](#features)
|
* [Features](#features)
|
||||||
* [Bootstrap](doc/bootstrap.md#bootstrap)
|
* [Bootstrap](docs/bootstrap.md#bootstrap)
|
||||||
* [Runlevels](#runlevels)
|
* [Runlevels](#runlevels)
|
||||||
* [Syntax](doc/config.md#syntax)
|
* [Syntax](docs/config.md#syntax)
|
||||||
* [Inetd](doc/inetd.md#inetd)
|
* [Inetd](docs/inetd.md#inetd)
|
||||||
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
|
||||||
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
|
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
|
||||||
* [Rebooting & Halting](#rebooting--halting)
|
* [Rebooting & Halting](#rebooting--halting)
|
||||||
* [Commands & Status](#commands--status)
|
* [Commands & Status](#commands--status)
|
||||||
* [Building](doc/build.md#building)
|
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
|
||||||
* [Running](doc/build.md#running)
|
* [Building](docs/build.md#building)
|
||||||
* [Debugging](doc/build.md#debugging)
|
* [Running](docs/build.md#running)
|
||||||
|
* [Recovery](docs/build.md#recovery)
|
||||||
|
* [Debugging](docs/build.md#debugging)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Origin & References](#origin--references)
|
* [Origin & References](#origin--references)
|
||||||
|
|
||||||
@@ -27,10 +29,13 @@ was reverse engineered from the [EeePC fastinit][] ten years ago by
|
|||||||
|
|
||||||
Focus is on small and embedded systems, although Finit is fully usable
|
Focus is on small and embedded systems, although Finit is fully usable
|
||||||
on server and desktop systems as well. For working examples, see the
|
on server and desktop systems as well. For working examples, see the
|
||||||
[contrib/](contrib/) section where both [Debian](contrib/debian/) and
|
[contrib/](contrib/) section with tutorials for:
|
||||||
up to date [Alpine Linux](contrib/alpine/) tutorials can be found.
|
|
||||||
|
|
||||||

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

|
||||||
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
||||||
|
|
||||||
|
|
||||||
@@ -49,12 +54,23 @@ host default
|
|||||||
# Runlevel to start after bootstrap, 'S', default: 2
|
# Runlevel to start after bootstrap, 'S', default: 2
|
||||||
runlevel 2
|
runlevel 2
|
||||||
|
|
||||||
|
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||||
|
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||||
|
log size=100k count=4
|
||||||
|
|
||||||
# Services to be monitored and respawned as needed
|
# Services to be monitored and respawned as needed
|
||||||
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
|
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
|
||||||
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
|
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
|
||||||
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
service [S12345] /sbin/klogd -n -- Kernel log daemon
|
||||||
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
||||||
|
|
||||||
|
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||||
|
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||||
|
# log file is rotated with the above settings. The condition declares
|
||||||
|
# a dependency on a system default route (gateway) to be set. A single
|
||||||
|
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||||
|
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
|
||||||
|
|
||||||
# For multiple instances of the same service, add :ID somewhere between
|
# For multiple instances of the same service, add :ID somewhere between
|
||||||
# the service/run/task keyword and the command.
|
# the service/run/task keyword and the command.
|
||||||
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
|
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
|
||||||
@@ -87,10 +103,13 @@ tty [12345] /sbin/getty -L 115200 /dev/tty3 linux
|
|||||||
# Use built-in getty for serial port and USB serial
|
# Use built-in getty for serial port and USB serial
|
||||||
tty [12345] /dev/ttyAMA0 115200 noclear nowait
|
tty [12345] /dev/ttyAMA0 115200 noclear nowait
|
||||||
tty [12345] /dev/ttyUSB0 115200 noclear
|
tty [12345] /dev/ttyUSB0 115200 noclear
|
||||||
|
|
||||||
|
# Just give me a shell, I need to debug this embedded system!
|
||||||
|
tty [12345] @console noclear nologin
|
||||||
```
|
```
|
||||||
|
|
||||||
The `service` stanza, as well as `task`, `run`, `inetd` and others are
|
The `service` stanza, as well as `task`, `run`, `inetd` and others are
|
||||||
described in full in [doc/config.md](doc/config.md). Here's a quick
|
described in full in [docs/config.md](docs/config.md). Here's a quick
|
||||||
overview of some of the most common components needed to start a UNIX
|
overview of some of the most common components needed to start a UNIX
|
||||||
daemon:
|
daemon:
|
||||||
|
|
||||||
@@ -111,7 +130,7 @@ advanced uses as well:
|
|||||||
|
|
||||||
service /usr/sbin/sshd -D
|
service /usr/sbin/sshd -D
|
||||||
|
|
||||||
Dependencies are handled using [conditions](doc/conditions.md). One 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:
|
||||||
|
|
||||||
@@ -152,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.
|
||||||
|
|
||||||
@@ -167,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**
|
||||||
@@ -192,7 +211,9 @@ figures it out by querying sysfs: `/sys/class/tty/console/active`.
|
|||||||
tty [12345] @console 115200 linux noclear
|
tty [12345] @console 115200 linux noclear
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, see [doc/config.md](doc/config.md#syntax).
|
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**
|
||||||
@@ -201,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**
|
||||||
@@ -229,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
|
||||||
@@ -325,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,
|
||||||
@@ -373,6 +426,7 @@ Commands:
|
|||||||
list List all .conf in /etc/finit.d/
|
list List all .conf in /etc/finit.d/
|
||||||
enable <CONF> Enable .conf in /etc/finit.d/available/
|
enable <CONF> Enable .conf in /etc/finit.d/available/
|
||||||
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
|
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)
|
reload Reload *.conf in /etc/finit.d/ (activates changes)
|
||||||
|
|
||||||
cond set <COND> Set (assert) condition => +COND
|
cond set <COND> Set (assert) condition => +COND
|
||||||
@@ -384,6 +438,7 @@ Commands:
|
|||||||
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
|
||||||
|
status <JOB|NAME>[:ID] Show service status, by job# or name
|
||||||
status | show Show status of services, default command
|
status | show Show status of services, default command
|
||||||
|
|
||||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||||
@@ -396,7 +451,7 @@ Commands:
|
|||||||
|
|
||||||
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
|
||||||
@@ -422,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
-9
@@ -1,10 +1,17 @@
|
|||||||
AC_INIT(Finit, 3.1-rc1, 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,7 +41,7 @@ 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 >= 2.1.0])
|
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
||||||
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
|
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
|
||||||
|
|
||||||
# Check for configured Finit features
|
# Check for configured Finit features
|
||||||
@@ -58,6 +66,10 @@ AC_ARG_ENABLE(watchdog,
|
|||||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
||||||
enable_watchdog=no])
|
enable_watchdog=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(redirect,
|
||||||
|
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
|
||||||
|
enable_redirect_output=no])
|
||||||
|
|
||||||
AC_ARG_ENABLE(inetd,
|
AC_ARG_ENABLE(inetd,
|
||||||
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
|
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
|
||||||
enable_inetd=yes])
|
enable_inetd=yes])
|
||||||
@@ -66,6 +78,14 @@ AC_ARG_ENABLE(logrotate,
|
|||||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
|
||||||
enable_logrotate=yes])
|
enable_logrotate=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(docs,
|
||||||
|
AS_HELP_STRING([--disable-docs], [Disable build and install of docs section]),,[
|
||||||
|
enable_docs=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(contrib,
|
||||||
|
AS_HELP_STRING([--disable-contrib], [Disable build and install of contrib section]),,[
|
||||||
|
enable_contrib=yes])
|
||||||
|
|
||||||
# Check for extra plugins to enable
|
# Check for extra plugins to enable
|
||||||
enable_all_plugins=auto
|
enable_all_plugins=auto
|
||||||
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
|
||||||
@@ -75,7 +95,7 @@ AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864])
|
|||||||
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
|
AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867])
|
||||||
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
|
AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863])
|
||||||
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
|
AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868])
|
||||||
AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!])
|
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
|
||||||
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
|
||||||
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
|
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
|
||||||
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
|
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
|
||||||
@@ -89,11 +109,6 @@ AC_ARG_WITH(fifo,
|
|||||||
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
|
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
|
||||||
[fifo=$withval], [fifo=$localstatedir/run/initctl])
|
[fifo=$withval], [fifo=$localstatedir/run/initctl])
|
||||||
|
|
||||||
AC_ARG_WITH(plugindir,
|
|
||||||
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
|
|
||||||
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
|
|
||||||
AC_SUBST([pkglibdir])
|
|
||||||
|
|
||||||
AC_ARG_WITH(config,
|
AC_ARG_WITH(config,
|
||||||
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
|
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
|
||||||
[conf=$withval], [conf=$sysconfdir/finit.conf])
|
[conf=$withval], [conf=$sysconfdir/finit.conf])
|
||||||
@@ -143,6 +158,9 @@ AS_IF([test "x$enable_progress" = "xyes"], [
|
|||||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
||||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
||||||
|
|
||||||
|
AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||||
|
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||||
|
|
||||||
### Disable features ###########################################################################
|
### Disable features ###########################################################################
|
||||||
AS_IF([test "x$enable_inetd" != "xno"], [
|
AS_IF([test "x$enable_inetd" != "xno"], [
|
||||||
enable_inetd="yes"
|
enable_inetd="yes"
|
||||||
@@ -206,6 +224,21 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
|||||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||||
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
|
||||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
||||||
|
AM_CONDITIONAL(DOCS, [test "x$enable_docs" = "xyes"])
|
||||||
|
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||||
|
|
||||||
|
# Override default libdir, used for plugins and rescue.conf
|
||||||
|
pkglibdir=$libdir/finit
|
||||||
|
AC_SUBST(pkglibexecdir)
|
||||||
|
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
|
||||||
|
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
|
||||||
|
AC_SUBST(plugin_path)
|
||||||
|
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
|
||||||
|
|
||||||
|
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
|
||||||
|
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
|
||||||
|
|
||||||
# Generate all files
|
# Generate all files
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
@@ -220,6 +253,7 @@ cat <<EOF
|
|||||||
FIFO path.............: $fifo_path
|
FIFO path.............: $fifo_path
|
||||||
Finit config file.....: $conf_path
|
Finit config file.....: $conf_path
|
||||||
Finit config.d path...: $rcsd_path
|
Finit config.d path...: $rcsd_path
|
||||||
|
Finit plugin path.....: $plugin_path
|
||||||
Compat rc.local path..: $rclocal_path
|
Compat rc.local path..: $rclocal_path
|
||||||
Random seed path......: $random_path
|
Random seed path......: $random_path
|
||||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||||
@@ -230,6 +264,8 @@ Behavior:
|
|||||||
Plugins...............: $plugins
|
Plugins...............: $plugins
|
||||||
|
|
||||||
Optional features:
|
Optional features:
|
||||||
|
Install docs/.........: $enable_docs
|
||||||
|
Install contrib/......: $enable_contrib
|
||||||
Built-in inetd........: $enable_inetd
|
Built-in inetd........: $enable_inetd
|
||||||
Built-in watchdogd....: $enable_watchdog
|
Built-in watchdogd....: $enable_watchdog
|
||||||
Built-in logrotate....: $enable_logrotate
|
Built-in logrotate....: $enable_logrotate
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
SUBDIRS = alpine debian void
|
||||||
|
bin_SCRIPT = service
|
||||||
|
docsdir := @docdir@/contrib
|
||||||
|
docs_DATA = README.md finit.conf procmon.sh
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
SUBDIRS = finit.d
|
||||||
|
docsdir := @docdir@/contrib/alpine
|
||||||
|
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
SUBDIRS = available
|
||||||
|
docsdir := @docdir@/contrib/alpine/finit.d
|
||||||
|
d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
|
||||||
|
syslogd.conf
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ -L $$lnk ]; then \
|
||||||
|
echo "Skipping $$lnk exists."; \
|
||||||
|
else \
|
||||||
|
$(LN_S) $$src $$lnk; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
|
$(RM) $$lnk; \
|
||||||
|
done
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||||
|
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||||
|
lxdm.conf modules.conf ntpd.conf klogd.conf syslogd.conf
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SUBDIRS = finit.d grub.d
|
||||||
|
docsdir := @docdir@/contrib/debian
|
||||||
|
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
SUBDIRS = available
|
||||||
|
docsdir := @docdir@/contrib/debian/finit.d
|
||||||
|
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
|
||||||
|
modules.conf rsyslogd.conf sshd.conf
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ -L $$lnk ]; then \
|
||||||
|
echo "Skipping $$lnk exists."; \
|
||||||
|
else \
|
||||||
|
$(LN_S) $$src $$lnk; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
|
$(RM) $$lnk; \
|
||||||
|
done
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
docsdir := @docdir@/contrib/debian/finit.d/available
|
||||||
|
docs_DATA = acpid.conf console-setup.conf getty.conf \
|
||||||
|
keyboard-setup.conf lightdm.conf modules.conf \
|
||||||
|
ntpclient.conf rsyslogd.conf sshd.conf xdm.conf
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
docsdir := @docdir@/contrib/debian/grub.d
|
||||||
|
docs_DATA = 40_custom
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -17,6 +17,10 @@ runlevel 2
|
|||||||
# Network bringup script
|
# Network bringup script
|
||||||
network service networking start
|
network service networking start
|
||||||
|
|
||||||
|
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||||
|
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||||
|
log size=100k count=4
|
||||||
|
|
||||||
# Virtual consoles to start getty on
|
# Virtual consoles to start getty on
|
||||||
tty /dev/tty1
|
tty /dev/tty1
|
||||||
tty /dev/tty2
|
tty /dev/tty2
|
||||||
@@ -33,6 +37,13 @@ service [2345] /sbin/klogd -n -- Kernel logging server
|
|||||||
service [2345] /sbin/syslogd -n -- Syslog server
|
service [2345] /sbin/syslogd -n -- Syslog server
|
||||||
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
||||||
|
|
||||||
|
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||||
|
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||||
|
# log file is rotated with the above settings. The condition declares
|
||||||
|
# a dependency on a system default route (gateway) to be set. A single
|
||||||
|
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||||
|
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
|
||||||
|
|
||||||
# Run start scripts from this directory
|
# Run start scripts from this directory
|
||||||
# runparts /etc/start.d
|
# runparts /etc/start.d
|
||||||
|
|
||||||
|
|||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
USAGE="Usage: $0 processName"
|
||||||
|
LOG_FILE="/tmp/memusage.csv"
|
||||||
|
ELAPSED_TIME=0
|
||||||
|
PERIOD=1 # seconds
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo $USAGE
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "ElapsedTime,VmSize,VmRSS" > $LOG_FILE
|
||||||
|
|
||||||
|
# Monitor memory usage forever until script is killed
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
SUM_VM_SIZE=0
|
||||||
|
SUM_RSS_SIZE=0
|
||||||
|
# In case the monitored process has not yet started
|
||||||
|
# keep searching until its PID is found
|
||||||
|
PROCESS_PIDS=""
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
PROCESS_PIDS=`/bin/pidof $1`
|
||||||
|
if [ "$PROCESS_PIDS.X" != ".X" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for PID in ${PROCESS_PIDS} ; do
|
||||||
|
VM_SIZE=`awk '/VmSize/ {print $2}' < /proc/$PID/status`
|
||||||
|
if [ "$VM_SIZE.X" = ".X" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
#echo exprVM_ $SUM_VM_SIZE + $VM_SIZE
|
||||||
|
SUM_VM_SIZE=`expr $SUM_VM_SIZE + $VM_SIZE`
|
||||||
|
|
||||||
|
VM_RSS=`awk '/VmRSS/ {print $2}' < /proc/$PID/status`
|
||||||
|
if [ "$VM_RSS.X" = ".X" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
SUM_RSS_SIZE=`expr $SUM_RSS_SIZE + $VM_RSS`
|
||||||
|
done
|
||||||
|
echo "$ELAPSED_TIME sec, $SUM_VM_SIZE KB, $SUM_RSS_SIZE KB"
|
||||||
|
echo "$ELAPSED_TIME,$SUM_VM_SIZE,$SUM_RSS_SIZE" >> $LOG_FILE
|
||||||
|
sleep $PERIOD
|
||||||
|
VM_SIZE=""
|
||||||
|
VM_RSS=""
|
||||||
|
# Needs to get actual elapsed time instead of doing this
|
||||||
|
ELAPSED_TIME=`expr $ELAPSED_TIME + $PERIOD`
|
||||||
|
done
|
||||||
Executable
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
JOB=$1
|
||||||
|
CMD=$2
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: service [job# | name] <stop | start | restart | status | show>"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
initctl show
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $CMD in
|
||||||
|
stop)
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
;;
|
||||||
|
show)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
initctl $2 $1
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SUBDIRS = finit.d
|
||||||
|
docsdir := @docdir@/contrib/void
|
||||||
|
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
SUBDIRS = available
|
||||||
|
docsdir := @docdir@/contrib/void/finit.d
|
||||||
|
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ -L $$lnk ]; then \
|
||||||
|
echo "Skipping $$lnk exists."; \
|
||||||
|
else \
|
||||||
|
$(LN_S) $$src $$lnk; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@for file in $(d_LINKS); do \
|
||||||
|
lnk=$(DESTDIR)$(docsdir)/$$file; \
|
||||||
|
src=available/$$file; \
|
||||||
|
if [ "`readlink $$lnk`" != "$$src" ]; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
|
$(RM) $$lnk; \
|
||||||
|
done
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
docsdir := @docdir@/contrib/void/finit.d/available
|
||||||
|
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
||||||
|
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
|
||||||
|
sshd.conf syslogd.conf uuidd.conf
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,4 @@
|
|||||||
|
docsdir := @docdir@/docs
|
||||||
|
docs_DATA = config.md conditions.md distro.md inetd.md plugins.md \
|
||||||
|
service.md signals.md svc-machine.png cmdline.md
|
||||||
|
EXTRA_DIST = $(docs_DATA)
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
config.md
|
||||||
+28
-36
@@ -18,27 +18,27 @@ after a reconfiguration (`initctl reload`), the Finit dependency system
|
|||||||
can also be used.
|
can also be used.
|
||||||
|
|
||||||
|
|
||||||
General
|
Near Future
|
||||||
-------
|
-----------
|
||||||
|
|
||||||
* Add support for `rescue` on the kernel command line, start rescue mode
|
* Merge UDP redirect service uredir as an UDP inetd variant
|
||||||
if enabled with `configure` script. Could mean skip `/etc/finit.conf`
|
|
||||||
et al and jump straight to a single getty login on console.
|
|
||||||
* 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
|
||||||
* Implement `initctl stop|start|restart|reload|status <SVC>` and
|
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||||
`service <SVC> stop|start|restart|reload|status` on top
|
`pimd` man pages ...
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
@@ -49,6 +49,15 @@ General
|
|||||||
* `initctl add /sbin/service -n -- Service description`, for details
|
* `initctl add /sbin/service -n -- Service description`, for details
|
||||||
see above [Solaris SMF][] featuer, issue #55 and issue #69,
|
see above [Solaris SMF][] featuer, issue #55 and issue #69,
|
||||||
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
|
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
|
||||||
|
* Changing the name of a pidfile, `pid:[/path/]file.pid`, after a
|
||||||
|
service has been started is not supported atm. The previous name will
|
||||||
|
not be (never) removed and the new name will not be created until the
|
||||||
|
process has been stopped and started again.
|
||||||
|
* PID files created in subdirectories to `/run` is not supported right
|
||||||
|
now inotify listeners should be automatically created (and removed)
|
||||||
|
when a subdirectory is added (or removed).
|
||||||
|
|
||||||
|
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
|
||||||
|
|
||||||
|
|
||||||
Init
|
Init
|
||||||
@@ -85,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
|
||||||
@@ -103,22 +114,3 @@ could support the basic functionality of the `at` command directly from
|
|||||||
the shell -- that way setting up one-time jobs would not entail
|
the shell -- that way setting up one-time jobs would not entail
|
||||||
re-reading `/etc/finit.conf`
|
re-reading `/etc/finit.conf`
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
|
||||||
`pimd` man pages ...
|
|
||||||
* Add simple, *very* simple, `finit-simple.conf` example. To illustrate
|
|
||||||
how close Finit3 still is to the original easy-to-use Finit0 :)
|
|
||||||
|
|
||||||
|
|
||||||
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
|
|
||||||
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
|
|
||||||
[Solaris SMF]: http://www.oracle.com/technetwork/articles/servers-storage-admin/intro-smf-basics-s11-1729181.html
|
|
||||||
|
|
||||||
<!--
|
|
||||||
-- Local Variables:
|
|
||||||
-- mode: markdown
|
|
||||||
-- End:
|
|
||||||
-->
|
|
||||||
@@ -5,7 +5,7 @@ Bootstrap
|
|||||||
2. Set up initial signal handlers
|
2. Set up initial signal handlers
|
||||||
3. Set up default $PATH early
|
3. Set up default $PATH early
|
||||||
4. Mount `/proc` and `/sys`
|
4. Mount `/proc` and `/sys`
|
||||||
5. Check kernel command line for `--debug` to figure out log level
|
5. Check kernel command line for `debug` to figure out log level
|
||||||
6. Load all `.so` plugins
|
6. Load all `.so` plugins
|
||||||
7. Call `fsck` on file systems listed in `/etc/fstab`
|
7. Call `fsck` on file systems listed in `/etc/fstab`
|
||||||
8. Populate `/dev` using either udev or mdev, depending on system type
|
8. Populate `/dev` using either udev or mdev, depending on system type
|
||||||
@@ -25,13 +25,13 @@ Bootstrap
|
|||||||
20. Bring up loopback interface and all `/etc/network/interfaces`, if
|
20. Bring up loopback interface and all `/etc/network/interfaces`, if
|
||||||
the `.conf` setting `network <SCRIPT>` is set, it is called instead
|
the `.conf` setting `network <SCRIPT>` is set, it is called instead
|
||||||
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
|
||||||
22. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
||||||
|
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
|
||||||
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
|
||||||
(re)loaded and new services, tasks, and blocking run commands are
|
(re)loaded and new services, tasks, and blocking run commands are
|
||||||
started. Provided they are allowed in the new runlevel and all of
|
started. Provided they are allowed in the new runlevel and all of
|
||||||
their conditions, if any, are set.
|
their conditions, if any, are set.
|
||||||
23. Call 4th level hooks, `HOOK_SVC_UP`
|
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||||
24. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
|
|
||||||
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||||
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
|
||||||
27. Start all configured TTYs, or a fallback shell on `/dev/console`
|
27. Start all configured TTYs, or a fallback shell on `/dev/console`
|
||||||
@@ -53,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.
|
||||||
@@ -124,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
|
||||||
+209
-34
@@ -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)
|
||||||
@@ -16,7 +17,7 @@ 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,111 @@ 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|unlimited>`
|
* `cgroup <NAME> cpuset:SPEC cpu:SPEC mem:SPEC`
|
||||||
Set the hard or soft limit for a resource. `RESOURCE` is a lower-case
|
Create a control group for resource limiting services using cgroups.
|
||||||
string matching the `RLIMIT_` constants from `setrlimit(2)`, without
|
Unlike `rlimit` this setting defines a common group to be selected for
|
||||||
the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
any set of run/task/services using the `group` directive (below).
|
||||||
|
|
||||||
The limit is an integer that depends on the resource being modified,
|
A cpuset for a cgroup can be the traditional list of CPU cores to
|
||||||
see the man page, or the kernel `/proc/PID/limits` file, for more
|
assign to the group, or a range that Finit will use to dynamically
|
||||||
information. Finit versions before v3.1 used `infinity` for
|
allocate cores based on the amount available. The range values can be
|
||||||
`unlimited`, which is still a supported keyword.
|
either in absolute form or percent.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup first cpuset:1
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a cgroup with one exclusive CPU:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup single cpuset:[1,1]
|
||||||
|
```
|
||||||
|
|
||||||
|
To create cgroup that uses at most two shared CPUs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup uptotwo cpuset:[,2]
|
||||||
|
```
|
||||||
|
|
||||||
|
To create cgroup with at least two shared CPUs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup dual cpuset:[2,]
|
||||||
|
```
|
||||||
|
|
||||||
|
To create cgroup with at least one CPU and at most half of all
|
||||||
|
available CPUs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cgroup greedy cpuset:[1,50%]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* `group <NAME>`
|
||||||
|
All subsequent `service`, `task`, or `run` directives are executed in the
|
||||||
|
given control group `NAME`, which must be defined prior to this line. If
|
||||||
|
`NAME` does not yet exist the default group `default` is used.
|
||||||
|
|
||||||
|
When separate Finit `.conf` files are used for services the default group
|
||||||
|
is reset for each `.conf` file read.
|
||||||
|
|
||||||
|
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
|
||||||
|
limit for a resource, or both if that argument is omitted. `RESOURCE`
|
||||||
|
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
|
||||||
|
without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||||
|
|
||||||
|
LIMIT is an integer that depends on the resource being modified, see
|
||||||
|
the man page, or the kernel `/proc/PID/limits` file, for details.
|
||||||
|
Finit versions before v3.1 used `infinity` for `unlimited`, which is
|
||||||
|
still supported, albeit deprecated.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# No process is allowed more than 8MB of address space
|
# No process is allowed more than 8MB of address space
|
||||||
@@ -67,8 +162,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 +208,42 @@ 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.
|
||||||
|
|
||||||
|
If a service should not be automatically started, it can be configured
|
||||||
|
as manual with the optional `manual` argument. The service can then be
|
||||||
|
started at any time by running `initctl start <service>`.
|
||||||
|
|
||||||
|
manual:yes
|
||||||
|
|
||||||
|
The name of a service, shown by the `initctl` tool, defaults to the
|
||||||
|
basename of the service executable. It can be changed with the
|
||||||
|
optional `name` argument:
|
||||||
|
|
||||||
|
name:<service-name>
|
||||||
|
|
||||||
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
|
* `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,22 +275,39 @@ 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
|
||||||
@@ -182,6 +334,11 @@ 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.
|
||||||
|
|
||||||
@@ -236,8 +393,23 @@ 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:console
|
||||||
|
log:null
|
||||||
|
log
|
||||||
|
|
||||||
|
Default `prio` is `daemon.info` and default `tag` is the basename of the
|
||||||
|
service or run/task command.
|
||||||
|
|
||||||
|
Log rotation is controlled using the global `log` setting.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||||
|
|
||||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
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.
|
||||||
@@ -248,31 +420,34 @@ 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`
|
||||||
File renamed without changes.
File renamed without changes.
@@ -42,13 +42,11 @@ For your convenience a set of *optional* plugins are available:
|
|||||||
|
|
||||||
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
|
* *discard.so*: RFC 863 plugin. Start as inetd service, like time below.
|
||||||
|
|
||||||
* *hwclock.so*: Restore and save system clock from/to RTC on
|
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
|
||||||
startup/shutdown.
|
|
||||||
|
|
||||||
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
* *initctl.so*: Extends finit with a traditional `initctl` functionality.
|
||||||
|
|
||||||
* *lost.so*: Very simple `HOOK_SVC_LOST` example. Logs process ID and
|
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
|
||||||
name to syslog. _Optional plugin._
|
|
||||||
|
|
||||||
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
|
||||||
interfaces. These events are then sent to the Finit service monitor
|
interfaces. These events are then sent to the Finit service monitor
|
||||||
@@ -102,19 +100,6 @@ Hooks
|
|||||||
all modified/removed services have been stopped. When the hook has
|
all modified/removed services have been stopped. When the hook has
|
||||||
completed, Finit continues to start all modified and new services.
|
completed, Finit continues to start all modified and new services.
|
||||||
|
|
||||||
* `HOOK_SVC_LOST`: Called when a process is lost. When reconfiguring
|
|
||||||
services at runtime this hook may be called a lot. However, it may be
|
|
||||||
a quite useful hook to monitor a system post bootstrap when no, or
|
|
||||||
few, services are expected to exit. A default plugin `lost.so` is
|
|
||||||
available in the `plugins/` subdirectory as an example.
|
|
||||||
|
|
||||||
**NOTE:** This hook callback gets the lost PID as argument.
|
|
||||||
|
|
||||||
* `HOOK_SVC_START`: Like `HOOK_SVC_LOST`, but called when a process is
|
|
||||||
started. Same caveats apply.
|
|
||||||
|
|
||||||
**NOTE:** This hook callback gets the new PID as argument.
|
|
||||||
|
|
||||||
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel
|
||||||
change. The hook is called when services not matching the new
|
change. The hook is called when services not matching the new
|
||||||
runlevel have been been stopped. When the hook has completed, Finit
|
runlevel have been been stopped. When the hook has completed, Finit
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
-6
@@ -1,3 +1,4 @@
|
|||||||
|
pkglibdir = $(plugin_path)
|
||||||
AM_LDFLAGS = -module -avoid-version -shared
|
AM_LDFLAGS = -module -avoid-version -shared
|
||||||
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
|
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
|
||||||
@@ -6,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
|||||||
|
|
||||||
if STATIC
|
if STATIC
|
||||||
noinst_LTLIBRARIES = libplug.la
|
noinst_LTLIBRARIES = libplug.la
|
||||||
libplug_la_SOURCES = bootmisc.c hwclock.c initctl.c pidfile.c procps.c tty.c urandom.c
|
libplug_la_SOURCES = bootmisc.c rtc.c initctl.c pidfile.c procps.c tty.c urandom.c
|
||||||
|
|
||||||
if BUILD_ALSA_UTILS_PLUGIN
|
if BUILD_ALSA_UTILS_PLUGIN
|
||||||
libplug_la_SOURCES += alsa-utils.c
|
libplug_la_SOURCES += alsa-utils.c
|
||||||
@@ -38,8 +39,8 @@ libplug_la_SOURCES += time.c
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_LOST_PLUGIN
|
if BUILD_MODULES_LOAD_PLUGIN
|
||||||
libplug_la_SOURCES += lost.c
|
libplug_la_SOURCES += modules-load.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_NETLINK_PLUGIN
|
if BUILD_NETLINK_PLUGIN
|
||||||
@@ -51,7 +52,7 @@ libplug_la_SOURCES += resolvconf.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
else
|
else
|
||||||
pkglib_LTLIBRARIES = bootmisc.la hwclock.la initctl.la pidfile.la procps.la tty.la urandom.la
|
pkglib_LTLIBRARIES = bootmisc.la rtc.la initctl.la pidfile.la procps.la tty.la urandom.la
|
||||||
|
|
||||||
if BUILD_ALSA_UTILS_PLUGIN
|
if BUILD_ALSA_UTILS_PLUGIN
|
||||||
pkglib_LTLIBRARIES += alsa-utils.la
|
pkglib_LTLIBRARIES += alsa-utils.la
|
||||||
@@ -83,8 +84,8 @@ pkglib_LTLIBRARIES += time.la
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_LOST_PLUGIN
|
if BUILD_MODULES_LOAD_PLUGIN
|
||||||
pkglib_LTLIBRARIES += lost.la
|
pkglib_LTLIBRARIES += modules-load.la
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BUILD_NETLINK_PLUGIN
|
if BUILD_NETLINK_PLUGIN
|
||||||
|
|||||||
@@ -170,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) {
|
||||||
|
|||||||
+2
-2
@@ -38,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) {
|
||||||
@@ -58,7 +58,7 @@ 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, global_rlimit, NULL))
|
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||||
_pe("Failed registering %s", DAEMON);
|
_pe("Failed registering %s", DAEMON);
|
||||||
free(cmd);
|
free(cmd);
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/* Scans /etc/modules-load.d for modules to load.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Robert Andersson <robert.m.andersson@se.atlascopco.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lite/lite.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
|
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||||
|
#define SERVICE_LINE \
|
||||||
|
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
||||||
|
|
||||||
|
static void load(void *arg)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
DIR *dirp;
|
||||||
|
struct dirent *direntp;
|
||||||
|
char line[256];
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
|
||||||
|
|
||||||
|
dirp = opendir(MODULES_LOAD_PATH);
|
||||||
|
if (!dirp) return;
|
||||||
|
|
||||||
|
while ((direntp = readdir(dirp)))
|
||||||
|
{
|
||||||
|
char module_path[PATH_MAX];
|
||||||
|
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
|
||||||
|
strlcat(module_path, direntp->d_name, sizeof(module_path));
|
||||||
|
|
||||||
|
fp = fopen(module_path, "r");
|
||||||
|
if (!fp) continue;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
char cmd[CMD_SIZE], *mod, *args;
|
||||||
|
|
||||||
|
mod = strip_line(line);
|
||||||
|
if (!*mod) continue;
|
||||||
|
|
||||||
|
mod = chomp(mod);
|
||||||
|
if (!mod || !*mod) continue;
|
||||||
|
|
||||||
|
mod = strtok_r(mod, " ", &args);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
|
||||||
|
index, mod, mod, args, mod);
|
||||||
|
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dirp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.name = __FILE__,
|
||||||
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
|
.cb = load
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+20
-23
@@ -94,6 +94,17 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void net_cond_set(char *ifname, char *cond, int set)
|
||||||
|
{
|
||||||
|
char msg[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "net/%s/%s", ifname, cond);
|
||||||
|
if (set)
|
||||||
|
cond_set(msg);
|
||||||
|
else
|
||||||
|
cond_clear(msg);
|
||||||
|
}
|
||||||
|
|
||||||
static void nl_link(struct nlmsghdr *nlmsg)
|
static void nl_link(struct nlmsghdr *nlmsg)
|
||||||
{
|
{
|
||||||
int la;
|
int la;
|
||||||
@@ -112,39 +123,25 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
|||||||
|
|
||||||
while (RTA_OK(a, la)) {
|
while (RTA_OK(a, la)) {
|
||||||
if (a->rta_type == IFLA_IFNAME) {
|
if (a->rta_type == IFLA_IFNAME) {
|
||||||
char msg[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||||
switch (nlmsg->nlmsg_type) {
|
switch (nlmsg->nlmsg_type) {
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
/*
|
/*
|
||||||
* New interface has appearad, or interface flags has changed.
|
* New interface has appeared, or interface flags has changed.
|
||||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||||
*/
|
*/
|
||||||
_d("%s: New link, ifi_change 0x%x", ifname, i->ifi_change);
|
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||||
if (i->ifi_change & IFF_UP) {
|
net_cond_set(ifname, "exist", 1);
|
||||||
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
|
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||||
|
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||||
if (i->ifi_flags & IFF_UP)
|
|
||||||
cond_set(msg);
|
|
||||||
else
|
|
||||||
cond_clear(msg);
|
|
||||||
|
|
||||||
if (string_compare("lo", ifname)) {
|
|
||||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
|
||||||
cond_set(msg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
|
||||||
cond_set(msg);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
/* NOTE: Interface has dissapeared, not link down ... */
|
/* NOTE: Interface has disappeared, not link down ... */
|
||||||
_d("%s: Delete link", ifname);
|
_d("%s: Delete link", ifname);
|
||||||
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
net_cond_set(ifname, "exist", 0);
|
||||||
cond_clear(msg);
|
net_cond_set(ifname, "up", 0);
|
||||||
|
net_cond_set(ifname, "running", 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_NEWADDR:
|
case RTM_NEWADDR:
|
||||||
|
|||||||
+42
-39
@@ -37,12 +37,6 @@ struct context {
|
|||||||
int wd;
|
int wd;
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *mkcond(char *buf, size_t len, char *nm)
|
|
||||||
{
|
|
||||||
snprintf(buf, len, "svc%s%s", nm[0] != '/' ? "/" : "", nm);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pidfile_callback(void *arg, int fd, int events)
|
static void pidfile_callback(void *arg, int fd, int events)
|
||||||
{
|
{
|
||||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
@@ -50,7 +44,6 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
|
|
||||||
struct inotify_event *ev;
|
struct inotify_event *ev;
|
||||||
ssize_t sz, len;
|
ssize_t sz, len;
|
||||||
char *basename;
|
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
@@ -60,25 +53,18 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
}
|
}
|
||||||
ev_buf[sz] = 0;
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
|
for (ev = (void *)ev_buf;
|
||||||
|
sz > (ssize_t)sizeof(*ev);
|
||||||
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||||
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
|
|
||||||
if (!ev->mask || !strstr(ev->name, ".pid"))
|
if (!ev->mask || !strstr(ev->name, ".pid"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
basename = strtok(ev->name, ".");
|
svc = svc_find_by_pidfile(ev->name);
|
||||||
if (!basename)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
svc = svc_find_by_nameid(basename, 1);
|
|
||||||
if (!svc)
|
if (!svc)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
|
|
||||||
|
|
||||||
_d("%s: match %s", basename, svc->cmd);
|
|
||||||
mkcond(cond, sizeof(cond), svc->cmd);
|
mkcond(cond, sizeof(cond), svc->cmd);
|
||||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
|
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||||
svc_started(svc);
|
svc_started(svc);
|
||||||
cond_set(cond);
|
cond_set(cond);
|
||||||
} else if (ev->mask & IN_DELETE)
|
} else if (ev->mask & IN_DELETE)
|
||||||
@@ -87,43 +73,60 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assert condition only if the service is running, but not if it's
|
* This function is called after `initctl reload` to reassert conditions
|
||||||
* recently been changed or while it's starting up.
|
* for services that have not been changed.
|
||||||
*
|
*
|
||||||
* We must wait for the service to create/touch its pidfile.
|
* We reassert the run/task/service's condition only if it is running,
|
||||||
|
* but not if it's recently been changed or while it's starting up.
|
||||||
*/
|
*/
|
||||||
static void pidfile_reconf(void *arg)
|
static void pidfile_reconf(void *arg)
|
||||||
{
|
{
|
||||||
static char cond[MAX_COND_LEN];
|
static char cond[MAX_COND_LEN];
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = 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(&iter, 1); svc; svc = svc_iterator(&iter, 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_RUNTASK | SVC_TYPE_INETD);
|
if (cond_get(cond) == COND_ON)
|
||||||
|
continue;
|
||||||
|
|
||||||
} while (restart);
|
cond_set_path(cond_path(cond), COND_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will call service_step(), which in turn will schedule itself
|
||||||
|
* as long as stepped services change state. Services going from
|
||||||
|
* WAITING to RUNNING will reassert their conditions in that loop,
|
||||||
|
* which in turn may unlock other services, and so on.
|
||||||
|
*/
|
||||||
|
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pidfile_init(void *arg)
|
static void pidfile_init(void *arg)
|
||||||
{
|
{
|
||||||
|
char *path;
|
||||||
struct context *ctx = arg;
|
struct context *ctx = arg;
|
||||||
|
uint32_t mask = IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The bootmisc plugin is responsible for setting up /var/run.
|
||||||
|
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
||||||
|
* so it's safe here to query realpath() and set up inotify.
|
||||||
|
*/
|
||||||
|
path = realpath(_PATH_VARRUN, NULL);
|
||||||
|
if (!path) {
|
||||||
|
_pe("Failed ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->wd = inotify_add_watch(ctx->fd, path, mask);
|
||||||
|
free(path);
|
||||||
|
|
||||||
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
|
|
||||||
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
|
|
||||||
if (ctx->wd < 0) {
|
if (ctx->wd < 0) {
|
||||||
_pe("inotify_add_watch()");
|
_pe("inotify_add_watch()");
|
||||||
close(ctx->fd);
|
close(ctx->fd);
|
||||||
|
|||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
/* Save and restore RTC using hwclock
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2019 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <lite/lite.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
|
/* Kernel RTC driver validates against this date for sanity check */
|
||||||
|
#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */
|
||||||
|
|
||||||
|
static int rtc_open(void)
|
||||||
|
{
|
||||||
|
char *alt[] = {
|
||||||
|
"/dev/rtc",
|
||||||
|
"/dev/rtc0",
|
||||||
|
"/dev/misc/rtc"
|
||||||
|
};
|
||||||
|
size_t i;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
for (i = 0; i < NELEMS(alt); i++) {
|
||||||
|
fd = open(alt[i], O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtc_save(void *arg)
|
||||||
|
{
|
||||||
|
struct timeval tv = { 0 };
|
||||||
|
struct tm tm = { 0 };
|
||||||
|
int fd, rc = 0;
|
||||||
|
|
||||||
|
fd = rtc_open();
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rc = gettimeofday(&tv, NULL);
|
||||||
|
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
||||||
|
print_desc(NULL, "System clock invalid, not saving to RTC");
|
||||||
|
invalid:
|
||||||
|
logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC");
|
||||||
|
rc = 2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_desc(NULL, "Saving system time (UTC) to RTC");
|
||||||
|
gmtime_r(&tv.tv_sec, &tm);
|
||||||
|
if (ioctl(fd, RTC_SET_TIME, &tm) < 0) {
|
||||||
|
if (EINVAL == errno)
|
||||||
|
goto invalid;
|
||||||
|
rc = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
print(rc, NULL);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtc_restore(void *arg)
|
||||||
|
{
|
||||||
|
struct timeval tv = { 0 };
|
||||||
|
struct tm tm = { 0 };
|
||||||
|
int fd, rc = 0;
|
||||||
|
|
||||||
|
fd = rtc_open();
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
|
||||||
|
char msg[120];
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
|
||||||
|
EINVAL == errno ? "RTC time is too old" :
|
||||||
|
ENOENT == errno ? "RTC has no saved time" : "see log for details");
|
||||||
|
print_desc(NULL, msg);
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
||||||
|
if (EINVAL == errno)
|
||||||
|
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)");
|
||||||
|
else if (ENOENT == errno)
|
||||||
|
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||||
|
else
|
||||||
|
logit(LOG_ERR, "RTC error code %d: %m", errno);
|
||||||
|
rc = 2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||||
|
tv.tv_sec = mktime(&tm);
|
||||||
|
if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settimeofday(&tv, NULL) == -1)
|
||||||
|
rc = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
print(rc, NULL);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.name = __FILE__,
|
||||||
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
|
.cb = rtc_restore
|
||||||
|
},
|
||||||
|
.hook[HOOK_SHUTDOWN] = {
|
||||||
|
.cb = rtc_save
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+5
-5
@@ -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
|
||||||
+33
-15
@@ -1,15 +1,24 @@
|
|||||||
|
EXTRA_DIST = rescue.conf
|
||||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||||
if STATIC
|
if STATIC
|
||||||
AM_CPPFLAGS += -DENABLE_STATIC
|
AM_CPPFLAGS += -DENABLE_STATIC
|
||||||
AM_LDFLAGS = -static-libtool-libs
|
AM_LDFLAGS = -static-libtool-libs
|
||||||
else
|
else
|
||||||
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
|
|
||||||
AM_LDFLAGS = -export-dynamic
|
AM_LDFLAGS = -export-dynamic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
bin_PROGRAMS = logit
|
||||||
sbin_PROGRAMS = finit initctl reboot
|
sbin_PROGRAMS = finit initctl reboot
|
||||||
finit_SOURCES = api.c \
|
pkglibexec_SCRIPTS = cgreaper.sh
|
||||||
|
if WATCHDOGD
|
||||||
|
pkglibexec_PROGRAMS = watchdogd
|
||||||
|
endif
|
||||||
|
|
||||||
|
logit_SOURCES = logit.c
|
||||||
|
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
|
|
||||||
|
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||||
cond.c cond-w.c cond.h \
|
cond.c cond-w.c cond.h \
|
||||||
telinit.c \
|
telinit.c \
|
||||||
conf.c conf.h \
|
conf.c conf.h \
|
||||||
@@ -17,8 +26,10 @@ finit_SOURCES = api.c \
|
|||||||
getty.c stty.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,15 +41,12 @@ 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
|
||||||
@@ -57,12 +65,19 @@ reboot_LDADD = $(lite_LIBS)
|
|||||||
INIT_LNKS = telinit init
|
INIT_LNKS = telinit init
|
||||||
REBOOT_LNKS = shutdown halt poweroff suspend
|
REBOOT_LNKS = shutdown halt poweroff suspend
|
||||||
|
|
||||||
|
if WATCHDOGD
|
||||||
|
watchdogd_SOURCES = watchdog.c watchdog.h
|
||||||
|
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
|
#log.c log.h
|
||||||
|
endif
|
||||||
|
|
||||||
# Hook in install to add finit and reboot symlink(s)
|
# Hook in install to add finit and reboot symlink(s)
|
||||||
install-exec-hook:
|
install-exec-hook:
|
||||||
|
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
|
||||||
@for file in $(INIT_LNKS); do \
|
@for file in $(INIT_LNKS); do \
|
||||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||||
if [ -e $$lnk ]; then \
|
if [ -e $$lnk ]; then \
|
||||||
echo "Skipping $$lnk exists already."; \
|
echo "Skipping $$lnk exists."; \
|
||||||
else \
|
else \
|
||||||
$(LN_S) finit $$lnk; \
|
$(LN_S) finit $$lnk; \
|
||||||
fi; \
|
fi; \
|
||||||
@@ -71,16 +86,17 @@ install-exec-hook:
|
|||||||
echo "Non-finit reboot, skipping install."; \
|
echo "Non-finit reboot, skipping install."; \
|
||||||
else \
|
else \
|
||||||
for file in $(REBOOT_LNKS); do \
|
for file in $(REBOOT_LNKS); do \
|
||||||
link=$(DESTDIR)$(sbindir)/$$file; \
|
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||||
if [ -e $$link ]; then \
|
if [ -e $$lnk ]; then \
|
||||||
echo "$$link exists, skipping.";\
|
echo "Skipping $$lnk exists."; \
|
||||||
else \
|
else \
|
||||||
$(LN_S) reboot $$link; \
|
$(LN_S) reboot $$lnk; \
|
||||||
fi; \
|
fi; \
|
||||||
done; \
|
done; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
uninstall-hook:
|
uninstall-hook:
|
||||||
|
@$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf
|
||||||
@for file in $(INIT_LNKS); do \
|
@for file in $(INIT_LNKS); do \
|
||||||
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
||||||
if [ "$$lnk" != "finit" ]; then \
|
if [ "$$lnk" != "finit" ]; then \
|
||||||
@@ -93,9 +109,11 @@ uninstall-hook:
|
|||||||
echo "Non-finit reboot, skipping uninstall."; \
|
echo "Non-finit reboot, skipping uninstall."; \
|
||||||
else \
|
else \
|
||||||
for file in $(REBOOT_LNKS); do \
|
for file in $(REBOOT_LNKS); do \
|
||||||
if [ "`readlink $$file`" != "reboot" ]; \
|
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||||
then continue; \
|
dst=`readlink $$lnk`; \
|
||||||
|
if [ "$$dst" != "reboot" ]; then \
|
||||||
|
continue; \
|
||||||
fi; \
|
fi; \
|
||||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
$(RM) $$lnk; \
|
||||||
done; \
|
done; \
|
||||||
fi
|
fi
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
extern svc_t *wdog;
|
||||||
static uev_t api_watcher;
|
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)
|
||||||
@@ -51,7 +52,7 @@ 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;
|
||||||
@@ -62,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;
|
||||||
@@ -73,33 +74,35 @@ 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); }
|
||||||
|
|
||||||
static char query_buf[368];
|
static char query_buf[368];
|
||||||
static int missing(char *job, int id)
|
static int missing(char *job, char *id)
|
||||||
{
|
{
|
||||||
char buf[20];
|
char buf[20];
|
||||||
char idstr[10] = "";
|
|
||||||
|
|
||||||
if (!job)
|
if (!job)
|
||||||
job = "";
|
job = "";
|
||||||
if (id > 1)
|
if (!id)
|
||||||
snprintf(idstr, sizeof(idstr), ":%d", id);
|
id = "";
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "%s%s ", job, idstr);
|
snprintf(buf, sizeof(buf), "%s:%s ", job, id);
|
||||||
strlcat(query_buf, buf, sizeof(query_buf));
|
strlcat(query_buf, buf, sizeof(query_buf));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -118,8 +121,9 @@ static int do_query(struct init_request *rq, size_t len)
|
|||||||
|
|
||||||
static svc_t *do_find(char *buf, size_t len)
|
static svc_t *do_find(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
int id = 1;
|
char *id = NULL;
|
||||||
char *ptr, *input;
|
char *ptr, *input;
|
||||||
|
svc_t *iter = NULL;
|
||||||
|
|
||||||
input = sanitize(buf, len);
|
input = sanitize(buf, len);
|
||||||
if (!input)
|
if (!input)
|
||||||
@@ -128,11 +132,28 @@ static svc_t *do_find(char *buf, size_t len)
|
|||||||
ptr = strchr(input, ':');
|
ptr = strchr(input, ':');
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
*ptr++ = 0;
|
*ptr++ = 0;
|
||||||
id = atonum(ptr);
|
id = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isdigit(input[0]))
|
if (isdigit(input[0])) {
|
||||||
return svc_find_by_jobid(atonum(input), id);
|
char *ep;
|
||||||
|
long job = 0;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
job = strtol(input, &ep, 10);
|
||||||
|
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
|
||||||
|
(errno != 0 && job == 0) ||
|
||||||
|
(input == ep))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!id)
|
||||||
|
return svc_job_iterator(&iter, 1, job);
|
||||||
|
|
||||||
|
return svc_find_by_jobid(job, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id)
|
||||||
|
return svc_named_iterator(&iter, 1, input);
|
||||||
|
|
||||||
return svc_find_by_nameid(input, id);
|
return svc_find_by_nameid(input, id);
|
||||||
}
|
}
|
||||||
@@ -337,12 +358,23 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wdogpid > 0 && wdogpid != rq.runlevel) {
|
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||||
_d("Sending SIGTERM to %d", wdogpid);
|
svc = svc_find_by_pid(rq.runlevel);
|
||||||
kill(wdogpid, SIGTERM);
|
if (!svc) {
|
||||||
do_sleep(1);
|
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
wdogpid = rq.runlevel;
|
|
||||||
|
/* Disable and allow Finit to collect bundled watchdog */
|
||||||
|
if (wdog) {
|
||||||
|
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
|
||||||
|
stop(wdog);
|
||||||
|
if (wdog->protect) {
|
||||||
|
wdog->protect = 0;
|
||||||
|
wdog->runlevels = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wdog = svc;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_SVC_ITER:
|
case INIT_CMD_SVC_ITER:
|
||||||
@@ -380,13 +412,11 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
_d("Failed sending ACK/NACK back to client");
|
_d("Failed sending ACK/NACK back to client");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UEV_ERROR == events)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
close(sd);
|
close(sd);
|
||||||
|
if (UEV_ERROR == events)
|
||||||
|
goto error;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
api_exit();
|
api_exit();
|
||||||
if (api_init(w->ctx))
|
if (api_init(w->ctx))
|
||||||
@@ -430,8 +460,9 @@ error:
|
|||||||
|
|
||||||
int api_exit(void)
|
int api_exit(void)
|
||||||
{
|
{
|
||||||
return shutdown(api_watcher.fd, SHUT_RDWR) ||
|
uev_io_stop(&api_watcher);
|
||||||
close(api_watcher.fd);
|
|
||||||
|
return close(api_watcher.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Called by kernel when last child in cgroup exists
|
||||||
|
|
||||||
|
BASE=/sys/fs/cgroup/finit
|
||||||
|
PROC=`basename $1`
|
||||||
|
DIR="$BASE$1"
|
||||||
|
|
||||||
|
logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
||||||
|
rmdir $DIR
|
||||||
|
|
||||||
|
exit 0
|
||||||
+219
@@ -0,0 +1,219 @@
|
|||||||
|
/* Finit control group support functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <lite/lite.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int active;
|
||||||
|
char cgroup[16];
|
||||||
|
char path[32];
|
||||||
|
} cg_avail[200];
|
||||||
|
|
||||||
|
static int cg_init = 0;
|
||||||
|
static int cg_num = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by Finit at early boot to mount initial cgroups
|
||||||
|
*/
|
||||||
|
void cgroup_init(void)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char buf[80];
|
||||||
|
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||||
|
|
||||||
|
fp = fopen("/proc/cgroups", "r");
|
||||||
|
if (!fp) {
|
||||||
|
_d("No cgroup support");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
|
||||||
|
_d("Failed mounting cgroups");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip first line, header */
|
||||||
|
fgets(buf, sizeof(buf), fp);
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
char *cgroup;
|
||||||
|
char rc[80];
|
||||||
|
|
||||||
|
cgroup = strtok(buf, "\t ");
|
||||||
|
if (!cgroup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
||||||
|
mkdir(rc, 0755);
|
||||||
|
if (mount("cgroup", rc, "cgroup", opts, cgroup)) {
|
||||||
|
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cg_avail[cg_num].active = 1;
|
||||||
|
strlcpy(cg_avail[cg_num].cgroup, cgroup, sizeof(cg_avail[cg_num].cgroup));
|
||||||
|
strlcpy(cg_avail[cg_num].path, rc, sizeof(cg_avail[cg_num].path));
|
||||||
|
cg_num++;
|
||||||
|
|
||||||
|
/* Create default group in all available cgroups */
|
||||||
|
strlcat(rc, "/default", sizeof(rc));
|
||||||
|
mkdir(rc, 0755);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default cgroups for process monitoring */
|
||||||
|
mkdir("/sys/fs/cgroup/finit", 0755);
|
||||||
|
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
|
||||||
|
_pe("Failed mounting Finit cgroup hierarchy");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up reaper and enable callbacks */
|
||||||
|
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||||
|
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
|
||||||
|
|
||||||
|
/* Default groups, PID 1, services, and user/login processes */
|
||||||
|
mkdir("/sys/fs/cgroup/finit/init", 0755);
|
||||||
|
mkdir("/sys/fs/cgroup/finit/system", 0755);
|
||||||
|
mkdir("/sys/fs/cgroup/finit/user", 0755);
|
||||||
|
|
||||||
|
/* Move ourselves to init */
|
||||||
|
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
|
||||||
|
|
||||||
|
/* We have signal, main screen turn on! */
|
||||||
|
cg_init = 1;
|
||||||
|
fail:
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_add(char *name, void (*cb)(char *, void *), void *arg)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cg_num; i++) {
|
||||||
|
char path[80];
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", cg_avail[i].path, name);
|
||||||
|
if (mkdir(path, 0755) && EEXIST != errno) {
|
||||||
|
rc++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: Add to cache of added groups, for cgroup_find() */
|
||||||
|
if (cb)
|
||||||
|
cb(path, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: Temporary hackish implementation */
|
||||||
|
int cgroup_find(char *name, char *group, size_t len)
|
||||||
|
{
|
||||||
|
char path[80];
|
||||||
|
|
||||||
|
if (cg_num <= 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", cg_avail[0].path, name);
|
||||||
|
if (access(path, F_OK))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
strlcpy(group, name, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* int cgroup_cpuset_range(char *group, int min, int max) */
|
||||||
|
/* { */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
static int move_pid(char *group, char *name, int pid)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||||
|
if (mkdir(path, 0755) && errno != EEXIST)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
strlcat(path, "/cgroup.procs", sizeof(path));
|
||||||
|
|
||||||
|
return echo(path, 0, "%d", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_user(char *name)
|
||||||
|
{
|
||||||
|
if (!cg_init)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return move_pid("finit/user", name, getpid());
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_service(char *cmd, int pid)
|
||||||
|
{
|
||||||
|
char *nm;
|
||||||
|
|
||||||
|
if (!cg_init)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (pid <= 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm = strrchr(cmd, '/');
|
||||||
|
if (nm)
|
||||||
|
nm++;
|
||||||
|
else
|
||||||
|
nm = cmd;
|
||||||
|
|
||||||
|
return move_pid("finit/system", nm, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_assign(char *name, int pid)
|
||||||
|
{
|
||||||
|
char path[120];
|
||||||
|
int i, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < cg_num; i++) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/cgroup.procs",
|
||||||
|
cg_avail[i].path, name);
|
||||||
|
rc += echo(path, 0, "%d", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/* Finit control group support functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_CGROUP_H_
|
||||||
|
#define FINIT_CGROUP_H_
|
||||||
|
|
||||||
|
void cgroup_init (void);
|
||||||
|
|
||||||
|
int cgroup_add (char *name, void (*cb)(char *, void *), void *arg);
|
||||||
|
int cgroup_find (char *name, char *group, size_t len);
|
||||||
|
|
||||||
|
int cgroup_user (char *name);
|
||||||
|
int cgroup_service (char *cmd, int pid);
|
||||||
|
int cgroup_assign (char *name, int pid);
|
||||||
|
|
||||||
|
#endif /* FINIT_CGROUP_H_ */
|
||||||
+1
-1
@@ -125,7 +125,7 @@ error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_t *client_svc_find(char *arg)
|
svc_t *client_svc_find(const char *arg)
|
||||||
{
|
{
|
||||||
int sd = -1;
|
int sd = -1;
|
||||||
struct init_request rq = {
|
struct init_request rq = {
|
||||||
|
|||||||
+1
-6
@@ -27,16 +27,11 @@
|
|||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *cmd;
|
|
||||||
int (*cb)(char *arg);
|
|
||||||
} command_t;
|
|
||||||
|
|
||||||
int client_connect (void);
|
int client_connect (void);
|
||||||
int client_disconnect (void);
|
int client_disconnect (void);
|
||||||
|
|
||||||
int client_send (struct init_request *rq, ssize_t len);
|
int client_send (struct init_request *rq, ssize_t len);
|
||||||
svc_t *client_svc_iterator (int first);
|
svc_t *client_svc_iterator (int first);
|
||||||
svc_t *client_svc_find (char *arg);
|
svc_t *client_svc_find (const char *arg);
|
||||||
|
|
||||||
#endif /* FINIT_CLIENT_H_ */
|
#endif /* FINIT_CLIENT_H_ */
|
||||||
+62
-16
@@ -28,14 +28,19 @@
|
|||||||
|
|
||||||
#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 *path, unsigned int gen)
|
static int cond_set_gen(const char *file, unsigned int gen)
|
||||||
{
|
{
|
||||||
|
char *ptr, path[256];
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
fp = fopen(path, "w");
|
/* /var/run --> /run symlink may not exist (yet) */
|
||||||
|
ptr = pid_runpath(file, path, sizeof(path));
|
||||||
|
|
||||||
|
fp = fopen(ptr, "w");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -59,9 +64,27 @@ static void cond_bump_reconf(void)
|
|||||||
cond_set_gen(COND_RECONF, rgen);
|
cond_set_gen(COND_RECONF, rgen);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cond_set_path(const char *path, enum cond_state new)
|
static int cond_checkpath(const char *path)
|
||||||
{
|
{
|
||||||
char buf[MAX_ARG_LEN], *dir;
|
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)
|
||||||
|
{
|
||||||
enum cond_state old;
|
enum cond_state old;
|
||||||
unsigned int rgen;
|
unsigned int rgen;
|
||||||
|
|
||||||
@@ -77,16 +100,8 @@ int cond_set_path(const char *path, enum cond_state new)
|
|||||||
|
|
||||||
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) {
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
cond_set_gen(path, rgen);
|
cond_set_gen(path, rgen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -137,17 +152,40 @@ static void cond_update(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cond_set(const char *name)
|
void cond_set(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!cond_set_path(cond_path(name), COND_ON))
|
if (!cond_set_path(cond_path(name), COND_ON))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cond_update(name);
|
cond_update(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cond_set_oneshot(const char *name)
|
||||||
|
{
|
||||||
|
const char *path;
|
||||||
|
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = cond_path(name);
|
||||||
|
_d("s => %s", name, path);
|
||||||
|
|
||||||
|
if (cond_checkpath(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
symlink(COND_RECONF, path);
|
||||||
|
cond_update(name);
|
||||||
|
}
|
||||||
|
|
||||||
void cond_clear(const char *name)
|
void cond_clear(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -172,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);
|
||||||
|
|
||||||
@@ -191,7 +235,9 @@ void cond_reassert(const char *type)
|
|||||||
|
|
||||||
void cond_init(void)
|
void cond_init(void)
|
||||||
{
|
{
|
||||||
if (makepath(COND_PATH) && errno != EEXIST) {
|
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);
|
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-6
@@ -26,9 +26,16 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
|
#include "pid.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
static const char *strs[] = {
|
static const char *strs[] = {
|
||||||
@@ -42,20 +49,25 @@ 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 *path)
|
unsigned int cond_get_gen(const char *file)
|
||||||
{
|
{
|
||||||
|
char *ptr, path[256];
|
||||||
unsigned int gen;
|
unsigned int gen;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
fp = fopen(path, "r");
|
/* /var/run --> /run symlink may not exist (yet) */
|
||||||
|
ptr = pid_runpath(file, path, sizeof(path));
|
||||||
|
|
||||||
|
fp = fopen(ptr, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -89,7 +101,7 @@ enum cond_state cond_get_agg(const char *names)
|
|||||||
{
|
{
|
||||||
char *cond;
|
char *cond;
|
||||||
enum cond_state s = COND_ON;
|
enum cond_state s = COND_ON;
|
||||||
static char conds[MAX_ARG_LEN];
|
static char conds[MAX_COND_LEN];
|
||||||
|
|
||||||
if (!names)
|
if (!names)
|
||||||
return COND_ON;
|
return COND_ON;
|
||||||
@@ -104,7 +116,7 @@ enum cond_state cond_get_agg(const char *names)
|
|||||||
int cond_affects(const char *name, const char *names)
|
int cond_affects(const char *name, const char *names)
|
||||||
{
|
{
|
||||||
char *cond;
|
char *cond;
|
||||||
static char conds[MAX_ARG_LEN];
|
static char conds[MAX_COND_LEN];
|
||||||
|
|
||||||
if (!name || !names)
|
if (!name || !names)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+10
-8
@@ -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,6 +13,7 @@ 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);
|
unsigned int cond_get_gen (const char *path);
|
||||||
@@ -21,12 +22,13 @@ 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_init (void);
|
void cond_reassert (const char *pat);
|
||||||
|
void cond_init (void);
|
||||||
|
|
||||||
#endif /* FINIT_COND_H_ */
|
#endif /* FINIT_COND_H_ */
|
||||||
|
|
||||||
|
|||||||
+250
-65
@@ -30,17 +30,23 @@
|
|||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <glob.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#define BOOTSTRAP (runlevel == 0)
|
#define 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)))
|
||||||
|
|
||||||
|
int logfile_size_max = 200000; /* 200 kB */
|
||||||
|
int logfile_count_max = 5;
|
||||||
|
|
||||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
struct conf_change {
|
struct conf_change {
|
||||||
@@ -48,7 +54,8 @@ struct conf_change {
|
|||||||
char *name;
|
char *name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uev_t w1, w2, w3;
|
static char cgroup[32];
|
||||||
|
static uev_t w1, w2, w3, w4;
|
||||||
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||||
|
|
||||||
static int parse_conf(char *file);
|
static int parse_conf(char *file);
|
||||||
@@ -58,24 +65,85 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -200,11 +268,33 @@ char *rlim2str(int rlim)
|
|||||||
return "unknown";
|
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[])
|
void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||||
{
|
{
|
||||||
char *level, *limit, *val;
|
char *level, *limit, *val;
|
||||||
int resource = -1;
|
int resource = -1;
|
||||||
rlim_t cfg, *set;
|
rlim_t cfg;
|
||||||
|
|
||||||
level = strtok(line, " \t");
|
level = strtok(line, " \t");
|
||||||
if (!level)
|
if (!level)
|
||||||
@@ -214,21 +304,18 @@ void conf_parse_rlimit(char *line, struct rlimit arr[])
|
|||||||
if (!limit)
|
if (!limit)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
val = strtok(NULL, " \t");
|
||||||
|
if (!val) {
|
||||||
|
/* Second form: `rlimit RESOURCE LIMIT` */
|
||||||
|
val = limit;
|
||||||
|
limit = level;
|
||||||
|
level = "both";
|
||||||
|
}
|
||||||
|
|
||||||
resource = str2rlim(limit);
|
resource = str2rlim(limit);
|
||||||
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
val = strtok(NULL, " \t");
|
|
||||||
if (!val)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (!strcmp(level, "soft"))
|
|
||||||
set = &arr[resource].rlim_cur;
|
|
||||||
else if (!strcmp(level, "hard"))
|
|
||||||
set = &arr[resource].rlim_max;
|
|
||||||
else
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
||||||
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
||||||
cfg = RLIM_INFINITY;
|
cfg = RLIM_INFINITY;
|
||||||
@@ -243,18 +330,65 @@ void conf_parse_rlimit(char *line, struct rlimit arr[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*set = cfg;
|
if (!strcmp(level, "soft"))
|
||||||
|
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;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
error:
|
error:
|
||||||
logit(LOG_WARNING, "rlimit: parse error");
|
logit(LOG_WARNING, "rlimit: parse error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cpuset_cb(char *grp, void *arg)
|
||||||
|
{
|
||||||
|
int cpuset = *(int *)arg;
|
||||||
|
char path[128];
|
||||||
|
|
||||||
|
if (!strstr(grp, "cpuset"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Both .cpus and .mems are mandatory settings */
|
||||||
|
snprintf(path, sizeof(path), "%s/cpuset.cpus", grp);
|
||||||
|
echo(path, 0, "%d", cpuset);
|
||||||
|
snprintf(path, sizeof(path), "%s/cpuset.mems", grp);
|
||||||
|
echo(path, 0, "0");
|
||||||
|
|
||||||
|
/* XXX: HACK for now, fix proper CPU allocation */
|
||||||
|
snprintf(path, sizeof(path), "%s/cpuset.cpu_exclusive", grp);
|
||||||
|
echo(path, 0, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_cgroup(char *line)
|
||||||
|
{
|
||||||
|
char *group, *token;
|
||||||
|
int cpuset = -1;
|
||||||
|
|
||||||
|
group = strtok(line, " ");
|
||||||
|
if (!group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((token = strtok(NULL, " "))) {
|
||||||
|
if (!strncmp(token, "cpuset:", 7)) {
|
||||||
|
token += 7;
|
||||||
|
cpuset = atoi(token); /* XXX: fixme */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpuset >= 0)
|
||||||
|
cgroup_add(group, cpuset_cb, &cpuset);
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_static(char *line)
|
static void parse_static(char *line)
|
||||||
{
|
{
|
||||||
char *x;
|
char *x;
|
||||||
char cmd[CMD_SIZE];
|
char cmd[CMD_SIZE];
|
||||||
|
|
||||||
if (BOOTSTRAP && 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;
|
||||||
@@ -295,6 +429,26 @@ 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));
|
||||||
@@ -317,12 +471,16 @@ static void parse_static(char *line)
|
|||||||
cfglevel = 2; /* Fallback */
|
cfglevel = 2; /* Fallback */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||||
|
parse_cgroup(strip_line(x));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
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))
|
||||||
@@ -330,41 +488,32 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
|||||||
|
|
||||||
/* 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, rlimit, file);
|
service_register(SVC_TYPE_SERVICE, x, rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* One-shot task, will not be respawned */
|
/* One-shot task, will not be respawned */
|
||||||
if (MATCH_CMD(line, "task ", x)) {
|
if (MATCH_CMD(line, "task ", x)) {
|
||||||
service_register(SVC_TYPE_TASK, x, rlimit, file);
|
service_register(SVC_TYPE_TASK, x, rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like task but waits for completion, useful w/ [S] */
|
/* Like task but waits for completion, useful w/ [S] */
|
||||||
if (MATCH_CMD(line, "run ", x)) {
|
if (MATCH_CMD(line, "run ", x)) {
|
||||||
service_register(SVC_TYPE_RUN, x, rlimit, file);
|
service_register(SVC_TYPE_RUN, x, rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Classic inetd service */
|
/* Classic inetd service */
|
||||||
if (MATCH_CMD(line, "inetd ", x)) {
|
if (MATCH_CMD(line, "inetd ", x)) {
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
service_register(SVC_TYPE_INETD, x, rlimit, file);
|
service_register(SVC_TYPE_INETD, x, rlimit, cgroup, file);
|
||||||
#else
|
#else
|
||||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||||
#endif
|
#endif
|
||||||
@@ -377,9 +526,15 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* All subsequent run/task/service/tty/etc. directives to use this cgroup */
|
||||||
|
if (MATCH_CMD(line, "group ", x)) {
|
||||||
|
cgroup_find(x, cgroup, sizeof(cgroup));
|
||||||
|
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), rlimit, file);
|
tty_register(strip_line(x), rlimit, cgroup, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,8 +552,8 @@ static void tabstospaces(char *line)
|
|||||||
|
|
||||||
static int parse_conf_dynamic(char *file)
|
static int parse_conf_dynamic(char *file)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
|
||||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
fp = fopen(file, "r");
|
fp = fopen(file, "r");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
@@ -409,6 +564,9 @@ static int parse_conf_dynamic(char *file)
|
|||||||
/* Prepare default limits for each service */
|
/* Prepare default limits for each service */
|
||||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||||
|
|
||||||
|
/* Prepare default cgroup for each service */
|
||||||
|
strlcpy(cgroup, "default", sizeof(cgroup));
|
||||||
|
|
||||||
_d("Parsing %s <<<<<<", file);
|
_d("Parsing %s <<<<<<", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
char line[LINE_SIZE] = "";
|
char line[LINE_SIZE] = "";
|
||||||
@@ -463,7 +621,8 @@ static int parse_conf(char *file)
|
|||||||
/* Set global limits */
|
/* Set global limits */
|
||||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||||
if (setrlimit(i, &global_rlimit[i]) == -1)
|
if (setrlimit(i, &global_rlimit[i]) == -1)
|
||||||
logit(LOG_WARNING, "rlimit: Failed setting %s", rlim2str(i));
|
logit(LOG_WARNING, "rlimit: Failed setting %s: %s",
|
||||||
|
rlim2str(i), lim2str(&global_rlimit[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -474,35 +633,38 @@ static int parse_conf(char *file)
|
|||||||
*/
|
*/
|
||||||
int conf_reload(void)
|
int conf_reload(void)
|
||||||
{
|
{
|
||||||
int i, num;
|
size_t i;
|
||||||
struct dirent **e;
|
glob_t gl;
|
||||||
|
|
||||||
/* Mark and sweep */
|
/* Mark and sweep */
|
||||||
svc_mark_dynamic();
|
svc_mark_dynamic();
|
||||||
tty_mark();
|
tty_mark();
|
||||||
|
|
||||||
|
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, cgroup, NULL);
|
||||||
|
|
||||||
|
print(rc, "Entering rescue mode");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* First, read /etc/finit.conf */
|
/* First, read /etc/finit.conf */
|
||||||
parse_conf(FINIT_CONF);
|
parse_conf(FINIT_CONF);
|
||||||
|
|
||||||
/* Next, read all *.conf in /etc/finit.d/ */
|
/* Next, read all *.conf in /etc/finit.d/ */
|
||||||
num = scandir(rcsd, &e, NULL, alphasort);
|
glob("/etc/finit.d/*.conf", 0, NULL, &gl);
|
||||||
if (num < 0) {
|
glob("/etc/finit.d/enabled/*.conf", GLOB_APPEND, NULL, &gl);
|
||||||
_d("Skipping %s, no files found ...", rcsd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update global limits in case operator used prlimit(1) on us */
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
for (i = 0; i < RLIMIT_NLIMITS; i++)
|
char *path = gl.gl_pathv[i];
|
||||||
getrlimit(i, &global_rlimit[i]);
|
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
|
||||||
char *name = e[i]->d_name;
|
|
||||||
char path[LINE_SIZE];
|
|
||||||
size_t len;
|
size_t len;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
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)) {
|
||||||
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
||||||
@@ -538,13 +700,16 @@ int conf_reload(void)
|
|||||||
parse_conf_dynamic(path);
|
parse_conf_dynamic(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (num--)
|
globfree(&gl);
|
||||||
free(e[num]);
|
|
||||||
free(e);
|
|
||||||
|
|
||||||
|
done:
|
||||||
/* Drop record of all .conf changes */
|
/* Drop record of all .conf changes */
|
||||||
drop_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
|
* Set host name, from %DEFHOST, *.conf or /etc/hostname. The
|
||||||
* latter wins, if neither exists we default to "noname"
|
* latter wins, if neither exists we default to "noname"
|
||||||
@@ -610,16 +775,28 @@ static int do_change(char *name, uint32_t mask)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_d("Event registered for %s, mask 0x%x", name, mask);
|
||||||
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
|
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int conf_any_change(void)
|
||||||
|
{
|
||||||
|
if (TAILQ_EMPTY(&conf_change_list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int conf_changed(char *file)
|
int conf_changed(char *file)
|
||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
if (file && (ptr = strrchr(file, '/')))
|
if (!file)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((ptr = strrchr(file, '/')))
|
||||||
file = ++ptr;
|
file = ++ptr;
|
||||||
|
|
||||||
if (conf_find(file))
|
if (conf_find(file))
|
||||||
@@ -654,6 +831,9 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conf_any_change())
|
||||||
|
service_reload_dynamic();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
||||||
@@ -717,6 +897,10 @@ int conf_monitor(uev_ctx_t *ctx)
|
|||||||
{
|
{
|
||||||
int rc = 0;
|
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
|
* 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
|
* one of /etc/finit.conf or /etc/finit.d in use, and may also
|
||||||
@@ -724,8 +908,9 @@ int conf_monitor(uev_ctx_t *ctx)
|
|||||||
* changes to either symlink or target.
|
* changes to either symlink or target.
|
||||||
*/
|
*/
|
||||||
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
|
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
|
||||||
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW);
|
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available/", IN_DONT_FOLLOW);
|
||||||
rc += add_watcher(ctx, &w3, FINIT_CONF, 0);
|
rc += add_watcher(ctx, &w3, FINIT_RCSD "/enabled/", 0);
|
||||||
|
rc += add_watcher(ctx, &w4, FINIT_CONF, 0);
|
||||||
|
|
||||||
return rc + conf_reload();
|
return rc + conf_reload();
|
||||||
}
|
}
|
||||||
@@ -736,7 +921,7 @@ int conf_monitor(uev_ctx_t *ctx)
|
|||||||
int conf_init(void)
|
int conf_init(void)
|
||||||
{
|
{
|
||||||
hostname = strdup(DEFHOST);
|
hostname = strdup(DEFHOST);
|
||||||
w1.fd = w2.fd = w3.fd = -1;
|
w1.fd = w2.fd = w3.fd = w4.fd = -1;
|
||||||
|
|
||||||
return conf_monitor(NULL);
|
return conf_monitor(NULL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
|
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
|
extern int logfile_size_max;
|
||||||
|
extern int logfile_count_max;
|
||||||
|
|
||||||
extern struct rlimit global_rlimit[];
|
extern struct rlimit global_rlimit[];
|
||||||
|
|
||||||
int str2rlim(char *str);
|
int str2rlim(char *str);
|
||||||
@@ -33,6 +36,7 @@ char *rlim2str(int rlim);
|
|||||||
|
|
||||||
int conf_init (void);
|
int conf_init (void);
|
||||||
void conf_reload (void);
|
void conf_reload (void);
|
||||||
|
int conf_any_change (void);
|
||||||
int conf_changed (char *file);
|
int conf_changed (char *file);
|
||||||
int conf_monitor (uev_ctx_t *ctx);
|
int conf_monitor (uev_ctx_t *ctx);
|
||||||
|
|
||||||
|
|||||||
+30
-19
@@ -363,7 +363,7 @@ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, stru
|
|||||||
if (!pid) {
|
if (!pid) {
|
||||||
speed_t speed = B38400;
|
speed_t speed = B38400;
|
||||||
|
|
||||||
if (baud) {
|
if (baud && baud[0]) {
|
||||||
speed = stty_parse_speed(baud);
|
speed = stty_parse_speed(baud);
|
||||||
if (B0 == speed)
|
if (B0 == speed)
|
||||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||||
@@ -423,6 +423,20 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, st
|
|||||||
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;
|
||||||
@@ -435,14 +449,18 @@ int run_parts(char *dir, char *cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
int j = 0;
|
|
||||||
pid_t pid = 0;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char *args[NUM_ARGS];
|
char path[512];
|
||||||
|
char *argv[4] = {
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
path,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
char *name = e[i]->d_name;
|
char *name = e[i]->d_name;
|
||||||
char path[LINE_SIZE];
|
pid_t pid = 0;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
snprintf(path, sizeof(path), "%s/%s ", dir, name);
|
||||||
if (stat(path, &st)) {
|
if (stat(path, &st)) {
|
||||||
_d("Failed stat(%s): %s", path, strerror(errno));
|
_d("Failed stat(%s): %s", path, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
@@ -453,29 +471,22 @@ int run_parts(char *dir, char *cmd)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in args[], starting with full path to executable */
|
|
||||||
args[j++] = path;
|
|
||||||
|
|
||||||
/* If the callee didn't supply a run_parts() argument */
|
/* If the callee didn't supply a run_parts() argument */
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
_d("Checking if %s is a sysvinit startstop script ...", name);
|
||||||
if (name[0] == 'S' && isdigit(name[1])) {
|
if (name[0] == 'S' && isdigit(name[1]))
|
||||||
args[j++] = "start";
|
strlcat(path, "start", sizeof(path));
|
||||||
} else if (name[0] == 'K' && isdigit(name[1])) {
|
else if (name[0] == 'K' && isdigit(name[1]))
|
||||||
args[j++] = "stop";
|
strlcat(path, "stop", sizeof(path));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
args[j++] = cmd;
|
strlcat(path, cmd, sizeof(path));
|
||||||
}
|
}
|
||||||
args[j++] = NULL;
|
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
_d("Calling %s ...", path);
|
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
execv(path, args);
|
return execvp(_PATH_BSHELL, argv);
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(path, pid);
|
complete(path, pid);
|
||||||
|
|||||||
+198
-178
@@ -36,6 +36,7 @@
|
|||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
@@ -47,12 +48,14 @@
|
|||||||
#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;
|
||||||
@@ -60,6 +63,10 @@ char *rcsd = FINIT_RCSD;
|
|||||||
char *runparts = NULL;
|
char *runparts = NULL;
|
||||||
|
|
||||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||||
|
svc_t *wdog = NULL; /* No watchdog by default */
|
||||||
|
|
||||||
|
static int udev = 0; /* Runtime detection of udev */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Show user configured banner before service bootstrap progress
|
* Show user configured banner before service bootstrap progress
|
||||||
@@ -78,17 +85,16 @@ static void banner(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
memset(separator, '=', sizeof(separator));
|
memset(separator, '=', sizeof(separator));
|
||||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
fprintf(stderr, "\n\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Requires /proc to be mounted */
|
static int ismnt(char *file, char *dir)
|
||||||
static int fismnt(char *dir)
|
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
struct mntent *mnt;
|
struct mntent *mnt;
|
||||||
|
|
||||||
fp = setmntent("/proc/mounts", "r");
|
fp = setmntent(file, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return 0; /* Dunno, maybe not */
|
return 0; /* Dunno, maybe not */
|
||||||
|
|
||||||
@@ -103,6 +109,12 @@ 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
|
||||||
*/
|
*/
|
||||||
@@ -150,54 +162,6 @@ static int fsck(int pass)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void networking(void)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
/* Run user network start script if enabled */
|
|
||||||
if (network) {
|
|
||||||
run_interactive(network, "Starting networking: %s", network);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Debian/Ubuntu/Busybox/RH/Suse */
|
|
||||||
if (!whichp("ifup"))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
fp = fopen("/etc/network/interfaces", "r");
|
|
||||||
if (fp) {
|
|
||||||
int i = 0;
|
|
||||||
char buf[160];
|
|
||||||
|
|
||||||
/* Bring up all 'auto' interfaces */
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
|
||||||
char cmd[80];
|
|
||||||
char *line, *ifname = NULL;
|
|
||||||
|
|
||||||
chomp(buf);
|
|
||||||
line = strip_line(buf);
|
|
||||||
|
|
||||||
if (!strncmp(line, "auto", 4))
|
|
||||||
ifname = &line[5];
|
|
||||||
if (!strncmp(line, "allow-hotplug", 13))
|
|
||||||
ifname = &line[14];
|
|
||||||
|
|
||||||
if (!ifname)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(cmd, 80, "ifup %s", ifname);
|
|
||||||
run_interactive(cmd, "Bringing up interface %s", ifname);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
/* Fall back to bring up at least loopback */
|
|
||||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If everything goes south we can use this to give the operator an
|
* If everything goes south we can use this to give the operator an
|
||||||
* emergency shell to debug the problem -- Finit should not crash!
|
* emergency shell to debug the problem -- Finit should not crash!
|
||||||
@@ -227,7 +191,7 @@ static void emergency_shell(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
||||||
fprintf(stderr, "=> To debug, add '--debug' to the kernel command line.\n\n");
|
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Become session leader and set controlling TTY
|
* Become session leader and set controlling TTY
|
||||||
@@ -254,16 +218,20 @@ static void emergency_shell(void)
|
|||||||
*/
|
*/
|
||||||
static void finalize(void)
|
static void finalize(void)
|
||||||
{
|
{
|
||||||
/*
|
svc_t *svc;
|
||||||
* Network stuff
|
|
||||||
*/
|
|
||||||
_d("Setting up networking ...");
|
|
||||||
networking();
|
|
||||||
umask(022);
|
|
||||||
|
|
||||||
/* Hooks that rely on loopback, or basic networking being up. */
|
/*
|
||||||
_d("Calling all network up hooks ...");
|
* Track bundled watchdogd in case a better one turns up
|
||||||
plugin_run_hooks(HOOK_NETWORK_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
|
* Start all tasks/services in the configured runlevel
|
||||||
@@ -280,20 +248,9 @@ static void finalize(void)
|
|||||||
plugin_run_hooks(HOOK_SVC_UP);
|
plugin_run_hooks(HOOK_SVC_UP);
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 ... */
|
/* Convenient SysV compat for when you just don't care ... */
|
||||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue)
|
||||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||||
service_reload_dynamic();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hooks that should run at the very end */
|
/* Hooks that should run at the very end */
|
||||||
_d("Calling all system up hooks ...");
|
_d("Calling all system up hooks ...");
|
||||||
@@ -309,13 +266,61 @@ static void finalize(void)
|
|||||||
tty_runlevel();
|
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 udev = 0;
|
|
||||||
uev_t timer; /* Bootstrap timer, on timeout call finalize() */
|
|
||||||
uev_ctx_t loop;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* finit/init/telinit client tool uses /dev/initctl pipe
|
* finit/init/telinit client tool uses /dev/initctl pipe
|
||||||
@@ -324,8 +329,44 @@ int main(int argc, char* argv[])
|
|||||||
if (getpid() != 1)
|
if (getpid() != 1)
|
||||||
return client(argc, argv);
|
return client(argc, argv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initalize event context.
|
||||||
|
*/
|
||||||
|
uev_init1(&loop, 1);
|
||||||
|
ctx = &loop;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set PATH and SHELL early to something sane
|
||||||
|
*/
|
||||||
|
setenv("PATH", _PATH_STDPATH, 1);
|
||||||
|
setenv("SHELL", _PATH_BSHELL, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||||
|
*/
|
||||||
|
chdir("/");
|
||||||
|
umask(0);
|
||||||
|
if (mount("none", "/proc", "proc", 0, NULL))
|
||||||
|
_pe("Failed mounting /proc");
|
||||||
|
if (mount("none", "/sys", "sysfs", 0, NULL))
|
||||||
|
_pe("Failed mounting /sysfs");
|
||||||
|
if (fisdir("/proc/bus/usb"))
|
||||||
|
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize default control groups, if available
|
||||||
|
*/
|
||||||
|
cgroup_init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse kernel command line (debug, rescue, splash, etc.)
|
||||||
|
* Also calls log_init() to set correct log level
|
||||||
|
*/
|
||||||
|
conf_parse_cmdline();
|
||||||
|
|
||||||
/* Set up canvas */
|
/* Set up canvas */
|
||||||
screen_init();
|
if (!rescue && !log_is_debug())
|
||||||
|
screen_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of emergency.
|
* In case of emergency.
|
||||||
@@ -337,37 +378,11 @@ int main(int argc, char* argv[])
|
|||||||
*/
|
*/
|
||||||
sig_init();
|
sig_init();
|
||||||
|
|
||||||
/*
|
|
||||||
* Initalize event context.
|
|
||||||
*/
|
|
||||||
uev_init(&loop);
|
|
||||||
ctx = &loop;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the PATH early to something sane
|
|
||||||
*/
|
|
||||||
setenv("PATH", _PATH_STDPATH, 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
|
||||||
*/
|
|
||||||
chdir("/");
|
|
||||||
umask(0);
|
|
||||||
mount("none", "/proc", "proc", 0, NULL);
|
|
||||||
mount("none", "/sys", "sysfs", 0, NULL);
|
|
||||||
if (fisdir("/proc/bus/usb"))
|
|
||||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse kernel parameters, including log_init()
|
|
||||||
*/
|
|
||||||
conf_parse_cmdline();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load plugins early, finit.conf may contain references to
|
* Load plugins early, finit.conf may contain references to
|
||||||
* features implemented by plugins.
|
* features implemented by plugins.
|
||||||
*/
|
*/
|
||||||
plugin_init(&loop, PLUGIN_PATH);
|
plugin_init(&loop);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hello world.
|
* Hello world.
|
||||||
@@ -377,11 +392,17 @@ int main(int argc, char* argv[])
|
|||||||
/*
|
/*
|
||||||
* 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
|
||||||
@@ -395,8 +416,15 @@ 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 /dev/shm being there, for `mount -a` below */
|
/*
|
||||||
|
* 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") && !ismnt("/etc/fstab", "/dev/shm"))
|
||||||
|
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* New tmpfs based /run for volatile runtime data
|
* New tmpfs based /run for volatile runtime data
|
||||||
@@ -406,118 +434,110 @@ 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, global_rlimit, NULL)) {
|
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, "default", NULL)) {
|
||||||
_pe("Failed registering %s", path);
|
_pe("Failed registering %s", path);
|
||||||
udev = 0;
|
udev = 0;
|
||||||
}
|
} else {
|
||||||
|
snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
|
||||||
|
"udevadm trigger -c add -t devices "
|
||||||
|
"-- Requesting device events", path);
|
||||||
|
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||||
|
|
||||||
/* Start a temporary udevd instance to populate /dev */
|
snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
|
||||||
snprintf(cmd, sizeof(cmd), "%s --daemon", path);
|
"udevadm trigger -c add -t subsystems "
|
||||||
|
"-- Requesting subsystem events", path);
|
||||||
|
service_register(SVC_TYPE_RUN, cmd, global_rlimit, "default", NULL);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
free(path);
|
free(path);
|
||||||
|
} else {
|
||||||
|
path = which("mdev");
|
||||||
|
if (path) {
|
||||||
|
/* Embedded Linux systems usually have BusyBox mdev */
|
||||||
|
if (log_is_debug())
|
||||||
|
touch("/dev/mdev.log");
|
||||||
|
|
||||||
run_interactive(cmd, "Populating device tree");
|
snprintf(cmd, sizeof(cmd), "%s -s", path);
|
||||||
if (udev && whichp("udevadm")) {
|
free(path);
|
||||||
run("udevadm trigger --action=add --type=subsystems");
|
|
||||||
run("udevadm trigger --action=add --type=devices");
|
|
||||||
run("udevadm settle --timeout=120");
|
|
||||||
|
|
||||||
/* Tell temporary udevd to exit, we'll start a monitored instance later */
|
run_interactive(cmd, "Populating device tree");
|
||||||
run("udevadm control --exit");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize .conf system and load static /etc/finit.conf
|
* Start bundled watchdogd as soon as possible, if enabled
|
||||||
*/
|
*/
|
||||||
conf_init();
|
if (which(FINIT_LIBPATH_ "/watchdogd"))
|
||||||
|
service_register(SVC_TYPE_SERVICE, FINIT_LIBPATH_ "/watchdogd", global_rlimit, "default", NULL);
|
||||||
/*
|
|
||||||
* Start built-in watchdog as soon as possible, if enabled
|
|
||||||
*/
|
|
||||||
wdogpid = watchdog(argv[0]);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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);
|
||||||
|
|
||||||
/* Bootstrap conditions, needed for hooks */
|
umask(0);
|
||||||
cond_init();
|
if (run_interactive("mount -na", "Mounting filesystems"))
|
||||||
|
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||||
|
|
||||||
_d("Root FS up, calling hooks ...");
|
_d("Calling extra mount hook, after mount -a ...");
|
||||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||||
|
|
||||||
umask(0);
|
run("swapon -ea");
|
||||||
if (run_interactive("mount -na", "Mounting filesystems"))
|
umask(0022);
|
||||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
}
|
||||||
|
|
||||||
run("swapon -ea");
|
|
||||||
umask(0022);
|
|
||||||
|
|
||||||
/* Base FS up, enable standard SysV init signals */
|
/* Base FS up, enable standard SysV init signals */
|
||||||
sig_setup(&loop);
|
sig_setup(&loop);
|
||||||
|
|
||||||
|
if (!rescue) {
|
||||||
|
_d("Base FS up, calling hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up inotify watcher for /etc/finit.d and read all .conf
|
* Set up inotify watcher for /etc/finit.d and read all .conf
|
||||||
* files to figure out how to bootstrap the system.
|
* files to figure out how to bootstrap the system.
|
||||||
*/
|
*/
|
||||||
conf_monitor(&loop);
|
conf_monitor(&loop);
|
||||||
|
|
||||||
_d("Base FS up, calling hooks ...");
|
_d("Starting initctl API responder ...");
|
||||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initalize state machine and start all bootstrap tasks
|
|
||||||
* NOTE: no network available!
|
|
||||||
*/
|
|
||||||
sm_init(&sm);
|
|
||||||
sm_step(&sm);
|
|
||||||
|
|
||||||
/* Start new initctl API responder */
|
|
||||||
api_init(&loop);
|
api_init(&loop);
|
||||||
|
umask(022);
|
||||||
|
|
||||||
|
_d("Starting the big state machine ...");
|
||||||
|
schedule_work(&crank);
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
|
|
||||||
* [S], or timeout, before calling finalize()
|
|
||||||
*/
|
|
||||||
_d("Starting bootstrap finalize timer ...");
|
_d("Starting bootstrap finalize timer ...");
|
||||||
uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);
|
schedule_work(&final);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enter main loop to monior /dev/initctl and services
|
* Enter main loop to monitor /dev/initctl and services
|
||||||
*/
|
*/
|
||||||
_d("Entering main loop ...");
|
_d("Entering main loop ...");
|
||||||
return uev_run(&loop, 0);
|
return uev_run(&loop, 0);
|
||||||
|
|||||||
+3
-1
@@ -92,10 +92,12 @@ 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;
|
||||||
|
|||||||
+57
-1
@@ -32,6 +32,7 @@
|
|||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
@@ -192,6 +193,7 @@ static int do_login(char *name)
|
|||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
cgroup_user(name);
|
||||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -237,12 +239,66 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
|||||||
stty(fd, speed);
|
stty(fd, speed);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (term)
|
if (term && term[0])
|
||||||
setenv("TERM", term, 1);
|
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;
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -52,13 +52,11 @@ struct fstab *getfsent (void);
|
|||||||
char *strip_line (char *line);
|
char *strip_line (char *line);
|
||||||
|
|
||||||
int getty (char *tty, speed_t speed, char *term, char *user);
|
int getty (char *tty, speed_t speed, char *term, char *user);
|
||||||
|
int sh (char *tty);
|
||||||
|
|
||||||
int stty (int fd, speed_t speed);
|
int stty (int fd, speed_t speed);
|
||||||
speed_t stty_parse_speed(char *baud);
|
speed_t stty_parse_speed(char *baud);
|
||||||
|
|
||||||
int pid_alive (pid_t pid);
|
|
||||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -67,6 +65,7 @@ int print_result (int fail);
|
|||||||
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);
|
||||||
@@ -74,6 +73,7 @@ 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 *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
|
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, struct rlimit rlimit[]);
|
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)
|
||||||
|
|||||||
+65
-25
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -73,26 +76,28 @@ static int inetd_dgram_peek(int sd, char *ifname)
|
|||||||
/* 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 void inetd_dgram_drop(int sd, const char *ifname)
|
static void inetd_dgram_drop(int sd, const char *ifname)
|
||||||
{
|
{
|
||||||
char pkt_interface[17];
|
char pkt_interface[IF_NAMESIZE + 1];
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
do {
|
while (1) {
|
||||||
inetd_dgram_peek(sd, pkt_interface);
|
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
|
||||||
if(string_compare(pkt_interface, ifname)) {
|
if (string_compare(pkt_interface, ifname)) {
|
||||||
recv(sd, buf, sizeof(buf), 0);
|
if (recv(sd, buf, sizeof(buf), 0) < 0)
|
||||||
|
break;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
} while (0);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
||||||
@@ -111,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,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);
|
||||||
@@ -136,9 +142,9 @@ 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)) {
|
||||||
@@ -151,6 +157,9 @@ static int get_stdin(svc_t *svc)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return ingress interface */
|
||||||
|
strlcpy(iifname, ifname, len);
|
||||||
|
|
||||||
return stdin;
|
return stdin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +167,9 @@ static int get_stdin(svc_t *svc)
|
|||||||
static void socket_cb(uev_t *w, void *arg, int events)
|
static void socket_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
svc_t *svc = (svc_t *)arg, *task;
|
svc_t *svc = (svc_t *)arg, *task;
|
||||||
|
const char *conn = " connection";
|
||||||
|
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
|
||||||
|
char id[MAX_ID_LEN];
|
||||||
int stdin;
|
int stdin;
|
||||||
|
|
||||||
_d("%s: Got socket event ...", svc->cmd);
|
_d("%s: Got socket event ...", svc->cmd);
|
||||||
@@ -166,7 +178,7 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stdin = get_stdin(svc);
|
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;
|
||||||
@@ -183,7 +195,8 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN);
|
snprintf(id, sizeof(id), "%d", svc->inetd.next_id++);
|
||||||
|
task = svc_new(svc->cmd, id, SVC_TYPE_INETD_CONN);
|
||||||
if (!task) {
|
if (!task) {
|
||||||
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
|
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
|
||||||
if (svc->inetd.type == SOCK_STREAM)
|
if (svc->inetd.type == SOCK_STREAM)
|
||||||
@@ -207,11 +220,15 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
|||||||
task->inetd.cmd = svc->inetd.cmd;
|
task->inetd.cmd = svc->inetd.cmd;
|
||||||
task->inetd.type = svc->inetd.type;
|
task->inetd.type = svc->inetd.type;
|
||||||
|
|
||||||
|
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
|
||||||
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
||||||
memcpy(task->username, svc->username, sizeof(task->username));
|
memcpy(task->username, svc->username, sizeof(task->username));
|
||||||
memcpy(task->group, svc->group, sizeof(task->group));
|
memcpy(task->group, svc->group, sizeof(task->group));
|
||||||
memcpy(task->args, svc->args, sizeof(task->args));
|
memcpy(task->args, svc->args, sizeof(task->args));
|
||||||
snprintf(task->desc, sizeof(task->desc), "%52.52s connection", svc->desc);
|
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
|
||||||
|
strlcat(task->desc, conn, sizeof(task->desc));
|
||||||
|
strlcpy(task->iifname, iifname, sizeof(task->iifname));
|
||||||
|
strlcpy(task->name, svc->name, sizeof(task->name));
|
||||||
|
|
||||||
task->stdin_fd = stdin;
|
task->stdin_fd = stdin;
|
||||||
service_step(task);
|
service_step(task);
|
||||||
@@ -331,20 +348,43 @@ int inetd_start(inetd_t *inetd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inetd_stop_children(inetd_t *inetd, int check_allowed)
|
||||||
|
{
|
||||||
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
svc = svc_job_iterator(&iter, 1, inetd->svc->job);
|
||||||
|
while (svc) {
|
||||||
|
if (!svc_is_inetd(svc)) {
|
||||||
|
if (!check_allowed || !inetd_is_allowed(inetd, svc->iifname)) {
|
||||||
|
svc_stop(svc);
|
||||||
|
service_step(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svc = svc_job_iterator(&iter, 0, inetd->svc->job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void inetd_stop(inetd_t *inetd)
|
void inetd_stop(inetd_t *inetd)
|
||||||
{
|
{
|
||||||
|
if (!inetd || !inetd->svc) {
|
||||||
|
_e("Invalid inetd, cannot stop it...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (inetd->watcher.fd != -1) {
|
if (inetd->watcher.fd != -1) {
|
||||||
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
||||||
uev_io_stop(&inetd->watcher);
|
uev_io_stop(&inetd->watcher);
|
||||||
|
|
||||||
/* For dgram inetd services we block the parent SVC
|
/*
|
||||||
* and halt the watcher, so don't close the socket! */
|
* For dgram inetd services we block the parent SVC
|
||||||
|
* and halt the watcher, so don't close the socket!
|
||||||
|
*/
|
||||||
if (!svc_is_busy(inetd->svc)) {
|
if (!svc_is_busy(inetd->svc)) {
|
||||||
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
||||||
if (inetd->type == SOCK_STREAM)
|
|
||||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
|
||||||
close(inetd->watcher.fd);
|
close(inetd->watcher.fd);
|
||||||
inetd->watcher.fd = -1;
|
inetd->watcher.fd = -1;
|
||||||
|
|
||||||
|
inetd_stop_children(inetd, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
+96
-18
@@ -43,6 +43,11 @@
|
|||||||
|
|
||||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||||
|
|
||||||
|
struct command {
|
||||||
|
char *cmd;
|
||||||
|
int (*cb)(char *arg);
|
||||||
|
};
|
||||||
|
|
||||||
int verbose = 0;
|
int verbose = 0;
|
||||||
int runlevel = 0;
|
int runlevel = 0;
|
||||||
|
|
||||||
@@ -168,7 +173,7 @@ static int do_restart(char *arg) { return do_startstop(INIT_CMD_RESTART_SVC, arg
|
|||||||
|
|
||||||
static void show_cond_one(const char *_conds)
|
static void show_cond_one(const char *_conds)
|
||||||
{
|
{
|
||||||
static char conds[MAX_ARG_LEN];
|
static char conds[MAX_COND_LEN];
|
||||||
char *cond;
|
char *cond;
|
||||||
|
|
||||||
strlcpy(conds, _conds, sizeof(conds));
|
strlcpy(conds, _conds, sizeof(conds));
|
||||||
@@ -272,7 +277,7 @@ static int do_cond(char *cmd)
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
char *arg;
|
char *arg;
|
||||||
command_t command[] = {
|
struct command command[] = {
|
||||||
{ "show", do_cond_show },
|
{ "show", do_cond_show },
|
||||||
{ "set", do_cond_set },
|
{ "set", do_cond_set },
|
||||||
{ "clear", do_cond_clear },
|
{ "clear", do_cond_clear },
|
||||||
@@ -411,6 +416,9 @@ static int show_status(char *arg)
|
|||||||
runlevel = runlevel_get(NULL);
|
runlevel = runlevel_get(NULL);
|
||||||
|
|
||||||
if (arg && arg[0]) {
|
if (arg && arg[0]) {
|
||||||
|
long now = jiffies();
|
||||||
|
char buf[42] = "N/A";
|
||||||
|
|
||||||
svc = client_svc_find(arg);
|
svc = client_svc_find(arg);
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -418,6 +426,7 @@ static int show_status(char *arg)
|
|||||||
printf("Service : %s\n", svc->cmd);
|
printf("Service : %s\n", svc->cmd);
|
||||||
printf("Description : %s\n", svc->desc);
|
printf("Description : %s\n", svc->desc);
|
||||||
printf("PID : %d\n", svc->pid);
|
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("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||||
printf("Status : %s\n", svc_status(svc));
|
printf("Status : %s\n", svc_status(svc));
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@@ -426,17 +435,17 @@ static int show_status(char *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!verbose)
|
if (!verbose)
|
||||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||||
|
|
||||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||||
char jobid[10], args[512] = "", *lvls;
|
char jobid[20], args[512] = "", *lvls;
|
||||||
|
|
||||||
// if (svc_is_unique(svc))
|
// if (svc_is_unique(svc))
|
||||||
// snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
// snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
||||||
// else
|
// else
|
||||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
snprintf(jobid, sizeof(jobid), "%d:%s", svc->job, svc->id);
|
||||||
|
|
||||||
printf("%-5s %7s ", jobid, svc_status(svc));
|
printf("%-9s %7s ", jobid, svc_status(svc));
|
||||||
if (svc_is_inetd(svc))
|
if (svc_is_inetd(svc))
|
||||||
printf("inetd ");
|
printf("inetd ");
|
||||||
else
|
else
|
||||||
@@ -450,14 +459,7 @@ static int show_status(char *arg)
|
|||||||
|
|
||||||
if (!verbose) {
|
if (!verbose) {
|
||||||
int adj = screen_cols - 60;
|
int adj = screen_cols - 60;
|
||||||
char *name = strrchr(svc->cmd, '/');
|
printf("%-16.16s %-*.*s\n", svc->name, adj, adj, svc->desc);
|
||||||
|
|
||||||
if (!name)
|
|
||||||
name = svc->cmd;
|
|
||||||
else
|
|
||||||
name++;
|
|
||||||
|
|
||||||
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,12 +509,77 @@ static int show_status(char *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *group;
|
||||||
|
char path[256];
|
||||||
|
char buf[256];
|
||||||
|
int user = 0;
|
||||||
|
int num = 0;
|
||||||
|
|
||||||
|
if (tflag == FTW_F)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/cgroup.procs", fpath);
|
||||||
|
fp = fopen(path, "r");
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (strstr(fpath, "finit/user"))
|
||||||
|
user = 1;
|
||||||
|
|
||||||
|
group = strrchr(fpath, '/');
|
||||||
|
if (!group)
|
||||||
|
return 0;
|
||||||
|
group++;
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
FILE *cfp;
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
printf("%c :- %s/", user ? ' ' : '|', group);
|
||||||
|
num++;
|
||||||
|
|
||||||
|
pid = atoi(chomp(buf));
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||||
|
cfp = fopen(path, "r");
|
||||||
|
if (!cfp)
|
||||||
|
continue;
|
||||||
|
fgets(buf, sizeof(buf), cfp);
|
||||||
|
fclose(cfp);
|
||||||
|
|
||||||
|
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (num)
|
||||||
|
printf("\r%c\n", user ? ' ' : '|');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int show_cgroup(char *arg)
|
||||||
|
{
|
||||||
|
puts("finit/");
|
||||||
|
puts("|- init/");
|
||||||
|
nftw("/sys/fs/cgroup/finit/init", dump_cgroup, 20, 0);
|
||||||
|
puts("|- system/");
|
||||||
|
nftw("/sys/fs/cgroup/finit/system", dump_cgroup, 20, 0);
|
||||||
|
puts("`- user/");
|
||||||
|
nftw("/sys/fs/cgroup/finit/user", dump_cgroup, 20, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int usage(int rc)
|
static int usage(int rc)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: %s [OPTIONS] [COMMAND]\n"
|
"Usage: %s [OPTIONS] [COMMAND]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
|
" -b, --batch Batch mode, no screen size probing\n"
|
||||||
" -v, --verbose Verbose output\n"
|
" -v, --verbose Verbose output\n"
|
||||||
" -h, --help This help text\n"
|
" -h, --help This help text\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -537,8 +604,11 @@ 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 <JOB|NAME>[:ID] Show service status, by job# or name\n"
|
||||||
" status | show Show status of services, default command\n"
|
" status | show Show status of services, default command\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" ps List processes based on cgroups\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"
|
||||||
" halt Halt system\n"
|
" halt Halt system\n"
|
||||||
@@ -557,9 +627,9 @@ static int do_help(char *arg)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c;
|
int interactive = 1, c;
|
||||||
char *cmd, arg[120];
|
char *cmd, arg[120];
|
||||||
command_t command[] = {
|
struct command command[] = {
|
||||||
{ "debug", toggle_debug },
|
{ "debug", toggle_debug },
|
||||||
{ "help", do_help },
|
{ "help", do_help },
|
||||||
{ "version", show_version },
|
{ "version", show_version },
|
||||||
@@ -579,6 +649,8 @@ int main(int argc, char *argv[])
|
|||||||
{ "status", show_status },
|
{ "status", show_status },
|
||||||
{ "show", show_status }, /* Convenience alias */
|
{ "show", show_status }, /* Convenience alias */
|
||||||
|
|
||||||
|
{ "ps", show_cgroup },
|
||||||
|
|
||||||
{ "runlevel", do_runlevel },
|
{ "runlevel", do_runlevel },
|
||||||
{ "reboot", do_reboot },
|
{ "reboot", do_reboot },
|
||||||
{ "halt", do_halt },
|
{ "halt", do_halt },
|
||||||
@@ -588,6 +660,7 @@ int main(int argc, char *argv[])
|
|||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
struct option long_options[] = {
|
struct option long_options[] = {
|
||||||
|
{"batch", 0, NULL, 'b'},
|
||||||
{"help", 0, NULL, 'h'},
|
{"help", 0, NULL, 'h'},
|
||||||
{"debug", 0, NULL, 'd'},
|
{"debug", 0, NULL, 'd'},
|
||||||
{"verbose", 0, NULL, 'v'},
|
{"verbose", 0, NULL, 'v'},
|
||||||
@@ -595,8 +668,12 @@ int main(int argc, char *argv[])
|
|||||||
};
|
};
|
||||||
|
|
||||||
progname(argv[0]);
|
progname(argv[0]);
|
||||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
while ((c = getopt_long(argc, argv, "bh?v", long_options, NULL)) != EOF) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
|
case 'b':
|
||||||
|
interactive = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
@@ -607,7 +684,8 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
screen_init();
|
if (interactive)
|
||||||
|
screen_init();
|
||||||
|
|
||||||
if (optind >= argc)
|
if (optind >= argc)
|
||||||
return show_status(NULL);
|
return show_status(NULL);
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
*/
|
||||||
+7
-21
@@ -108,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);
|
||||||
|
|
||||||
@@ -186,21 +189,11 @@ 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)
|
||||||
{
|
{
|
||||||
static int last = -1;
|
|
||||||
plugin_t *p, *tmp;
|
plugin_t *p, *tmp;
|
||||||
|
|
||||||
/*
|
|
||||||
* End recursion: any plugin hook => start service => SVC start
|
|
||||||
* hook => start service ... err ... wait a second
|
|
||||||
*/
|
|
||||||
if (HOOK_SVC_START == last) {
|
|
||||||
_d("End plugin recursion in our lifetime!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PLUGIN_ITERATOR(p, tmp) {
|
PLUGIN_ITERATOR(p, tmp) {
|
||||||
if (p->hook[no].cb) {
|
if (p->hook[no].cb) {
|
||||||
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
|
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
|
||||||
@@ -208,15 +201,8 @@ void plugin_run_hook(hook_point_t no, void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Guard against infinite recursion */
|
cond_set_oneshot(hook_cond[no]);
|
||||||
if (HOOK_SVC_START == no)
|
|
||||||
last = no;
|
|
||||||
|
|
||||||
cond_set(hook_cond[no]);
|
|
||||||
service_step_all(SVC_TYPE_RUNTASK);
|
service_step_all(SVC_TYPE_RUNTASK);
|
||||||
|
|
||||||
if (HOOK_SVC_START == no)
|
|
||||||
last = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Regular hooks are called with the registered plugin's argument */
|
/* Regular hooks are called with the registered plugin's argument */
|
||||||
@@ -384,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;
|
||||||
|
|||||||
+13
-8
@@ -29,10 +29,6 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -54,22 +50,31 @@
|
|||||||
* Predefined hook points and corresponding conditions in Finit,
|
* Predefined hook points and corresponding conditions in Finit,
|
||||||
* for use by plugins and scripts. Recommended to use the task
|
* for use by plugins and scripts. Recommended to use the task
|
||||||
* or run stanzas: task <hook/mount/error> /bin/rescue.sh
|
* 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.
|
||||||
*/
|
*/
|
||||||
#define HOOK_TYPES { \
|
#define HOOK_TYPES { \
|
||||||
/* Bootstrap hooks, runlevel [S] */ \
|
/* Bootstrap hooks, runlevel [S] */ \
|
||||||
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
|
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
|
||||||
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
|
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
|
||||||
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
||||||
|
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
|
||||||
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
||||||
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
||||||
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
||||||
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
||||||
\
|
\
|
||||||
/* Runtime hooks, runlevel [S1-9] */ \
|
/* Runtime hooks, runlevel [S1-9] */ \
|
||||||
CHOOSE(HOOK_SVC_RECONF, "hook/svc/reconf"), \
|
CHOOSE(HOOK_SVC_RECONF, "nop"), \
|
||||||
CHOOSE(HOOK_SVC_LOST, "hook/svc/lost"), \
|
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
|
||||||
CHOOSE(HOOK_SVC_START, "hook/svc/start"), \
|
|
||||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "hook/sys/runlevel"), \
|
|
||||||
\
|
\
|
||||||
/* Shutdown hooks, runlevel [06] */ \
|
/* Shutdown hooks, runlevel [06] */ \
|
||||||
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
||||||
|
|||||||
+1
-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);
|
||||||
|
|
||||||
@@ -45,7 +40,7 @@ int plugin_exists (hook_point_t no);
|
|||||||
void plugin_run_hook (hook_point_t no, void *arg);
|
void plugin_run_hook (hook_point_t no, void *arg);
|
||||||
void plugin_run_hooks (hook_point_t no);
|
void plugin_run_hooks (hook_point_t no);
|
||||||
|
|
||||||
int plugin_init (uev_ctx_t *ctx, char *path);
|
int plugin_init (uev_ctx_t *ctx);
|
||||||
void plugin_exit (void);
|
void plugin_exit (void);
|
||||||
|
|
||||||
#endif /* FINIT_PRIVATE_H_ */
|
#endif /* FINIT_PRIVATE_H_ */
|
||||||
|
|||||||
+1
-1
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (c)
|
if (c)
|
||||||
err(1, "Failed signalling init to %s", msg);
|
err(1, "Failed signalling init to %s", msg);
|
||||||
do_sleep(2);
|
do_sleep(5);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
runlevel 1
|
||||||
|
hostname rescue
|
||||||
|
tty [12345] @console noclear nologin
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Example HOOK_SVC_LOST plugin, very noisy do not use as-is!
|
/* Work queue helper functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,35 +21,44 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "schedule.h"
|
||||||
#include "plugin.h"
|
|
||||||
|
|
||||||
static void lost_pid(void *arg)
|
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libuEv callback wrapper
|
||||||
|
*/
|
||||||
|
static void cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
pid_t pid = (uintptr_t)arg;
|
struct wq *work = (struct wq *)arg;
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
svc = svc_find_by_pid(pid);
|
if (UEV_ERROR == events) {
|
||||||
if (!svc)
|
uev_timer_start(w);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logit(LOG_INFO, "Lost PID %u, svc %s", pid, svc->cmd);
|
work->cb(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
/*
|
||||||
.name = __FILE__,
|
* Place work on event queue
|
||||||
.hook[HOOK_SVC_LOST] = { .cb = lost_pid },
|
*/
|
||||||
};
|
int schedule_work(struct wq *work)
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
|
||||||
{
|
{
|
||||||
plugin_register(&plugin);
|
int msec;
|
||||||
}
|
|
||||||
|
|
||||||
PLUGIN_EXIT(plugin_exit)
|
if (!work)
|
||||||
{
|
return errno = EINVAL;
|
||||||
plugin_unregister(&plugin);
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Save and restore RTC using hwclock
|
/* Work queue helper functions
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,49 +21,17 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <lite/lite.h>
|
#ifndef FINIT_SCHEDULE_H_
|
||||||
|
#define FINIT_SCHEDULE_H_
|
||||||
|
|
||||||
#include "finit.h"
|
struct wq {
|
||||||
#include "helpers.h"
|
uev_t watcher;
|
||||||
#include "plugin.h"
|
int init;
|
||||||
|
int delay; /* msec delay before starting work */
|
||||||
static void save(void *arg)
|
void (*cb)(void *);
|
||||||
{
|
void *arg;
|
||||||
_d("Saving system clock to RTC ...");
|
|
||||||
/* -w,--systohc, -u,--utc */
|
|
||||||
run_interactive("hwclock -w -u", "Saving system time (UTC) to RTC");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void restore(void *arg)
|
|
||||||
{
|
|
||||||
_d("Restoring system clock from RTC ...");
|
|
||||||
/* -s,--hctosys, -u,--utc */
|
|
||||||
run_interactive("hwclock -s -u", "Restoring system clock (UTC) from RTC");
|
|
||||||
}
|
|
||||||
|
|
||||||
static plugin_t plugin = {
|
|
||||||
.name = __FILE__,
|
|
||||||
.hook[HOOK_BASEFS_UP] = {
|
|
||||||
.cb = restore
|
|
||||||
},
|
|
||||||
.hook[HOOK_SHUTDOWN] = {
|
|
||||||
.cb = save
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
int schedule_work(struct wq *work);
|
||||||
{
|
|
||||||
plugin_register(&plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
PLUGIN_EXIT(plugin_exit)
|
#endif /* FINIT_SCHEDULE_H_ */
|
||||||
{
|
|
||||||
plugin_unregister(&plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Variables:
|
|
||||||
* indent-tabs-mode: t
|
|
||||||
* c-file-style: "linux"
|
|
||||||
* End:
|
|
||||||
*/
|
|
||||||
+15
-4
@@ -120,7 +120,7 @@ int serv_list(char *arg)
|
|||||||
if (fisdir(enabled))
|
if (fisdir(enabled))
|
||||||
do_list("ENABLED ", enabled);
|
do_list("ENABLED ", enabled);
|
||||||
if (fisdir(FINIT_RCSD))
|
if (fisdir(FINIT_RCSD))
|
||||||
do_list("ENABLED ", FINIT_RCSD);
|
do_list("STATIC ", FINIT_RCSD);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -131,17 +131,22 @@ int serv_enable(char *arg)
|
|||||||
char link[80];
|
char link[80];
|
||||||
char path[80];
|
char path[80];
|
||||||
|
|
||||||
|
if (!arg || !arg[0])
|
||||||
|
return serv_list(NULL);
|
||||||
|
|
||||||
if (!strstr(arg, ".conf")) {
|
if (!strstr(arg, ".conf")) {
|
||||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||||
arg = corr;
|
arg = corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushd(FINIT_RCSD);
|
pushd(FINIT_RCSD);
|
||||||
snprintf(path, sizeof(path), "available/%s", arg);
|
mkdir("enabled", 0755);
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", available, arg);
|
||||||
if (!fexist(path))
|
if (!fexist(path))
|
||||||
errx(1, "Cannot find %s", path);
|
errx(1, "Cannot find %s", path);
|
||||||
|
|
||||||
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
|
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
|
||||||
if (fexist(link))
|
if (fexist(link))
|
||||||
errx(1, "%s already exists", link);
|
errx(1, "%s already exists", link);
|
||||||
|
|
||||||
@@ -154,12 +159,15 @@ int serv_disable(char *arg)
|
|||||||
char link[80];
|
char link[80];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
if (!arg || !arg[0])
|
||||||
|
return serv_list(NULL);
|
||||||
|
|
||||||
if (!strstr(arg, ".conf")) {
|
if (!strstr(arg, ".conf")) {
|
||||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||||
arg = corr;
|
arg = corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
|
snprintf(link, sizeof(link), "%s/%s", enabled, arg);
|
||||||
if (stat(link, &st))
|
if (stat(link, &st))
|
||||||
err(1, "Cannot find %s", link);
|
err(1, "Cannot find %s", link);
|
||||||
|
|
||||||
@@ -173,6 +181,9 @@ int serv_touch(char *arg)
|
|||||||
{
|
{
|
||||||
char corr[40];
|
char corr[40];
|
||||||
|
|
||||||
|
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;
|
||||||
|
|||||||
+390
-154
@@ -25,26 +25,35 @@
|
|||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
#include <ctype.h> /* isblank() */
|
#include <ctype.h> /* isblank() */
|
||||||
|
#include <sched.h> /* sched_yield() */
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
|
#include "cgroup.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "inetd.h"
|
#include "inetd.h"
|
||||||
|
#include "pid.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "sm.h"
|
#include "sm.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
|
||||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||||
|
|
||||||
|
static struct wq work = {
|
||||||
|
.cb = service_worker,
|
||||||
|
};
|
||||||
|
|
||||||
static void svc_set_state(svc_t *svc, svc_state_t new);
|
static void svc_set_state(svc_t *svc, svc_state_t new);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,7 +69,8 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
|
|||||||
svc_t *svc = arg;
|
svc_t *svc = arg;
|
||||||
|
|
||||||
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
|
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
|
||||||
svc->timer_cb(svc);
|
if (svc->timer_cb)
|
||||||
|
svc->timer_cb(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,6 +116,18 @@ static int service_timeout_cancel(svc_t *svc)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void redirect_null(void)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen("/dev/null", "w");
|
||||||
|
if (fp) {
|
||||||
|
dup2(fileno(fp), STDOUT_FILENO);
|
||||||
|
dup2(fileno(fp), STDERR_FILENO);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int is_norespawn(void)
|
static int is_norespawn(void)
|
||||||
{
|
{
|
||||||
return sig_stopped() ||
|
return sig_stopped() ||
|
||||||
@@ -118,11 +140,11 @@ static int is_norespawn(void)
|
|||||||
* @svc: Service to start
|
* @svc: Service to start
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* 0 if the service was successfully started. Non-zero otherwise.
|
* 0 if the service was successfully started. Non-zero otherwise.
|
||||||
*/
|
*/
|
||||||
static int service_start(svc_t *svc)
|
static int service_start(svc_t *svc)
|
||||||
{
|
{
|
||||||
int i, result = 0;
|
int i, result = 0, do_progress = 1;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
sigset_t nmask, omask;
|
sigset_t nmask, omask;
|
||||||
|
|
||||||
@@ -145,10 +167,15 @@ static int service_start(svc_t *svc)
|
|||||||
return inetd_start(&svc->inetd);
|
return inetd_start(&svc->inetd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (svc_is_daemon(svc))
|
if (!svc->desc[0])
|
||||||
print_desc("Starting ", svc->desc);
|
do_progress = 0;
|
||||||
else
|
|
||||||
print_desc("", svc->desc);
|
if (do_progress) {
|
||||||
|
if (svc_is_daemon(svc))
|
||||||
|
print_desc("Starting ", svc->desc);
|
||||||
|
else
|
||||||
|
print_desc("", svc->desc);
|
||||||
|
}
|
||||||
|
|
||||||
/* Declare we're waiting for svc to create its pidfile */
|
/* Declare we're waiting for svc to create its pidfile */
|
||||||
svc_starting(svc);
|
svc_starting(svc);
|
||||||
@@ -159,6 +186,12 @@ static int service_start(svc_t *svc)
|
|||||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid > 0) {
|
||||||
|
cgroup_service(svc->cmd, pid);
|
||||||
|
cgroup_assign(svc->cgroup, pid);
|
||||||
|
}
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
int status;
|
int status;
|
||||||
char *home = NULL;
|
char *home = NULL;
|
||||||
@@ -210,30 +243,41 @@ static int service_start(svc_t *svc)
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (svc->log) {
|
if (svc->log.enabled) {
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
if (svc->log.null) {
|
||||||
|
redirect_null();
|
||||||
|
goto logit_done;
|
||||||
|
}
|
||||||
|
if (svc->log.console) {
|
||||||
|
goto logit_done;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open PTY to connect to logger. A pty isn't buffered
|
* Open PTY to connect to logger. A pty isn't buffered
|
||||||
* like a pipe, and it eats newlines so they aren't logged
|
* like a pipe, and it eats newlines so they aren't logged
|
||||||
*/
|
*/
|
||||||
fd = posix_openpt(O_RDWR);
|
fd = posix_openpt(O_RDWR);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
svc->log = 0;
|
svc->log.enabled = 0;
|
||||||
goto logger_err;
|
goto logit_done;
|
||||||
}
|
}
|
||||||
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
|
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
|
||||||
close(fd);
|
close(fd);
|
||||||
svc->log = 0;
|
svc->log.enabled = 0;
|
||||||
goto logger_err;
|
goto logit_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SIGCHLD is still blocked for grantpt() and fork() */
|
/* SIGCHLD is still blocked for grantpt() and fork() */
|
||||||
sigprocmask(SIG_BLOCK, &nmask, NULL);
|
sigprocmask(SIG_BLOCK, &nmask, NULL);
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
int fds = open(ptsname(fd), O_RDONLY);
|
int fds;
|
||||||
|
char *tag = basename(svc->cmd);
|
||||||
|
char *prio = "daemon.info";
|
||||||
|
|
||||||
|
fds = open(ptsname(fd), O_RDONLY);
|
||||||
close(fd);
|
close(fd);
|
||||||
if (fds == -1)
|
if (fds == -1)
|
||||||
_exit(0);
|
_exit(0);
|
||||||
@@ -242,7 +286,22 @@ static int service_start(svc_t *svc)
|
|||||||
/* Reset signals */
|
/* Reset signals */
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
execlp("logger", "logger", "-t", svc->cmd, "-p", "daemon.info", NULL);
|
if (svc->log.file[0] == '/') {
|
||||||
|
char sz[20], num[3];
|
||||||
|
|
||||||
|
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
|
||||||
|
snprintf(num, sizeof(num), "%d", logfile_count_max);
|
||||||
|
|
||||||
|
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->log.ident[0])
|
||||||
|
tag = svc->log.ident;
|
||||||
|
if (svc->log.prio[0])
|
||||||
|
prio = svc->log.prio;
|
||||||
|
|
||||||
|
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,18 +317,13 @@ static int service_start(svc_t *svc)
|
|||||||
dup2(fd, STDERR_FILENO);
|
dup2(fd, STDERR_FILENO);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
fp = fopen("/dev/null", "w");
|
|
||||||
if (fp) {
|
|
||||||
dup2(fileno(fp), STDOUT_FILENO);
|
|
||||||
dup2(fileno(fp), STDERR_FILENO);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#ifdef REDIRECT_OUTPUT
|
||||||
|
else
|
||||||
|
redirect_null();
|
||||||
|
#endif
|
||||||
|
|
||||||
logger_err:
|
logit_done:
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
if (svc->inetd.cmd)
|
if (svc->inetd.cmd)
|
||||||
@@ -288,9 +342,9 @@ static int service_start(svc_t *svc)
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
if (svc->log)
|
if (svc->log.enabled && !svc->log.null)
|
||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
exit(status);
|
_exit(status);
|
||||||
} else if (log_is_debug()) {
|
} else if (log_is_debug()) {
|
||||||
char buf[CMD_SIZE] = "";
|
char buf[CMD_SIZE] = "";
|
||||||
|
|
||||||
@@ -305,30 +359,34 @@ static int service_start(svc_t *svc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
svc->pid = pid;
|
svc->pid = pid;
|
||||||
|
svc->start_time = jiffies();
|
||||||
|
|
||||||
|
switch (svc->type) {
|
||||||
|
case SVC_TYPE_RUN:
|
||||||
|
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||||
|
svc->start_time = svc->pid = 0;
|
||||||
|
svc->once++;
|
||||||
|
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVC_TYPE_SERVICE:
|
||||||
|
pid_file_create(svc);
|
||||||
|
break;
|
||||||
|
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
if (svc_is_inetd_conn(svc) && svc->inetd.type == SOCK_STREAM)
|
case SVC_TYPE_INETD_CONN:
|
||||||
close(svc->stdin_fd);
|
if (svc->inetd.type == SOCK_STREAM)
|
||||||
|
close(svc->stdin_fd);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (SVC_TYPE_RUN == svc->type) {
|
default:
|
||||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
break;
|
||||||
if (!svc_clean_bootstrap(svc)) {
|
|
||||||
svc->pid = 0;
|
|
||||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
|
||||||
print_result(result);
|
|
||||||
|
|
||||||
/*
|
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||||
* Only run hook on successful start, and *after* having printed
|
if (do_progress)
|
||||||
* the result, otherwise any hook tasks may overwrite it and the
|
print_result(result);
|
||||||
* result would be like double "[ OK ]" but only one service.
|
|
||||||
*/
|
|
||||||
if (!result)
|
|
||||||
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -349,10 +407,10 @@ static void service_kill(svc_t *svc)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
|
||||||
if (runlevel != 1)
|
if (runlevel != 1)
|
||||||
print_desc("Killing ", svc->desc);
|
print_desc("Killing ", svc->desc);
|
||||||
|
|
||||||
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
|
|
||||||
kill(svc->pid, SIGKILL);
|
kill(svc->pid, SIGKILL);
|
||||||
|
|
||||||
/* Let SIGKILLs stand out, show result as [WARN] */
|
/* Let SIGKILLs stand out, show result as [WARN] */
|
||||||
@@ -365,7 +423,7 @@ static void service_kill(svc_t *svc)
|
|||||||
* @svc: Service to stop
|
* @svc: Service to stop
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* 0 if the service was successfully stopped. Non-zero otherwise.
|
* 0 if the service was successfully stopped. Non-zero otherwise.
|
||||||
*/
|
*/
|
||||||
static int service_stop(svc_t *svc)
|
static int service_stop(svc_t *svc)
|
||||||
{
|
{
|
||||||
@@ -374,33 +432,36 @@ static int service_stop(svc_t *svc)
|
|||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if (svc->state <= SVC_STOPPING_STATE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
if (svc_is_inetd(svc)) {
|
if (svc_is_inetd(svc)) {
|
||||||
int do_print = runlevel != 1 && !svc_is_busy(svc);
|
int do_progress = runlevel != 1 && !svc_is_busy(svc);
|
||||||
|
|
||||||
if (do_print)
|
if (do_progress)
|
||||||
print_desc("Stopping ", svc->desc);
|
print_desc("Stopping ", svc->desc);
|
||||||
|
|
||||||
inetd_stop(&svc->inetd);
|
inetd_stop(&svc->inetd);
|
||||||
|
|
||||||
if (do_print)
|
if (do_progress)
|
||||||
print_result(0);
|
print_result(0);
|
||||||
|
|
||||||
|
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
service_timeout_cancel(svc);
|
||||||
|
|
||||||
if (svc->pid <= 1) {
|
if (svc->pid <= 1)
|
||||||
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
if (SVC_TYPE_SERVICE != svc->type)
|
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||||
return 0;
|
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||||
|
|
||||||
if (runlevel != 1)
|
if (runlevel != 1)
|
||||||
print_desc("Stopping ", svc->desc);
|
print_desc("Stopping ", svc->desc);
|
||||||
|
|
||||||
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
|
||||||
res = kill(svc->pid, SIGTERM);
|
res = kill(svc->pid, SIGTERM);
|
||||||
|
|
||||||
if (runlevel != 1)
|
if (runlevel != 1)
|
||||||
@@ -415,12 +476,15 @@ static int service_stop(svc_t *svc)
|
|||||||
*
|
*
|
||||||
* This function does some basic checks of the runtime state of Finit
|
* This function does some basic checks of the runtime state of Finit
|
||||||
* and a sanity check of the @svc before sending %SIGHUP.
|
* and a sanity check of the @svc before sending %SIGHUP.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* POSIX OK(0) or non-zero on error.
|
* POSIX OK(0) or non-zero on error.
|
||||||
*/
|
*/
|
||||||
static int service_restart(svc_t *svc)
|
static int service_restart(svc_t *svc)
|
||||||
{
|
{
|
||||||
|
int do_progress = 1;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* Ignore if finit is SIGSTOP'ed */
|
/* Ignore if finit is SIGSTOP'ed */
|
||||||
if (is_norespawn())
|
if (is_norespawn())
|
||||||
return 1;
|
return 1;
|
||||||
@@ -430,17 +494,33 @@ static int service_restart(svc_t *svc)
|
|||||||
|
|
||||||
if (svc->pid <= 1) {
|
if (svc->pid <= 1) {
|
||||||
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
||||||
svc->pid = 0;
|
svc->start_time = svc->pid = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_desc("Restarting ", svc->desc);
|
/* Skip progress if desc disabled or bootstrap task */
|
||||||
|
if (!svc->desc[0] || svc_in_runlevel(svc, 0))
|
||||||
|
do_progress = 0;
|
||||||
|
|
||||||
|
if (do_progress)
|
||||||
|
print_desc("Restarting ", svc->desc);
|
||||||
|
|
||||||
|
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||||
|
rc = kill(svc->pid, SIGHUP);
|
||||||
|
|
||||||
/* Declare we're waiting for svc to re-assert/touch its pidfile */
|
/* Declare we're waiting for svc to re-assert/touch its pidfile */
|
||||||
svc_starting(svc);
|
svc_starting(svc);
|
||||||
|
|
||||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
/* Service does not maintain a PID file on its own */
|
||||||
return print_result(kill(svc->pid, SIGHUP));
|
if (svc_has_pidfile(svc)) {
|
||||||
|
sched_yield();
|
||||||
|
touch(pid_file(svc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_progress)
|
||||||
|
print_result(rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -465,15 +545,92 @@ void service_reload_dynamic(void)
|
|||||||
*/
|
*/
|
||||||
void service_runlevel(int newlevel)
|
void service_runlevel(int newlevel)
|
||||||
{
|
{
|
||||||
|
if (!rescue && runlevel <= 1 && newlevel > 1)
|
||||||
|
networking(1);
|
||||||
|
|
||||||
sm_set_runlevel(&sm, newlevel);
|
sm_set_runlevel(&sm, newlevel);
|
||||||
sm_step(&sm);
|
sm_step(&sm);
|
||||||
|
|
||||||
|
if (!rescue && runlevel <= 1)
|
||||||
|
networking(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* log:/path/to/logfile,priority:facility.level,tag:ident
|
||||||
|
*/
|
||||||
|
static void parse_log(svc_t *svc, char *arg)
|
||||||
|
{
|
||||||
|
char *tok;
|
||||||
|
|
||||||
|
tok = strtok(arg, ":, ");
|
||||||
|
while (tok) {
|
||||||
|
if (!strcmp(tok, "log"))
|
||||||
|
svc->log.enabled = 1;
|
||||||
|
else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null"))
|
||||||
|
svc->log.null = 1;
|
||||||
|
else if (!strcmp(tok, "console") || !strcmp(tok, "/dev/console"))
|
||||||
|
svc->log.console = 1;
|
||||||
|
else if (tok[0] == '/')
|
||||||
|
strlcpy(svc->log.file, tok, sizeof(svc->log.file));
|
||||||
|
else if (!strcmp(tok, "priority") || !strcmp(tok, "prio"))
|
||||||
|
strlcpy(svc->log.prio, strtok(NULL, ","), sizeof(svc->log.prio));
|
||||||
|
else if (!strcmp(tok, "tag") || !strcmp(tok, "identity") || !strcmp(tok, "ident"))
|
||||||
|
strlcpy(svc->log.ident, strtok(NULL, ","), sizeof(svc->log.ident));
|
||||||
|
|
||||||
|
tok = strtok(NULL, ":=, ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* name:<name>
|
||||||
|
*/
|
||||||
|
static void parse_name(svc_t *svc, char *arg)
|
||||||
|
{
|
||||||
|
char *name = NULL;
|
||||||
|
|
||||||
|
if (arg && !strncasecmp(arg, "name:", 5)) {
|
||||||
|
name = arg + 5;
|
||||||
|
} else {
|
||||||
|
name = strrchr(svc->cmd, '/');
|
||||||
|
name = name ? name + 1 : svc->cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(svc->name, name, sizeof(svc->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse_cmdline_args - Update the command line args in the svc struct
|
||||||
|
*
|
||||||
|
* strtok internal pointer must be positioned at first command line arg
|
||||||
|
* when this function is called.
|
||||||
|
*
|
||||||
|
* Side effect: strtok internal pointer will be modified.
|
||||||
|
*/
|
||||||
|
static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *arg;
|
||||||
|
|
||||||
|
strlcpy(svc->args[0], cmd, sizeof(svc->args[0]));
|
||||||
|
|
||||||
|
/* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
|
||||||
|
* array to be zero-terminated. */
|
||||||
|
for (i=1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS-1); i++)
|
||||||
|
strlcpy(svc->args[i], arg, sizeof(svc->args[0]));
|
||||||
|
|
||||||
|
/* clear remaining args in case they were set earlier.
|
||||||
|
* This also zero-terminates the args array.*/
|
||||||
|
for (; i < MAX_NUM_SVC_ARGS; i++)
|
||||||
|
svc->args[i][0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* service_register - Register service, task or run commands
|
* service_register - Register service, task or run commands
|
||||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||||
* @cfg: Configuration, complete command, with -- for description text
|
* @cfg: Configuration, complete command, with -- for description text
|
||||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||||
|
* @cgroup: Control group, may be default
|
||||||
* @file: The file name service was loaded from
|
* @file: The file name service was loaded from
|
||||||
*
|
*
|
||||||
* This function is used to register commands to be run on different
|
* This function is used to register commands to be run on different
|
||||||
@@ -507,7 +664,7 @@ void service_runlevel(int newlevel)
|
|||||||
*
|
*
|
||||||
* Service conditions can be: svc/<PATH> for PID files, net/<IFNAME>/up
|
* Service conditions can be: svc/<PATH> for PID files, net/<IFNAME>/up
|
||||||
* and net/<IFNAME>/exists. The condition handling is further described
|
* and net/<IFNAME>/exists. The condition handling is further described
|
||||||
* doc/conditions.md.
|
* docs/conditions.md.
|
||||||
*
|
*
|
||||||
* For multiple instances of the same command, e.g. multiple DHCP
|
* For multiple instances of the same command, e.g. multiple DHCP
|
||||||
* clients, the user must enter an ID, using the :ID syntax.
|
* clients, the user must enter an ID, using the :ID syntax.
|
||||||
@@ -521,17 +678,20 @@ void service_runlevel(int newlevel)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||||
*/
|
*/
|
||||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
int service_register(int type, char *cfg, struct rlimit rlimit[], char *cgroup, char *file)
|
||||||
{
|
{
|
||||||
int i = 0;
|
char id_str[MAX_ID_LEN];
|
||||||
int id = -1;
|
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
int forking = 0;
|
int forking = 0;
|
||||||
#endif
|
#endif
|
||||||
int log = 0, levels = 0;
|
int levels = 0;
|
||||||
char *username = NULL, *line;
|
int manual = 0;
|
||||||
|
char *line;
|
||||||
|
char *id = NULL;
|
||||||
|
char *username = NULL, *log = NULL, *pid = NULL;
|
||||||
char *service = NULL, *proto = NULL, *ifaces = NULL;
|
char *service = NULL, *proto = NULL, *ifaces = NULL;
|
||||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||||
|
char *name = NULL;
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
plugin_t *plugin = NULL;
|
plugin_t *plugin = NULL;
|
||||||
|
|
||||||
@@ -551,6 +711,15 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
|
|
||||||
while (*desc && isblank(*desc))
|
while (*desc && isblank(*desc))
|
||||||
desc++;
|
desc++;
|
||||||
|
} else {
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
/* Find "--\n" to denote empty/no description */
|
||||||
|
pos = (int)strlen(line) - 2;
|
||||||
|
if (pos > 0 && !strcmp(&line[pos], "--")) {
|
||||||
|
line[pos] = 0;
|
||||||
|
desc = &line[pos];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = strtok(line, " ");
|
cmd = strtok(line, " ");
|
||||||
@@ -562,24 +731,30 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (cmd) {
|
while (cmd) {
|
||||||
if (cmd[0] == '@') /* @username[:group] */
|
if (cmd[0] == '@') /* @username[:group] */
|
||||||
username = &cmd[1];
|
username = &cmd[1];
|
||||||
else if (cmd[0] == '[') /* [runlevels] */
|
else if (cmd[0] == '[') /* [runlevels] */
|
||||||
runlevels = &cmd[0];
|
runlevels = &cmd[0];
|
||||||
else if (cmd[0] == '<') /* <[!][cond][,cond..]> */
|
else if (cmd[0] == '<') /* <[!][cond][,cond..]> */
|
||||||
cond = &cmd[1];
|
cond = &cmd[1];
|
||||||
else if (cmd[0] == ':') /* :ID */
|
else if (cmd[0] == ':') /* :ID */
|
||||||
id = atoi(&cmd[1]);
|
id = &cmd[1];
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
else if (!strncasecmp(cmd, "nowait", 6))
|
else if (!strncasecmp(cmd, "nowait", 6))
|
||||||
forking = 1;
|
forking = 1;
|
||||||
else if (!strncasecmp(cmd, "wait", 4))
|
else if (!strncasecmp(cmd, "wait", 4))
|
||||||
forking = 0;
|
forking = 0;
|
||||||
#endif
|
#endif
|
||||||
|
else if (!strncasecmp(cmd, "log", 3))
|
||||||
|
log = cmd;
|
||||||
|
else if (!strncasecmp(cmd, "pid", 3))
|
||||||
|
pid = cmd;
|
||||||
|
else if (!strncasecmp(cmd, "name:", 5))
|
||||||
|
name = cmd;
|
||||||
|
else if (!strncasecmp(cmd, "manual:yes", 10))
|
||||||
|
manual = 1;
|
||||||
else if (cmd[0] != '/' && strchr(cmd, '/'))
|
else if (cmd[0] != '/' && strchr(cmd, '/'))
|
||||||
service = cmd; /* inetd service/proto */
|
service = cmd; /* inetd service/proto */
|
||||||
else if (!strncasecmp(cmd, "log", 3))
|
|
||||||
log = 1;
|
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -629,29 +804,40 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if known inetd, then add ifnames for filtering only. */
|
/* Check if known inetd, update command line, then add ifnames for filtering only. */
|
||||||
svc = inetd_find_svc(cmd, service, proto);
|
svc = inetd_find_svc(cmd, service, proto);
|
||||||
if (svc)
|
if (svc) {
|
||||||
|
parse_cmdline_args(svc, cmd);
|
||||||
goto inetd_setup;
|
goto inetd_setup;
|
||||||
|
}
|
||||||
|
if (!id) {
|
||||||
|
int n = svc_next_id_int(cmd);
|
||||||
|
|
||||||
if (id <= 0)
|
if (n) {
|
||||||
id = svc_next_id(cmd);
|
snprintf(id_str, sizeof(id_str), "%d", n);
|
||||||
|
id = id_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
recreate:
|
recreate:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (id <= 0)
|
if (!id)
|
||||||
id = 1;
|
id = "1";
|
||||||
|
|
||||||
svc = svc_find(cmd, id);
|
svc = svc_find(cmd, id);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_d("Creating new svc for %s id #%d type %d", cmd, id, type);
|
_d("Creating new svc for %s id #%s type %d", cmd, id, type);
|
||||||
svc = svc_new(cmd, id, type);
|
svc = svc_new(cmd, id, type);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_e("Out of memory, cannot register service %s", cmd);
|
_e("Out of memory, cannot register service %s", cmd);
|
||||||
free(line);
|
free(line);
|
||||||
return errno = ENOMEM;
|
return errno = ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == SVC_TYPE_SERVICE && manual) {
|
||||||
|
svc_stop(svc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
else {
|
else {
|
||||||
@@ -664,9 +850,11 @@ recreate:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
svc->log = log;
|
/* Always clear svc PID file, for now. See TODO */
|
||||||
if (desc)
|
svc->pidfile[0] = 0;
|
||||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
/* Decode any optional pid:/optional/path/to/file.pid */
|
||||||
|
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid))
|
||||||
|
_e("Invalid 'pid' argument to service: %s", pid);
|
||||||
|
|
||||||
if (username) {
|
if (username) {
|
||||||
char *ptr = strchr(username, ':');
|
char *ptr = strchr(username, ':');
|
||||||
@@ -682,18 +870,21 @@ recreate:
|
|||||||
/* Internal plugin provides this service */
|
/* Internal plugin provides this service */
|
||||||
svc->inetd.cmd = plugin->inetd.cmd;
|
svc->inetd.cmd = plugin->inetd.cmd;
|
||||||
svc->inetd.builtin = 1;
|
svc->inetd.builtin = 1;
|
||||||
} else {
|
} else
|
||||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
parse_cmdline_args(svc, cmd);
|
||||||
while ((cmd = strtok(NULL, " ")))
|
|
||||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
|
||||||
svc->args[i][0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
svc->runlevels = levels;
|
svc->runlevels = levels;
|
||||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||||
|
|
||||||
conf_parse_cond(svc, cond);
|
conf_parse_cond(svc, cond);
|
||||||
|
|
||||||
|
parse_name(svc, name);
|
||||||
|
|
||||||
|
if (log)
|
||||||
|
parse_log(svc, log);
|
||||||
|
if (desc)
|
||||||
|
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||||
|
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
if (svc_is_inetd(svc)) {
|
if (svc_is_inetd(svc)) {
|
||||||
char *iface, *name = service;
|
char *iface, *name = service;
|
||||||
@@ -726,26 +917,69 @@ recreate:
|
|||||||
/* Set configured limits */
|
/* Set configured limits */
|
||||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||||
|
|
||||||
|
/* Register desired control group */
|
||||||
|
strlcpy(svc->cgroup, cgroup, sizeof(svc->cgroup));
|
||||||
|
|
||||||
/* New, recently modified or unchanged ... used on reload. */
|
/* New, recently modified or unchanged ... used on reload. */
|
||||||
if (file && conf_changed(file))
|
if (file && conf_changed(file))
|
||||||
svc_mark_dirty(svc);
|
svc_mark_dirty(svc);
|
||||||
else
|
else
|
||||||
svc_mark_clean(svc);
|
svc_mark_clean(svc);
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
svc->protect = 1;
|
||||||
|
|
||||||
/* Free duped line, from above */
|
/* Free duped line, from above */
|
||||||
free(line);
|
free(line);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is called when cleaning up lingering (stopped) services
|
||||||
|
* after a .conf reload, as well as when an inetd connection terminates.
|
||||||
|
*
|
||||||
|
* We need to ensure we properly stop the service before removing it,
|
||||||
|
* including stopping any pending restart or SIGKILL timers before we
|
||||||
|
* proceed to free() the svc itself.
|
||||||
|
*
|
||||||
|
* Remember to not try to stop inetd connections, they only get here
|
||||||
|
* when already stopped, if we do we end up in a recursive loop.
|
||||||
|
*/
|
||||||
void service_unregister(svc_t *svc)
|
void service_unregister(svc_t *svc)
|
||||||
{
|
{
|
||||||
|
if (!svc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only try stopping @svc if it's *not* an inetd connection.
|
||||||
|
* Prevents infinite recursion when called from service_step()
|
||||||
|
*/
|
||||||
|
switch (svc->type) {
|
||||||
|
#ifdef INETD_ENABLED
|
||||||
|
case SVC_TYPE_INETD:
|
||||||
|
inetd_del(&svc->inetd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVC_TYPE_INETD_CONN:
|
||||||
|
/* inetd connection, if UDP unblock parent */
|
||||||
|
if (svc_is_busy(svc->inetd.svc)) {
|
||||||
|
svc_unblock(svc->inetd.svc);
|
||||||
|
service_step(svc->inetd.svc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
service_stop(svc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
svc_del(svc);
|
svc_del(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void service_monitor(pid_t lost)
|
void service_monitor(pid_t lost)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
char pidfile[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||||
return;
|
return;
|
||||||
@@ -753,8 +987,6 @@ void service_monitor(pid_t lost)
|
|||||||
if (tty_respawn(lost))
|
if (tty_respawn(lost))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plugin_run_hook(HOOK_SVC_LOST, (void *)(uintptr_t)lost);
|
|
||||||
|
|
||||||
svc = svc_find_by_pid(lost);
|
svc = svc_find_by_pid(lost);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_d("collected unknown PID %d", lost);
|
_d("collected unknown PID %d", lost);
|
||||||
@@ -764,19 +996,22 @@ void service_monitor(pid_t lost)
|
|||||||
_d("collected %s(%d)", svc->cmd, lost);
|
_d("collected %s(%d)", svc->cmd, lost);
|
||||||
|
|
||||||
/* Try removing PID file (in case service does not clean up after itself) */
|
/* Try removing PID file (in case service does not clean up after itself) */
|
||||||
snprintf(pidfile, sizeof(pidfile), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
if (svc_is_daemon(svc)) {
|
||||||
if (remove(pidfile)) {
|
char *fn;
|
||||||
if (errno != ENOENT)
|
|
||||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), pidfile);
|
fn = pid_file(svc);
|
||||||
|
if (remove(fn) && errno != ENOENT)
|
||||||
|
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No longer running, update books. */
|
/* No longer running, update books. */
|
||||||
svc->pid = 0;
|
svc->start_time = svc->pid = 0;
|
||||||
service_step(svc);
|
|
||||||
|
|
||||||
/* Clean out any bootstrap tasks, they've had their time in the sun. */
|
if (!service_step(svc)) {
|
||||||
if (svc_clean_bootstrap(svc))
|
/* Clean out any bootstrap tasks, they've had their time in the sun. */
|
||||||
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
|
if (svc_clean_bootstrap(svc))
|
||||||
|
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
|
||||||
|
}
|
||||||
|
|
||||||
sm_step(&sm);
|
sm_step(&sm);
|
||||||
}
|
}
|
||||||
@@ -790,13 +1025,13 @@ static void service_retry(svc_t *svc)
|
|||||||
|
|
||||||
if (svc->state != SVC_HALTED_STATE ||
|
if (svc->state != SVC_HALTED_STATE ||
|
||||||
svc->block != SVC_BLOCK_RESTARTING) {
|
svc->block != SVC_BLOCK_RESTARTING) {
|
||||||
_d("%s not crashing anymore", svc->desc);
|
_d("%s not crashing anymore", svc->cmd);
|
||||||
*restart_cnt = 0;
|
*restart_cnt = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*restart_cnt >= RESPAWN_MAX) {
|
if (*restart_cnt >= RESPAWN_MAX) {
|
||||||
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->desc);
|
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->cmd);
|
||||||
svc_crashing(svc);
|
svc_crashing(svc);
|
||||||
*restart_cnt = 0;
|
*restart_cnt = 0;
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
@@ -805,7 +1040,7 @@ static void service_retry(svc_t *svc)
|
|||||||
|
|
||||||
(*restart_cnt)++;
|
(*restart_cnt)++;
|
||||||
|
|
||||||
_d("%s crashed, trying to start it again, attempt %d", svc->desc, *restart_cnt);
|
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
|
||||||
@@ -820,20 +1055,26 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
|||||||
|
|
||||||
*state = new;
|
*state = new;
|
||||||
|
|
||||||
/* if the PID isn't collected within 3s, kill it! */
|
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
|
||||||
if (*state == SVC_STOPPING_STATE) {
|
if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) {
|
||||||
service_timeout_cancel(svc);
|
service_timeout_cancel(svc);
|
||||||
service_timeout_after(svc, 3000, service_kill);
|
service_timeout_after(svc, SVC_TERM_TIMEOUT, service_kill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void service_step(svc_t *svc)
|
/*
|
||||||
|
* Transition inetd/task/run/service
|
||||||
|
*
|
||||||
|
* Returns: non-zero if the @svc is no longer valid (removed)
|
||||||
|
*/
|
||||||
|
int service_step(svc_t *svc)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
|
||||||
svc_cmd_t enabled;
|
|
||||||
svc_state_t old_state;
|
|
||||||
cond_state_t cond;
|
cond_state_t cond;
|
||||||
|
svc_state_t old_state;
|
||||||
|
svc_cmd_t enabled;
|
||||||
|
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||||
|
int changed = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
restart:
|
restart:
|
||||||
old_state = svc->state;
|
old_state = svc->state;
|
||||||
@@ -852,12 +1093,8 @@ restart:
|
|||||||
case SVC_DONE_STATE:
|
case SVC_DONE_STATE:
|
||||||
#ifdef INETD_ENABLED
|
#ifdef INETD_ENABLED
|
||||||
if (svc_is_inetd_conn(svc)) {
|
if (svc_is_inetd_conn(svc)) {
|
||||||
if (svc_is_busy(svc->inetd.svc)) {
|
|
||||||
svc_unblock(svc->inetd.svc);
|
|
||||||
service_step(svc->inetd.svc);
|
|
||||||
}
|
|
||||||
service_unregister(svc);
|
service_unregister(svc);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (svc_is_changed(svc))
|
if (svc_is_changed(svc))
|
||||||
@@ -896,28 +1133,22 @@ restart:
|
|||||||
if (sm_is_in_teardown(&sm))
|
if (sm_is_in_teardown(&sm))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
|
||||||
* Make state transition *before* service_start(), because
|
|
||||||
* of HOOK_SVC_START, which may call service_step()
|
|
||||||
*/
|
|
||||||
svc_mark_clean(svc);
|
|
||||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
|
||||||
|
|
||||||
err = service_start(svc);
|
err = service_start(svc);
|
||||||
if (err) {
|
if (err) {
|
||||||
(*restart_cnt)++;
|
(*restart_cnt)++;
|
||||||
svc_set_state(svc, SVC_READY_STATE);
|
|
||||||
|
|
||||||
if (!svc_is_inetd_conn(svc))
|
if (!svc_is_inetd_conn(svc))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Everything went fine, clean and set state */
|
||||||
|
svc_mark_clean(svc);
|
||||||
|
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_RUNNING_STATE:
|
case SVC_RUNNING_STATE:
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
service_stop(svc);
|
service_stop(svc);
|
||||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,7 +1161,7 @@ restart:
|
|||||||
* Restart directly after the first crash,
|
* Restart directly after the first crash,
|
||||||
* then retry after 2 sec
|
* then retry after 2 sec
|
||||||
*/
|
*/
|
||||||
_d("delayed restart of %s", svc->desc);
|
_d("delayed restart of %s", svc->cmd);
|
||||||
service_timeout_after(svc, 1, service_retry);
|
service_timeout_after(svc, 1, service_retry);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -942,6 +1173,7 @@ restart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (svc_is_runtask(svc)) {
|
if (svc_is_runtask(svc)) {
|
||||||
|
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||||
svc->once++;
|
svc->once++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -951,7 +1183,6 @@ restart:
|
|||||||
switch (cond) {
|
switch (cond) {
|
||||||
case COND_OFF:
|
case COND_OFF:
|
||||||
service_stop(svc);
|
service_stop(svc);
|
||||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COND_FLUX:
|
case COND_FLUX:
|
||||||
@@ -967,8 +1198,12 @@ restart:
|
|||||||
break;
|
break;
|
||||||
service_restart(svc);
|
service_restart(svc);
|
||||||
} else {
|
} else {
|
||||||
service_stop(svc);
|
#ifdef INETD_ENABLED
|
||||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
if (svc_is_inetd(svc))
|
||||||
|
inetd_stop_children(&svc->inetd, 1);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
service_stop(svc);
|
||||||
}
|
}
|
||||||
svc_mark_clean(svc);
|
svc_mark_clean(svc);
|
||||||
}
|
}
|
||||||
@@ -980,7 +1215,6 @@ restart:
|
|||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
kill(svc->pid, SIGCONT);
|
kill(svc->pid, SIGCONT);
|
||||||
service_stop(svc);
|
service_stop(svc);
|
||||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -995,12 +1229,18 @@ restart:
|
|||||||
case COND_ON:
|
case COND_ON:
|
||||||
kill(svc->pid, SIGCONT);
|
kill(svc->pid, SIGCONT);
|
||||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||||
|
/* Reassert condition if we go from waiting and no change */
|
||||||
|
if (!svc_is_changed(svc)) {
|
||||||
|
char cond[MAX_COND_LEN];
|
||||||
|
|
||||||
|
mkcond(cond, sizeof(cond), svc->cmd);
|
||||||
|
cond_set_path(cond_path(cond), COND_ON);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COND_OFF:
|
case COND_OFF:
|
||||||
kill(svc->pid, SIGCONT);
|
kill(svc->pid, SIGCONT);
|
||||||
service_stop(svc);
|
service_stop(svc);
|
||||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COND_FLUX:
|
case COND_FLUX:
|
||||||
@@ -1011,8 +1251,18 @@ restart:
|
|||||||
|
|
||||||
if (svc->state != old_state) {
|
if (svc->state != old_state) {
|
||||||
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
|
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
|
||||||
|
changed++;
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When a run/task/service changes state, e.g. transitioning from
|
||||||
|
* waiting to running, other services may need to change state too.
|
||||||
|
*/
|
||||||
|
if (changed)
|
||||||
|
schedule_work(&work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void service_step_all(int types)
|
void service_step_all(int types)
|
||||||
@@ -1020,6 +1270,11 @@ void service_step_all(int types)
|
|||||||
svc_foreach_type(types, service_step);
|
svc_foreach_type(types, service_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void service_worker(void *unused)
|
||||||
|
{
|
||||||
|
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_clean_runtask - Clear once flag of runtasks
|
* svc_clean_runtask - Clear once flag of runtasks
|
||||||
*
|
*
|
||||||
@@ -1040,21 +1295,20 @@ void service_runtask_clean(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* service_runtask_completed - Have run/task completed in current runlevel
|
* service_completed - Have run/task completed in current runlevel
|
||||||
* @skip: Hardcoded list of conditions to skip
|
|
||||||
*
|
*
|
||||||
* This function checks if all run/task have run once in the current
|
* This function checks if all run/task have run once in the current
|
||||||
* runlevel. E.g., at bootstrap we must wait for these scripts or
|
* runlevel. E.g., at bootstrap we must wait for these scripts or
|
||||||
* programs to complete their run before switching to the configured
|
* programs to complete their run before switching to the configured
|
||||||
* runlevel.
|
* runlevel.
|
||||||
*
|
*
|
||||||
* Setting @skip means skipping tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP
|
* All tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP set in their condition
|
||||||
* set in their condition mask. These tasks cannot run until finalize()
|
* mask are skipped. These tasks cannot run until finalize()
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* %TRUE(1) or %FALSE(0)
|
* %TRUE(1) or %FALSE(0)
|
||||||
*/
|
*/
|
||||||
int service_runtask_completed(int skip)
|
int service_completed(void)
|
||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
@@ -1081,24 +1335,6 @@ int service_runtask_completed(int skip)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void service_bootstrap_cb(uev_t *w, void *arg, int events)
|
|
||||||
{
|
|
||||||
static int cnt = 10;
|
|
||||||
void (*finalize)(void) = arg;
|
|
||||||
|
|
||||||
_d("Checking completion of all run/task ... %d", cnt);
|
|
||||||
if (cnt-- > 0 && !service_runtask_completed(!runlevel))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (cnt > 0)
|
|
||||||
_d("All run/task have completed, resuming bootstrap.");
|
|
||||||
else
|
|
||||||
_d("Timeout, resuming bootstrap.");
|
|
||||||
cnt = 10;
|
|
||||||
uev_timer_stop(w);
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
+4
-3
@@ -28,16 +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 rlimit rlimit[], char *file);
|
int service_register (int type, char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||||
void service_unregister (svc_t *svc);
|
void service_unregister (svc_t *svc);
|
||||||
|
|
||||||
void service_runtask_clean (void);
|
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);
|
||||||
|
|
||||||
void service_bootstrap_cb (uev_t *w, void *arg, int events);
|
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);
|
||||||
}
|
}
|
||||||
@@ -229,6 +242,10 @@ static void sighup_cb(uev_t *w, void *arg, int events)
|
|||||||
return;
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -137,19 +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();
|
conf_reload();
|
||||||
|
|
||||||
/* Reset once flag of runtasks */
|
/* Reset once flag of runtasks */
|
||||||
service_runtask_clean();
|
service_runtask_clean();
|
||||||
|
|
||||||
_d("Stopping services services not allowed in new runlevel ...");
|
_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);
|
||||||
|
|
||||||
@@ -171,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);
|
||||||
|
|
||||||
@@ -205,7 +218,7 @@ restart:
|
|||||||
* 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);
|
||||||
|
|||||||
@@ -115,6 +115,9 @@ void stty(int fd, speed_t speed)
|
|||||||
term.c_cc[VKILL] = CTRL('U');
|
term.c_cc[VKILL] = CTRL('U');
|
||||||
term.c_cc[VERASE] = CERASE;
|
term.c_cc[VERASE] = CERASE;
|
||||||
tcsetattr(fd, TCSANOW, &term);
|
tcsetattr(fd, TCSANOW, &term);
|
||||||
|
|
||||||
|
/* Restore cursor if it was hidden previously */
|
||||||
|
write(fd, "\033[?25h", 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@@ -32,11 +33,40 @@
|
|||||||
#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 TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
||||||
|
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
|
||||||
|
|
||||||
|
static void svc_gc(void *arg)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
struct wq *work = (struct wq *)arg;
|
||||||
|
svc_t *svc, *next;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
||||||
|
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
|
||||||
|
char cond[MAX_COND_LEN];
|
||||||
|
int msec;
|
||||||
|
|
||||||
|
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
|
||||||
|
(now.tv_nsec - svc->gc.tv_nsec) / 1000000;
|
||||||
|
if (msec < SVC_TERM_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&gc_list, svc, link);
|
||||||
|
cond_clear(mkcond(cond, sizeof(cond), svc->cmd));
|
||||||
|
free(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TAILQ_EMPTY(&gc_list))
|
||||||
|
schedule_work(work);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_new - Create a new service
|
* svc_new - Create a new service
|
||||||
@@ -47,10 +77,9 @@ static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_new(char *cmd, int id, int type)
|
svc_t *svc_new(char *cmd, char *id, int type)
|
||||||
{
|
{
|
||||||
int job = -1;
|
int job = -1;
|
||||||
char *desc;
|
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
/* Find first job n:o if registering multiple instances */
|
/* Find first job n:o if registering multiple instances */
|
||||||
@@ -69,24 +98,24 @@ svc_t *svc_new(char *cmd, int id, int type)
|
|||||||
|
|
||||||
svc->type = type;
|
svc->type = type;
|
||||||
svc->job = job;
|
svc->job = job;
|
||||||
svc->id = id;
|
strlcpy(svc->id, id, sizeof(svc->id));
|
||||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
|
||||||
/* Default description, if missing */
|
/* Default description, if missing */
|
||||||
desc = rindex(cmd, '/');
|
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
||||||
if (desc)
|
|
||||||
desc++;
|
|
||||||
else
|
|
||||||
desc = cmd;
|
|
||||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(&svc_list, svc, link);
|
TAILQ_INSERT_TAIL(&svc_list, svc, link);
|
||||||
|
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
@@ -95,8 +124,10 @@ svc_t *svc_new(char *cmd, int id, int type)
|
|||||||
int svc_del(svc_t *svc)
|
int svc_del(svc_t *svc)
|
||||||
{
|
{
|
||||||
TAILQ_REMOVE(&svc_list, svc, link);
|
TAILQ_REMOVE(&svc_list, svc, link);
|
||||||
memset(svc, 0, sizeof(*svc));
|
TAILQ_INSERT_TAIL(&gc_list, svc, link);
|
||||||
free(svc);
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &svc->gc);
|
||||||
|
schedule_work(&work);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -167,7 +198,7 @@ svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
|
|||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||||
char *name = basename(svc->cmd);
|
char *name = svc->name;
|
||||||
|
|
||||||
if (!strncmp(name, cmd, strlen(name)))
|
if (!strncmp(name, cmd, strlen(name)))
|
||||||
return svc;
|
return svc;
|
||||||
@@ -205,7 +236,7 @@ svc_t *svc_job_iterator(svc_t **iter, 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, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
@@ -222,7 +253,7 @@ void svc_foreach(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, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
@@ -264,12 +295,12 @@ svc_t *svc_stop_completed(void)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to an &svc_t object, or %NULL if not found.
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_find(char *cmd, int id)
|
svc_t *svc_find(char *cmd, char *id)
|
||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
if (svc->id && !strcmp(svc->id, id) && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,12 +334,12 @@ svc_t *svc_find_by_pid(pid_t pid)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to an &svc_t object, or %NULL if not found.
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_find_by_jobid(int job, int id)
|
svc_t *svc_find_by_jobid(int job, char *id)
|
||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (svc->job == job && svc->id == id)
|
if (svc->job == job && svc->id && !strcmp(svc->id, id))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,19 +354,33 @@ svc_t *svc_find_by_jobid(int job, int id)
|
|||||||
* Returns:
|
* Returns:
|
||||||
* A pointer to an &svc_t object, or %NULL if not found.
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
*/
|
*/
|
||||||
svc_t *svc_find_by_nameid(char *name, int id)
|
svc_t *svc_find_by_nameid(char *name, char *id)
|
||||||
{
|
{
|
||||||
char *ptr;
|
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
ptr = strrchr(svc->cmd, '/');
|
if (svc->id && !strcmp(svc->id, id) && !strcmp(name, svc->name))
|
||||||
if (ptr)
|
return svc;
|
||||||
ptr++;
|
}
|
||||||
else
|
|
||||||
ptr = svc->cmd;
|
|
||||||
|
|
||||||
if (svc->id == id && !strcmp(name, ptr))
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_find_by_plidfile - Find an service object by its PID file
|
||||||
|
* @fn: PID file, can be absolute path or relative to /run
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A pointer to an &svc_t object, or %NULL if not found.
|
||||||
|
*/
|
||||||
|
svc_t *svc_find_by_pidfile(char *fn)
|
||||||
|
{
|
||||||
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
if (string_compare(pid_runpath(fn, path, sizeof(path)), pid_file(svc)))
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,8 +401,14 @@ void svc_mark_dynamic(void)
|
|||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
if (svc->protect)
|
||||||
|
continue;
|
||||||
|
if (svc_is_inetd_conn(svc))
|
||||||
|
continue;
|
||||||
|
|
||||||
*((int *)&svc->dirty) = -1;
|
*((int *)&svc->dirty) = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void svc_mark_dirty(svc_t *svc)
|
void svc_mark_dirty(svc_t *svc)
|
||||||
@@ -382,10 +433,8 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
|
|||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,18 +496,24 @@ int svc_enabled(svc_t *svc)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Same base service, return unique ID */
|
/* Same base service, return unique ID as an integer */
|
||||||
int svc_next_id(char *cmd)
|
int svc_next_id_int(char *cmd)
|
||||||
{
|
{
|
||||||
int id = 0;
|
int n = 1;
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
char id[MAX_ID_LEN];
|
||||||
id = svc->id;
|
|
||||||
|
snprintf(id, sizeof(id), "%d", n);
|
||||||
|
|
||||||
|
if (!strcmp(svc->cmd, cmd) && svc->id && strcmp(svc->id, id))
|
||||||
|
return n;
|
||||||
|
|
||||||
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return id + 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int svc_is_unique(svc_t *svc)
|
int svc_is_unique(svc_t *svc)
|
||||||
@@ -485,7 +540,7 @@ int svc_is_unique(svc_t *svc)
|
|||||||
/*
|
/*
|
||||||
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
||||||
*/
|
*/
|
||||||
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int))
|
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *))
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
char *input, *token, *pos;
|
char *input, *token, *pos;
|
||||||
@@ -496,17 +551,27 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
|||||||
|
|
||||||
token = strtok_r(input, " ", &pos);
|
token = strtok_r(input, " ", &pos);
|
||||||
while (token) {
|
while (token) {
|
||||||
int id = 1;
|
char *id = NULL;
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
char *ptr = strchr(token, ':');
|
char *ptr = strchr(token, ':');
|
||||||
|
|
||||||
if (isdigit(token[0])) {
|
if (isdigit(token[0])) {
|
||||||
int job = atonum(token);
|
char *ep;
|
||||||
|
long job = 0;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
job = strtol(token, &ep, 10);
|
||||||
|
if ((errno == ERANGE && (job == LONG_MAX || job == LONG_MIN)) ||
|
||||||
|
(errno != 0 && job == 0) ||
|
||||||
|
(token == ep)) {
|
||||||
|
result++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
svc = svc_job_iterator(&iter, 1, job);
|
svc = svc_job_iterator(&iter, 1, job);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
result += not_found(NULL, job);
|
result += not_found(NULL, token);
|
||||||
|
|
||||||
while (svc) {
|
while (svc) {
|
||||||
if (found)
|
if (found)
|
||||||
@@ -515,8 +580,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*ptr++ = 0;
|
*ptr++ = 0;
|
||||||
id = atonum(ptr);
|
id = ptr;
|
||||||
job = atonum(token);
|
|
||||||
|
|
||||||
svc = svc_find_by_jobid(job, id);
|
svc = svc_find_by_jobid(job, id);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
@@ -537,7 +601,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*ptr++ = 0;
|
*ptr++ = 0;
|
||||||
id = atonum(ptr);
|
id = ptr;
|
||||||
|
|
||||||
svc = svc_find_by_nameid(token, id);
|
svc = svc_find_by_nameid(token, id);
|
||||||
if (!svc && not_found)
|
if (!svc && not_found)
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ typedef enum {
|
|||||||
SVC_BLOCK_RESTARTING,
|
SVC_BLOCK_RESTARTING,
|
||||||
} svc_block_t;
|
} svc_block_t;
|
||||||
|
|
||||||
|
#define MAX_ID_LEN 16
|
||||||
#define MAX_ARG_LEN 64
|
#define MAX_ARG_LEN 64
|
||||||
#define MAX_STR_LEN 64
|
#define MAX_STR_LEN 64
|
||||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||||
@@ -73,6 +74,9 @@ typedef enum {
|
|||||||
#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_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.
|
||||||
@@ -83,21 +87,29 @@ typedef struct svc {
|
|||||||
TAILQ_ENTRY(svc) link;
|
TAILQ_ENTRY(svc) link;
|
||||||
|
|
||||||
/* Instance specifics */
|
/* Instance specifics */
|
||||||
int job, id; /* JOB:ID */
|
int job; /* JOB: */
|
||||||
|
char id[MAX_ID_LEN]; /* :ID */
|
||||||
|
|
||||||
/* Limits and scoping */
|
/* Limits and scoping */
|
||||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
|
/* Control group */
|
||||||
|
char cgroup[32];
|
||||||
|
|
||||||
/* Service details */
|
/* Service details */
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
char pidfile[MAX_ARG_LEN];
|
||||||
|
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||||
svc_type_t type; /* Service, run, task, inetd, ... */
|
svc_type_t type; /* Service, run, task, inetd, ... */
|
||||||
|
int protect; /* Services like dbus-daemon & udev by Finit */
|
||||||
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
||||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||||
int runlevels;
|
int runlevels;
|
||||||
int sighup; /* This service supports SIGHUP :) */
|
int sighup; /* This service supports SIGHUP :) */
|
||||||
svc_block_t block; /* Reason that this service is currently stopped */
|
svc_block_t block; /* Reason that this service is currently stopped */
|
||||||
char cond[MAX_COND_LEN];
|
char cond[MAX_COND_LEN];
|
||||||
|
char name[MAX_ARG_LEN];
|
||||||
|
|
||||||
/* Counters */
|
/* Counters */
|
||||||
char once; /* run/task, (at least) once per runlevel */
|
char once; /* run/task, (at least) once per runlevel */
|
||||||
@@ -106,9 +118,17 @@ typedef struct svc {
|
|||||||
/* For inetd services */
|
/* For inetd services */
|
||||||
inetd_t inetd;
|
inetd_t inetd;
|
||||||
int stdin_fd;
|
int stdin_fd;
|
||||||
|
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
|
||||||
|
|
||||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||||
int log;
|
struct {
|
||||||
|
char enabled;
|
||||||
|
char null;
|
||||||
|
char console;
|
||||||
|
char file[64];
|
||||||
|
char prio[20];
|
||||||
|
char ident[20];
|
||||||
|
} log;
|
||||||
|
|
||||||
/* Identity */
|
/* Identity */
|
||||||
char username[MAX_USER_LEN];
|
char username[MAX_USER_LEN];
|
||||||
@@ -125,23 +145,27 @@ 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;
|
||||||
|
|
||||||
svc_t *svc_new (char *cmd, int id, int type);
|
svc_t *svc_new (char *cmd, char *id, int type);
|
||||||
int svc_del (svc_t *svc);
|
int svc_del (svc_t *svc);
|
||||||
|
|
||||||
svc_t *svc_find (char *cmd, int id);
|
svc_t *svc_find (char *cmd, char *id);
|
||||||
svc_t *svc_find_by_pid (pid_t pid);
|
svc_t *svc_find_by_pid (pid_t pid);
|
||||||
svc_t *svc_find_by_jobid (int job, int id);
|
svc_t *svc_find_by_jobid (int job, char *id);
|
||||||
svc_t *svc_find_by_nameid (char *name, int id);
|
svc_t *svc_find_by_nameid (char *name, char *id);
|
||||||
|
svc_t *svc_find_by_pidfile (char *fn);
|
||||||
|
|
||||||
svc_t *svc_iterator (svc_t **iter, int first);
|
svc_t *svc_iterator (svc_t **iter, int first);
|
||||||
svc_t *svc_inetd_iterator (svc_t **iter, int first);
|
svc_t *svc_inetd_iterator (svc_t **iter, int first);
|
||||||
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
|
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
|
||||||
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
|
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
|
||||||
|
|
||||||
void svc_foreach (void (*cb)(svc_t *));
|
void svc_foreach (int (*cb)(svc_t *));
|
||||||
void svc_foreach_type (int types, void (*cb)(svc_t *));
|
void svc_foreach_type (int types, int (*cb)(svc_t *));
|
||||||
|
|
||||||
svc_t *svc_stop_completed (void);
|
svc_t *svc_stop_completed (void);
|
||||||
|
|
||||||
@@ -153,39 +177,43 @@ int svc_clean_bootstrap (svc_t *svc);
|
|||||||
void svc_prune_bootstrap (void);
|
void svc_prune_bootstrap (void);
|
||||||
|
|
||||||
int svc_enabled (svc_t *svc);
|
int svc_enabled (svc_t *svc);
|
||||||
int svc_next_id (char *cmd);
|
int svc_next_id_int (char *cmd);
|
||||||
int svc_is_unique (svc_t *svc);
|
int svc_is_unique (svc_t *svc);
|
||||||
|
|
||||||
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
|
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
|
||||||
|
|
||||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
|
||||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
|
||||||
|
|
||||||
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
|
|
||||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
|
||||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
|
||||||
|
|
||||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
|
||||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
|
||||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
|
||||||
|
|
||||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
||||||
|
|
||||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||||
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
|
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
||||||
|
|
||||||
|
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||||
|
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||||
|
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
||||||
|
|
||||||
|
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||||
|
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||||
|
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||||
|
|
||||||
|
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||||
|
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||||
|
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
||||||
#define svc_start(svc) svc_unblock(svc)
|
#define svc_start(svc) svc_unblock(svc)
|
||||||
static inline void svc_stop (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
|
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||||
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
|
static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_BUSY; }
|
||||||
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
|
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
|
||||||
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
|
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
||||||
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
|
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
||||||
|
|
||||||
static inline char *svc_status(svc_t *svc)
|
static inline char *svc_status(svc_t *svc)
|
||||||
{
|
{
|
||||||
|
if (!svc)
|
||||||
|
return "Unknown";
|
||||||
|
|
||||||
switch (svc->state) {
|
switch (svc->state) {
|
||||||
case SVC_HALTED_STATE:
|
case SVC_HALTED_STATE:
|
||||||
switch (svc->block) {
|
switch (svc->block) {
|
||||||
@@ -207,6 +235,7 @@ static inline char *svc_status(svc_t *svc)
|
|||||||
case SVC_BLOCK_RESTARTING:
|
case SVC_BLOCK_RESTARTING:
|
||||||
return "restart";
|
return "restart";
|
||||||
}
|
}
|
||||||
|
return "unknown";
|
||||||
|
|
||||||
case SVC_DONE_STATE:
|
case SVC_DONE_STATE:
|
||||||
return "done";
|
return "done";
|
||||||
@@ -238,6 +267,9 @@ static inline char *svc_status(svc_t *svc)
|
|||||||
|
|
||||||
static inline const char *svc_dirtystr(svc_t *svc)
|
static inline const char *svc_dirtystr(svc_t *svc)
|
||||||
{
|
{
|
||||||
|
if (!svc)
|
||||||
|
return "Unknown";
|
||||||
|
|
||||||
if (svc_is_removed(svc))
|
if (svc_is_removed(svc))
|
||||||
return "removed";
|
return "removed";
|
||||||
if (svc_is_updated(svc))
|
if (svc_is_updated(svc))
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
@@ -41,32 +42,16 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
char buf[42];
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
static char path[80];
|
static char path[80];
|
||||||
|
|
||||||
if (!tty)
|
if (!tty)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Auto-detect serial console, for embedded devices mostly */
|
|
||||||
if (!strcmp(tty, "@console")) {
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
fp = fopen("/sys/class/tty/console/active", "r");
|
|
||||||
if (!fp) {
|
|
||||||
_e("Cannot find system console, is sysfs not mounted?");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fgets(buf, sizeof(buf), fp))
|
|
||||||
tty = chomp(buf);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
strlcpy(path, tty, sizeof(path));
|
strlcpy(path, tty, sizeof(path));
|
||||||
if (stat(path, &st)) {
|
if (stat(path, &st)) {
|
||||||
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
|
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
|
||||||
@@ -88,7 +73,7 @@ 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)
|
||||||
tty->dirty = -1;
|
tty->dirty = -1;
|
||||||
@@ -96,17 +81,17 @@ void tty_mark(void)
|
|||||||
|
|
||||||
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->dirty)
|
if (!tty->dirty)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("TTY %s dirty, stopping ...", tty->data.name);
|
_d("TTY %s dirty, stopping ...", tty->name);
|
||||||
tty_stop(&tty->data);
|
tty_stop(tty);
|
||||||
|
|
||||||
if (tty->dirty == -1) {
|
if (tty->dirty == -1) {
|
||||||
_d("TTY %s removed, cleaning up.", tty->data.name);
|
_d("TTY %s removed, cleaning up.", tty->name);
|
||||||
tty_unregister(tty);
|
tty_unregister(tty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,6 +101,7 @@ void tty_sweep(void)
|
|||||||
* 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"
|
||||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||||
|
* @cgroup: Control group, may be default
|
||||||
* @file: The file name TTY was loaded from
|
* @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
|
||||||
@@ -134,14 +120,14 @@ void tty_sweep(void)
|
|||||||
* Different getty implementations prefer the TTY device argument in
|
* Different getty implementations prefer the TTY device argument in
|
||||||
* different order, so take care to investigate this first.
|
* different order, so take care to investigate this first.
|
||||||
*/
|
*/
|
||||||
int tty_register(char *line, struct rlimit rlimit[], char *file)
|
int tty_register(char *line, struct rlimit rlimit[], char *cgroup, char *file)
|
||||||
{
|
{
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
int insert = 0, noclear = 0, nowait = 0;
|
|
||||||
size_t i, num = 0;
|
size_t i, num = 0;
|
||||||
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
|
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256];
|
||||||
char *dev = NULL, *baud = NULL;
|
char *dev = NULL, *baud = NULL;
|
||||||
char *runlevels = NULL, *term = NULL;
|
char *runlevels = NULL, *term = NULL;
|
||||||
|
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
|
||||||
|
|
||||||
if (!line) {
|
if (!line) {
|
||||||
_e("Missing argument");
|
_e("Missing argument");
|
||||||
@@ -159,6 +145,8 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
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;
|
||||||
|
|
||||||
@@ -218,6 +206,24 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
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)
|
||||||
@@ -236,12 +242,13 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -252,12 +259,12 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
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",
|
||||||
@@ -267,7 +274,10 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||||
|
|
||||||
/* Register configured limits */
|
/* Register configured limits */
|
||||||
memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit));
|
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
|
||||||
|
|
||||||
|
/* Register desired control group */
|
||||||
|
strlcpy(entry->cgroup, cgroup, sizeof(entry->cgroup));
|
||||||
|
|
||||||
if (file && conf_changed(file))
|
if (file && conf_changed(file))
|
||||||
entry->dirty = 1; /* Modified, restart */
|
entry->dirty = 1; /* Modified, restart */
|
||||||
@@ -275,10 +285,18 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
entry->dirty = 0; /* Not modified */
|
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");
|
||||||
@@ -287,20 +305,14 @@ int tty_unregister(tty_node_t *tty)
|
|||||||
|
|
||||||
LIST_REMOVE(tty, link);
|
LIST_REMOVE(tty, link);
|
||||||
|
|
||||||
if (tty->data.name)
|
if (tty->cmd) {
|
||||||
free(tty->data.name);
|
|
||||||
if (tty->data.baud)
|
|
||||||
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);
|
||||||
@@ -308,12 +320,12 @@ int tty_unregister(tty_node_t *tty)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tty_node_t *tty_find(char *dev)
|
struct tty *tty_find(char *dev)
|
||||||
{
|
{
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
if (!strcmp(dev, entry->data.name))
|
if (!strcmp(dev, entry->name))
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +335,7 @@ tty_node_t *tty_find(char *dev)
|
|||||||
size_t tty_num(void)
|
size_t tty_num(void)
|
||||||
{
|
{
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link)
|
LIST_FOREACH(entry, &tty_list, link)
|
||||||
num++;
|
num++;
|
||||||
@@ -334,22 +346,22 @@ size_t tty_num(void)
|
|||||||
size_t tty_num_active(void)
|
size_t tty_num_active(void)
|
||||||
{
|
{
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
if (entry->data.pid)
|
if (entry->pid)
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
tty_node_t *tty_find_by_pid(pid_t pid)
|
struct tty *tty_find_by_pid(pid_t pid)
|
||||||
{
|
{
|
||||||
tty_node_t *entry;
|
struct tty *entry;
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
if (entry->data.pid == pid)
|
if (entry->pid == pid)
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +384,7 @@ static int tty_exist(char *dev)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_start(finit_tty_t *tty)
|
void tty_start(struct tty *tty)
|
||||||
{
|
{
|
||||||
char *dev;
|
char *dev;
|
||||||
|
|
||||||
@@ -392,13 +404,22 @@ void tty_start(finit_tty_t *tty)
|
|||||||
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, tty->rlimit);
|
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, tty->rlimit);
|
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
||||||
|
|
||||||
|
cgroup_assign(tty->cgroup, tty->pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_stop(finit_tty_t *tty)
|
void tty_stop(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty->pid)
|
if (!tty->pid)
|
||||||
return;
|
return;
|
||||||
@@ -415,7 +436,7 @@ void tty_stop(finit_tty_t *tty)
|
|||||||
tty->pid = 0;
|
tty->pid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tty_enabled(finit_tty_t *tty)
|
int tty_enabled(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty)
|
if (!tty)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -463,12 +484,12 @@ int tty_fallback(pid_t lost)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tty_action(tty_node_t *tty)
|
static void tty_action(struct tty *tty)
|
||||||
{
|
{
|
||||||
if (!tty_enabled(&tty->data))
|
if (!tty_enabled(tty))
|
||||||
tty_stop(&tty->data);
|
tty_stop(tty);
|
||||||
else
|
else
|
||||||
tty_start(&tty->data);
|
tty_start(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -476,7 +497,7 @@ static void tty_action(tty_node_t *tty)
|
|||||||
*/
|
*/
|
||||||
int tty_respawn(pid_t pid)
|
int tty_respawn(pid_t pid)
|
||||||
{
|
{
|
||||||
tty_node_t *tty = tty_find_by_pid(pid);
|
struct tty *tty = tty_find_by_pid(pid);
|
||||||
|
|
||||||
if (!tty)
|
if (!tty)
|
||||||
return tty_fallback(pid);
|
return tty_fallback(pid);
|
||||||
@@ -485,7 +506,7 @@ int tty_respawn(pid_t pid)
|
|||||||
utmp_set_dead(pid);
|
utmp_set_dead(pid);
|
||||||
|
|
||||||
/* Clear PID to be able to respawn it. */
|
/* Clear PID to be able to respawn it. */
|
||||||
tty->data.pid = 0;
|
tty->pid = 0;
|
||||||
tty_action(tty);
|
tty_action(tty);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -496,7 +517,7 @@ 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);
|
||||||
@@ -521,7 +542,7 @@ void tty_reload(char *dev)
|
|||||||
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
||||||
void tty_runlevel(void)
|
void tty_runlevel(void)
|
||||||
{
|
{
|
||||||
tty_node_t *tty;
|
struct tty *tty;
|
||||||
|
|
||||||
LIST_FOREACH(tty, &tty_list, link)
|
LIST_FOREACH(tty, &tty_list, link)
|
||||||
tty_action(tty);
|
tty_action(tty);
|
||||||
|
|||||||
@@ -32,12 +32,15 @@
|
|||||||
#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 */
|
||||||
@@ -47,32 +50,27 @@ typedef struct {
|
|||||||
|
|
||||||
/* Limits and scoping */
|
/* Limits and scoping */
|
||||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
} finit_tty_t;
|
|
||||||
|
|
||||||
typedef struct tty_node {
|
/* Control group */
|
||||||
LIST_ENTRY(tty_node) link;
|
char cgroup[32];
|
||||||
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 */
|
||||||
int dirty; /* Set if old mtime != new mtime => reloaded,
|
int dirty;
|
||||||
* 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_sweep (void);
|
void tty_sweep (void);
|
||||||
|
|
||||||
int tty_register (char *line, struct rlimit rlimit[], char *file);
|
int tty_register (char *line, struct rlimit rlimit[], char *cgroup, char *file);
|
||||||
int tty_unregister (tty_node_t *tty);
|
int tty_unregister (struct tty *tty);
|
||||||
|
|
||||||
tty_node_t *tty_find (char *dev);
|
struct tty *tty_find (char *dev);
|
||||||
size_t tty_num (void);
|
size_t tty_num (void);
|
||||||
size_t tty_num_active (void);
|
size_t tty_num_active (void);
|
||||||
tty_node_t *tty_find_by_pid (pid_t pid);
|
struct tty *tty_find_by_pid (pid_t pid);
|
||||||
void tty_start (finit_tty_t *tty);
|
void tty_start (struct tty *tty);
|
||||||
void tty_stop (finit_tty_t *tty);
|
void tty_stop (struct tty *tty);
|
||||||
int tty_enabled (finit_tty_t *tty);
|
int tty_enabled (struct tty *tty);
|
||||||
int tty_respawn (pid_t pid);
|
int tty_respawn (pid_t pid);
|
||||||
void tty_reload (char *dev);
|
void tty_reload (char *dev);
|
||||||
void tty_runlevel (void);
|
void tty_runlevel (void);
|
||||||
|
|||||||
+125
@@ -23,8 +23,13 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <ctype.h> /* isprint() */
|
#include <ctype.h> /* isprint() */
|
||||||
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdarg.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 +47,118 @@ char *progname(char *arg0)
|
|||||||
return prognm;
|
return prognm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int echo(char *file, int append, char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
fp = stdout;
|
||||||
|
else
|
||||||
|
fp = fopen(file, append ? "a" : "w");
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (fmt) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(fp, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* echo(1) always adds a newline */
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
if (file)
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@@ -73,6 +184,10 @@ char *sanitize(char *arg, size_t len)
|
|||||||
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))
|
if (!isatty(STDOUT_FILENO))
|
||||||
@@ -81,6 +196,16 @@ void screen_init(void)
|
|||||||
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,17 @@ extern char *prognm;
|
|||||||
#include <lite/conio.h>
|
#include <lite/conio.h>
|
||||||
|
|
||||||
char *progname (char *arg0);
|
char *progname (char *arg0);
|
||||||
|
int echo (char *file, int append, char *fmt, ...);
|
||||||
|
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_ */
|
||||||
|
|||||||
+53
-28
@@ -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,37 +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 <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 <syslog.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
#include "finit.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "watchdog.h"
|
#include "watchdog.h"
|
||||||
|
|
||||||
int running = 1;
|
int running = 1;
|
||||||
int handover = 0;
|
int handover = 0;
|
||||||
|
int shutdown = 0;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -47,25 +57,37 @@ 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, int period)
|
static int loop(int fd, int timeout)
|
||||||
{
|
{
|
||||||
int dummy = 0;
|
int dummy = 0;
|
||||||
|
int period = timeout / 2;
|
||||||
|
|
||||||
|
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||||
sleep(period);
|
sleep(period);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
if (handover) {
|
||||||
|
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
|
||||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||||
return !write(fd, "V", 1);
|
return !write(fd, "V", 1);
|
||||||
}
|
}
|
||||||
@@ -73,27 +95,30 @@ static int loop(int fd, int period)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int watchdog(char *progname)
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int pid;
|
int fd, ret;
|
||||||
|
|
||||||
pid = fork();
|
fd = init(argv[0], WDT_DEVNODE);
|
||||||
if (pid == 0) {
|
if (fd == -1) {
|
||||||
int fd, ret;
|
if (ENOENT != errno)
|
||||||
|
syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||||
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT);
|
return 1;
|
||||||
if (fd == -1) {
|
|
||||||
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = loop(fd, WDT_TIMEOUT / 2);
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
_exit(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in new issue
Block a user