mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
286
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40042da8ff | ||
|
|
c251304146 | ||
|
|
c047e37a79 | ||
|
|
d2c57447f0 | ||
|
|
79e9dabc11 | ||
|
|
b04dc4d457 | ||
|
|
f86e7810be | ||
|
|
2018c82ea1 | ||
|
|
c27f3603e6 | ||
|
|
6f82b806ad | ||
|
|
cbb8935660 | ||
|
|
21ebbb942f | ||
|
|
988ecd95e3 | ||
|
|
3f3e70c686 | ||
|
|
073b040981 | ||
|
|
b1a058ccdd | ||
|
|
cbc7b8c3c7 | ||
|
|
13340e4200 | ||
|
|
0076c1f218 | ||
|
|
6fc71b7528 | ||
|
|
b42f1e99e2 | ||
|
|
afd6ad06a6 | ||
|
|
ea0bfaa2f3 | ||
|
|
5c3ec176c2 | ||
|
|
40425ee9ec | ||
|
|
424db825a3 | ||
|
|
b9cc4cd629 | ||
|
|
243b8f025b | ||
|
|
6c60b67588 | ||
|
|
910bb13711 | ||
|
|
add55cfc2a | ||
|
|
565820325b | ||
|
|
6bb4d67d92 | ||
|
|
534d50c03a | ||
|
|
9a42883a96 | ||
|
|
7c77e2c2b6 | ||
|
|
ea8c46cd30 | ||
|
|
fead3b0671 | ||
|
|
081990b49e | ||
|
|
6b89f8c6db | ||
|
|
373097040a | ||
|
|
a4ab75172c | ||
|
|
5cf9958957 | ||
|
|
b137e08e84 | ||
|
|
a1af8e8687 | ||
|
|
53c7ddcb22 | ||
|
|
3872077ff1 | ||
|
|
9e85c4f3d1 | ||
|
|
2531102623 | ||
|
|
33deada6f0 | ||
|
|
9d1506bb7e | ||
|
|
c3f115c9ec | ||
|
|
430d402b14 | ||
|
|
e011dac98b | ||
|
|
42b84d1ccb | ||
|
|
288105eac7 | ||
|
|
842b522e68 | ||
|
|
e6f36974c8 | ||
|
|
679b4df881 | ||
|
|
666cae6f49 | ||
|
|
be2ac1d1b1 | ||
|
|
266e5b30eb | ||
|
|
4e5d06b580 | ||
|
|
fe334135ce | ||
|
|
aef10fb5e7 | ||
|
|
86707c1f0b | ||
|
|
a28d5b3c09 | ||
|
|
550ae9c784 | ||
|
|
cd5b91680a | ||
|
|
221ab20898 | ||
|
|
96534789db | ||
|
|
26128dd788 | ||
|
|
799e992542 | ||
|
|
fa524eec60 | ||
|
|
6dced29b49 | ||
|
|
2ea613a42b | ||
|
|
985df20519 | ||
|
|
f6161b9be9 | ||
|
|
25c5014d3c | ||
|
|
b8b7b53d96 | ||
|
|
7707c4b8e6 | ||
|
|
437f95f00f | ||
|
|
099672fabb | ||
|
|
860515a716 | ||
|
|
c85551a42e | ||
|
|
79dc6e7472 | ||
|
|
06e456384c | ||
|
|
38f7d24a80 | ||
|
|
145c78ef3e | ||
|
|
dd437bf0a5 | ||
|
|
d82d119729 | ||
|
|
7f76202865 | ||
|
|
aee92751ac | ||
|
|
2ea7236d45 | ||
|
|
cedc04e1e3 | ||
|
|
edc6eb1747 | ||
|
|
be5ec94fdf | ||
|
|
23a13fe1b6 | ||
|
|
0168b765c1 | ||
|
|
01358a5a45 | ||
|
|
106516d12d | ||
|
|
ba858743e5 | ||
|
|
9fd860ef3c | ||
|
|
46ccdc3820 | ||
|
|
242fe54afd | ||
|
|
df5d296cd9 | ||
|
|
dc4630f214 | ||
|
|
ac07d3bac7 | ||
|
|
f641eef8f7 | ||
|
|
f17b5b3cd3 | ||
|
|
d945f4960b | ||
|
|
6a5a6ac62f | ||
|
|
a9ba4300ec | ||
|
|
7bb649f11a | ||
|
|
85ada0ac99 | ||
|
|
be343a3b34 | ||
|
|
0ac0e5c7d3 | ||
|
|
7f272768e4 | ||
|
|
7b74c9900f | ||
|
|
36f1a39b31 | ||
|
|
6d380082eb | ||
|
|
8d3917e9fa | ||
|
|
c3cd1a5bfa | ||
|
|
eaeddc36c2 | ||
|
|
4f40b65f0c | ||
|
|
f958aa504e | ||
|
|
3d1b95aa7a | ||
|
|
4bb67bc06f | ||
|
|
0929c93caf | ||
|
|
c0368d2b16 | ||
|
|
85a212ef93 | ||
|
|
12c3cb3dc0 | ||
|
|
16710e7ab5 | ||
|
|
b652aaf0cb | ||
|
|
30020263ba | ||
|
|
3cf5d36972 | ||
|
|
c9ae96bcf7 | ||
|
|
5955110e67 | ||
|
|
4191e15c77 | ||
|
|
d60baa2b80 | ||
|
|
a50bc331db | ||
|
|
eff4453de9 | ||
|
|
4cfc8fec0e | ||
|
|
c9e1c2f80f | ||
|
|
93039e40cc | ||
|
|
015a52a20d | ||
|
|
d53cc8bfe7 | ||
|
|
3c4eca60b7 | ||
|
|
c85e28b971 | ||
|
|
d373068619 | ||
|
|
997f3d7862 | ||
|
|
f12f53692e | ||
|
|
b921b8cf0f | ||
|
|
61781c72fe | ||
|
|
3b5cfc5ecf | ||
|
|
a42a789d29 | ||
|
|
d77c014723 | ||
|
|
23fca9c067 | ||
|
|
37eb1901f9 | ||
|
|
98c4e7764f | ||
|
|
d3e4463612 | ||
|
|
b812045d82 | ||
|
|
b1bcdc88e0 | ||
|
|
62c5a9d06d | ||
|
|
dcdc842336 | ||
|
|
12eaa3073a | ||
|
|
34875f57a5 | ||
|
|
f0bf1343b0 | ||
|
|
4e6b9514d5 | ||
|
|
3b495d7518 | ||
|
|
badf8701bf | ||
|
|
85fa0d4926 | ||
|
|
3de9643893 | ||
|
|
73629944c0 | ||
|
|
c97c83bfb6 | ||
|
|
19b18afac9 | ||
|
|
e3c424e129 | ||
|
|
c0716d7f63 | ||
|
|
d43af8c9c1 | ||
|
|
2825100f77 | ||
|
|
b1bee31d61 | ||
|
|
522276b80d | ||
|
|
7bc1470a1e | ||
|
|
ca32cb0dfc | ||
|
|
d59f55b90c | ||
|
|
02936de993 | ||
|
|
8ed223343d | ||
|
|
90f7ff1c23 | ||
|
|
a56220f158 | ||
|
|
d40b426c1b | ||
|
|
5342881bf4 | ||
|
|
c84c17ea8c | ||
|
|
f976b8261f | ||
|
|
10f4c7fa18 | ||
|
|
665d212bd6 | ||
|
|
9e6e653c57 | ||
|
|
bda4da71a6 | ||
|
|
a24fc941a6 | ||
|
|
f307ad060b | ||
|
|
1845b7be54 | ||
|
|
a260e157eb | ||
|
|
0641b1a73e | ||
|
|
9ba57f0c9d | ||
|
|
9c2f0b2dc8 | ||
|
|
d4bbebeb93 | ||
|
|
1a22b4f0aa | ||
|
|
9c4d5341ae | ||
|
|
b9d498e01c | ||
|
|
a1e38a3e40 | ||
|
|
c0ae7763a6 | ||
|
|
9f98b1d3d1 | ||
|
|
a7e2224f05 | ||
|
|
3bb1e41394 | ||
|
|
e437379939 | ||
|
|
3ad4f6ef4b | ||
|
|
4170a61d0b | ||
|
|
b3c5ab41e7 | ||
|
|
b3fad1077e | ||
|
|
522e1f57d7 | ||
|
|
d00a53dfbf | ||
|
|
ff86f29f41 | ||
|
|
3107027132 | ||
|
|
728db73b09 | ||
|
|
fcb8ec9967 | ||
|
|
5b8fe12502 | ||
|
|
6e24f2ff13 | ||
|
|
74d715243c | ||
|
|
d9993860cb | ||
|
|
6a6f3a8291 | ||
|
|
99a8cf3eb7 | ||
|
|
da95c7d80e | ||
|
|
a9f7f692a9 | ||
|
|
ce55f88a00 | ||
|
|
74a26f559d | ||
|
|
08144df5ea | ||
|
|
230c64927b | ||
|
|
d1e56f300d | ||
|
|
9842786453 | ||
|
|
6d0c679e05 | ||
|
|
5428dea5bf | ||
|
|
4e84ecae89 | ||
|
|
959c9fe5aa | ||
|
|
03d8ef9c8f | ||
|
|
084b4ce8d3 | ||
|
|
31cae6a56d | ||
|
|
1fd45c6f2e | ||
|
|
46995fe446 | ||
|
|
600b2874e6 | ||
|
|
b080b970b7 | ||
|
|
6106574e13 | ||
|
|
2385a1209b | ||
|
|
e8b942cf95 | ||
|
|
ac17d7f5a7 | ||
|
|
1edc4356a6 | ||
|
|
9f06a2db99 | ||
|
|
cfb3a9f2b9 | ||
|
|
8aba63a9e3 | ||
|
|
3d98ad293b | ||
|
|
156e86f395 | ||
|
|
d859ecd21d | ||
|
|
cbec78dbbc | ||
|
|
572291a1d9 | ||
|
|
280608f30e | ||
|
|
5c4146cfe4 | ||
|
|
30e3f5a33c | ||
|
|
2c12379a30 | ||
|
|
5f026c8149 | ||
|
|
90005235e8 | ||
|
|
6b688c4211 | ||
|
|
bdf5f8fc90 | ||
|
|
79f7a8ccfb | ||
|
|
3b19db04e3 | ||
|
|
93402b81d7 | ||
|
|
6dec4c9ca0 | ||
|
|
548acf27cf | ||
|
|
cd0bc57ca8 | ||
|
|
11a3318c02 | ||
|
|
2711b27971 | ||
|
|
05ac9c18c9 | ||
|
|
1a7092f545 | ||
|
|
8000d361b3 | ||
|
|
e3c8febe3b | ||
|
|
32ca117949 | ||
|
|
b966d031c8 | ||
|
|
905c2678a3 | ||
|
|
7d429bdbb1 |
+10
-10
@@ -27,7 +27,7 @@ addons:
|
|||||||
name: "troglobit/finit"
|
name: "troglobit/finit"
|
||||||
description: "finit | Fast & Extensible init for Linux"
|
description: "finit | Fast & Extensible init for Linux"
|
||||||
notification_email: troglobit@gmail.com
|
notification_email: troglobit@gmail.com
|
||||||
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp --disable-doc --disable-contrib --enable-watchdog --enable-fallback-shell"
|
build_command_prepend: "./autogen.sh && ./configure --prefix= --disable-doc --disable-contrib --with-watchdog"
|
||||||
build_command: "make"
|
build_command: "make"
|
||||||
branch_pattern: dev
|
branch_pattern: dev
|
||||||
|
|
||||||
@@ -42,20 +42,20 @@ install:
|
|||||||
# 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
|
||||||
- ./configure --prefix=/tmp --enable-static
|
- ./configure --prefix= --enable-static
|
||||||
- make clean
|
- make clean
|
||||||
- make V=1 -j5
|
- make V=1 -j5
|
||||||
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog
|
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog
|
||||||
- cat config.log
|
- cat config.log
|
||||||
- make clean
|
- make clean
|
||||||
- make V=1 -j5
|
- make V=1 -j5
|
||||||
- make install-strip
|
- make install-strip DESTDIR=/tmp/finit
|
||||||
- tree /tmp
|
- tree /tmp/finit
|
||||||
- ldd /tmp/sbin/finit
|
- ldd /tmp/finit/sbin/finit
|
||||||
- size /tmp/sbin/finit
|
- size /tmp/finit/sbin/finit
|
||||||
- size /tmp/sbin/initctl
|
- size /tmp/finit/sbin/initctl
|
||||||
- size /tmp/sbin/reboot
|
- size /tmp/finit/sbin/reboot
|
||||||
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
|
- LD_LIBRARY_PATH=/tmp/lib /tmp/finit/sbin/initctl -h
|
||||||
- unshare -V
|
- unshare -V
|
||||||
- nsenter -V
|
- nsenter -V
|
||||||
- chroot --version
|
- chroot --version
|
||||||
|
|||||||
+81
-21
@@ -4,12 +4,47 @@ Change Log
|
|||||||
All relevant changes are documented in this file.
|
All relevant changes are documented in this file.
|
||||||
|
|
||||||
|
|
||||||
[4.0][UNRELEASED] - 2021-02-21
|
[4.1][UNRELEASED]
|
||||||
------------------------------
|
-----------------
|
||||||
|
|
||||||
|
Bug fix release.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
* Change behavior on SIGUSR1 to be compatible with sysvinit and systemd.
|
||||||
|
Previously SIGUSR1 caused Finit to halt, like BusyBox init. This had
|
||||||
|
"interesting" side effects on Debian systems when coexisting with
|
||||||
|
sysvinit (upgrading/reinstalling causes scripts to `kill -USR1 1`)
|
||||||
|
* Change how `contrib/debian/install.sh` sets up a Grub boot entry for
|
||||||
|
finit. We now modify the $SUPPORTED_INITS variable in `10_linux`
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* Stricter interface name validation in netlink plugin, modeled after
|
||||||
|
the kernel. Suggested by Coverity Scan
|
||||||
|
* The `contrib/*/install.sh` scripts failed to run from tarball
|
||||||
|
* Fix #170: detect loss of default route when interfaces go down. This
|
||||||
|
emulates the missing kernel netlink message to remove the condition
|
||||||
|
net/default/route to allow stopping dependent services
|
||||||
|
* Fix #171: restore automatic mount of `/dev/shm`, `/dev/pts`, `/run`
|
||||||
|
and `/tmp`, unless mounted already by `/etc/fstab`. This is what most
|
||||||
|
desktop systems expect PID 1 to do
|
||||||
|
* Fix #173: netlink plugin runs out of socket buffer space;
|
||||||
|
|
||||||
|
finit[1]: nl_callback():recv(): No buffer space available
|
||||||
|
|
||||||
|
Fixed by adding support for resync with kernel on `ENOBUFS`. See
|
||||||
|
netlink(7) for details. As a spin-off the plugin now supports any
|
||||||
|
number of interfaces and routes on a system. On resync, the following
|
||||||
|
message is now logged, as a warning:
|
||||||
|
|
||||||
|
finit[1]: nl_callback():busy system, resynchronizing with kernel.
|
||||||
|
|
||||||
|
|
||||||
|
[4.0][] - 2021-04-26
|
||||||
|
--------------------
|
||||||
|
|
||||||
This release became v4.0, and not v3.2, because of incompatible changes
|
This release became v4.0, and not v3.2, because of incompatible changes
|
||||||
to service conditions. There are other significant changes as well, so
|
to service conditions. There are other significant changes as well, so
|
||||||
make sure to read the whole changelog when upgrading.
|
make sure to read the whole change log when upgrading.
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
* The stand-alone `reboot` tool has been replaced with a symlink to
|
* The stand-alone `reboot` tool has been replaced with a symlink to
|
||||||
@@ -18,31 +53,53 @@ make sure to read the whole changelog when upgrading.
|
|||||||
with a fallback to sending signals as per traditional SysV init. The
|
with a fallback to sending signals as per traditional SysV init. The
|
||||||
`-f` (force) flag remains, where `reboot(2)` is called directly
|
`-f` (force) flag remains, where `reboot(2)` is called directly
|
||||||
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
|
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
|
||||||
|
* The `inictl cond set|clear COND` have changed completely. Constrained
|
||||||
|
to a flat `<usr/...>` namespace and automatically activated by a new
|
||||||
|
`usr.so` plugin that checks services for usr condition changes
|
||||||
* Removed built-in inetd super server. If you need this functionality,
|
* Removed built-in inetd super server. If you need this functionality,
|
||||||
use an external inetd, like xinetd, instead. A pull request for a
|
use an external inetd, like xinetd, instead. A pull request for a
|
||||||
stand-alone inetd, like watchdogd and getty, is most welcome!
|
stand-alone inetd, like watchdogd and getty, is most welcome!
|
||||||
* Incompatible `configure` script changes, i.e., you must give proper
|
* Incompatible `configure` script changes, i.e., no guessing `--prefix`
|
||||||
path arguments to the script, no more guessing just GNU defaults.
|
and other paths. Also, many options have been changed, renamed, or
|
||||||
There are examples in the documentation and the `contrib/` section
|
flipped defaults, or even dropped altogether. There are examples in
|
||||||
* Change service conditions from the non-obvious `<svc/path/to/foo>` to
|
the documentation and the `contrib/` section
|
||||||
`<pid/foo:id>`. This to hopefully make it clear that one service's
|
* Service conditions change from the non-obvious `<svc/path/to/foo>` to
|
||||||
'pid:!foo' pidfile is another service's `<pid/foo>` condition
|
`<pid/foo:id>`. Not only does this give simpler internal semantics,
|
||||||
* Initial support for cgroups, every service now runs in its own cgroup
|
it hopefully also makes it clear that one service's `pid:!foo` pidfile
|
||||||
https://twitter.com/b0rk/status/1214341831049252870?s=20
|
is another service's `<pid/foo>` condition, issue #143
|
||||||
|
* Initial support for cgroups v2:
|
||||||
|
- services runs in a cgroup named after their respective *.conf file
|
||||||
|
- top-level groups are; init, user, and system
|
||||||
|
- all top-level groups can be configured from finit *.conf files
|
||||||
|
- each service can tweak the cgroup settings
|
||||||
|
- Use `initctl [top|ps|cgroup]` commands to inspect runtime state
|
||||||
|
- https://twitter.com/b0rk/status/1214341831049252870?s=20
|
||||||
* Major refactor of Finit's `main()` function to be able to start the
|
* Major refactor of Finit's `main()` function to be able to start the
|
||||||
event loop earlier. This also facilitated factoring out functionality
|
event loop earlier. This also facilitated factoring out functionality
|
||||||
previously hard-coded in Finit, e.g., starting the bundled watchdogd,
|
previously hard-coded in Finit, e.g., starting the bundled watchdogd,
|
||||||
various distro packed udevd and other hotplugging tools
|
various distro packed udevd and other hotplugging tools
|
||||||
|
* A proper rescue mode has been added. It is started extremely early
|
||||||
|
and is protected with a bundled `suslogin`. Exiting rescue mode now
|
||||||
|
brings up the system as a normal boot, as one expects
|
||||||
* Support for `sysv` start/stop scripts as well as monitoring forking
|
* Support for `sysv` start/stop scripts as well as monitoring forking
|
||||||
services, stared using `sysv` or `service` stanza
|
services, stared using `sysv` or `service` stanza
|
||||||
* Support for custom `kill:DELAY`, default 3 sec.
|
* Support for custom `kill:DELAY`, default 3 sec.
|
||||||
* Support for custom `halt:SIGNAL`, default SIGTERM
|
* Support for custom `halt:SIGNAL`, default SIGTERM
|
||||||
|
* Support for `pre:script` and `post:script`, allows for setup and
|
||||||
|
teardown/cleanup before and after a service runs, issue #129
|
||||||
|
* Support for `env:file` in `/etc/default/foo` or `/etc/conf.d/foo`, see
|
||||||
|
the contrib section for examples that utilize this feature. Variables
|
||||||
|
expanded from env files, and the env files themselves, are tracked for
|
||||||
|
changes to see if a service .conf file is "dirty" and needs restart on
|
||||||
|
`initctl reload`
|
||||||
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
|
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
|
||||||
useful with new `sysv` or `service` which fork to background
|
useful with new `sysv` or `service` which fork to background
|
||||||
* Support starting run/task/services without absolute path, trust `$PATH`
|
* Support starting run/task/services without absolute path, trust `$PATH`
|
||||||
* Add support for `--disable-doc` and `--disable-contrib` to speed up
|
* Add support for `--disable-doc` and `--disable-contrib` to speed up
|
||||||
builds and work around issue with massively parallel builds
|
builds and work around issue with massively parallel builds
|
||||||
* Add support for `@console` also for external getty
|
* Support for `@console` also for external getty
|
||||||
|
* Support for `notty` option to built-in getty, for board bring-up
|
||||||
|
* Support for `rescue` option to built-in getty, for rescue shells
|
||||||
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
* Add `-b`, batch mode, for non-interactive use to `initctl`
|
||||||
* Prefer udev to handle `/dev/` if mdev is also available
|
* Prefer udev to handle `/dev/` if mdev is also available
|
||||||
* Redirect dbus daemon output to syslog
|
* Redirect dbus daemon output to syslog
|
||||||
@@ -92,6 +149,8 @@ make sure to read the whole changelog when upgrading.
|
|||||||
* Always append `:ID` qualifier to conditions if set for a service
|
* Always append `:ID` qualifier to conditions if set for a service
|
||||||
* The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket`
|
* The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket`
|
||||||
officially only supported for use by the `initctl` tool
|
officially only supported for use by the `initctl` tool
|
||||||
|
* The IPC socket now uses `SOCK_SEQPACKET` instead of `SOCK_STREAM`.
|
||||||
|
Recommend using watchdogd v3.4, or later, which support this
|
||||||
* Improved support for modern `/etc/network/interfaces`, which has
|
* Improved support for modern `/etc/network/interfaces`, which has
|
||||||
include statements. No more native `ifup` of individual interfaces,
|
include statements. No more native `ifup` of individual interfaces,
|
||||||
Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to
|
Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to
|
||||||
@@ -109,16 +168,21 @@ make sure to read the whole changelog when upgrading.
|
|||||||
that do not have a watchdog
|
that do not have a watchdog
|
||||||
* Fix #100: Early condition handling may not work if `/var/run` does
|
* Fix #100: Early condition handling may not work if `/var/run` does
|
||||||
not yet exist (symlink to `/run`). Added compat layer for access
|
not yet exist (symlink to `/run`). Added compat layer for access
|
||||||
|
* Fix #101: Built-in inetd removed
|
||||||
|
* Fix #102: Start built-in watchdogd as a regular service
|
||||||
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
|
* 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 #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
|
||||||
Fixed by Jonas Johansson, Westermo
|
Fixed by Jonas Johansson, Westermo
|
||||||
* Fix #109: Support for PID files in sub-directories to `/var/run`
|
* Fix #109: Support for PID files in sub-directories to `/var/run`
|
||||||
* Handle rename of PID files, by Robert Andersson, Atlas Copco
|
* Handle rename of PID files, by Robert Andersson, Atlas Copco
|
||||||
|
* Fix #110: automatic modprobe of RTC devices, built-in hwclock
|
||||||
* Fix #120: Redirect `stdin` to `/dev/null` for services by default
|
* Fix #120: Redirect `stdin` to `/dev/null` for services by default
|
||||||
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
|
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
|
||||||
fixed by Jacques de Laval, Westermo
|
fixed by Jacques de Laval, Westermo
|
||||||
* Fix #124: Lingering processes in process group when session leader
|
* Fix #124: Lingering processes in process group when session leader
|
||||||
exits. E.g., lingering `logit` processes when parent dies
|
exits. E.g., lingering `logit` processes when parent dies
|
||||||
|
* Fix #127: Show all runparts scripts as they start, like rc.local, fixed
|
||||||
|
by Jacques de Laval, Westermo
|
||||||
* Fix service name matching, e.g. for condition handling, may match with
|
* Fix service name matching, e.g. for condition handling, may match with
|
||||||
wrong service, by Jonas Holmberg, Westermo
|
wrong service, by Jonas Holmberg, Westermo
|
||||||
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard
|
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard
|
||||||
@@ -137,6 +201,8 @@ make sure to read the whole changelog when upgrading.
|
|||||||
* Provide service description for built-in watchdog daemon
|
* Provide service description for built-in watchdog daemon
|
||||||
* Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system
|
* Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system
|
||||||
* Drop the '%m' GNUism, for compat with older musl libc
|
* Drop the '%m' GNUism, for compat with older musl libc
|
||||||
|
* Fix #139: call `tzset()` on `initctl reload` to activate system
|
||||||
|
timezone changes (for logging)
|
||||||
|
|
||||||
|
|
||||||
[3.1][] - 2018-01-23
|
[3.1][] - 2018-01-23
|
||||||
@@ -745,14 +811,14 @@ Major bug fix release.
|
|||||||
* Makefile fixes for installation, paths encoded wrong
|
* Makefile fixes for installation, paths encoded wrong
|
||||||
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip
|
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip
|
||||||
* Default install is now to `/sbin/finit` and `/usr/`
|
* Default install is now to `/sbin/finit` and `/usr/`
|
||||||
* Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir`
|
* Note change in `$PLUGIN_DIR` environment variable to `$plugindir`
|
||||||
|
|
||||||
|
|
||||||
[1.2][] - 2012-09-27
|
[1.2][] - 2012-09-27
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
* Update README with section on building and enviroment variables
|
* Update README with section on building and environment variables
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* Fix installation paths encoded in finit binary
|
* Fix installation paths encoded in finit binary
|
||||||
@@ -844,7 +910,7 @@ Major bug fix release.
|
|||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
|
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.0...HEAD
|
||||||
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
|
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
|
||||||
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
|
[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
|
||||||
@@ -876,9 +942,3 @@ Major bug fix release.
|
|||||||
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
|
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
|
||||||
[libuEv]: https://github.com/troglobit/libuev
|
[libuEv]: https://github.com/troglobit/libuev
|
||||||
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
|
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
|
||||||
|
|
||||||
<!--
|
|
||||||
-- Local Variables:
|
|
||||||
-- mode: markdown
|
|
||||||
-- End:
|
|
||||||
-->
|
|
||||||
|
|||||||
@@ -29,6 +29,23 @@ by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
|||||||

|

|
||||||
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
|
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
|
||||||
|
|
||||||
|
Features include:
|
||||||
|
|
||||||
|
* Runlevels
|
||||||
|
* One-shot tasks, daemons, or SysV init scripts
|
||||||
|
* Runparts and rc.local
|
||||||
|
* Process supervision
|
||||||
|
* Sourcing environment files
|
||||||
|
* Conditions for network/process/custom dependencies
|
||||||
|
* Pre/Post script actions
|
||||||
|
* Tooling to enable/disable services
|
||||||
|
* Built-in getty
|
||||||
|
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
|
||||||
|
* Built-in support for Debian/BusyBox `/etc/network/interfaces`
|
||||||
|
* Cgroups v2, both configuration and monitoring in `initctl top`
|
||||||
|
* Plugin support for customization
|
||||||
|
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
||||||
|
|
||||||
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 with tutorials for:
|
[contrib/](contrib/) section with tutorials for:
|
||||||
@@ -58,10 +75,10 @@ runlevel 2
|
|||||||
log size=100k count=4
|
log size=100k count=4
|
||||||
|
|
||||||
# Services to be monitored and respawned as needed
|
# Services to be monitored and respawned as needed
|
||||||
service [S12345] watchdogd -L -f -- System watchdog daemon
|
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon
|
||||||
service [S12345] syslogd -n -b 3 -D -- System log daemon
|
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
|
||||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||||
service [2345] lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab)
|
||||||
|
|
||||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
# 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
|
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||||
@@ -76,24 +93,24 @@ service :80 [2345] merecat -n -p 80 /var/www -- Web server
|
|||||||
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
|
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
|
||||||
|
|
||||||
# Alternative method instead of below runparts, can also use /etc/rc.local
|
# Alternative method instead of below runparts, can also use /etc/rc.local
|
||||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
|
||||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
||||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
#task [S] /etc/init.d/kbd -- Preparing console
|
||||||
|
|
||||||
# Run start scripts from this directory
|
# Run start scripts from this directory
|
||||||
# runparts /etc/start.d
|
# runparts /etc/start.d
|
||||||
|
|
||||||
# Virtual consoles run BusyBox getty, keep kernel default speed
|
# Virtual consoles run BusyBox getty, keep kernel default speed
|
||||||
tty [12345] /sbin/getty -L 0 /dev/tty1 linux
|
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
|
||||||
tty [12345] /sbin/getty -L 0 /dev/tty2 linux
|
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
|
||||||
tty [12345] /sbin/getty -L 0 /dev/tty3 linux
|
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
|
||||||
|
|
||||||
# Use built-in getty for serial port and USB serial
|
# Use built-in getty for serial port and USB serial
|
||||||
tty [12345] /dev/ttyAMA0 noclear nowait
|
#tty [12345] /dev/ttyAMA0 noclear nowait
|
||||||
tty [12345] /dev/ttyUSB0 noclear
|
#tty [12345] /dev/ttyUSB0 noclear
|
||||||
|
|
||||||
# Just give me a shell, I need to debug this embedded system!
|
# Just give me a shell, I need to debug this embedded system!
|
||||||
tty [12345] @console noclear nologin
|
#tty [12345] console noclear nologin
|
||||||
```
|
```
|
||||||
|
|
||||||
The `service` stanza, as well as `task`, `run` and others are described
|
The `service` stanza, as well as `task`, `run` and others are described
|
||||||
@@ -117,7 +134,7 @@ Some components are optional: runlevel(s), condition(s) and description,
|
|||||||
making it easy to create simple start scripts and still possible for more
|
making it easy to create simple start scripts and still possible for more
|
||||||
advanced uses as well:
|
advanced uses as well:
|
||||||
|
|
||||||
service sshd -D
|
service /usr/sbin/sshd -D
|
||||||
|
|
||||||
Dependencies are handled using [conditions](doc/conditions.md). One of
|
Dependencies are handled using [conditions](doc/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
|
||||||
@@ -232,8 +249,11 @@ For more information, see [doc/plugins.md](doc/plugins.md).
|
|||||||
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
|
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
|
||||||
registering any changes to `.conf` files. To activate a change the user
|
registering any changes to `.conf` files. To activate a change the user
|
||||||
must call `initctl reload`, which reloads all modified files, stops any
|
must call `initctl reload`, which reloads all modified files, stops any
|
||||||
removed services, starts new ones, and restarts any modified ones, with
|
removed services, starts new ones, and restarts any modified ones. If the
|
||||||
SIGHUP if the process supports it.
|
command line arguments of a service have changed, the process will be
|
||||||
|
terminated and then started again with the updated arguments. If the arguments
|
||||||
|
have not been modified and the process supports SIGHUP, the process will
|
||||||
|
receive a SIGHUP rather than being terminated and started.
|
||||||
|
|
||||||
For some use-cases the extra step of calling `initctl reload` creates an
|
For some use-cases the extra step of calling `initctl reload` creates an
|
||||||
unnecessary overhead, which can be removed at build-time using:
|
unnecessary overhead, which can be removed at build-time using:
|
||||||
@@ -241,6 +261,46 @@ unnecessary overhead, which can be removed at build-time using:
|
|||||||
configure --enable-auto-reload
|
configure --enable-auto-reload
|
||||||
|
|
||||||
|
|
||||||
|
**Cgroups**
|
||||||
|
|
||||||
|
Finit supports cgroups v2 and comes with the following default groups
|
||||||
|
in which services and user sessions are placed in:
|
||||||
|
|
||||||
|
/sys/fs/cgroup
|
||||||
|
|-- init/ # cpu.weight:100
|
||||||
|
|-- system/ # cpu.weight:9800
|
||||||
|
`-- user/ # cpu.weight:100
|
||||||
|
|
||||||
|
Finit itself and its helper scripts and services are placed in the
|
||||||
|
top-level leaf-node group `init/`, which also is _reserved_.
|
||||||
|
|
||||||
|
All run/task/service/sysv processes are placed in their own sub-group
|
||||||
|
in `system/`. The name of each sub-group is taken from the respective
|
||||||
|
`.conf` file from `/etc/finit.d`.
|
||||||
|
|
||||||
|
All getty/tty processes are placed in their own sub-group in `user/`.
|
||||||
|
The name of each sub-group is taken from the username.
|
||||||
|
|
||||||
|
A fourth group also exists, the `root` group. It is also _reserved_ and
|
||||||
|
primarily intended for RT tasks. If you have RT tasks they need to be
|
||||||
|
declared as such in their service stanza like this:
|
||||||
|
|
||||||
|
service [...] <...> cgroup.root /path/to/foo args -- description
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
cgroup.root
|
||||||
|
service [...] <...> /path/to/foo args -- description
|
||||||
|
service [...] <...> /path/to/bar args -- description
|
||||||
|
|
||||||
|
See [doc/config.md](doc/config.md#cgroups) for more information, e.g.,
|
||||||
|
how to configure per-group limits.
|
||||||
|
|
||||||
|
The `initctl` tool has three commands to help debug and optimize the
|
||||||
|
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
|
||||||
|
commands for details.
|
||||||
|
|
||||||
|
|
||||||
Runparts & /etc/rc.local
|
Runparts & /etc/rc.local
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
@@ -356,6 +416,9 @@ Options:
|
|||||||
-b, --batch Batch mode, no screen size probing
|
-b, --batch Batch mode, no screen size probing
|
||||||
-c, --create Create missing paths (and files) as needed
|
-c, --create Create missing paths (and files) as needed
|
||||||
-f, --force Ignore missing files and arguments, never prompt
|
-f, --force Ignore missing files and arguments, never prompt
|
||||||
|
-1, --once Only one lap in commands like 'top'
|
||||||
|
-p, --plain Use plain table headings, no ctrl chars
|
||||||
|
-q, --quiet Silent, only return status of command
|
||||||
-t, --no-heading Skip table headings
|
-t, --no-heading Skip table headings
|
||||||
-v, --verbose Verbose output
|
-v, --verbose Verbose output
|
||||||
-h, --help This help text
|
-h, --help This help text
|
||||||
@@ -375,17 +438,22 @@ Commands:
|
|||||||
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
||||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||||
|
|
||||||
cond show Show condition status
|
cond set <COND> Set (assert) user-defined condition +usr/COND
|
||||||
|
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
|
||||||
|
cond status Show condition status, default cond command
|
||||||
cond dump Dump all conditions and their status
|
cond dump Dump all conditions and their status
|
||||||
|
|
||||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||||
start <NAME>[:ID] Start service by name, with optional ID
|
start <NAME>[:ID] Start service by name, with optional ID
|
||||||
stop <NAME>[:ID] Stop/Pause a running service by name
|
stop <NAME>[:ID] Stop/Pause a running service by name
|
||||||
|
reload <NAME>[:ID] Reload service by name (SIGHUP or restart)
|
||||||
restart <NAME>[:ID] Restart (stop/start) service by name
|
restart <NAME>[:ID] Restart (stop/start) service by name
|
||||||
status <NAME>[:ID] Show service status, by name
|
status <NAME>[:ID] Show service status, by name
|
||||||
status Show status of services, default command
|
status Show status of services, default command
|
||||||
|
|
||||||
|
cgroup List cgroup config overview
|
||||||
ps List processes based on cgroups
|
ps List processes based on cgroups
|
||||||
|
top Show top-like listing based on cgroups
|
||||||
|
|
||||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||||
reboot Reboot system
|
reboot Reboot system
|
||||||
@@ -399,13 +467,17 @@ 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](doc/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
|
||||||
the current runlevel these services will not be respawned automatically
|
> the current runlevel these services will not be respawned automatically
|
||||||
by Finit if they exit (crash). Hence, if the runlevel is 2, the below
|
> by Finit if they exit (crash). Hence, if the runlevel is 2, the below
|
||||||
Dropbear SSH service will not be restarted if it is killed or exits.
|
> Dropbear SSH service will not be restarted if it is killed or exits.
|
||||||
|
|
||||||
|
The `status` command is the default, it displays a quick overview of all
|
||||||
|
monitored run/task/services. Here we call `initctl -p`, suitable for
|
||||||
|
scripting and documentation:
|
||||||
|
|
||||||
```
|
```
|
||||||
alpine:~# initctl
|
alpine:~# initctl -p
|
||||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||||
======================================================================
|
======================================================================
|
||||||
1506 acpid running [--2345----] ACPI daemon
|
1506 acpid running [--2345----] ACPI daemon
|
||||||
@@ -415,7 +487,7 @@ PID IDENT STATUS RUNLEVELS DESCRIPTION
|
|||||||
1512 ntpd running [--2345----] NTP daemon
|
1512 ntpd running [--2345----] NTP daemon
|
||||||
1473 syslogd running [S12345----] Syslog daemon
|
1473 syslogd running [S12345----] Syslog daemon
|
||||||
|
|
||||||
alpine:~# initctl -v
|
alpine:~# initctl -pv
|
||||||
PID IDENT STATUS RUNLEVELS COMMAND
|
PID IDENT STATUS RUNLEVELS COMMAND
|
||||||
======================================================================
|
======================================================================
|
||||||
1506 acpid running [--2345----] acpid -f
|
1506 acpid running [--2345----] acpid -f
|
||||||
@@ -430,6 +502,40 @@ The environment variables to each of the services above are read from,
|
|||||||
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
|
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
|
||||||
have other directories, e.g., Debian use `/etc/default/`.
|
have other directories, e.g., Debian use `/etc/default/`.
|
||||||
|
|
||||||
|
The `status` command takes an optional `NAME:ID` argument. Here we
|
||||||
|
check the status of `dropbear`, which only has one instance in this
|
||||||
|
system:
|
||||||
|
|
||||||
|
```
|
||||||
|
alpine:~# initctl -p status dropbear
|
||||||
|
Status : running
|
||||||
|
Identity : dropbear
|
||||||
|
Description : Dropbear SSH daemon
|
||||||
|
Origin : /etc/finit.d/enabled/dropbear.conf
|
||||||
|
Environment : -/etc/conf.d/dropbear
|
||||||
|
Condition(s):
|
||||||
|
Command : dropbear -R -F $DROPBEAR_OPTS
|
||||||
|
PID file : !/run/dropbear.pid
|
||||||
|
PID : 1485
|
||||||
|
User : root
|
||||||
|
Group : root
|
||||||
|
Uptime : 2 hour 46 min 56 sec
|
||||||
|
Runlevels : [--2345----]
|
||||||
|
Memory : 1.2M
|
||||||
|
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|
||||||
|
|- 1485 dropbear -R -F
|
||||||
|
|- 2634 dropbear -R -F
|
||||||
|
|- 2635 ash
|
||||||
|
`- 2652 initctl -p status dropbear
|
||||||
|
|
||||||
|
Apr 8 12:19:49 alpine authpriv.info dropbear[1485]: Not backgrounding
|
||||||
|
Apr 8 12:37:45 alpine authpriv.info dropbear[2300]: Child connection from 192.168.121.1:47834
|
||||||
|
Apr 8 12:37:46 alpine authpriv.notice dropbear[2300]: Password auth succeeded for 'root' from 192.168.121.1:47834
|
||||||
|
Apr 8 12:37:46 alpine authpriv.info dropbear[2300]: Exit (root) from <192.168.121.1:47834>: Disconnect received
|
||||||
|
Apr 8 15:02:11 alpine authpriv.info dropbear[2634]: Child connection from 192.168.121.1:48576
|
||||||
|
Apr 8 15:02:12 alpine authpriv.notice dropbear[2634]: Password auth succeeded for 'root' from 192.168.121.1:48576
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
+19
-40
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT(Finit, 4.0-rc2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
AC_INIT(Finit, 4.1-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||||
AC_CONFIG_AUX_DIR(aux)
|
AC_CONFIG_AUX_DIR(aux)
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
@@ -8,7 +8,7 @@ AC_CONFIG_HEADER([config.h])
|
|||||||
AC_CONFIG_FILES([Makefile
|
AC_CONFIG_FILES([Makefile
|
||||||
contrib/Makefile
|
contrib/Makefile
|
||||||
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/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/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile
|
||||||
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
|
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
plugins/Makefile
|
plugins/Makefile
|
||||||
@@ -48,30 +48,13 @@ PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
|||||||
PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
|
PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
|
||||||
|
|
||||||
# Check for configured Finit features
|
# Check for configured Finit features
|
||||||
AC_ARG_ENABLE(emergency_shell,
|
|
||||||
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
|
|
||||||
Use only for debugging very early boot problems with Finit]),,[
|
|
||||||
enable_emergency_shell=no])
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(fallback_shell,
|
|
||||||
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
|
|
||||||
enable_fallback_shell=no])
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(auto_reload,
|
AC_ARG_ENABLE(auto_reload,
|
||||||
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
|
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
|
||||||
enable_auto_reload=no])
|
enable_auto_reload=no])
|
||||||
|
|
||||||
AC_ARG_ENABLE(watchdog,
|
|
||||||
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
|
|
||||||
enable_watchdog=no])
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(redirect,
|
AC_ARG_ENABLE(redirect,
|
||||||
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
|
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
||||||
enable_redirect_output=no])
|
enable_redirect=yes])
|
||||||
|
|
||||||
AC_ARG_ENABLE(logit,
|
|
||||||
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
|
|
||||||
enable_logit=yes])
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(logrotate,
|
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]),,[
|
||||||
@@ -128,28 +111,20 @@ AC_ARG_WITH(random-seed,
|
|||||||
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
|
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
|
||||||
[random_seed=$withval], [random_seed=yes])
|
[random_seed=$withval], [random_seed=yes])
|
||||||
|
|
||||||
|
AC_ARG_WITH(watchdog,
|
||||||
|
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
|
||||||
|
[watchdog=$withval], [with_watchdog=no watchdog=])
|
||||||
|
|
||||||
### Enable features ###########################################################################
|
### Enable features ###########################################################################
|
||||||
|
|
||||||
# Create config.h from selected features and fallback defautls
|
# Create config.h from selected features and fallback defaults
|
||||||
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
|
|
||||||
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
|
|
||||||
|
|
||||||
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
|
|
||||||
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
|
|
||||||
|
|
||||||
AS_IF([test "x$enable_auto_reload" = "xyes"], [
|
AS_IF([test "x$enable_auto_reload" = "xyes"], [
|
||||||
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
|
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
|
||||||
|
|
||||||
AS_IF([test "x$enable_watchdog" = "xyes"], [
|
|
||||||
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
|
|
||||||
|
|
||||||
AS_IF([test "x$enable_redirect" = "xyes"], [
|
AS_IF([test "x$enable_redirect" = "xyes"], [
|
||||||
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||||
|
|
||||||
### Disable features ###########################################################################
|
### Disable features ###########################################################################
|
||||||
AS_IF([test "x$enable_logit" != "xno"], [
|
|
||||||
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
|
|
||||||
|
|
||||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||||
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
||||||
|
|
||||||
@@ -204,10 +179,16 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
|||||||
AC_EXPAND_DIR(random_path, "$random_seed")
|
AC_EXPAND_DIR(random_path, "$random_seed")
|
||||||
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
|
||||||
|
|
||||||
|
AS_IF([test "x$with_watchdog" != "xno"], [
|
||||||
|
AS_IF([test "x$watchdog" = "xyes"], [
|
||||||
|
watchdog=/dev/watchdog])
|
||||||
|
with_watchdog=yes
|
||||||
|
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
|
||||||
|
watchdog=])
|
||||||
|
|
||||||
# Control build with automake flags
|
# Control build with automake flags
|
||||||
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
|
||||||
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
|
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
|
||||||
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
|
|
||||||
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
|
||||||
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||||
|
|
||||||
@@ -253,11 +234,9 @@ Behavior:
|
|||||||
Optional features:
|
Optional features:
|
||||||
Install doc/..........: $enable_doc
|
Install doc/..........: $enable_doc
|
||||||
Install contrib/......: $enable_contrib
|
Install contrib/......: $enable_contrib
|
||||||
Built-in watchdogd....: $enable_watchdog
|
Built-in watchdogd....: $with_watchdog $watchdog
|
||||||
Built-in logrotate....: $enable_logrotate
|
Built-in logrotate....: $enable_logrotate
|
||||||
Scripting tool logit..: $enable_logit
|
Redirect output.......: $enable_redirect
|
||||||
Emergency shell.......: $enable_emergency_shell
|
|
||||||
Fallback shell........: $enable_fallback_shell
|
|
||||||
Default hostname......: $hostname
|
Default hostname......: $hostname
|
||||||
Default runlevel......: $runlevel
|
Default runlevel......: $runlevel
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
|||||||
--prefix=/usr --exec-prefix= \
|
--prefix=/usr --exec-prefix= \
|
||||||
--sysconfdir=/etc --localstatedir=/var \
|
--sysconfdir=/etc --localstatedir=/var \
|
||||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||||
--enable-alsa-utils-plugin --enable-watchdog \
|
--enable-alsa-utils-plugin --with-watchdog \
|
||||||
--with-heading="Alpine Linux 3.13"
|
--with-heading="Alpine Linux 3.13"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
docsdir := @docdir@/contrib/alpine/finit.d/available
|
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||||
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||||
lxdm.conf modules.conf ntpd.conf klogd.conf syslog.conf \
|
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf
|
||||||
watchdog.conf
|
|
||||||
EXTRA_DIST = $(docs_DATA)
|
EXTRA_DIST = $(docs_DATA)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# Consoles to start getty on when system is up
|
# Consoles to start getty on when system is up
|
||||||
tty [12345] /dev/tty1 noclear nowait
|
tty [12345] /dev/ttyS0 noclear
|
||||||
tty [2345] /dev/tty2 noclear nowait
|
tty [12345] /dev/tty1 noclear nowait
|
||||||
tty [2345] /dev/tty3 noclear nowait
|
tty [2345] /dev/tty2 noclear nowait
|
||||||
|
tty [2345] /dev/tty3 noclear nowait
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
# Start klogd as soon as possible, after syslogd, should always run
|
|
||||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
|
||||||
@@ -4,3 +4,4 @@
|
|||||||
#module evdev
|
#module evdev
|
||||||
#module mousedev
|
#module mousedev
|
||||||
#module 8139cp
|
#module 8139cp
|
||||||
|
module softdog
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
# Start syslogd as soon as possible, should always run
|
# Start syslogd as soon as possible, should always run
|
||||||
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
|
# Start klogd as soon as possible, but after syslogd
|
||||||
|
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
|
||||||
|
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fi
|
|||||||
# Adjust base directory
|
# Adjust base directory
|
||||||
if [ -e alpine-release ]; then
|
if [ -e alpine-release ]; then
|
||||||
cd ../..
|
cd ../..
|
||||||
elif [ ! -e autogen.sh ]; then
|
elif [ ! -e configure.ac ]; then
|
||||||
echo "*** Please run this script from the Finit base directory."
|
echo "*** Please run this script from the Finit base directory."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
SUBDIRS = finit.d grub.d
|
SUBDIRS = finit.d
|
||||||
docsdir := @docdir@/contrib/debian
|
docsdir := @docdir@/contrib/debian
|
||||||
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
docs_DATA = README.md finit.conf os-release build.sh install.sh
|
||||||
EXTRA_DIST = $(docs_DATA)
|
EXTRA_DIST = $(docs_DATA)
|
||||||
|
|||||||
@@ -20,29 +20,41 @@ tools pre-systemd work as intended.
|
|||||||
|
|
||||||
root@debian:~# apt install initscripts console-setup
|
root@debian:~# apt install initscripts console-setup
|
||||||
|
|
||||||
|
> **Note:** as of Debian 11 (Bullseye), libuev and libite are part of
|
||||||
|
> the main section of Debian. So just install the -dev packages :)
|
||||||
|
|
||||||
The following script can then be used to configure, build, install and
|
The following script can then be used to configure, build, install and
|
||||||
set up your system to run Finit:
|
set up your system to run Finit:
|
||||||
|
|
||||||
user@debian:~/finit$ contrib/debian/build.sh
|
user@debian:~/finit$ contrib/debian/build.sh
|
||||||
|
|
||||||
However, since `/sbin/init` already exists on your system, the script
|
However, since `/sbin/init` already exists on your system, the script
|
||||||
creates another entry in your GRUB config, in `/etc/grub.d/40_custom`,
|
creates another entry in your GRUB config by updating `$SUPPORTED_INITS`
|
||||||
where `init=/sbin/finit` is added to your kernel line. It is of course
|
in `/etc/grub.d/10_inux` and run `update-grub`. On reboot you will find
|
||||||
also possible to change the default init to Finit, if you do you can
|
a (finit) entry in the "Advanced options for Debian GNU/Linux" section.
|
||||||
remove the custom Grub entry:
|
It is of course also possible to change the default init to Finit:
|
||||||
|
|
||||||
user@debian:~/finit# cd /sbin
|
user@debian:~/finit# cd /sbin
|
||||||
user@debian:/sbin# sudo rm init
|
user@debian:/sbin# sudo mv init oldinit
|
||||||
user@debian:/sbin# sudo ln -s finit init
|
user@debian:/sbin# sudo ln -s finit init
|
||||||
|
|
||||||
Before rebooting, check the default [/etc/finit.conf](finit.conf) and
|
Before rebooting, check the default [/etc/finit.conf](finit.conf) and
|
||||||
`/etc/finit.d/*.conf` files. The build + install script above provides
|
`/etc/finit.d/*.conf` files. The build + install script above provides
|
||||||
a few sample `.conf` files. See `initctl list` after boot for a list of
|
a few sample `.conf` files. See `initctl ls` after boot for a list of
|
||||||
enabled and available services.
|
enabled and available services, you can then use `enable` and `disable`
|
||||||
|
comands to `initctl`, followed by `reload` to activate your changes.
|
||||||
|
|
||||||
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
|
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
|
||||||
and initialization like keyboard language etc.
|
and initialization like keyboard language etc.
|
||||||
|
|
||||||
|
If you want to use the X Window system, `sudo apt install elogind`
|
||||||
|
(Bullseye and later). Followed by `initctl reload` (enabled by default)
|
||||||
|
and logout/login again. The elogind daemon ensures a regular non-root
|
||||||
|
user can start and interact with an X session, otherwise keyboard and
|
||||||
|
mouse won't work. When you're happy, you can enable the lightdm.conf,
|
||||||
|
change the default runlevel to 3, and presto! you have a desktop again.
|
||||||
|
|
||||||
|
|
||||||
Have fun!
|
Have fun!
|
||||||
/Joachim ツ
|
/Joachim ツ
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
SUBDIRS = available
|
SUBDIRS = available
|
||||||
docsdir := @docdir@/contrib/debian/finit.d
|
docsdir := @docdir@/contrib/debian/finit.d
|
||||||
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
|
d_LINKS = acpid.conf apparmor.conf avahi-demon.conf \
|
||||||
modules.conf rsyslogd.conf sshd.conf
|
bluetooth.conf console-setup.conf elogind.conf \
|
||||||
|
getty.conf keyboard-setup.conf modules.conf \
|
||||||
|
rsyslogd.conf sshd.conf
|
||||||
|
|
||||||
install-exec-hook:
|
install-exec-hook:
|
||||||
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
docsdir := @docdir@/contrib/debian/finit.d/available
|
docsdir := @docdir@/contrib/debian/finit.d/available
|
||||||
docs_DATA = acpid.conf console-setup.conf getty.conf keyboard-setup.conf \
|
docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \
|
||||||
lightdm.conf modules.conf rsyslogd.conf sntpd.conf \
|
console-setup.conf elogind.conf getty.conf \
|
||||||
sshd.conf syslogd.conf xdm.conf
|
keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \
|
||||||
|
rsyslogd.conf sshd.conf syslogd.conf xdm.conf
|
||||||
EXTRA_DIST = $(docs_DATA)
|
EXTRA_DIST = $(docs_DATA)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# To ignore errors like "org.freedesktop.hostname1", update the env file with
|
||||||
|
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
|
||||||
|
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# On some systems, bootstrap can be really quick and cause elogind
|
||||||
|
# to not start before a user has managed to log in. To prevent
|
||||||
|
# this, add <pid/elogind> as condition to your TTYs in getty.conf
|
||||||
|
service [2345] <pid/syslogd> /lib/elogind/elogind -- Login manager
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
# Consoles to start getty on when system is up
|
# Consoles to start getty on when system is up
|
||||||
tty [12345] /dev/tty1 linux noclear nowait
|
tty [12345] /dev/tty1 linux noclear nowait
|
||||||
tty [2345] /dev/tty2 linux noclear nowait
|
tty [2345] /dev/tty2 linux nowait
|
||||||
tty [2345] /dev/tty3 linux noclear nowait
|
tty [2345] /dev/tty3 linux nowait
|
||||||
|
tty [2345] /dev/tty4 linux nowait
|
||||||
|
tty [2345] /dev/tty5 linux nowait
|
||||||
|
tty [2345] /dev/tty6 linux nowait
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
exec tail -n +3 $0
|
|
||||||
# This file provides an easy way to add custom menu entries. Simply type the
|
|
||||||
# menu entries you want to add after this comment. Be careful not to change
|
|
||||||
# the 'exec tail' line above.
|
|
||||||
|
|
||||||
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
|
|
||||||
insmod gzio
|
|
||||||
insmod part_msdos
|
|
||||||
insmod ext2
|
|
||||||
search --no-floppy --fs-uuid --set=root $BOOT
|
|
||||||
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
|
|
||||||
initrd /initrd.img
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
docsdir := @docdir@/contrib/debian/grub.d
|
|
||||||
docs_DATA = 40_custom
|
|
||||||
EXTRA_DIST = $(docs_DATA)
|
|
||||||
@@ -11,7 +11,7 @@ fi
|
|||||||
grep -q Debian os-release 2>/dev/null
|
grep -q Debian os-release 2>/dev/null
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
cd ../..
|
cd ../..
|
||||||
elif [ ! -e autogen.sh ]; then
|
elif [ ! -e configure.ac ]; then
|
||||||
echo "*** Please run this script from the Finit base directory."
|
echo "*** Please run this script from the Finit base directory."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -23,7 +23,7 @@ echo "/sbin/finit - PID 1"
|
|||||||
echo "/lib/finit/plugins/* - All enabled Finit plugins"
|
echo "/lib/finit/plugins/* - All enabled Finit plugins"
|
||||||
echo "/etc/finit.conf - Finit configuration file"
|
echo "/etc/finit.conf - Finit configuration file"
|
||||||
echo "/etc/finit.d/ - Finit services"
|
echo "/etc/finit.d/ - Finit services"
|
||||||
echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader"
|
echo "/etc/grub.d/10_linux - Add Finit to Grub's SUPPORTED_INITS"
|
||||||
echo
|
echo
|
||||||
read -p "Do you want to continue (y/N)? " yorn
|
read -p "Do you want to continue (y/N)? " yorn
|
||||||
echo
|
echo
|
||||||
@@ -38,12 +38,18 @@ if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
|
|||||||
cp -va finit.d /etc/
|
cp -va finit.d /etc/
|
||||||
|
|
||||||
echo "*** Setting up a GRUB boot entry ..."
|
echo "*** Setting up a GRUB boot entry ..."
|
||||||
MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"`
|
fn=/etc/grub.d/10_linux
|
||||||
TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"`
|
if [ -e $fn ]; then
|
||||||
BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
|
if `grep SUPPORTED_INITS $fn |head -1 |grep -q finit`; then
|
||||||
ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
|
echo "Already installed, done."
|
||||||
cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom
|
else
|
||||||
update-grub
|
echo "Adding Finit to list of SUPPORTED_INITS ..."
|
||||||
|
sed -i 's/SUPPORTED_INITS="[^"]*/& finit:\/sbin\/finit/' $fn
|
||||||
|
update-grub
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Cannot find $fn, you'll have to set up your bootloader on your own."
|
||||||
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "*** Done"
|
echo "*** Done"
|
||||||
|
|||||||
+29
-2
@@ -8,6 +8,18 @@ module evdev
|
|||||||
module loop
|
module loop
|
||||||
module psmouse
|
module psmouse
|
||||||
|
|
||||||
|
# Top-level cgroups and their default settings. All groups mandatory
|
||||||
|
# but more can be added, max 8 groups in total currently. The cgroup
|
||||||
|
# 'root' is also available, reserved for RT processes. Settings are
|
||||||
|
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||||
|
# cgroup v2 controller default names.
|
||||||
|
cgroup init cpu.weight:100
|
||||||
|
cgroup user cpu.weight:100
|
||||||
|
cgroup system cpu.weight:9700 cpu.max:50000
|
||||||
|
|
||||||
|
# Example extra cgroup
|
||||||
|
cgroup maint cpu.weight:100
|
||||||
|
|
||||||
# Runlevel to start after bootstrap, runlevel 'S'
|
# Runlevel to start after bootstrap, runlevel 'S'
|
||||||
runlevel 2
|
runlevel 2
|
||||||
|
|
||||||
@@ -16,7 +28,7 @@ network service networking start
|
|||||||
|
|
||||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
# 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 => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||||
log size=100k count=4
|
log size:100k count:4
|
||||||
|
|
||||||
# Virtual consoles to start getty on
|
# Virtual consoles to start getty on
|
||||||
tty /dev/tty1
|
tty /dev/tty1
|
||||||
@@ -30,9 +42,24 @@ tty /dev/tty3
|
|||||||
#run [2] /etc/init.d/networking start -- Start networking
|
#run [2] /etc/init.d/networking start -- Start networking
|
||||||
|
|
||||||
# Services to be monitored and respawned as needed
|
# Services to be monitored and respawned as needed
|
||||||
|
|
||||||
|
# klgod and syslogd are placed in the maint cgroup
|
||||||
|
cgroup.maint
|
||||||
service [2345] <pid/syslogd> klogd -n -- Kernel logging server
|
service [2345] <pid/syslogd> klogd -n -- Kernel logging server
|
||||||
service [2345] syslogd -n -- Syslog server
|
service [2345] syslogd -n -- Syslog server
|
||||||
service [3] gdm -- GNOME Display Manager
|
|
||||||
|
# gdm in the system cgroup (default when read from it's own .conf file)
|
||||||
|
cgroup.system
|
||||||
|
service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
|
||||||
|
|
||||||
|
# Start SSH daemon with opts from /etc/default/ssh (if available), move
|
||||||
|
# to user cgroup with cpu.weight:250. Default for services is system
|
||||||
|
# group. Default weight in user group is 100 (above). Max length of
|
||||||
|
# cgroup argument is (currently) 63 chars.
|
||||||
|
#
|
||||||
|
# Notice alternative syntax where current cgroup is only set for this
|
||||||
|
# particular service.
|
||||||
|
service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
|
||||||
|
|
||||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
# 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
|
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
|||||||
--prefix=/usr --exec-prefix= \
|
--prefix=/usr --exec-prefix= \
|
||||||
--sysconfdir=/etc --localstatedir=/var \
|
--sysconfdir=/etc --localstatedir=/var \
|
||||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||||
--enable-alsa-utils-plugin --enable-watchdog \
|
--enable-alsa-utils-plugin --with-watchdog \
|
||||||
--with-heading="Void Linux"
|
--with-heading="Void Linux"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
docsdir := @docdir@/contrib/void/finit.d/available
|
docsdir := @docdir@/contrib/void/finit.d/available
|
||||||
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
||||||
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
|
keymap.conf lxdm.conf modules.conf ntpd.conf \
|
||||||
sshd.conf syslogd.conf uuidd.conf
|
sshd.conf syslog.conf uuidd.conf
|
||||||
EXTRA_DIST = $(docs_DATA)
|
EXTRA_DIST = $(docs_DATA)
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
# Start klogd as soon as possible, should always run
|
|
||||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
|
||||||
@@ -4,3 +4,4 @@
|
|||||||
#module evdev
|
#module evdev
|
||||||
#module mousedev
|
#module mousedev
|
||||||
#module 8139cp
|
#module 8139cp
|
||||||
|
module softdog
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Start syslogd as soon as possible, should always run
|
||||||
|
# Start klogd as soon as possible, but after syslogd
|
||||||
|
service [S12345] syslogd -n -- Syslog daemon
|
||||||
|
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# Start syslogd as soon as possible, should always run
|
|
||||||
service [S12345] syslogd -n -- Syslog daemon
|
|
||||||
@@ -10,7 +10,7 @@ fi
|
|||||||
# Adjust base directory
|
# Adjust base directory
|
||||||
if [ -e xbps ]; then
|
if [ -e xbps ]; then
|
||||||
cd ../..
|
cd ../..
|
||||||
elif [ ! -e autogen.sh ]; then
|
elif [ ! -e configure.ac ]; then
|
||||||
echo "*** Please run this script from the Finit base directory."
|
echo "*** Please run this script from the Finit base directory."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
+10
-4
@@ -19,9 +19,6 @@ Some of these are also registered in the issue tracker on GitHub.
|
|||||||
* Add new (complimentary) .svc file format to slowly migrate away
|
* Add new (complimentary) .svc file format to slowly migrate away
|
||||||
from the very terse one-liner format currently used
|
from the very terse one-liner format currently used
|
||||||
* Allow running as non-pid1 => read .conf and RCSD form cmdline
|
* Allow running as non-pid1 => read .conf and RCSD form cmdline
|
||||||
* When a process dies, and Finit does not restart it, we should collect
|
|
||||||
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
|
|
||||||
`initctl status <process>` is called to debug the issue.
|
|
||||||
* 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,
|
||||||
@@ -29,9 +26,18 @@ Some of these are also registered in the issue tracker on GitHub.
|
|||||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||||
`pimd` man pages ...
|
`pimd` man pages ...
|
||||||
* Add support for timed shutdown in Finit, including cancelled shutdown
|
|
||||||
|
|
||||||
|
Initctl
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Add support for timed shutdown in Finit, including cancelled shutdown:
|
||||||
|
|
||||||
|
shutdown -h 03:35 "foobar was here"
|
||||||
|
|
||||||
* 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`
|
||||||
|
* Add support for `-s` to redirect all output to syslog, for scripting
|
||||||
|
|
||||||
|
|
||||||
Init
|
Init
|
||||||
|
|||||||
+6
-4
@@ -9,9 +9,11 @@ Bootstrap
|
|||||||
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
|
||||||
9. Parse `/etc/finit.conf` and all `/etc/finit.d/*.conf` files
|
9. Parse `/etc/finit.conf`
|
||||||
10. Start built-in watchdog, if enabled
|
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
|
||||||
11. Set hostname
|
means any WDT that requires a kernel module need to be either
|
||||||
|
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
|
||||||
|
11. Load all `/etc/finit.d/*.conf` files and set hostname
|
||||||
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
|
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
|
||||||
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
|
||||||
14. Mount all file systems listed in `/etc/fstab` and swap, if available
|
14. Mount all file systems listed in `/etc/fstab` and swap, if available
|
||||||
@@ -34,7 +36,7 @@ Bootstrap
|
|||||||
24. Call 4th level hooks, `HOOK_SVC_UP`
|
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||||
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
|
||||||
|
|
||||||
In (19) and (22) tasks and services defined in `/etc/finit.conf` and
|
In (19) and (22) tasks and services defined in `/etc/finit.conf` and
|
||||||
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
|
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
|
||||||
|
|||||||
+10
-7
@@ -153,19 +153,22 @@ kernel messages and add `debug` to enable Finit debug messages.
|
|||||||
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, add
|
||||||
`configure --enable-fallback-shell`. When no TTYs are detected, and
|
the following to your Finit .conf file:
|
||||||
Finit is configured with this option, Finit will try to start a bare
|
|
||||||
`/bin/sh` on the boot console. Remember, this is only for debugging
|
tty [12345789] notty noclear
|
||||||
and would leave your production system potentially wide open.
|
|
||||||
|
The `notty` option ensures reusing the stdin/stdout set up by the
|
||||||
|
kernel. Remember, this is only for debugging and would leave your
|
||||||
|
production system potentially wide open.
|
||||||
|
|
||||||
There is also a rescue shell available, in case Finit crashes and the
|
There is also a rescue shell available, in case Finit crashes and the
|
||||||
kernel usually reboots: `configure --enable-emergency-shell`. However,
|
kernel usually reboots: `configure --enable-emergency-shell`. However,
|
||||||
the behavior of Finit is severely limited when this is enabled, so use
|
the behavior of Finit is severely limited when this is enabled, so use
|
||||||
it only for debugging start up issues when Finit crashes.
|
it only for debugging start up issues when Finit crashes.
|
||||||
|
|
||||||
**NOTE:** Neither of these two configure options should be enabled on
|
**NOTE:** Neither of these options should be enabled on production
|
||||||
production systems since they can potentially give a user root access.
|
systems since they can potentially give a user root access.
|
||||||
|
|
||||||
|
|
||||||
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
|
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
|
||||||
|
|||||||
+7
-5
@@ -30,10 +30,12 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
|||||||
* `quiet`: Suppress kernel logging to console, except for warnings and
|
* `quiet`: Suppress kernel logging to console, except for warnings and
|
||||||
errors.
|
errors.
|
||||||
|
|
||||||
* `rescue`: Start Finit rescue mode; no network, no `fsck` of file
|
* `rescue`: Start rescue/maintenance mode. If your system comes with
|
||||||
systems in `/etc/fstab`, no `/etc/rc.local`, no `runparts`. The
|
the bundled `sulogin` program (Finit, or from util-linux/Busybox),
|
||||||
configuration is read from `/lib/finit/rescue.conf`, and can be
|
you will be given a root login to a maintenance shell. However, if
|
||||||
modified by the operator.
|
`sulogin` is missing, the file `/lib/finit/rescue.conf` is read and
|
||||||
|
the system booted in a limited fallback mode. See [config.md][]
|
||||||
|
for more information.
|
||||||
|
|
||||||
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
|
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
|
||||||
debug problems with the regular runlevel. All services and TTYs in
|
debug problems with the regular runlevel. All services and TTYs in
|
||||||
@@ -42,5 +44,5 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
|||||||
|
|
||||||
For more on kernel boot paramaters, see the man page [bootparam(7)][].
|
For more on kernel boot paramaters, see the man page [bootparam(7)][].
|
||||||
|
|
||||||
|
[config.md][]: config.md#rescue-mode
|
||||||
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
|
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
|
||||||
|
|||||||
+27
-1
@@ -45,7 +45,31 @@ being created.
|
|||||||
Triggering
|
Triggering
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Conditions can only be triggered by built-in plugins.
|
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
|
||||||
|
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
|
||||||
|
also supports user-defined conditions, sometimes referred to as static
|
||||||
|
or one-shot conditions. They live in the `<usr/...` namespace and are
|
||||||
|
constrained to a flat hierarchy without any sub-directories, unlike the
|
||||||
|
pidfile plugin, for instance.
|
||||||
|
|
||||||
|
User-defined conditions are controlled using the `initctl cond set` and
|
||||||
|
`initctl cond clear` commands:
|
||||||
|
|
||||||
|
initctl cond set foo
|
||||||
|
initctl cond clear foo
|
||||||
|
|
||||||
|
The purpose of user-defined conditions is to be able to start or stop
|
||||||
|
services, or run/task jobs, on external site-dependent stimuli.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
|
||||||
|
|
||||||
|
For convenience, prefixing with `usr/` is allowed, but any other slashes
|
||||||
|
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
|
||||||
|
the same as above, can also be achieved like this:
|
||||||
|
|
||||||
|
initctl cond set usr/foo
|
||||||
|
initctl cond clear usr/foo
|
||||||
|
|
||||||
Conditions retain their current state until the next reconfiguration or
|
Conditions retain their current state until the next reconfiguration or
|
||||||
runlevel change. At that point all set conditions transition into the
|
runlevel change. At that point all set conditions transition into the
|
||||||
@@ -57,6 +81,8 @@ unchanged) state of it.
|
|||||||
> **Note:** For `pid/` conditions it is expected that services "touch"
|
> **Note:** For `pid/` conditions it is expected that services "touch"
|
||||||
> or recreate their PID file on `SIGHUP`.
|
> or recreate their PID file on `SIGHUP`.
|
||||||
|
|
||||||
|
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||||
|
|
||||||
|
|
||||||
Built-in Conditions
|
Built-in Conditions
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
+224
-28
@@ -2,7 +2,9 @@ Configuration
|
|||||||
=============
|
=============
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
|
* [Service Environment](#service-environment)
|
||||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||||
|
* [Cgroups](#cgroups)
|
||||||
* [Configuration File Syntax](#configuration-file-syntax)
|
* [Configuration File Syntax](#configuration-file-syntax)
|
||||||
* [Hostname](#hostname)
|
* [Hostname](#hostname)
|
||||||
* [Kernel Modules](#kernel-modules)
|
* [Kernel Modules](#kernel-modules)
|
||||||
@@ -19,9 +21,11 @@ Configuration
|
|||||||
* [TTYs and Consoles](#ttys-and-consoles)
|
* [TTYs and Consoles](#ttys-and-consoles)
|
||||||
* [Non-privileged Services](#non-privileged-services)
|
* [Non-privileged Services](#non-privileged-services)
|
||||||
* [Redirecting Output](#redirecting-output)
|
* [Redirecting Output](#redirecting-output)
|
||||||
|
* [Rescue Mode](#rescue-mode)
|
||||||
* [Limitations](#limitations)
|
* [Limitations](#limitations)
|
||||||
* [/etc/finit.conf](#etcfinitconf)
|
* [/etc/finit.conf](#etcfinitconf)
|
||||||
* [/etc/finit.d](#etcfinitd)
|
* [/etc/finit.d](#etcfinitd)
|
||||||
|
* [Watchdog](#watchdog)
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
@@ -35,8 +39,8 @@ Linux distributions -- each package can provide its own "script" 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`
|
||||||
and some directives are only available at bootstrap, runlevel `S`, see
|
and some directives are only available at [bootstrap][], runlevel `S`,
|
||||||
the section [Limitations](#limitations) below for details.
|
see 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
|
||||||
@@ -128,6 +132,67 @@ example employs a wrapper script in `/etc/start.d`.
|
|||||||
> instead of sending SIGHUP at restart/reload events.
|
> instead of sending SIGHUP at restart/reload events.
|
||||||
|
|
||||||
|
|
||||||
|
Cgroups
|
||||||
|
-------
|
||||||
|
|
||||||
|
There are three major cgroup configuration directives:
|
||||||
|
|
||||||
|
1. Global top-level group: init, system, user, or a custom group
|
||||||
|
2. Selecting a top-level group for a set of run/task/services
|
||||||
|
3. Per run/task/service limits
|
||||||
|
|
||||||
|
Top-level group configuration.
|
||||||
|
|
||||||
|
# Top-level cgroups and their default settings. All groups mandatory
|
||||||
|
# but more can be added, max 8 groups in total currently. The cgroup
|
||||||
|
# 'root' is also available, reserved for RT processes. Settings are
|
||||||
|
# as-is, only one shorthand 'mem.' exists, other than that it's the
|
||||||
|
# cgroup v2 controller default names.
|
||||||
|
cgroup init cpu.weight:100
|
||||||
|
cgroup user cpu.weight:100
|
||||||
|
cgroup system cpu.weight:9800
|
||||||
|
|
||||||
|
Adding an extra cgroup `maint/` will require you to adjust the weight of
|
||||||
|
the above three. We leave `init/` and `user /` as-is reducing weight of
|
||||||
|
`system/` to 9700.
|
||||||
|
|
||||||
|
cgroup system cpu.weight:9700
|
||||||
|
|
||||||
|
# Example extra cgroup 'maint'
|
||||||
|
cgroup maint cpu.weight:100
|
||||||
|
|
||||||
|
By default, the `system/` cgroup is selected for almost everything. The
|
||||||
|
`init/` cgroup is reserved for PID 1 itself and its closest relatives.
|
||||||
|
The `user/` cgroup is for local TTY logins spawned by getty.
|
||||||
|
|
||||||
|
To select a different top-level cgroup, e.g. `maint/`, one can either
|
||||||
|
define it for a group of run/task/service directives in a `.conf` or per
|
||||||
|
each stanza:
|
||||||
|
|
||||||
|
cgroup.maint
|
||||||
|
service [...] <...> /path/to/foo args -- description
|
||||||
|
service [...] <...> /path/to/bar args -- description
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
service [...] <...> cgroup.maint /path/to/foo args -- description
|
||||||
|
|
||||||
|
The latter form also allows per-stanza limits on the form:
|
||||||
|
|
||||||
|
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||||
|
|
||||||
|
Notice the comma separation and the `mem.` exception to the rule: every
|
||||||
|
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
|
||||||
|
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is not filtering,
|
||||||
|
except for expanding the shorthand `mem.` to `memory.`, if the file is
|
||||||
|
not available, either the cgroup controller is not available in your
|
||||||
|
Linux kernel, or the name is misspelled.
|
||||||
|
|
||||||
|
> Linux cgroups and details surrounding values are not explained in the
|
||||||
|
> Finit documentation. The Linux admin-guide cover this well:
|
||||||
|
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
|
||||||
|
|
||||||
|
|
||||||
Configuration File Syntax
|
Configuration File Syntax
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
@@ -140,7 +205,7 @@ the contents of that file is used.
|
|||||||
|
|
||||||
Deprecated. We recommend using `/etc/hostname` instead.
|
Deprecated. We recommend using `/etc/hostname` instead.
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||||
|
|
||||||
|
|
||||||
### Kernel Modules
|
### Kernel Modules
|
||||||
@@ -158,7 +223,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
|
|||||||
|
|
||||||
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
|
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||||
|
|
||||||
|
|
||||||
### Networking
|
### Networking
|
||||||
@@ -171,7 +236,7 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
|
|||||||
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
|
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
|
||||||
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||||
|
|
||||||
|
|
||||||
### Resource Limits
|
### Resource Limits
|
||||||
@@ -205,13 +270,13 @@ stanzas can share the same rlimits if they are in the same .conf.
|
|||||||
|
|
||||||
**Syntax:** `runlevel <N>`
|
**Syntax:** `runlevel <N>`
|
||||||
|
|
||||||
Defines the system runlevel to go to after bootstrap (S) has completed.
|
The system runlevel to go to after [bootstrap][] (S) has completed. `N`
|
||||||
N is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
|
||||||
halt.
|
halt.
|
||||||
|
|
||||||
Default: 2
|
Default: 2
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||||
|
|
||||||
|
|
||||||
### One-shot Commands (sequence)
|
### One-shot Commands (sequence)
|
||||||
@@ -333,6 +398,28 @@ process using a different signal, use the option `halt:SIGNAL`, e.g.,
|
|||||||
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
|
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
|
||||||
sending `SIGKILL`.
|
sending `SIGKILL`.
|
||||||
|
|
||||||
|
Services support `pre:script` and `post:script` actions as well. These
|
||||||
|
run as the same `@USER:GROUP` as the service itself, with any `env:file`
|
||||||
|
sourced. The scripts must use an absolute path, but are executed from
|
||||||
|
the `$HOME` of the given user. The scripts are not called with any
|
||||||
|
argument (currently), but both get the `SERVICE_IDENT=foo` environment
|
||||||
|
variable set. Here `foo` denotes the identity of the service, which if
|
||||||
|
there are multiple services named `foo`, may be `foo:1`, or any unique
|
||||||
|
identifier specified in the .conf file. The `post:script` is called
|
||||||
|
with an additional set of environment variables:
|
||||||
|
|
||||||
|
- `EXIT_CODE=[exited,signal]`: set to one of `exited` or `signal`
|
||||||
|
- `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from
|
||||||
|
the program, if it exited normally, or the signal name (`HUP`,
|
||||||
|
`TERM`, etc.) if it exited due to signal
|
||||||
|
|
||||||
|
The scripts have a default execution time of 3 seconds before they are
|
||||||
|
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
|
||||||
|
|
||||||
|
> **Note:** the `pre:script` _must_ be idempotent, because a service
|
||||||
|
> can transition between READY and HALTED states any number
|
||||||
|
> of times before going to RUNNING.
|
||||||
|
|
||||||
|
|
||||||
### Run-parts Scripts
|
### Run-parts Scripts
|
||||||
|
|
||||||
@@ -341,7 +428,7 @@ sending `SIGKILL`.
|
|||||||
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
|
The scripts in this directory are executed at the very end of runlevel
|
||||||
`S`, bootstrap.
|
`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
|
||||||
@@ -351,7 +438,10 @@ 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.
|
||||||
|
|
||||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
> **Note:** only read and executed in runlevel S ([bootstrap][]).
|
||||||
|
|
||||||
|
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||||
|
|
||||||
|
|
||||||
### Including Finit Configs
|
### Including Finit Configs
|
||||||
|
|
||||||
@@ -377,8 +467,9 @@ size limit is reached.
|
|||||||
|
|
||||||
### TTYs and Consoles
|
### TTYs and Consoles
|
||||||
|
|
||||||
**Syntax:** `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||||
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
|
||||||
|
`tty [LVLS] <COND> [notty] [rescue]`
|
||||||
|
|
||||||
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. The DEV may be the special
|
TTY device DEV, in the given runlevels. The DEV may be the special
|
||||||
@@ -386,15 +477,29 @@ keyword `@console`, or `console`, useful on embedded systems.
|
|||||||
|
|
||||||
The default baud rate is 0, i.e., keep kernel default.
|
The default baud rate is 0, i.e., keep kernel default.
|
||||||
|
|
||||||
|
> The `tty` stanza inherits runlevel, condition (and other feature)
|
||||||
|
> parsing from the `service` stanza. So TTYs can run in one or many
|
||||||
|
> runlevels and depend on any condition supported by Finit. This is
|
||||||
|
> useful e.g. to depend on `<pid/elogind>` before starting a TTY.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||||
|
|
||||||
The second `tty` syntax variant is for using an external getty, like
|
The second `tty` syntax variant is for using an external getty, like
|
||||||
agetty or the BusyBox getty.
|
agetty or the BusyBox getty.
|
||||||
|
|
||||||
By default both variants *clear* the TTY and *wait* for the user to
|
The third variant is for board bringup and the `rescue` boot mode. No
|
||||||
press enter before starting getty.
|
device node is required in this variant, the same output that the kernel
|
||||||
|
uses is reused for stdio. If the `rescue` option is omitted, a shell is
|
||||||
|
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
|
||||||
|
option is set the bundled `/libexec/finit/sulogin` is started to present
|
||||||
|
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
|
||||||
|
not have a password set, no rescue is possible. For more information,
|
||||||
|
see the [Rescue Mode](#rescue-mode) section.
|
||||||
|
|
||||||
|
By default, the first two syntax variants *clear* the TTY and *wait* for
|
||||||
|
the user to press enter before starting getty.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
@@ -423,16 +528,23 @@ uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
|
|||||||
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
|
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
|
||||||
can be omitted to keep the kernel default.
|
can be omitted to keep the kernel default.
|
||||||
|
|
||||||
|
> Most systems get by fine by just using `console`, which will evaluate
|
||||||
|
> to `/dev/console`. If you have to use `@console` to get any output,
|
||||||
|
> you may have some issue with your kernel config.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
tty [12345] @console noclear vt220
|
tty [12345] @console noclear vt220
|
||||||
|
|
||||||
On really bare bones systems Finit offers a fallback shell, which
|
On really bare bones systems, or for board bringup, Finit can give you a
|
||||||
should not be enabled on production systems since. This because it
|
shell prompt as soon as [bootstrap][] is done, without opening any
|
||||||
may give a user root access without having to log in. However, for
|
device node:
|
||||||
bringup and system debugging it can come in handy:
|
|
||||||
|
|
||||||
configure --enable-fallback-shell
|
tty [12345789] notty
|
||||||
|
|
||||||
|
This should of course not be enabled on production systems. Because it
|
||||||
|
may give a user root access without having to log in. However, for
|
||||||
|
board bringup and system debugging it can come in handy.
|
||||||
|
|
||||||
One can also use the `service` stanza to start a stand-alone shell:
|
One can also use the `service` stanza to start a stand-alone shell:
|
||||||
|
|
||||||
@@ -483,8 +595,58 @@ Log rotation is controlled using the global `log` setting.
|
|||||||
|
|
||||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||||
|
|
||||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
|
||||||
detailed description, see the [Conditions](conditions.md) document.
|
Rescue Mode
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Finit supports a rescue mode which is activated by the `rescue` option
|
||||||
|
on the kernel command line. See [cmdline docs](cmdline.md) for how to
|
||||||
|
activate it.
|
||||||
|
|
||||||
|
The rescue mode comes in two flavors; *traditional* and *fallback*.
|
||||||
|
|
||||||
|
|
||||||
|
### Traditional
|
||||||
|
|
||||||
|
This is what most users expect. A very early maintenance login prompt,
|
||||||
|
served by the bundled `/libexec/finit/sulogin` program, or the standard
|
||||||
|
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
|
||||||
|
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
|
||||||
|
rescue mode is ended and the system boots up normally.
|
||||||
|
|
||||||
|
> **Note:** if the user (UID 0 and GID 0) does not have a password, or
|
||||||
|
> __the account is locked__, the user is presented with a password-less
|
||||||
|
> `"Press enter to enter maintenance mode."`, prompt which opens up a
|
||||||
|
> root shell.
|
||||||
|
|
||||||
|
|
||||||
|
### Fallback
|
||||||
|
|
||||||
|
If no `sulogin` program is found, Finit tries to bring up as much of its
|
||||||
|
own functionality as possible, yet limiting many aspects, meaning; no
|
||||||
|
network, no `fsck` of file systems in `/etc/fstab`, no `/etc/rc.local`,
|
||||||
|
no `runparts`, and most plugins are skipped (except those that provide
|
||||||
|
functionality for the condition subsystem).
|
||||||
|
|
||||||
|
Instead of reading `/etc/finit.conf` et al, system configuration is read
|
||||||
|
from `/lib/finit/rescue.conf`, which can be freely modified by the
|
||||||
|
system administrator.
|
||||||
|
|
||||||
|
The bundled default `rescue.conf` contains nothing more than:
|
||||||
|
|
||||||
|
runlevel 1
|
||||||
|
tty [12345] rescue
|
||||||
|
|
||||||
|
The `tty` has the `rescue` option set, which works similar to the board
|
||||||
|
bring-up tty option `notty`. The major difference being that `sulogin`
|
||||||
|
is started to query for root/admin password. If `sulogin` is not found,
|
||||||
|
`rescue` behaves like `notty` and gives a plain root shell prompt.
|
||||||
|
|
||||||
|
If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
|
||||||
|
|
||||||
|
tty [12345] rescue
|
||||||
|
|
||||||
|
There is no way to exit the *fallback* rescue mode.
|
||||||
|
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
@@ -496,15 +658,49 @@ from `/etc/finit.conf`, since it is the first `.conf` file Finit reads.
|
|||||||
|
|
||||||
Originally, `/etc/finit.conf` was the only way to set up a Finit system.
|
Originally, `/etc/finit.conf` was the only way to set up a Finit system.
|
||||||
Today it is mainly used for bootstrap settings like system hostname,
|
Today it is mainly used for bootstrap settings like system hostname,
|
||||||
network bringup and system shutdown. These can now also be set in any
|
early module loading for watchdogd, network bringup and system shutdown.
|
||||||
`.conf` file in `/etc/finit.d`.
|
These can now also be set in any `.conf` file in `/etc/finit.d`.
|
||||||
|
|
||||||
There is, however, nothing preventing you from having all configuration
|
There is, however, nothing preventing you from having all configuration
|
||||||
settings in `/etc/finit.conf`.
|
settings in `/etc/finit.conf`.
|
||||||
|
|
||||||
> **Note:** The `/etc/finit.d` directory was previously the default
|
> **Note:** The `/etc/finit.d` directory was previously the default
|
||||||
> Finit `runparts` directory. Finit no longer has a default
|
> Finit [runparts](#run-parts-scripts) directory. Finit no
|
||||||
> `runparts`, make sure to update your setup, or the finit
|
> longer has a default `runparts`, make sure to update your
|
||||||
> configuration, accordingly.
|
> setup, or the finit configuration, accordingly.
|
||||||
|
|
||||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
|
||||||
|
Watchdog
|
||||||
|
--------
|
||||||
|
|
||||||
|
When built `--with-watchdog` a separate service is built and installed
|
||||||
|
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
|
||||||
|
device node exists, Finit will start it and treat it as the elected
|
||||||
|
watchdog service to delegate its reboot to. See [bootstrap][] for
|
||||||
|
details. This delegation is to ensure that the system is rebooted by a
|
||||||
|
hardware watchdog timer -- on many embedded systems this is crucial to
|
||||||
|
ensure all circuits on the board are properly reset for the next boot,
|
||||||
|
in effect ensuring the system works the same after both a power-on and
|
||||||
|
reboot event.
|
||||||
|
|
||||||
|
The delegation is performed at the very last steps of system shutdown,
|
||||||
|
if reboot has been selected and an elected watchdog is known, first a
|
||||||
|
`SIGPWR` is sent to advise watchdogd of the pending reboot. Then, when
|
||||||
|
the necessary steps of preparing the system for shutdown (umount etc.)
|
||||||
|
are completed, Finit sends `SIGTERM` to watchdogd and puts itself in a
|
||||||
|
10 sec timeout loop waiting for the WDT to reset the board. If a reset
|
||||||
|
is not done before the timeout, Finit falls back to`reboot(RB_AUTOBOOT)`
|
||||||
|
which tells the kernel to do the reboot.
|
||||||
|
|
||||||
|
An external watchdog service can also be used. The more advanced cousin
|
||||||
|
[watchdogd](https://github.com/troglobit/watchdogd/) is the recommended
|
||||||
|
option here. It can register itself with Finit using the same IPC as
|
||||||
|
`initctl`. If the bundled watchdogd is running a hand-over takes place,
|
||||||
|
so it's safe to have both services installed on a system. For the
|
||||||
|
hand-over to work it requires that the WDT driver supports the safe exit
|
||||||
|
functionality where `"V"` is written to the device before closing the
|
||||||
|
device descriptor. If the kernel driver has been built without this,
|
||||||
|
the only option is to remove `/libexec/finit/watchdogd` or build without
|
||||||
|
it at configure time.
|
||||||
|
|
||||||
|
[bootstrap]: bootstrap.md
|
||||||
|
|||||||
+17
-9
@@ -12,28 +12,36 @@ have been adopted:
|
|||||||
Same effect as `finit q`, `init q`, or `initctl reload`, reloads all
|
Same effect as `finit q`, `init q`, or `initctl reload`, reloads all
|
||||||
*.conf files in `/etc/finit.d/`
|
*.conf files in `/etc/finit.d/`
|
||||||
|
|
||||||
|
This also restarts the API (initctl) socket, like SysV init and systemd
|
||||||
|
does on USR1 with their FIFO/D-Bus.
|
||||||
|
|
||||||
|
|
||||||
`SIGUSR1`
|
`SIGUSR1`
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
|
Since Finit 4.1 this signal causes Finit to restart its API (initctl)
|
||||||
processes, and unmounts all file systems. Then tells kernel to halt.
|
socket, like SysV init and systemd does on USR1 with their FIFO/D-Bus.
|
||||||
Most people these days want `SIGUSR2` though.
|
|
||||||
|
Finit <= 4.0 performed a system halt (like USR2 without power-off), but
|
||||||
|
this caused compatibility problems with systemd and sysvinit on desktop
|
||||||
|
systems. Hence, since Finit 4.1 it is no longer possible to halt a
|
||||||
|
system with a signal.
|
||||||
|
|
||||||
SysV init and systemd use this to re-open their FIFO/D-Bus.
|
|
||||||
|
|
||||||
`SIGUSR2`
|
`SIGUSR2`
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Like SIGUSR1, but tell kernel to power-off the system, if ACPI or
|
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
|
||||||
similar exists to actually do this. If the kernel fails power-off,
|
processes, and unmounts all file systems. Then tells kernel to power
|
||||||
Finit falls back to halt.
|
off the system, if ACPI or similar exists to actually do this. If the
|
||||||
|
kernel fails power-off, Finit falls back to halt.
|
||||||
|
|
||||||
SysV init N/A, systemd dumps its internal state to log.
|
SysV init N/A, systemd dumps its internal state to log.
|
||||||
|
|
||||||
`SIGTERM`
|
`SIGTERM`
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Like `SIGUSR1`, but tell kernel to reboot the system when done.
|
Like `SIGUSR2`, but tell kernel to reboot the system when done.
|
||||||
|
|
||||||
SysV init N/A, systemd rexecutes itself.
|
SysV init N/A, systemd rexecutes itself.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 138 KiB |
+2
-2
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
|||||||
|
|
||||||
if STATIC
|
if STATIC
|
||||||
noinst_LTLIBRARIES = libplug.la
|
noinst_LTLIBRARIES = libplug.la
|
||||||
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
|
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c sys.c tty.c urandom.c usr.c
|
||||||
|
|
||||||
if BUILD_ALSA_UTILS_PLUGIN
|
if BUILD_ALSA_UTILS_PLUGIN
|
||||||
libplug_la_SOURCES += alsa-utils.c
|
libplug_la_SOURCES += alsa-utils.c
|
||||||
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
else
|
else
|
||||||
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
|
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la sys.la tty.la urandom.la usr.la
|
||||||
|
|
||||||
if BUILD_ALSA_UTILS_PLUGIN
|
if BUILD_ALSA_UTILS_PLUGIN
|
||||||
pkglib_LTLIBRARIES += alsa-utils.la
|
pkglib_LTLIBRARIES += alsa-utils.la
|
||||||
|
|||||||
@@ -30,6 +30,11 @@
|
|||||||
|
|
||||||
static void save(void *arg)
|
static void save(void *arg)
|
||||||
{
|
{
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (whichp(ALSACTL)) {
|
if (whichp(ALSACTL)) {
|
||||||
_d("Saving sound settings ...");
|
_d("Saving sound settings ...");
|
||||||
run_interactive(ALSACTL " -g store", "Saving sound settings");
|
run_interactive(ALSACTL " -g store", "Saving sound settings");
|
||||||
@@ -38,6 +43,11 @@ static void save(void *arg)
|
|||||||
|
|
||||||
static void restore(void *arg)
|
static void restore(void *arg)
|
||||||
{
|
{
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (whichp(ALSACTL)) {
|
if (whichp(ALSACTL)) {
|
||||||
_d("Restoring sound settings ...");
|
_d("Restoring sound settings ...");
|
||||||
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
|
||||||
|
|||||||
+43
-44
@@ -65,7 +65,7 @@ static int is_tmpfs(char *path)
|
|||||||
return tmpfs;
|
return tmpfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||||
{
|
{
|
||||||
if (ftw->level == 0)
|
if (ftw->level == 0)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -76,8 +76,14 @@ static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We can safely skip tmpfs, nothing to clean from previous boot there */
|
/*
|
||||||
static void bootclean(void)
|
* Cleanup stale files from previous boot, if any still linger on.
|
||||||
|
* Some systems, e.g. Alpine Linux, still have a persistent /run and
|
||||||
|
* /tmp, i.e. not tmpfs.
|
||||||
|
*
|
||||||
|
* We can safely skip tmpfs, nothing to clean there.
|
||||||
|
*/
|
||||||
|
static void clean(void *arg)
|
||||||
{
|
{
|
||||||
char *dir[] = {
|
char *dir[] = {
|
||||||
"/tmp/",
|
"/tmp/",
|
||||||
@@ -90,7 +96,7 @@ static void bootclean(void)
|
|||||||
if (is_tmpfs(dir[i]))
|
if (is_tmpfs(dir[i]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nftw(dir[i], do_clean, 20, FTW_DEPTH);
|
nftw(dir[i], bootclean, 20, FTW_DEPTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,13 +105,11 @@ static void bootclean(void)
|
|||||||
*/
|
*/
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
int uid, gid;
|
mode_t prev;
|
||||||
|
|
||||||
/* Cleanup stale files, if any still linger on. */
|
prev = umask(0);
|
||||||
bootclean();
|
|
||||||
|
|
||||||
_d("Setting up FHS structure in /var ...");
|
_d("Setting up FHS structure in /var ...");
|
||||||
umask(0);
|
|
||||||
makedir("/var/cache", 0755);
|
makedir("/var/cache", 0755);
|
||||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||||
makedir("/var/games", 0755);
|
makedir("/var/games", 0755);
|
||||||
@@ -115,15 +119,16 @@ static void setup(void *arg)
|
|||||||
makedir("/var/lib/urandom",0755);
|
makedir("/var/lib/urandom",0755);
|
||||||
if (fisdir("/run")) {
|
if (fisdir("/run")) {
|
||||||
_d("System with new /run tmpfs ...");
|
_d("System with new /run tmpfs ...");
|
||||||
makedir("/run/lock", 1777);
|
if (!fisdir("/run/lock"))
|
||||||
|
makedir("/run/lock", 1777);
|
||||||
symlink("/run/lock", "/var/lock");
|
symlink("/run/lock", "/var/lock");
|
||||||
symlink("/dev/shm", "/run/shm");
|
symlink("/dev/shm", "/run/shm");
|
||||||
|
|
||||||
/* compat only, should really be set up by OS/dist */
|
/* compat only, should really be set up by OS/dist */
|
||||||
symlink("/run", "/var/run");
|
symlink("/run", "/var/run");
|
||||||
} else {
|
} else {
|
||||||
makedir("/var/lock", 1777);
|
makedir("/var/lock", 1777);
|
||||||
makedir("/var/run", 0755);
|
makedir("/var/run", 0755);
|
||||||
}
|
}
|
||||||
makedir("/var/log", 0755);
|
makedir("/var/log", 0755);
|
||||||
makedir("/var/mail", 0755);
|
makedir("/var/mail", 0755);
|
||||||
@@ -133,19 +138,21 @@ static void setup(void *arg)
|
|||||||
makedir("/var/tmp", 0755);
|
makedir("/var/tmp", 0755);
|
||||||
makedir("/var/empty", 0755);
|
makedir("/var/empty", 0755);
|
||||||
|
|
||||||
/*
|
|
||||||
* If /etc/group or "utmp" group is missing,
|
|
||||||
* default to "root"/"wheel" group
|
|
||||||
*/
|
|
||||||
_d("Setting up necessary UTMP files ...");
|
|
||||||
gid = getgroup("utmp");
|
|
||||||
if (gid < 0)
|
|
||||||
gid = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UTMP actually needs multiple db files
|
* UTMP actually needs multiple db files
|
||||||
*/
|
*/
|
||||||
if (has_utmp()) {
|
if (has_utmp()) {
|
||||||
|
int gid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If /etc/group or "utmp" group is missing, default to
|
||||||
|
* "root", or "wheel", group.
|
||||||
|
*/
|
||||||
|
_d("Setting up necessary UTMP files ...");
|
||||||
|
gid = getgroup("utmp");
|
||||||
|
if (gid < 0)
|
||||||
|
gid = 0;
|
||||||
|
|
||||||
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
|
||||||
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
|
||||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
||||||
@@ -165,39 +172,31 @@ static void setup(void *arg)
|
|||||||
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
||||||
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
||||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
||||||
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
|
||||||
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
|
||||||
makedir("/var/run/frr", 0755); /* Needed by FRR */
|
mksubsys("/var/run/frr", 0755, "frr", "frr");
|
||||||
makedir("/var/log/frr", 0755); /* Needed by FRR */
|
mksubsys("/var/log/frr", 0755, "frr", "frr");
|
||||||
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
makedir("/var/run/sshd", 01755); /* OpenSSH */
|
||||||
|
|
||||||
gid = getgroup("tty");
|
|
||||||
if (gid < 0)
|
|
||||||
gid = 0;
|
|
||||||
makefifo("/dev/xconsole", 0640); /* sysklogd */
|
|
||||||
chown("/dev/xconsole", 0, gid);
|
|
||||||
|
|
||||||
if (!fexist("/etc/mtab"))
|
if (!fexist("/etc/mtab"))
|
||||||
symlink("../proc/self/mounts", "/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);
|
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
||||||
if (uid >= 0) {
|
|
||||||
gid = getgroup("uuidd");
|
|
||||||
if (gid < 0)
|
|
||||||
gid = 0;
|
|
||||||
makedir("/var/run/uuidd", 0755);
|
|
||||||
chown("/var/run/uuidd", uid, gid);
|
|
||||||
}
|
|
||||||
|
|
||||||
umask(022);
|
/* Debian has /run/sudo, ensure correct perms and SELinux label */
|
||||||
|
mksubsys("/var/run/sudo", 0711, "root", "root");
|
||||||
|
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
|
||||||
|
if (whichp("restorecon"))
|
||||||
|
run("restorecon /var/run/sudo /var/run/sudo/ts");
|
||||||
|
|
||||||
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_BASEFS_UP] = {
|
.hook[HOOK_MOUNT_POST] = { .cb = clean },
|
||||||
.cb = setup
|
.hook[HOOK_BASEFS_UP] = { .cb = setup },
|
||||||
},
|
|
||||||
.depends = { "pidfile" },
|
.depends = { "pidfile" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+18
-11
@@ -32,13 +32,21 @@
|
|||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
|
||||||
#define DAEMON "dbus-daemon"
|
#define DAEMON "dbus-daemon"
|
||||||
#define ARGS "--nofork --system"
|
#define ARGS "--nofork --system --syslog-only"
|
||||||
#define DESC "D-Bus message bus daemon"
|
#define DESC "D-Bus message bus daemon"
|
||||||
|
|
||||||
|
#define DAEMONUSER "messagebus"
|
||||||
|
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
char *cmd;
|
|
||||||
char line[256];
|
char line[256];
|
||||||
|
mode_t prev;
|
||||||
|
char *cmd;
|
||||||
|
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
cmd = which(DAEMON);
|
cmd = which(DAEMON);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
@@ -46,29 +54,28 @@ static void setup(void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
umask(0);
|
prev =umask(0);
|
||||||
|
|
||||||
makedir("/var/run/dbus", 0755);
|
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
|
||||||
makedir("/var/lock/subsys", 0755);
|
|
||||||
makedir("/var/lock/subsys/messagebus", 0755);
|
|
||||||
if (whichp("dbus-uuidgen"))
|
if (whichp("dbus-uuidgen"))
|
||||||
run("dbus-uuidgen --ensure");
|
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
|
||||||
|
|
||||||
/* Clean up from any previous pre-bootstrap run */
|
/* Clean up from any previous pre-bootstrap run */
|
||||||
erase("/var/run/dbus/pid");
|
remove("/var/run/dbus/pid");
|
||||||
|
|
||||||
/* Register service with Finit */
|
/* Register service with Finit */
|
||||||
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
|
snprintf(line, sizeof(line), "[S12345789] cgroup.system @%s:%s %s %s -- %s",
|
||||||
|
DAEMONUSER, DAEMONUSER, 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);
|
||||||
|
|
||||||
umask(022);
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.name = __FILE__,
|
.name = __FILE__,
|
||||||
.hook[HOOK_NETWORK_UP] = {
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
.cb = setup
|
.cb = setup
|
||||||
},
|
},
|
||||||
.depends = { "bootmisc", },
|
.depends = { "bootmisc", },
|
||||||
|
|||||||
+9
-3
@@ -34,6 +34,11 @@ static void setup(void *arg)
|
|||||||
char cmd[256];
|
char cmd[256];
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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.
|
* Prefer udev if mdev is also available on the system.
|
||||||
@@ -43,17 +48,17 @@ static void setup(void *arg)
|
|||||||
path = which("/lib/systemd/systemd-udevd");
|
path = which("/lib/systemd/systemd-udevd");
|
||||||
if (path) {
|
if (path) {
|
||||||
/* Register udevd as a monitored service */
|
/* Register udevd as a monitored service */
|
||||||
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd name:udevd log %s "
|
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
|
||||||
"-- Device event managing daemon", path);
|
"-- Device event managing daemon", path);
|
||||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
||||||
_pe("Failed registering %s", path);
|
_pe("Failed registering %s", path);
|
||||||
} else {
|
} else {
|
||||||
snprintf(cmd, sizeof(cmd), ":1 [S] <pid/udevd> log "
|
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
|
||||||
"udevadm trigger -c add -t devices "
|
"udevadm trigger -c add -t devices "
|
||||||
"-- Requesting device events");
|
"-- Requesting device events");
|
||||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), ":2 [S] <pid/udevd> log "
|
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
|
||||||
"udevadm trigger -c add -t subsystems "
|
"udevadm trigger -c add -t subsystems "
|
||||||
"-- Requesting subsystem events");
|
"-- Requesting subsystem events");
|
||||||
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
|
||||||
@@ -90,6 +95,7 @@ static plugin_t plugin = {
|
|||||||
.hook[HOOK_BASEFS_UP] = {
|
.hook[HOOK_BASEFS_UP] = {
|
||||||
.cb = setup
|
.cb = setup
|
||||||
},
|
},
|
||||||
|
.depends = { "bootmisc" },
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
#define MODULES_LOAD_PATH "/etc/modules-load.d"
|
||||||
#endif
|
#endif
|
||||||
#define SERVICE_LINE \
|
#define SERVICE_LINE \
|
||||||
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
"cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
|
||||||
|
|
||||||
static void load(void *arg)
|
static void load(void *arg)
|
||||||
{
|
{
|
||||||
@@ -71,8 +71,7 @@ static void load(void *arg)
|
|||||||
|
|
||||||
mod = strtok_r(mod, " ", &args);
|
mod = strtok_r(mod, " ", &args);
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
|
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
|
||||||
index, mod, mod, args, mod);
|
|
||||||
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
|
|||||||
+227
-42
@@ -30,31 +30,35 @@
|
|||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
|
#define NL_BUFSZ 4096
|
||||||
{
|
|
||||||
if (!NLMSG_OK(nh, len))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (nh->nlmsg_type == NLMSG_DONE) {
|
struct nl_request {
|
||||||
_d("Done with netlink messages.");
|
struct nlmsghdr nh;
|
||||||
return 1;
|
union {
|
||||||
}
|
struct rtmsg rtm;
|
||||||
|
struct ifinfomsg ifi;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
if (nh->nlmsg_type == NLMSG_ERROR) {
|
static int nl_defidx;
|
||||||
_d("Netlink reports error.");
|
static int nl_ifdown;
|
||||||
return 1;
|
static char *nl_buf;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||||
{
|
{
|
||||||
|
char daddr[INET_ADDRSTRLEN];
|
||||||
|
char gaddr[INET_ADDRSTRLEN];
|
||||||
|
struct in_addr ind, ing;
|
||||||
struct rtmsg *r;
|
struct rtmsg *r;
|
||||||
struct rtattr *a;
|
struct rtattr *a;
|
||||||
|
int plen = 0;
|
||||||
|
int dst = 0;
|
||||||
|
int idx = 0;
|
||||||
|
int gw = 0;
|
||||||
int la;
|
int la;
|
||||||
int gw = 0, dst = 0, mask = 0, idx = 0;
|
|
||||||
|
|
||||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
|
||||||
_e("Packet too small or truncated!");
|
_e("Packet too small or truncated!");
|
||||||
@@ -80,8 +84,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
|
|
||||||
case RTA_DST:
|
case RTA_DST:
|
||||||
dst = *((int *)data);
|
dst = *((int *)data);
|
||||||
mask = r->rtm_dst_len;
|
plen = r->rtm_dst_len;
|
||||||
//_d("MASK: 0x%04x", mask);
|
//_d("Prefix LEN: 0x%04x", plen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTA_OIF:
|
case RTA_OIF:
|
||||||
@@ -93,11 +97,20 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
a = RTA_NEXT(a, la);
|
a = RTA_NEXT(a, la);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!dst && !mask) && (gw || idx)) {
|
ind.s_addr = dst;
|
||||||
if (nlmsg->nlmsg_type == RTM_DELROUTE)
|
ing.s_addr = gw;
|
||||||
|
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
|
||||||
|
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
|
||||||
|
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
|
||||||
|
|
||||||
|
if ((!dst && !plen) && (gw || idx)) {
|
||||||
|
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
|
||||||
cond_clear("net/route/default");
|
cond_clear("net/route/default");
|
||||||
else
|
nl_defidx = 0;
|
||||||
|
} else {
|
||||||
cond_set("net/route/default");
|
cond_set("net/route/default");
|
||||||
|
nl_defidx = idx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,14 +130,14 @@ static int validate_ifname(const char *ifname)
|
|||||||
if (!ifname || !ifname[0])
|
if (!ifname || !ifname[0])
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (strlen(ifname) >= IFNAMSIZ)
|
if (strnlen(ifname, IFNAMSIZ) == IFNAMSIZ)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (strstr(ifname, ".."))
|
if (!strcmp(ifname, ".") || !strcmp(ifname, ".."))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
while (*ifname) {
|
while (*ifname) {
|
||||||
if (*ifname == '/' || isspace(*ifname))
|
if (*ifname == '/' || *ifname == ':' || isspace(*ifname))
|
||||||
return 1;
|
return 1;
|
||||||
ifname++;
|
ifname++;
|
||||||
}
|
}
|
||||||
@@ -132,12 +145,25 @@ static int validate_ifname(const char *ifname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if this interface was associated with the default route
|
||||||
|
* previously, or if it's been removed. If so, trigger a recheck
|
||||||
|
* of the system default route.
|
||||||
|
*/
|
||||||
|
static void nl_check_default(char *ifname)
|
||||||
|
{
|
||||||
|
int idx = (int)if_nametoindex(ifname);
|
||||||
|
|
||||||
|
if ((nl_defidx > 0 && nl_defidx == idx) || (idx == 0 && errno == ENODEV))
|
||||||
|
nl_ifdown = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||||
{
|
{
|
||||||
int la;
|
|
||||||
char ifname[IFNAMSIZ + 1];
|
char ifname[IFNAMSIZ + 1];
|
||||||
struct rtattr *a;
|
|
||||||
struct ifinfomsg *i;
|
struct ifinfomsg *i;
|
||||||
|
struct rtattr *a;
|
||||||
|
int la;
|
||||||
|
|
||||||
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
|
||||||
_e("Packet too small or truncated!");
|
_e("Packet too small or truncated!");
|
||||||
@@ -172,6 +198,8 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
net_cond_set(ifname, "exist", 1);
|
net_cond_set(ifname, "exist", 1);
|
||||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||||
|
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
|
||||||
|
nl_check_default(ifname);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
@@ -180,6 +208,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
net_cond_set(ifname, "exist", 0);
|
net_cond_set(ifname, "exist", 0);
|
||||||
net_cond_set(ifname, "up", 0);
|
net_cond_set(ifname, "up", 0);
|
||||||
net_cond_set(ifname, "running", 0);
|
net_cond_set(ifname, "running", 0);
|
||||||
|
nl_check_default(ifname);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_NEWADDR:
|
case RTM_NEWADDR:
|
||||||
@@ -197,26 +226,175 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nl_callback(void *arg, int sd, int events)
|
static int nl_parse(int sd)
|
||||||
{
|
{
|
||||||
static char buf[4096];
|
while (1) {
|
||||||
struct nlmsghdr *nh;
|
struct nlmsghdr *nh;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
while ((len = recv(sd, nl_buf, NL_BUFSZ, 0)) < 0) {
|
||||||
len = recv(sd, buf, sizeof(buf), 0);
|
switch (errno) {
|
||||||
if (len < 0) {
|
case EAGAIN: /* Nothing more right now. */
|
||||||
if (errno != EINTR) /* Signal */
|
return 0;
|
||||||
_pe("recv()");
|
|
||||||
|
case EINTR: /* Signal */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case ENOBUFS: /* netlink(7) */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_pe("recv()");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("recv %lld bytes", len);
|
||||||
|
|
||||||
|
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||||
|
struct nlmsgerr *nle;
|
||||||
|
|
||||||
|
switch (nh->nlmsg_type) {
|
||||||
|
case NLMSG_DONE:
|
||||||
|
_d("Done with netlink messages.");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case NLMSG_ERROR:
|
||||||
|
_d("Kernel netlink comm. error.");
|
||||||
|
nle = NLMSG_DATA(nh);
|
||||||
|
if (nle) {
|
||||||
|
errno = -nle->error;
|
||||||
|
_pe("Kernel netlink error %d", errno);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
case RTM_NEWROUTE:
|
||||||
|
case RTM_DELROUTE:
|
||||||
|
_d("Netlink route ...");
|
||||||
|
nl_route(nh, len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RTM_NEWLINK:
|
||||||
|
case RTM_DELLINK:
|
||||||
|
_d("Netlink link ...");
|
||||||
|
nl_link(nh, len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_w("unhandled netlink message, type %d", nh->nlmsg_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nl_request(int sd, unsigned int seq, int type)
|
||||||
|
{
|
||||||
|
struct nl_request *nlr = (struct nl_request *)nl_buf;
|
||||||
|
|
||||||
|
memset(nlr, 0, sizeof(struct nl_request));
|
||||||
|
nlr->nh.nlmsg_type = type;
|
||||||
|
nlr->nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||||
|
nlr->nh.nlmsg_seq = seq;
|
||||||
|
nlr->nh.nlmsg_pid = 1;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case RTM_GETROUTE:
|
||||||
|
_d("RTM_GETROUTE");
|
||||||
|
nlr->rtm.rtm_family = AF_INET;
|
||||||
|
nlr->rtm.rtm_table = RT_TABLE_MAIN;
|
||||||
|
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RTM_GETLINK:
|
||||||
|
_d("RTM_GETLINK");
|
||||||
|
nlr->ifi.ifi_family = AF_UNSPEC;
|
||||||
|
nlr->ifi.ifi_change = 0xFFFFFFFF;
|
||||||
|
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_w("Cannot resync, unhandled message type %d", type);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send(sd, nlr, nlr->nh.nlmsg_len, 0) < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return nl_parse(sd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nl_resync_routes(int sd, unsigned int seq)
|
||||||
|
{
|
||||||
|
if (nl_request(sd, seq, RTM_GETROUTE))
|
||||||
|
_pe("Failed netlink route request");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nl_resync_ifaces(int sd, unsigned int seq)
|
||||||
|
{
|
||||||
|
if (nl_request(sd, seq, RTM_GETLINK))
|
||||||
|
_pe("Failed netlink link request");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We've potentially lost netlink events, let's resync with kernel.
|
||||||
|
*/
|
||||||
|
static void nl_resync(int all)
|
||||||
|
{
|
||||||
|
unsigned int seq = 0;
|
||||||
|
int sd;
|
||||||
|
|
||||||
|
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||||
|
if (sd < 0) {
|
||||||
|
_pe("netlink socket");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
if (all) {
|
||||||
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
|
_d("============================ RESYNC =================================");
|
||||||
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
|
/* this doesn't update condtions, and thus does not stop services */
|
||||||
nl_route(nh, len);
|
cond_deassert("net/");
|
||||||
else
|
|
||||||
nl_link(nh, len);
|
nl_resync_ifaces(sd, seq++);
|
||||||
|
nl_resync_routes(sd, seq++);
|
||||||
|
|
||||||
|
/* delayed update after we've corrected things */
|
||||||
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
_d("=========================== RESYNCED ================================");
|
||||||
|
} else
|
||||||
|
nl_resync_routes(sd, seq++);
|
||||||
|
|
||||||
|
close(sd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nl_callback(void *arg, int sd, int events)
|
||||||
|
{
|
||||||
|
if (nl_parse(sd) < 0) {
|
||||||
|
if (errno == ENOBUFS) { /* netlink(7) */
|
||||||
|
_w("busy system, resynchronizing with kernel.");
|
||||||
|
nl_resync(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linux doesn't send route changes when interfaces go down, so
|
||||||
|
* we need to check ourselves, e.g. for loss of default route.
|
||||||
|
*/
|
||||||
|
if (nl_ifdown) {
|
||||||
|
_d("interface down, checking default route.");
|
||||||
|
if (nl_defidx > 0) {
|
||||||
|
nl_defidx = 0;
|
||||||
|
nl_resync(0);
|
||||||
|
if (nl_defidx <= 0) {
|
||||||
|
cond_clear("net/route/default");
|
||||||
|
nl_defidx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_ifdown = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,8 +414,8 @@ static plugin_t plugin = {
|
|||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
{
|
{
|
||||||
int sd;
|
|
||||||
struct sockaddr_nl sa;
|
struct sockaddr_nl sa;
|
||||||
|
int sd;
|
||||||
|
|
||||||
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||||
if (sd < 0) {
|
if (sd < 0) {
|
||||||
@@ -247,7 +425,7 @@ PLUGIN_INIT(plugin_init)
|
|||||||
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
memset(&sa, 0, sizeof(sa));
|
||||||
sa.nl_family = AF_NETLINK;
|
sa.nl_family = AF_NETLINK;
|
||||||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR;
|
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK;
|
||||||
sa.nl_pid = getpid();
|
sa.nl_pid = getpid();
|
||||||
|
|
||||||
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||||
@@ -256,6 +434,13 @@ PLUGIN_INIT(plugin_init)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nl_buf = malloc(NL_BUFSZ);
|
||||||
|
if (!nl_buf) {
|
||||||
|
_pe("malloc()");
|
||||||
|
close(sd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
plugin.io.fd = sd;
|
plugin.io.fd = sd;
|
||||||
plugin_register(&plugin);
|
plugin_register(&plugin);
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-15
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return iwatch_add(iw, path, IN_ONLYDIR);
|
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||||
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
paste(fn, sizeof(fn), dir, name);
|
paste(fn, sizeof(fn), dir, name);
|
||||||
_d("path: %s, mask: %08x", fn, mask);
|
|
||||||
|
|
||||||
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_d("path: %s, mask: %08x", fn, mask);
|
||||||
|
|
||||||
svc = svc_find_by_pidfile(fn);
|
svc = svc_find_by_pidfile(fn);
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_d("No matching svc for %s", fn);
|
_d("No matching svc for %s", fn);
|
||||||
@@ -82,15 +82,25 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
|
||||||
|
|
||||||
mkcond(svc, cond, sizeof(cond));
|
mkcond(svc, cond, sizeof(cond));
|
||||||
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
|
||||||
svc_started(svc);
|
svc_started(svc);
|
||||||
|
if (!svc_has_pidfile(svc))
|
||||||
|
pid_file_set(svc, fn, 1);
|
||||||
|
|
||||||
if (svc_is_forking(svc)) {
|
if (svc_is_forking(svc)) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
pid = pid_file_read(pid_file(svc));
|
pid = pid_file_read(pid_file(svc));
|
||||||
_d("Forking service %s changed PID from %d to %d",
|
if (pid == -1) {
|
||||||
svc->cmd, svc->pid, pid);
|
cond_clear(cond);
|
||||||
svc->pid = pid;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid != svc->pid) {
|
||||||
|
_d("Forking service %s changed PID from %d to %d",
|
||||||
|
svc->cmd, svc->pid, pid);
|
||||||
|
svc->pid = pid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cond_set(cond);
|
cond_set(cond);
|
||||||
@@ -174,8 +184,6 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
if (!ev->mask)
|
if (!ev->mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
|
||||||
|
|
||||||
/* Find base path for this event */
|
/* Find base path for this event */
|
||||||
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
|
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
|
||||||
if (!iwp)
|
if (!iwp)
|
||||||
@@ -186,12 +194,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev->mask & IN_DELETE) {
|
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||||
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
|
||||||
pidfile_update_conds(iwp->path, ev->name, ev->mask);
|
pidfile_update_conds(iwp->path, ev->name, ev->mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,8 +236,14 @@ static void pidfile_reconf(void *arg)
|
|||||||
|
|
||||||
static void pidfile_init(void *arg)
|
static void pidfile_init(void *arg)
|
||||||
{
|
{
|
||||||
|
char piddir[MAX_ARG_LEN];
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
|
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
|
||||||
|
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The bootmisc plugin is responsible for setting up /var/run.
|
* The bootmisc plugin is responsible for setting up /var/run.
|
||||||
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
||||||
@@ -242,6 +251,7 @@ static void pidfile_init(void *arg)
|
|||||||
*/
|
*/
|
||||||
path = realpath(_PATH_VARRUN, NULL);
|
path = realpath(_PATH_VARRUN, NULL);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
|
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
|
||||||
path = realpath("/run", NULL);
|
path = realpath("/run", NULL);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
|
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
|
||||||
@@ -251,6 +261,8 @@ static void pidfile_init(void *arg)
|
|||||||
|
|
||||||
if (pidfile_add_path(&iw_pidfile, path))
|
if (pidfile_add_path(&iw_pidfile, path))
|
||||||
iwatch_exit(&iw_pidfile);
|
iwatch_exit(&iw_pidfile);
|
||||||
|
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
|
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
@@ -31,6 +34,11 @@ static void setup(void *arg)
|
|||||||
glob_t gl;
|
glob_t gl;
|
||||||
char buf[160];
|
char buf[160];
|
||||||
|
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
|
glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
|
||||||
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||||
glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ static void rtc_save(void *arg)
|
|||||||
int fd, rc = 0;
|
int fd, rc = 0;
|
||||||
char tz[128];
|
char tz[128];
|
||||||
|
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fd = rtc_open();
|
fd = rtc_open();
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return;
|
return;
|
||||||
@@ -121,6 +126,11 @@ static void rtc_restore(void *arg)
|
|||||||
int fd, rc = 0;
|
int fd, rc = 0;
|
||||||
char tz[128];
|
char tz[128];
|
||||||
|
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fd = rtc_open();
|
fd = rtc_open();
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return;
|
return;
|
||||||
|
|||||||
+197
@@ -0,0 +1,197 @@
|
|||||||
|
/* System condtion event monitor for the Finit condition engine
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Joachim Wiberg <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 <dirent.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <paths.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
|
#include "pid.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "iwatch.h"
|
||||||
|
|
||||||
|
static struct iwatch iw_sys;
|
||||||
|
|
||||||
|
|
||||||
|
static int sys_add_path(struct iwatch *iw, char *path)
|
||||||
|
{
|
||||||
|
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sys_update_conds(char *dir, char *name, uint32_t mask)
|
||||||
|
{
|
||||||
|
char fn[PATH_MAX];
|
||||||
|
char *cond;
|
||||||
|
|
||||||
|
paste(fn, sizeof(fn), dir, name);
|
||||||
|
cond = strstr(fn, COND_BASE);
|
||||||
|
if (!cond)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cond += strlen(COND_BASE) + 1;
|
||||||
|
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
|
||||||
|
cond_update(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* synthesize events in case of new run dirs */
|
||||||
|
static void sys_scandir(struct iwatch *iw, char *dir, int len)
|
||||||
|
{
|
||||||
|
glob_t gl;
|
||||||
|
size_t i;
|
||||||
|
char path[len + 6];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/*", dir);
|
||||||
|
rc = glob(path, GLOB_NOSORT, NULL, &gl);
|
||||||
|
if (rc && rc != GLOB_NOMATCH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
_d("scan found %s", gl.gl_pathv[i]);
|
||||||
|
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
|
||||||
|
}
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create/remove sub-directory in monitored directory
|
||||||
|
*/
|
||||||
|
static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
|
||||||
|
{
|
||||||
|
char path[strlen(dir) + strlen(name) + 2];
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
paste(path, sizeof(path), dir, name);
|
||||||
|
_d("path: %s", path);
|
||||||
|
|
||||||
|
iwp = iwatch_find_by_path(iw, path);
|
||||||
|
|
||||||
|
if (mask & IN_CREATE) {
|
||||||
|
if (!iwp) {
|
||||||
|
if (!sys_add_path(iw, path))
|
||||||
|
sys_scandir(iw, path, sizeof(path));
|
||||||
|
}
|
||||||
|
} else if (mask & IN_DELETE) {
|
||||||
|
if (iwp)
|
||||||
|
iwatch_del(&iw_sys, iwp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sys_callback(void *arg, int fd, int events)
|
||||||
|
{
|
||||||
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
|
struct inotify_event *ev;
|
||||||
|
ssize_t sz;
|
||||||
|
size_t off;
|
||||||
|
|
||||||
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
|
if (sz <= 0) {
|
||||||
|
_pe("invalid inotify event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
|
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
if (off + sizeof(*ev) > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ev = (struct inotify_event *)&ev_buf[off];
|
||||||
|
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
|
if (!ev->mask)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Find base path for this event */
|
||||||
|
iwp = iwatch_find_by_wd(&iw_sys, ev->wd);
|
||||||
|
if (!iwp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ev->mask & IN_ISDIR) {
|
||||||
|
sys_handle_dir(&iw_sys, iwp->path, ev->name, ev->mask);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||||
|
sys_update_conds(iwp->path, ev->name, ev->mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sys_init(void *arg)
|
||||||
|
{
|
||||||
|
char sysdir[MAX_ARG_LEN];
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
|
||||||
|
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = realpath(_PATH_CONDSYS, NULL);
|
||||||
|
if (!path) {
|
||||||
|
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
|
||||||
|
iwatch_exit(&iw_sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.name = __FILE__,
|
||||||
|
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = iwatch_init(&iw_sys);
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
plugin.io.fd = fd;
|
||||||
|
plugin.io.cb = sys_callback;
|
||||||
|
plugin.io.flags = PLUGIN_IO_READ;
|
||||||
|
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
iwatch_exit(&iw_sys);
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+16
-10
@@ -32,13 +32,14 @@
|
|||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "service.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
|
|
||||||
static void watcher(void *arg, int fd, int events);
|
static void tty_watcher(void *arg, int fd, int events);
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
.io = {
|
.io = {
|
||||||
.cb = watcher,
|
.cb = tty_watcher,
|
||||||
.flags = PLUGIN_IO_READ,
|
.flags = PLUGIN_IO_READ,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -56,19 +57,24 @@ static void setup(void)
|
|||||||
static void do_tty(char *tty, size_t len, int creat)
|
static void do_tty(char *tty, size_t len, int creat)
|
||||||
{
|
{
|
||||||
char name[len + 6];
|
char name[len + 6];
|
||||||
struct tty *entry;
|
svc_t *svc;
|
||||||
|
|
||||||
snprintf(name, sizeof(name), "/dev/%s", tty);
|
snprintf(name, sizeof(name), "/dev/%s", tty);
|
||||||
entry = tty_find(name);
|
svc = svc_find_by_tty(name);
|
||||||
if (entry && tty_enabled(entry)) {
|
if (svc) {
|
||||||
if (creat)
|
if (svc_is_blocked(svc) && creat) {
|
||||||
tty_start(entry);
|
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
|
||||||
else if (entry->pid)
|
svc_start(svc);
|
||||||
tty_stop(entry);
|
} else if (svc->pid) {
|
||||||
|
_d("%s: missing ...", svc_ident(svc, NULL, 0));
|
||||||
|
svc_missing(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
service_step_all(SVC_TYPE_TTY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void watcher(void *arg, int fd, int events)
|
static void tty_watcher(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];
|
||||||
struct inotify_event *ev;
|
struct inotify_event *ev;
|
||||||
|
|||||||
+19
-4
@@ -35,12 +35,18 @@
|
|||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
#ifdef RANDOMSEED
|
#ifdef RANDOMSEED
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fexist(RANDOMSEED)) {
|
if (!fexist(RANDOMSEED)) {
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
mode_t prev;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
print_desc("Bootstrapping random seed", NULL);
|
print_desc("Bootstrapping random seed", NULL);
|
||||||
umask(077);
|
prev = umask(077);
|
||||||
fp = fopen(RANDOMSEED, "w");
|
fp = fopen(RANDOMSEED, "w");
|
||||||
if (fp) {
|
if (fp) {
|
||||||
int iter = 128;
|
int iter = 128;
|
||||||
@@ -57,7 +63,7 @@ static void setup(void *arg)
|
|||||||
ret = fclose(fp);
|
ret = fclose(fp);
|
||||||
}
|
}
|
||||||
print_result(ret);
|
print_result(ret);
|
||||||
umask(0);
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_desc("Initializing random number generator", NULL);
|
print_desc("Initializing random number generator", NULL);
|
||||||
@@ -68,10 +74,19 @@ static void setup(void *arg)
|
|||||||
static void save(void *arg)
|
static void save(void *arg)
|
||||||
{
|
{
|
||||||
#ifdef RANDOMSEED
|
#ifdef RANDOMSEED
|
||||||
umask(077);
|
mode_t prev;
|
||||||
|
|
||||||
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = umask(077);
|
||||||
|
|
||||||
print_desc("Saving random seed", NULL);
|
print_desc("Saving random seed", NULL);
|
||||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||||
umask(0);
|
|
||||||
|
umask(prev);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
/* User-defined condtion event monitor for the Finit condition engine
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Joachim Wiberg <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 <fnmatch.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <paths.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
|
#include "pid.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "iwatch.h"
|
||||||
|
|
||||||
|
static struct iwatch iw_usr;
|
||||||
|
|
||||||
|
|
||||||
|
static void usr_cond(char *name, uint32_t mask)
|
||||||
|
{
|
||||||
|
char cond[MAX_COND_LEN] = COND_USR;
|
||||||
|
|
||||||
|
strlcat(cond, name, sizeof(cond));
|
||||||
|
cond_update(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usr_callback(void *arg, int fd, int events)
|
||||||
|
{
|
||||||
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
|
struct inotify_event *ev;
|
||||||
|
ssize_t sz;
|
||||||
|
size_t off;
|
||||||
|
|
||||||
|
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
|
if (sz <= 0) {
|
||||||
|
_pe("invalid inotify event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
|
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||||
|
if (off + sizeof(*ev) > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ev = (struct inotify_event *)&ev_buf[off];
|
||||||
|
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||||
|
if (!ev->mask)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ev->mask & IN_ISDIR)
|
||||||
|
continue; /* unsupported */
|
||||||
|
|
||||||
|
if (ev->mask & IN_DELETE)
|
||||||
|
sync(); /* dont ask */
|
||||||
|
|
||||||
|
usr_cond(ev->name, ev->mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usr_init(void *arg)
|
||||||
|
{
|
||||||
|
char usrdir[MAX_ARG_LEN];
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
|
||||||
|
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = realpath(_PATH_CONDUSR, NULL);
|
||||||
|
if (!path) {
|
||||||
|
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
|
||||||
|
iwatch_exit(&iw_usr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.name = __FILE__,
|
||||||
|
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = iwatch_init(&iw_usr);
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
plugin.io.fd = fd;
|
||||||
|
plugin.io.cb = usr_callback;
|
||||||
|
plugin.io.flags = PLUGIN_IO_READ;
|
||||||
|
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
iwatch_exit(&iw_usr);
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+10
-4
@@ -32,13 +32,19 @@
|
|||||||
|
|
||||||
static void setup(void *arg)
|
static void setup(void *arg)
|
||||||
{
|
{
|
||||||
|
const char *username = "nobody";
|
||||||
|
char line[LINE_SIZE];
|
||||||
|
mode_t prev;
|
||||||
#ifdef PAM_CONSOLE
|
#ifdef PAM_CONSOLE
|
||||||
int fd;
|
int fd;
|
||||||
#endif
|
#endif
|
||||||
char line[LINE_SIZE];
|
|
||||||
const char *username = "nobody";
|
|
||||||
|
|
||||||
umask(0);
|
if (rescue) {
|
||||||
|
_d("Skipping %s plugin in rescue mode.", __FILE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = umask(0);
|
||||||
|
|
||||||
makedir("/var/run/console", 01777);
|
makedir("/var/run/console", 01777);
|
||||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||||
@@ -58,7 +64,7 @@ static void setup(void *arg)
|
|||||||
if (whichp("restorecon"))
|
if (whichp("restorecon"))
|
||||||
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
||||||
|
|
||||||
umask(022);
|
umask(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static plugin_t plugin = {
|
static plugin_t plugin = {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
.deps/*
|
.deps/*
|
||||||
finit
|
finit
|
||||||
initctl
|
initctl
|
||||||
|
keventd
|
||||||
logit
|
logit
|
||||||
reboot
|
reboot
|
||||||
|
sulogin
|
||||||
watchdogd
|
watchdogd
|
||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
|
|||||||
+24
-21
@@ -1,4 +1,4 @@
|
|||||||
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
|
EXTRA_DIST = rescue.conf sample.conf
|
||||||
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
|
||||||
@@ -7,23 +7,27 @@ else
|
|||||||
AM_LDFLAGS = -export-dynamic
|
AM_LDFLAGS = -export-dynamic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
sbin_PROGRAMS = finit initctl
|
sbin_PROGRAMS = finit initctl
|
||||||
pkglibexec_SCRIPTS = cgreaper.sh
|
pkglibexec_PROGRAMS = keventd logit sulogin
|
||||||
|
if WATCHDOGD
|
||||||
|
pkglibexec_PROGRAMS += watchdogd
|
||||||
|
endif
|
||||||
|
|
||||||
finit_pkglibdir = $(pkglibdir)
|
finit_pkglibdir = $(pkglibdir)
|
||||||
finit_pkglib_SCRIPTS = rescue.conf sample.conf
|
finit_pkglib_SCRIPTS = rescue.conf sample.conf
|
||||||
|
|
||||||
if WATCHDOGD
|
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
|
||||||
pkglibexec_PROGRAMS = watchdogd
|
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||||
endif
|
keventd_LDADD = $(lite_LIBS)
|
||||||
|
|
||||||
if LOGIT
|
sulogin_SOURCES = sulogin.c
|
||||||
pkglibexec_PROGRAMS = logit
|
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
|
||||||
logit_SOURCES = logit.c logrotate.c
|
sulogin_LDADD = -lcrypt
|
||||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
|
||||||
logit_CFLAGS += $(lite_CFLAGS)
|
logit_SOURCES = logit.c logrotate.c
|
||||||
logit_LDADD = $(lite_LIBS)
|
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
endif
|
logit_CFLAGS += $(lite_CFLAGS)
|
||||||
|
logit_LDADD = $(lite_LIBS)
|
||||||
|
|
||||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||||
cond.c cond-w.c cond.h \
|
cond.c cond-w.c cond.h \
|
||||||
@@ -48,7 +52,7 @@ if LOGROTATE
|
|||||||
finit_SOURCES += logrotate.c
|
finit_SOURCES += logrotate.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
|
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||||
|
|
||||||
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)
|
||||||
@@ -59,20 +63,19 @@ else
|
|||||||
finit_LDADD += -ldl
|
finit_LDADD += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
initctl_SOURCES = initctl.c client.c client.h \
|
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||||
reboot.c serv.c serv.h svc.h \
|
client.c client.h cond.c cond.h reboot.c \
|
||||||
cond.c cond.h util.c util.h
|
serv.c serv.h svc.h util.c util.h
|
||||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
initctl_CFLAGS += $(lite_CFLAGS)
|
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||||
initctl_LDADD = $(lite_LIBS)
|
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||||
|
|
||||||
INIT_LNKS = init
|
INIT_LNKS = init telinit
|
||||||
REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
||||||
|
|
||||||
if WATCHDOGD
|
if WATCHDOGD
|
||||||
watchdogd_SOURCES = watchdog.c watchdog.h
|
watchdogd_SOURCES = watchdog.c watchdog.h
|
||||||
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||||
#log.c log.h
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Hook in install to add finit and reboot symlink(s)
|
# Hook in install to add finit and reboot symlink(s)
|
||||||
|
|||||||
@@ -74,7 +74,24 @@ static int start(svc_t *svc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: this does not wait for svc to be stopped first, that is the
|
||||||
|
* responsibility of initctl to do. Otherwise we'd block PID 1,
|
||||||
|
* or introduce some nasty race conditions.
|
||||||
|
*/
|
||||||
static int restart(svc_t *svc)
|
static int restart(svc_t *svc)
|
||||||
|
{
|
||||||
|
if (!svc)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
svc_mark_dirty(svc);
|
||||||
|
svc_start(svc);
|
||||||
|
service_step(svc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reload(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (!svc)
|
if (!svc)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -88,9 +105,10 @@ static int restart(svc_t *svc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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(stop, buf, len); }
|
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
|
||||||
|
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
|
||||||
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
|
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
|
||||||
|
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
|
||||||
|
|
||||||
static char query_buf[368];
|
static char query_buf[368];
|
||||||
static int missing(char *job, char *id)
|
static int missing(char *job, char *id)
|
||||||
@@ -110,7 +128,7 @@ static int missing(char *job, char *id)
|
|||||||
|
|
||||||
static int do_query(char *buf, size_t len)
|
static int do_query(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
memset(query_buf, 0, sizeof(query_buf));
|
query_buf[0] = 0;
|
||||||
if (svc_parse_jobstr(buf, len, NULL, missing)) {
|
if (svc_parse_jobstr(buf, len, NULL, missing)) {
|
||||||
memcpy(buf, query_buf, len);
|
memcpy(buf, query_buf, len);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -252,10 +270,10 @@ static void send_svc(int sd, svc_t *svc)
|
|||||||
|
|
||||||
static void api_cb(uev_t *w, void *arg, int events)
|
static void api_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
int sd, lvl;
|
|
||||||
svc_t *svc;
|
|
||||||
static svc_t *iter = NULL;
|
static svc_t *iter = NULL;
|
||||||
struct init_request rq;
|
struct init_request rq;
|
||||||
|
int sd, lvl;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
sd = accept(w->fd, NULL, NULL);
|
sd = accept(w->fd, NULL, NULL);
|
||||||
if (sd < 0) {
|
if (sd < 0) {
|
||||||
@@ -327,16 +345,22 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
result = do_start(rq.data, sizeof(rq.data));
|
result = do_start(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case INIT_CMD_RESTART_SVC:
|
||||||
|
_d("restart %s", rq.data);
|
||||||
|
strterm(rq.data, sizeof(rq.data));
|
||||||
|
result = do_restart(rq.data, sizeof(rq.data));
|
||||||
|
break;
|
||||||
|
|
||||||
case INIT_CMD_STOP_SVC:
|
case INIT_CMD_STOP_SVC:
|
||||||
_d("stop %s", rq.data);
|
_d("stop %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_stop(rq.data, sizeof(rq.data));
|
result = do_stop(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RESTART_SVC:
|
case INIT_CMD_RELOAD_SVC:
|
||||||
_d("restart %s", rq.data);
|
_d("reload %s", rq.data);
|
||||||
strterm(rq.data, sizeof(rq.data));
|
strterm(rq.data, sizeof(rq.data));
|
||||||
result = do_restart(rq.data, sizeof(rq.data));
|
result = do_reload(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_GET_RUNLEVEL:
|
case INIT_CMD_GET_RUNLEVEL:
|
||||||
@@ -364,24 +388,30 @@ static void api_cb(uev_t *w, void *arg, int events)
|
|||||||
}
|
}
|
||||||
|
|
||||||
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
|
||||||
if (!svc_find_by_pid(rq.runlevel)) {
|
svc = svc_find_by_pid(rq.runlevel);
|
||||||
|
if (!svc) {
|
||||||
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable and allow Finit to collect bundled watchdog */
|
if (wdog && wdog != svc) {
|
||||||
if (wdog) {
|
char name[32];
|
||||||
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
|
|
||||||
stop(wdog);
|
svc_ident(svc, name, sizeof(name));
|
||||||
|
logit(LOG_NOTICE, "Handing over wdog ctrl from %s[%d] to %s[%d]",
|
||||||
|
svc_ident(wdog, NULL, 0), wdog->pid, name, svc->pid);
|
||||||
|
|
||||||
if (wdog->protect) {
|
if (wdog->protect) {
|
||||||
wdog->protect = 0;
|
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
|
||||||
wdog->runlevels = 0;
|
stop(wdog);
|
||||||
|
svc_del(wdog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wdog = svc;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_SVC_ITER:
|
case INIT_CMD_SVC_ITER:
|
||||||
_d("svc iter, first: %d", rq.runlevel);
|
// _d("svc iter, first: %d", rq.runlevel);
|
||||||
/*
|
/*
|
||||||
* XXX: This severly limits the number of
|
* XXX: This severly limits the number of
|
||||||
* simultaneous client connections, but will
|
* simultaneous client connections, but will
|
||||||
@@ -444,7 +474,7 @@ int api_init(uev_ctx_t *ctx)
|
|||||||
};
|
};
|
||||||
|
|
||||||
_d("Setting up external API socket ...");
|
_d("Setting up external API socket ...");
|
||||||
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||||
if (-1 == sd) {
|
if (-1 == sd) {
|
||||||
_pe("Failed starting external API socket");
|
_pe("Failed starting external API socket");
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Called by kernel when last child in cgroup exists
|
|
||||||
|
|
||||||
BASE=/sys/fs/cgroup/finit
|
|
||||||
PROC=`basename $1`
|
|
||||||
DIR="$BASE$1"
|
|
||||||
|
|
||||||
#/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
|
|
||||||
rmdir $DIR
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
+429
-84
@@ -25,116 +25,461 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
#include <lite/queue.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
#include <sys/sysinfo.h> /* get_nprocs_conf() */
|
||||||
|
|
||||||
|
#include "cgroup.h"
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "iwatch.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
static int cg_init = 0;
|
struct cg {
|
||||||
|
TAILQ_ENTRY(cg) link;
|
||||||
|
|
||||||
/*
|
char *name; /* top-level group name */
|
||||||
* Called by Finit at early boot to mount initial cgroups
|
char *cfg; /* kernel settings */
|
||||||
*/
|
|
||||||
void cgroup_init(void)
|
int active; /* for mark & sweep */
|
||||||
|
int protected; /* for init/, user/, & system/ */
|
||||||
|
};
|
||||||
|
|
||||||
|
static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups);
|
||||||
|
|
||||||
|
static char controllers[256];
|
||||||
|
|
||||||
|
static struct iwatch iw_cgroup;
|
||||||
|
static uev_t cgw;
|
||||||
|
|
||||||
|
|
||||||
|
static void cgset(const char *path, char *ctrl, char *prop)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
char *val;
|
||||||
char buf[80];
|
|
||||||
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
|
||||||
|
|
||||||
fp = fopen("/proc/cgroups", "r");
|
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
|
||||||
if (!fp) {
|
if (!path || !ctrl) {
|
||||||
_d("No cgroup support");
|
_e("Missing path or controller, skipping!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
|
if (!prop) {
|
||||||
_d("Failed mounting cgroups");
|
prop = strchr(ctrl, '.');
|
||||||
goto fail;
|
if (!prop) {
|
||||||
|
_e("Invalid cgroup ctrl syntax: %s", ctrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*prop++ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skip first line, header */
|
val = strchr(prop, ':');
|
||||||
(void)fgets(buf, sizeof(buf), fp);
|
if (!val) {
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
_e("Missing cgroup ctrl value, prop %s", prop);
|
||||||
char *cgroup;
|
return;
|
||||||
char rc[80];
|
}
|
||||||
|
*val++ = 0;
|
||||||
|
|
||||||
cgroup = strtok(buf, "\t ");
|
/* disallow sneaky relative paths */
|
||||||
if (!cgroup)
|
if (strstr(ctrl, "..") || strstr(prop, "..")) {
|
||||||
continue;
|
_e("Possible security violation; '..' not allowed in cgroup config!");
|
||||||
|
return;
|
||||||
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
|
|
||||||
if (mkdir(rc, 0755) && EEXIST != errno)
|
|
||||||
continue;
|
|
||||||
if (mount("cgroup", rc, "cgroup", opts, cgroup))
|
|
||||||
_d("Failed mounting %s cgroup on %s", cgroup, rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default cgroups for process monitoring */
|
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
|
||||||
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
|
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
|
||||||
goto fail;
|
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
|
||||||
if (mount("none", FINIT_CGPATH, "cgroup", opts, "none,name=finit")) {
|
|
||||||
_pe("Failed mounting Finit cgroup hierarchy");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up reaper and enable callbacks */
|
|
||||||
echo(FINIT_CGPATH "/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
|
||||||
echo(FINIT_CGPATH "/notify_on_release", 0, "1");
|
|
||||||
|
|
||||||
/* Default groups, PID 1, services, and user/login processes */
|
|
||||||
if (mkdir(FINIT_CGPATH "/init", 0755) && EEXIST != errno)
|
|
||||||
goto fail;
|
|
||||||
if (mkdir(FINIT_CGPATH "/system", 0755) && EEXIST != errno)
|
|
||||||
goto fail;
|
|
||||||
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/* Move ourselves to init */
|
|
||||||
echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
|
|
||||||
|
|
||||||
/* We have signal, main screen turn on! */
|
|
||||||
cg_init = 1;
|
|
||||||
fail:
|
|
||||||
fclose(fp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int move_pid(char *group, char *name, char *id, int pid)
|
/*
|
||||||
|
* Settings for a cgroup are on the form: cpu.weight:1234,mem.max:4321,...
|
||||||
|
* Finit supports the short-form 'mem.', replacing it with 'memory.' when
|
||||||
|
* writing the setting to the file system.
|
||||||
|
*/
|
||||||
|
static void group_init(char *path, int leaf, const char *cfg)
|
||||||
|
{
|
||||||
|
char *ptr, *s;
|
||||||
|
|
||||||
|
_d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
|
||||||
|
if (!fisdir(path)) {
|
||||||
|
if (mkdir(path, 0755)) {
|
||||||
|
_pe("Failed creating cgroup %s", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable detected controllers on domain groups */
|
||||||
|
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
|
||||||
|
_pe("Failed enabling %s for %s", controllers, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cfg || !cfg[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
s = strdupa(cfg);
|
||||||
|
if (!s) {
|
||||||
|
_pe("Failed activating cgroup cfg for %s", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("%s <=> %s", path, s);
|
||||||
|
ptr = strtok(s, ",");
|
||||||
|
while (ptr) {
|
||||||
|
_d("ptr: %s", ptr);
|
||||||
|
if (!strncmp("mem.", ptr, 4))
|
||||||
|
cgset(path, "memory", &ptr[4]);
|
||||||
|
else
|
||||||
|
cgset(path, ptr, NULL);
|
||||||
|
|
||||||
|
ptr = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
||||||
{
|
{
|
||||||
char path[256];
|
char path[256];
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||||
if (id && strlen(id)) {
|
if (pid < 0 || pid == 1) {
|
||||||
strlcat(path, ":", sizeof(path));
|
|
||||||
strlcat(path, id, sizeof(path));
|
|
||||||
}
|
|
||||||
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, "0", getpid());
|
|
||||||
}
|
|
||||||
|
|
||||||
int cgroup_service(char *nm, char *id, int pid)
|
|
||||||
{
|
|
||||||
if (!cg_init)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (pid <= 0) {
|
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return move_pid("finit/system", nm, id, pid);
|
/* create and initialize new group */
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||||
|
group_init(path, 1, cfg);
|
||||||
|
|
||||||
|
/* move process to new group */
|
||||||
|
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
|
||||||
|
_pe("Failed moving pid %d to group %s", pid, path);
|
||||||
|
|
||||||
|
strlcat(path, "/cgroup.events", sizeof(path));
|
||||||
|
|
||||||
|
return iwatch_add(&iw_cgroup, path, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_user(char *name, int pid)
|
||||||
|
{
|
||||||
|
return cgroup_leaf_init("user", name, pid, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_service(char *name, int pid, struct cgroup *cg)
|
||||||
|
{
|
||||||
|
char *group = "system";
|
||||||
|
|
||||||
|
if (cg && cg->name[0]) {
|
||||||
|
char path[256];
|
||||||
|
|
||||||
|
if (!strcmp(cg->name, "root"))
|
||||||
|
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
|
||||||
|
|
||||||
|
if (!strcmp(cg->name, "init"))
|
||||||
|
return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs");
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
|
||||||
|
if (fisdir(path))
|
||||||
|
group = cg->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cgroup_leaf_init(group, name, pid, cg ? cg->cfg : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void append_ctrl(char *ctrl)
|
||||||
|
{
|
||||||
|
if (controllers[0])
|
||||||
|
strlcat(controllers, " ", sizeof(controllers));
|
||||||
|
|
||||||
|
strlcat(controllers, "+", sizeof(controllers));
|
||||||
|
strlcat(controllers, ctrl, sizeof(controllers));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cgroup_handle_event(char *event, uint32_t mask)
|
||||||
|
{
|
||||||
|
char path[strlen(event) + 1];
|
||||||
|
char buf[80];
|
||||||
|
char *ptr;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
_d("event: '%s', mask: %08x", event, mask);
|
||||||
|
if (!(mask & IN_MODIFY))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fp = fopen(event, "r");
|
||||||
|
if (!fp) {
|
||||||
|
_d("Failed opening %s, skipping ...", event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
if (strncmp(buf, "populated", 9))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chomp(buf);
|
||||||
|
if (atoi(&buf[10]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
strlcpy(path, event, sizeof(path));
|
||||||
|
ptr = strrchr(path, '/');
|
||||||
|
if (ptr) {
|
||||||
|
*ptr = 0;
|
||||||
|
if (!cgroup_del(path)) {
|
||||||
|
/*
|
||||||
|
* try with parent, top-level group, we
|
||||||
|
* may get events out-of-order *sigh*
|
||||||
|
*/
|
||||||
|
ptr = strrchr(path, '/');
|
||||||
|
if (!ptr)
|
||||||
|
break;
|
||||||
|
*ptr = 0;
|
||||||
|
cgroup_del(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cgroup_events_cb(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||||
|
struct inotify_event *ev;
|
||||||
|
ssize_t sz;
|
||||||
|
size_t off;
|
||||||
|
|
||||||
|
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||||
|
if (sz <= 0) {
|
||||||
|
_pe("invalid inotify event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ev_buf[sz] = 0;
|
||||||
|
|
||||||
|
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
|
||||||
|
struct iwatch_path *iwp;
|
||||||
|
|
||||||
|
if (off + sizeof(*ev) > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ev = (struct inotify_event *)&ev_buf[off];
|
||||||
|
if (off + sizeof(*ev) + ev->len > (size_t)sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!ev->mask)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Find base path for this event */
|
||||||
|
iwp = iwatch_find_by_wd(&iw_cgroup, ev->wd);
|
||||||
|
if (!iwp || !iwp->path)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cgroup_handle_event(iwp->path, ev->mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_AUTO_RELOAD
|
||||||
|
if (conf_any_change())
|
||||||
|
service_reload_dynamic();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cg *cgroup_find(char *name)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||||
|
if (strcmp(cg->name, name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Marks all unprotected cgroups for deletion (during reload)
|
||||||
|
*/
|
||||||
|
void cgroup_mark_all(void)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||||
|
if (cg->protected)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cg->active = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove (try to) all unused cgroups
|
||||||
|
*/
|
||||||
|
void cgroup_cleanup(void)
|
||||||
|
{
|
||||||
|
struct cg *cg, *tmp;
|
||||||
|
char path[256];
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) {
|
||||||
|
if (cg->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
|
||||||
|
cgroup_del(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add, or update, settings for top-level cgroup
|
||||||
|
*/
|
||||||
|
int cgroup_add(char *name, char *cfg, int protected)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
return -1;
|
||||||
|
if (!cfg)
|
||||||
|
cfg = "";
|
||||||
|
|
||||||
|
cg = cgroup_find(name);
|
||||||
|
if (!cg) {
|
||||||
|
cg = malloc(sizeof(struct cg));
|
||||||
|
if (!cg) {
|
||||||
|
_pe("Failed allocating 'struct cg' for %s", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cg->name = strdup(name);
|
||||||
|
if (!cg->name) {
|
||||||
|
_pe("Failed setting cgroup name %s", name);
|
||||||
|
free(cg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
TAILQ_INSERT_TAIL(&cgroups, cg, link);
|
||||||
|
} else
|
||||||
|
free(cg->cfg);
|
||||||
|
|
||||||
|
cg->cfg = strdup(cfg);
|
||||||
|
if (!cg->cfg) {
|
||||||
|
_pe("Failed add/update of cgroup %s", name);
|
||||||
|
TAILQ_REMOVE(&cgroups, cg, link);
|
||||||
|
free(cg->name);
|
||||||
|
free(cg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cg->protected = protected;
|
||||||
|
cg->active = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove inactive top-level cgroup
|
||||||
|
*/
|
||||||
|
int cgroup_del(char *dir)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
char path[256];
|
||||||
|
|
||||||
|
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||||
|
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
|
||||||
|
if (strcmp(path, dir))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cg->active)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmdir(dir) && errno != ENOENT) {
|
||||||
|
_d("Failed removing %s: %s", dir, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cg) {
|
||||||
|
TAILQ_REMOVE(&cgroups, cg, link);
|
||||||
|
free(cg->name);
|
||||||
|
free(cg->cfg);
|
||||||
|
free(cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
|
||||||
|
void cgroup_config(void)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(cg, &cgroups, link) {
|
||||||
|
char path[256];
|
||||||
|
int leaf = 0;
|
||||||
|
|
||||||
|
if (!cg->active)
|
||||||
|
continue;
|
||||||
|
if (!strcmp(cg->name, "init"))
|
||||||
|
leaf = 1; /* reserved */
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, cg->name);
|
||||||
|
group_init(path, leaf, cg->cfg);
|
||||||
|
|
||||||
|
strlcat(path, "/cgroup.events", sizeof(path));
|
||||||
|
iwatch_add(&iw_cgroup, path, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by Finit at early boot to mount initial cgroups
|
||||||
|
*/
|
||||||
|
void cgroup_init(uev_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||||
|
char buf[80];
|
||||||
|
FILE *fp;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
|
||||||
|
_pe("Failed mounting cgroup v2");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find available controllers */
|
||||||
|
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
|
||||||
|
if (!fp) {
|
||||||
|
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
char *cgroup;
|
||||||
|
|
||||||
|
cgroup = strtok(chomp(buf), "\t ");
|
||||||
|
while (cgroup) {
|
||||||
|
append_ctrl(cgroup);
|
||||||
|
cgroup = strtok(NULL, "\t ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* Enable all controllers */
|
||||||
|
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control"))
|
||||||
|
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
|
||||||
|
|
||||||
|
/* Default (protected) groups, PID 1, services, and user/login processes */
|
||||||
|
cgroup_add("init", "cpu.weight:100", 1);
|
||||||
|
cgroup_add("system", "cpu.weight:9800", 1);
|
||||||
|
cgroup_add("user", "cpu.weight:100", 1);
|
||||||
|
cgroup_config();
|
||||||
|
|
||||||
|
/* Move ourselves to init (best effort, otherwise run in 'root' group */
|
||||||
|
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs"))
|
||||||
|
_pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
|
||||||
|
|
||||||
|
/* prepare cgroup.events watcher */
|
||||||
|
fd = iwatch_init(&iw_cgroup);
|
||||||
|
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
|
||||||
|
_pe("Failed setting up cgroup.events watcher");
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+17
-3
@@ -24,9 +24,23 @@
|
|||||||
#ifndef FINIT_CGROUP_H_
|
#ifndef FINIT_CGROUP_H_
|
||||||
#define FINIT_CGROUP_H_
|
#define FINIT_CGROUP_H_
|
||||||
|
|
||||||
void cgroup_init (void);
|
#include <uev/uev.h>
|
||||||
|
|
||||||
int cgroup_user (char *name);
|
struct cgroup {
|
||||||
int cgroup_service (char *cmd, char *id, int pid);
|
char name[16];
|
||||||
|
char cfg[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
void cgroup_mark_all(void);
|
||||||
|
void cgroup_cleanup (void);
|
||||||
|
|
||||||
|
int cgroup_add (char *name, char *cfg, int protected);
|
||||||
|
int cgroup_del (char *dir);
|
||||||
|
void cgroup_config (void);
|
||||||
|
|
||||||
|
void cgroup_init (uev_ctx_t *ctx);
|
||||||
|
|
||||||
|
int cgroup_user (char *name, int pid);
|
||||||
|
int cgroup_service (char *name, int pid, struct cgroup *cg);
|
||||||
|
|
||||||
#endif /* FINIT_CGROUP_H_ */
|
#endif /* FINIT_CGROUP_H_ */
|
||||||
|
|||||||
+654
@@ -0,0 +1,654 @@
|
|||||||
|
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019-2021 Joachim Wiberg <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 <err.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <search.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <lite/lite.h>
|
||||||
|
#include <sys/sysinfo.h> /* sysinfo() */
|
||||||
|
|
||||||
|
#include "cgutil.h"
|
||||||
|
#include "initctl.h"
|
||||||
|
|
||||||
|
#define CDIM plain ? "" : "\e[2m"
|
||||||
|
#define CRST plain ? "" : "\e[0m"
|
||||||
|
|
||||||
|
#define NONE " "
|
||||||
|
#define PIPE plain ? "| " : "│ "
|
||||||
|
#define FORK plain ? "|-" : "├─"
|
||||||
|
#define END plain ? "`-" : "└─"
|
||||||
|
|
||||||
|
uint64_t total_ram; /* From sysinfo() */
|
||||||
|
|
||||||
|
struct cg dummy; /* empty result "NULL" */
|
||||||
|
struct cg *list;
|
||||||
|
|
||||||
|
|
||||||
|
char *pid_cmdline(int pid, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t i, sz;
|
||||||
|
char *ptr;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopenf("r", "/proc/%d/cmdline", pid);
|
||||||
|
if (!fp) {
|
||||||
|
buf[0] = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
|
||||||
|
fclose(fp);
|
||||||
|
if (!sz)
|
||||||
|
return NULL; /* kernel thread */
|
||||||
|
|
||||||
|
buf[sz] = 0;
|
||||||
|
|
||||||
|
ptr = strchr(buf, 0);
|
||||||
|
if (ptr && ptr != buf) {
|
||||||
|
ptr++;
|
||||||
|
sz -= ptr - buf;
|
||||||
|
memmove(buf, ptr, sz + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sz; i++) {
|
||||||
|
if (buf[i] == 0)
|
||||||
|
buf[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *pid_comm(int pid, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr = NULL;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopenf("r", "/proc/%d/comm", pid);
|
||||||
|
if (!fp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (fgets(buf, len, fp))
|
||||||
|
ptr = chomp(buf);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *pid_cgroup(int pid, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr = NULL;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopenf("r", "/proc/%d/cgroup", pid);
|
||||||
|
if (!fp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (fgets(buf, len, fp))
|
||||||
|
ptr = chomp(buf);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (ptr)
|
||||||
|
ptr = strchr(buf, '/');
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *cgroup_val(char *path, char *file, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *val = NULL;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopenf("r", "%s/%s", path, file);
|
||||||
|
if (fp) {
|
||||||
|
if (fgets(buf, len, fp)) {
|
||||||
|
val = chomp(buf);
|
||||||
|
len = strcspn(val, " \t");
|
||||||
|
val[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t cgroup_uint64(char *path, char *file)
|
||||||
|
{
|
||||||
|
uint64_t val = 0;
|
||||||
|
char buf[42];
|
||||||
|
|
||||||
|
if (cgroup_val(path, file, buf, sizeof(buf)))
|
||||||
|
val = strtoull(buf, NULL, 10);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *cgroup_memval(char *path, char *file, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char data[42];
|
||||||
|
|
||||||
|
if (cgroup_val(path, file, data, sizeof(data))) {
|
||||||
|
if (!strcmp(data, "max"))
|
||||||
|
strlcpy(buf, data, len);
|
||||||
|
else
|
||||||
|
memsz(strtoull(data, NULL, 10), buf, len);
|
||||||
|
} else
|
||||||
|
buf[0] = 0;
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t cgroup_memuse(struct cg *cg)
|
||||||
|
{
|
||||||
|
char buf[42];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
|
||||||
|
if (fp) {
|
||||||
|
cg->cg_rss = 0;
|
||||||
|
cg->cg_vmlib = 0;
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
chomp(buf);
|
||||||
|
|
||||||
|
if (!strncmp(buf, "anon", 4)) {
|
||||||
|
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "slab", 4)) {
|
||||||
|
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "kernel_stack", 12)) {
|
||||||
|
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "pagetables", 10)) {
|
||||||
|
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "percpu", 6)) {
|
||||||
|
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "sock", 4)) {
|
||||||
|
cg->cg_rss += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "file", 4)) {
|
||||||
|
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, "file_mapped", 11)) {
|
||||||
|
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg->cg_memshare = (float)(cg->cg_rss * 100 / total_ram);
|
||||||
|
|
||||||
|
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.current");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t cgroup_memory(char *group)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
|
||||||
|
paste(path, sizeof(path), FINIT_CGPATH, group);
|
||||||
|
|
||||||
|
return cgroup_uint64(path, "memory.current");
|
||||||
|
}
|
||||||
|
|
||||||
|
static float cgroup_cpuload(struct cg *cg)
|
||||||
|
{
|
||||||
|
char fn[256];
|
||||||
|
char buf[64];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
|
||||||
|
fp = fopen(fn, "r");
|
||||||
|
if (!fp)
|
||||||
|
err(1, "Cannot open %s", fn);
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
uint64_t curr;
|
||||||
|
|
||||||
|
chomp(buf);
|
||||||
|
if (strncmp(buf, "usage_usec", 10))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
curr = strtoull(&buf[11], NULL, 10);
|
||||||
|
if (cg->cg_prev != 0) {
|
||||||
|
uint64_t diff = curr - cg->cg_prev;
|
||||||
|
|
||||||
|
/* this expects 1 sec poll interval */
|
||||||
|
cg->cg_load = (float)(diff / 1000000);
|
||||||
|
cg->cg_load *= 100.0;
|
||||||
|
}
|
||||||
|
cg->cg_prev = curr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return cg->cg_load;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cg *append(char *path)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
char fn[256];
|
||||||
|
ENTRY item;
|
||||||
|
|
||||||
|
snprintf(fn, sizeof(fn), "%s/cpu.stat", path);
|
||||||
|
if (access(fn, F_OK)) {
|
||||||
|
/* older kernels, 4.19, don't have summary cpu.stat in root */
|
||||||
|
if (strcmp(path, FINIT_CGPATH))
|
||||||
|
warn("not a cgroup path with cpu controller, %s", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cg = calloc(1, sizeof(struct cg));
|
||||||
|
if (!cg)
|
||||||
|
err(1, "failed allocating struct cg");
|
||||||
|
|
||||||
|
cg->cg_path = strdup(path);
|
||||||
|
if (list)
|
||||||
|
cg->cg_next = list;
|
||||||
|
list = cg;
|
||||||
|
|
||||||
|
item.key = cg->cg_path;
|
||||||
|
item.data = cg;
|
||||||
|
if (!hsearch(item, ENTER))
|
||||||
|
err(1, "failed adding to hash table");
|
||||||
|
|
||||||
|
return cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cg *find(char *path)
|
||||||
|
{
|
||||||
|
ENTRY *ep, item = { path, NULL };
|
||||||
|
|
||||||
|
ep = hsearch(item, FIND);
|
||||||
|
if (ep)
|
||||||
|
return ep->data;
|
||||||
|
|
||||||
|
return append(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update stats */
|
||||||
|
struct cg *cg_stats(char *path)
|
||||||
|
{
|
||||||
|
struct cg *cg;
|
||||||
|
|
||||||
|
cg = find(path);
|
||||||
|
if (!cg)
|
||||||
|
return &dummy;
|
||||||
|
|
||||||
|
cgroup_cpuload(cg);
|
||||||
|
cgroup_memuse(cg);
|
||||||
|
|
||||||
|
return cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* query config */
|
||||||
|
struct cg *cg_conf(char *path)
|
||||||
|
{
|
||||||
|
static struct cg cg;
|
||||||
|
|
||||||
|
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
|
||||||
|
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
|
||||||
|
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
|
||||||
|
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
||||||
|
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
|
||||||
|
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
|
||||||
|
|
||||||
|
return &cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cgroup_filter(const struct dirent *entry)
|
||||||
|
{
|
||||||
|
/* Skip current dir ".", and prev dir "..", from list of files */
|
||||||
|
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
|
||||||
|
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (entry->d_name[0] == '.')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (entry->d_type != DT_DIR)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
||||||
|
{
|
||||||
|
struct dirent **namelist = NULL;
|
||||||
|
char s[32], r[32], l[32];
|
||||||
|
char row[ttcols + 9]; /* + control codes */
|
||||||
|
size_t rlen = sizeof(row) - 1;
|
||||||
|
size_t rplen = rlen - 9;
|
||||||
|
struct stat st;
|
||||||
|
struct cg *cg;
|
||||||
|
char buf[512];
|
||||||
|
int rc = 0;
|
||||||
|
FILE *fp;
|
||||||
|
int i, n;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
if (pos >= ttrows)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (-1 == lstat(path, &st))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if ((st.st_mode & S_IFMT) != S_IFDIR) {
|
||||||
|
errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopenf("r", "%s/cgroup.procs", path);
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
num = 0;
|
||||||
|
while (fgets(buf, sizeof(buf), fp))
|
||||||
|
num++;
|
||||||
|
|
||||||
|
if (!pfx) {
|
||||||
|
pfx = "";
|
||||||
|
switch (mode) {
|
||||||
|
case 1:
|
||||||
|
cg = cg_stats(path);
|
||||||
|
snprintf(row, rplen, "\r %6.6s %6.6s %6.6s %5.1f %5.1f %s",
|
||||||
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
|
memsz(cg->cg_rss, r, sizeof(r)),
|
||||||
|
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||||
|
cg->cg_memshare, cg->cg_load, path);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
cg = cg_conf(path);
|
||||||
|
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
|
||||||
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
|
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
|
||||||
|
cg->cg_cpu.weight, cg->cg_cpu.max, path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strlcpy(row, "\r", rplen);
|
||||||
|
strlcat(row, path, rplen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num > 0) {
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
char comm[80] = { 0 };
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = atoi(chomp(buf));
|
||||||
|
if (pid <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* skip kernel threads for now (no cmdline) */
|
||||||
|
pid_comm(pid, comm, sizeof(comm));
|
||||||
|
if (pid_cmdline(pid, buf, sizeof(buf))) {
|
||||||
|
char proc[ttcols];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 1:
|
||||||
|
snprintf(row, rplen, "\r%37s", " ");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
snprintf(row, rplen, "\r --.-- [ ] [ ] ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strlcpy(row, "\r", rplen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcat(row, pfx, rplen);
|
||||||
|
strlcat(row, ++i == num ? END : FORK, rlen);
|
||||||
|
|
||||||
|
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
|
||||||
|
|
||||||
|
if (plain) {
|
||||||
|
strlcat(row, proc, rplen);
|
||||||
|
} else {
|
||||||
|
strlcat(row, CDIM, rlen);
|
||||||
|
strlcat(row, proc, rlen);
|
||||||
|
|
||||||
|
len = strlen(row) + strlen(CRST);
|
||||||
|
if (len > (int)rlen)
|
||||||
|
row[rlen - strlen(CRST)] = 0;
|
||||||
|
|
||||||
|
strlcat(row, CRST, sizeof(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 1) {
|
||||||
|
pos += i;
|
||||||
|
if (pos >= ttrows)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||||
|
if (n > 0) {
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
char *nm = namelist[i]->d_name;
|
||||||
|
char prefix[80];
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||||
|
switch (mode) {
|
||||||
|
case 1:
|
||||||
|
cg = cg_stats(buf);
|
||||||
|
snprintf(row, rplen,
|
||||||
|
"\r %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
||||||
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
|
memsz(cg->cg_rss, r, sizeof(r)),
|
||||||
|
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||||
|
cg->cg_memshare, cg->cg_load);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
cg = cg_conf(buf);
|
||||||
|
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
|
||||||
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
|
cg->cg_mem.min, cg->cg_mem.max,
|
||||||
|
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strlcpy(row, "\r", rplen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcat(row, pfx, rplen);
|
||||||
|
if (i + 1 == n) {
|
||||||
|
strlcat(row, END, rplen);
|
||||||
|
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
||||||
|
} else {
|
||||||
|
strlcat(row, FORK, rplen);
|
||||||
|
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
||||||
|
}
|
||||||
|
strlcat(row, " ", rplen);
|
||||||
|
|
||||||
|
strlcat(row, nm, rplen);
|
||||||
|
strlcat(row, "/ ", rplen);
|
||||||
|
|
||||||
|
puts(row);
|
||||||
|
|
||||||
|
rc += cgroup_tree(buf, prefix, mode, pos + i);
|
||||||
|
|
||||||
|
free(namelist[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(namelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int show_cgps(char *arg)
|
||||||
|
{
|
||||||
|
char path[512];
|
||||||
|
|
||||||
|
if (!arg)
|
||||||
|
arg = FINIT_CGPATH;
|
||||||
|
else if (arg[0] != '/') {
|
||||||
|
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||||
|
arg = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cgroup_tree(arg, NULL, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cgtop(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
(void)w;
|
||||||
|
(void)events;
|
||||||
|
|
||||||
|
fputs("\e[2J\e[1;1H", stdout);
|
||||||
|
if (heading)
|
||||||
|
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
||||||
|
cgroup_tree(arg, NULL, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup(void)
|
||||||
|
{
|
||||||
|
ttcooked();
|
||||||
|
showcursor();
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void leave(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
(void)events;
|
||||||
|
|
||||||
|
uev_exit(w->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void key(uev_t *w, void *arg, int events)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
(void)arg;
|
||||||
|
(void)events;
|
||||||
|
|
||||||
|
if (read(w->fd, &ch, sizeof(ch)) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'q':
|
||||||
|
uev_exit(w->ctx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dbg("Got char 0x%02x", ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int show_cgtop(char *arg)
|
||||||
|
{
|
||||||
|
struct sysinfo si = { 0 };
|
||||||
|
uev_t timer, input, sigint, sigterm, sigquit;
|
||||||
|
char path[512];
|
||||||
|
uev_ctx_t ctx;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
if (!arg)
|
||||||
|
arg = FINIT_CGPATH;
|
||||||
|
else if (arg[0] != '/') {
|
||||||
|
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||||
|
arg = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hcreate(ttrows + 25))
|
||||||
|
err(1, "failed creating hash table");
|
||||||
|
|
||||||
|
sysinfo(&si);
|
||||||
|
total_ram = si.totalram * si.mem_unit;
|
||||||
|
|
||||||
|
uev_init(&ctx);
|
||||||
|
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
|
||||||
|
|
||||||
|
if (!ionce && !plain) {
|
||||||
|
atexit(cleanup);
|
||||||
|
ttraw();
|
||||||
|
hidecursor();
|
||||||
|
|
||||||
|
flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||||
|
if (flags != -1)
|
||||||
|
(void)fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
(void)uev_io_init(&ctx, &input, key, NULL, STDIN_FILENO, UEV_READ);
|
||||||
|
|
||||||
|
(void)uev_signal_init(&ctx, &sigint, leave, NULL, SIGINT);
|
||||||
|
(void)uev_signal_init(&ctx, &sigterm, leave, NULL, SIGTERM);
|
||||||
|
(void)uev_signal_init(&ctx, &sigquit, leave, NULL, SIGQUIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uev_run(&ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int show_cgroup(char *arg)
|
||||||
|
{
|
||||||
|
char path[512];
|
||||||
|
|
||||||
|
if (!arg)
|
||||||
|
arg = FINIT_CGPATH;
|
||||||
|
else if (arg[0] != '/') {
|
||||||
|
paste(path, sizeof(path), FINIT_CGPATH, arg);
|
||||||
|
arg = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* memory.current memory.min memory.max cpuset.cpus cpu.weight cpu.max */
|
||||||
|
if (heading)
|
||||||
|
print_header(" MEM [MIN MAX] CPU [WEIGHT MAX] GROUP");
|
||||||
|
|
||||||
|
return cgroup_tree(arg, NULL, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/* Cgroup utility functions for initctl
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019-2021 Joachim Wiberg <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_CGUTIL_H_
|
||||||
|
#define FINIT_CGUTIL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct cg {
|
||||||
|
struct cg *cg_next;
|
||||||
|
|
||||||
|
/* stats */
|
||||||
|
char *cg_path; /* path in /sys/fs/cgroup */
|
||||||
|
uint64_t cg_prev; /* cpuacct.usage */
|
||||||
|
uint64_t cg_rss; /* memory.stat */
|
||||||
|
uint64_t cg_vmlib; /* memory.stat */
|
||||||
|
uint64_t cg_vmsize; /* memory.current */
|
||||||
|
float cg_memshare; /* cg_rss / total_ram * 100 */
|
||||||
|
float cg_load; /* curr - prev / 10000000.0 */
|
||||||
|
|
||||||
|
/* config */
|
||||||
|
struct {
|
||||||
|
char min[32];
|
||||||
|
char max[32];
|
||||||
|
} cg_mem;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char set[16];
|
||||||
|
char weight[32];
|
||||||
|
char max[32];
|
||||||
|
} cg_cpu;
|
||||||
|
};
|
||||||
|
|
||||||
|
char *pid_cmdline (int pid, char *buf, size_t len);
|
||||||
|
char *pid_comm (int pid, char *buf, size_t len);
|
||||||
|
char *pid_cgroup (int pid, char *buf, size_t len);
|
||||||
|
|
||||||
|
uint64_t cgroup_memory(char *group);
|
||||||
|
|
||||||
|
struct cg *cg_stats(char *path);
|
||||||
|
struct cg *cg_conf (char *path);
|
||||||
|
|
||||||
|
int cgroup_tree (char *path, char *pfx, int mode, int pos);
|
||||||
|
|
||||||
|
int show_cgroup (char *arg);
|
||||||
|
int show_cgtop (char *arg);
|
||||||
|
int show_cgps (char *arg);
|
||||||
|
|
||||||
|
#endif /* FINIT_CGUTIL_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+30
-16
@@ -21,7 +21,9 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
||||||
@@ -36,19 +38,14 @@ int client_connect(void)
|
|||||||
.sun_path = INIT_SOCKET,
|
.sun_path = INIT_SOCKET,
|
||||||
};
|
};
|
||||||
|
|
||||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||||
if (-1 == sd)
|
if (-1 == sd)
|
||||||
goto error;
|
err(1, "Failed creating UNIX domain socket");
|
||||||
|
|
||||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||||
close(sd);
|
err(1, "Failed connecting to finit");
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sd;
|
return sd;
|
||||||
error:
|
|
||||||
perror("Failed connecting to finit");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int client_disconnect(void)
|
int client_disconnect(void)
|
||||||
@@ -68,27 +65,44 @@ int client_disconnect(void)
|
|||||||
|
|
||||||
int client_send(struct init_request *rq, ssize_t len)
|
int client_send(struct init_request *rq, ssize_t len)
|
||||||
{
|
{
|
||||||
|
struct pollfd pfd = { 0 };
|
||||||
int sd, result = 255;
|
int sd, result = 255;
|
||||||
|
|
||||||
sd = client_connect();
|
sd = client_connect();
|
||||||
if (-1 == sd)
|
if (-1 == sd)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (write(sd, rq, len) != len)
|
pfd.fd = sd;
|
||||||
goto error;
|
pfd.events = POLLOUT;
|
||||||
|
if (poll(&pfd, 1, 2000) <= 0) {
|
||||||
|
warn("Timed out waiting for Finit, errno %d", errno);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (read(sd, rq, len) != len)
|
if (write(sd, rq, len) != len) {
|
||||||
goto error;
|
warn("Failed communicating with Finit, errno %d", errno);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pfd.fd = sd;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
if (poll(&pfd, 1, 2000) <= 0) {
|
||||||
|
warn("Timed out waiting for reply from Finit, errno %d", errno);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(sd, rq, len) != len) {
|
||||||
|
warn("Failed reading reply from Finit, errno %d", errno);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (rq->cmd == INIT_CMD_NACK)
|
if (rq->cmd == INIT_CMD_NACK)
|
||||||
result = 1;
|
result = 1;
|
||||||
else
|
else
|
||||||
result = 0;
|
result = 0;
|
||||||
goto exit;
|
|
||||||
error:
|
|
||||||
perror("Failed communicating with finit");
|
|
||||||
exit:
|
exit:
|
||||||
client_disconnect();
|
client_disconnect();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+73
-30
@@ -99,13 +99,14 @@ static void cond_bump_reconf(void)
|
|||||||
unsigned int rgen;
|
unsigned int rgen;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If %COND_RECONF does not exist, cond_get_gen() returns 0
|
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
|
||||||
* meaning that rgen++ is always what we want.
|
* meaning that rgen++ is always what we want.
|
||||||
*/
|
*/
|
||||||
rgen = cond_get_gen(COND_RECONF);
|
rgen = cond_get_gen(_PATH_RECONF);
|
||||||
rgen++;
|
rgen++;
|
||||||
|
|
||||||
cond_set_gen(COND_RECONF, rgen);
|
if (cond_set_gen(_PATH_RECONF, rgen))
|
||||||
|
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cond_checkpath(const char *path)
|
static int cond_checkpath(const char *path)
|
||||||
@@ -119,7 +120,7 @@ static int cond_checkpath(const char *path)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (makepath(dir) && errno != EEXIST) {
|
if (mkpath(dir, 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -127,22 +128,22 @@ static int cond_checkpath(const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cond_set_path(const char *path, enum cond_state new)
|
int cond_set_path(const char *path, enum cond_state next)
|
||||||
{
|
{
|
||||||
enum cond_state old;
|
enum cond_state prev;
|
||||||
unsigned int rgen;
|
unsigned int rgen;
|
||||||
|
|
||||||
_d("%s", path);
|
_d("%s <= %d", path, next);
|
||||||
|
|
||||||
rgen = cond_get_gen(COND_RECONF);
|
rgen = cond_get_gen(_PATH_RECONF);
|
||||||
if (!rgen) {
|
if (!rgen) {
|
||||||
_e("Unable to read configuration generation (%s)", path);
|
_e("Unable to read configuration generation (%s)", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
old = cond_get_path(path);
|
prev = cond_get_path(path);
|
||||||
|
|
||||||
switch (new) {
|
switch (next) {
|
||||||
case COND_ON:
|
case COND_ON:
|
||||||
if (cond_checkpath(path))
|
if (cond_checkpath(path))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -159,10 +160,11 @@ int cond_set_path(const char *path, enum cond_state new)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new != old;
|
return next != prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cond_update(const char *name)
|
/* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */
|
||||||
|
void cond_update(const char *name)
|
||||||
{
|
{
|
||||||
svc_t *svc, *iter = NULL;
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
@@ -176,13 +178,22 @@ static void cond_update(const char *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cond_set(const char *name)
|
int cond_set_noupdate(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
if (string_compare(name, "nop"))
|
if (string_compare(name, "nop"))
|
||||||
return;
|
return 1;
|
||||||
|
|
||||||
if (!cond_set_path(cond_path(name), COND_ON))
|
if (!cond_set_path(cond_path(name), COND_ON))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cond_set(const char *name)
|
||||||
|
{
|
||||||
|
_d("%s", name);
|
||||||
|
if (cond_set_noupdate(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cond_update(name);
|
cond_update(name);
|
||||||
@@ -201,17 +212,27 @@ void cond_set_oneshot(const char *name)
|
|||||||
if (cond_checkpath(path))
|
if (cond_checkpath(path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
symlink(COND_RECONF, path);
|
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||||
|
_pe("Failed creating onshot cond %s", name);
|
||||||
cond_update(name);
|
cond_update(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cond_clear_noupdate(const char *name)
|
||||||
|
{
|
||||||
|
_d("%s", name);
|
||||||
|
if (string_compare(name, "nop"))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void cond_clear(const char *name)
|
void cond_clear(const char *name)
|
||||||
{
|
{
|
||||||
_d("%s", name);
|
_d("%s", name);
|
||||||
if (string_compare(name, "nop"))
|
if (cond_clear_noupdate(name))
|
||||||
return;
|
|
||||||
|
|
||||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cond_update(name);
|
cond_update(name);
|
||||||
@@ -224,7 +245,7 @@ void cond_reload(void)
|
|||||||
cond_bump_reconf();
|
cond_bump_reconf();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw, int set)
|
||||||
{
|
{
|
||||||
char *nm;
|
char *nm;
|
||||||
|
|
||||||
@@ -234,35 +255,57 @@ 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 = strstr((char *)fpath, COND_DIR);
|
nm = strstr((char *)fpath, COND_BASE);
|
||||||
if (!nm) {
|
if (!nm) {
|
||||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
_e("Incorrect condtion path %s, cannot %sassert", fpath, set ? "re" : "de");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nm += sizeof(COND_DIR);
|
nm += strlen(COND_BASE);
|
||||||
_d("Reasserting %s => %s", fpath, nm);
|
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
|
||||||
cond_set(nm);
|
if (set)
|
||||||
|
cond_set(nm);
|
||||||
|
else
|
||||||
|
cond_clear_noupdate(nm); /* important, see netlink plugin! */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||||
|
{
|
||||||
|
return do_assert(fpath, sb, tflg, ftw, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int deassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
|
||||||
|
{
|
||||||
|
return do_assert(fpath, sb, tflg, ftw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used only by netlink plugin atm.
|
* Used only by netlink plugin atm.
|
||||||
* type: is a one of pid/, net/, etc.
|
* type: is a one of pid/, net/, etc.
|
||||||
*/
|
*/
|
||||||
void cond_reassert(const char *type)
|
void cond_reassert(const char *pat)
|
||||||
{
|
{
|
||||||
_d("%s", type);
|
_d("%s", pat);
|
||||||
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used only by netlink plugin atm.
|
||||||
|
*/
|
||||||
|
void cond_deassert(const char *pat)
|
||||||
|
{
|
||||||
|
_d("%s", pat);
|
||||||
|
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cond_init(void)
|
void cond_init(void)
|
||||||
{
|
{
|
||||||
char path[MAX_ARG_LEN];
|
char path[MAX_ARG_LEN];
|
||||||
|
|
||||||
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
|
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
|
||||||
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
_pe("Failed creating condition base directory '%s'", _PATH_COND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -45,15 +45,15 @@ const char *cond_path(const char *name)
|
|||||||
static char path[256];
|
static char path[256];
|
||||||
char tmp[MAX_ARG_LEN];
|
char tmp[MAX_ARG_LEN];
|
||||||
|
|
||||||
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
|
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
|
||||||
|
|
||||||
return pid_runpath(tmp, path, sizeof(path));
|
return pid_runpath(tmp, path, sizeof(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int cond_get_gen(const char *file)
|
unsigned int cond_get_gen(const char *file)
|
||||||
{
|
{
|
||||||
unsigned int gen;
|
|
||||||
char *ptr, path[256];
|
char *ptr, path[256];
|
||||||
|
unsigned int gen;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
|
|||||||
{
|
{
|
||||||
int cgen, rgen;
|
int cgen, rgen;
|
||||||
|
|
||||||
rgen = cond_get_gen(COND_RECONF);
|
|
||||||
if (!rgen)
|
|
||||||
return COND_OFF;
|
|
||||||
|
|
||||||
cgen = cond_get_gen(path);
|
cgen = cond_get_gen(path);
|
||||||
if (!cgen)
|
if (!cgen)
|
||||||
return COND_OFF;
|
return COND_OFF;
|
||||||
|
|
||||||
|
rgen = cond_get_gen(_PATH_RECONF);
|
||||||
|
if (!rgen)
|
||||||
|
return COND_OFF;
|
||||||
|
|
||||||
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-3
@@ -4,9 +4,16 @@
|
|||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <svc.h>
|
#include <svc.h>
|
||||||
|
|
||||||
#define COND_DIR "finit/cond"
|
#define COND_BASE "finit/cond"
|
||||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
#define COND_PID "pid/"
|
||||||
#define COND_RECONF COND_PATH "/reconf"
|
#define COND_SYS "sys/"
|
||||||
|
#define COND_USR "usr/"
|
||||||
|
|
||||||
|
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||||
|
#define _PATH_CONDPID _PATH_COND COND_PID
|
||||||
|
#define _PATH_CONDSYS _PATH_COND COND_SYS
|
||||||
|
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||||
|
#define _PATH_RECONF _PATH_COND "reconf"
|
||||||
|
|
||||||
typedef enum cond_state {
|
typedef enum cond_state {
|
||||||
COND_OFF = 0,
|
COND_OFF = 0,
|
||||||
@@ -23,12 +30,19 @@ 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);
|
||||||
|
|
||||||
|
void cond_update (const char *name);
|
||||||
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_set_oneshot (const char *name);
|
void cond_set_oneshot (const char *name);
|
||||||
void cond_clear (const char *name);
|
void cond_clear (const char *name);
|
||||||
void cond_reload (void);
|
void cond_reload (void);
|
||||||
|
|
||||||
|
int cond_set_noupdate (const char *name);
|
||||||
|
int cond_clear_noupdate(const char *name);
|
||||||
|
|
||||||
void cond_reassert (const char *pat);
|
void cond_reassert (const char *pat);
|
||||||
|
void cond_deassert (const char *pat);
|
||||||
|
|
||||||
void cond_init (void);
|
void cond_init (void);
|
||||||
|
|
||||||
#endif /* FINIT_COND_H_ */
|
#endif /* FINIT_COND_H_ */
|
||||||
|
|||||||
+73
-13
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
@@ -50,6 +51,8 @@ int logfile_count_max = 5;
|
|||||||
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||||
|
|
||||||
|
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
|
||||||
|
|
||||||
struct conf_change {
|
struct conf_change {
|
||||||
TAILQ_ENTRY(conf_change) link;
|
TAILQ_ENTRY(conf_change) link;
|
||||||
char *name;
|
char *name;
|
||||||
@@ -352,7 +355,7 @@ char *lim2str(struct rlimit *rlim)
|
|||||||
char tmp[25];
|
char tmp[25];
|
||||||
static char buf[42];
|
static char buf[42];
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
buf[0] = 0;
|
||||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||||
else
|
else
|
||||||
@@ -423,6 +426,29 @@ error:
|
|||||||
logit(LOG_WARNING, "rlimit: parse error");
|
logit(LOG_WARNING, "rlimit: parse error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */
|
||||||
|
static void conf_parse_cgroup(char *line)
|
||||||
|
{
|
||||||
|
char config[strlen(line) + 1];
|
||||||
|
char *ptr, *name;
|
||||||
|
|
||||||
|
name = strtok(line, " \t");
|
||||||
|
if (!name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strstr(name, "..") || strchr(name, '/'))
|
||||||
|
return; /* illegal */
|
||||||
|
|
||||||
|
config[0] = 0;
|
||||||
|
while ((ptr = strtok(NULL, " \t"))) {
|
||||||
|
if (config[0])
|
||||||
|
strlcat(config, ",", sizeof(config));
|
||||||
|
strlcat(config, ptr, sizeof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
cgroup_add(name, config, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_static(char *line, int is_rcsd)
|
static void parse_static(char *line, int is_rcsd)
|
||||||
{
|
{
|
||||||
char cmd[CMD_SIZE];
|
char cmd[CMD_SIZE];
|
||||||
@@ -552,9 +578,21 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read contrl group limits */
|
||||||
|
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||||
|
conf_parse_cgroup(x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set current cgroup for the following services/run/tasks */
|
||||||
|
if (MATCH_CMD(line, "cgroup.", x)) {
|
||||||
|
strlcpy(cgroup_current, x, sizeof(cgroup_current));
|
||||||
|
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);
|
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,9 +618,11 @@ static int parse_conf(char *file, int is_rcsd)
|
|||||||
if (!fp)
|
if (!fp)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Prepare default limits for each service in /etc/finit.d/ */
|
/* Prepare default limits and group for each service in /etc/finit.d/ */
|
||||||
if (is_rcsd)
|
if (is_rcsd) {
|
||||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||||
|
cgroup_current[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
_d("*** Parsing %s", file);
|
_d("*** Parsing %s", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
@@ -622,8 +662,8 @@ int conf_reload(void)
|
|||||||
daylight, timezone, tzname[0], tzname[1]);
|
daylight, timezone, tzname[0], tzname[1]);
|
||||||
|
|
||||||
/* Mark and sweep */
|
/* Mark and sweep */
|
||||||
|
cgroup_mark_all();
|
||||||
svc_mark_dynamic();
|
svc_mark_dynamic();
|
||||||
tty_mark();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset global rlimit to bootstrap values from conf_init().
|
* Reset global rlimit to bootstrap values from conf_init().
|
||||||
@@ -632,12 +672,12 @@ int conf_reload(void)
|
|||||||
|
|
||||||
if (rescue) {
|
if (rescue) {
|
||||||
int rc;
|
int rc;
|
||||||
char line[80] = "tty [12345] @console noclear nologin";
|
char line[80] = "tty [12345] rescue";
|
||||||
|
|
||||||
/* If rescue.conf is missing, fall back to a root shell */
|
/* If rescue.conf is missing, fall back to a root shell */
|
||||||
rc = parse_conf(RESCUE_CONF, 0);
|
rc = parse_conf(RESCUE_CONF, 0);
|
||||||
if (rc)
|
if (rc)
|
||||||
tty_register(line, global_rlimit, NULL);
|
service_register(SVC_TYPE_TTY, line, global_rlimit, NULL);
|
||||||
|
|
||||||
print(rc, "Entering rescue mode");
|
print(rc, "Entering rescue mode");
|
||||||
goto done;
|
goto done;
|
||||||
@@ -677,7 +717,7 @@ int conf_reload(void)
|
|||||||
|
|
||||||
/* Check for dangling symlinks */
|
/* Check for dangling symlinks */
|
||||||
if (S_ISLNK(st.st_mode)) {
|
if (S_ISLNK(st.st_mode)) {
|
||||||
path = rp = realpath(path, NULL);
|
rp = realpath(path, NULL);
|
||||||
if (!rp) {
|
if (!rp) {
|
||||||
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
|
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
@@ -687,7 +727,7 @@ int conf_reload(void)
|
|||||||
/* Check that file ends with '.conf' */
|
/* Check that file ends with '.conf' */
|
||||||
len = strlen(path);
|
len = strlen(path);
|
||||||
if (len < 6 || strcmp(&path[len - 5], ".conf"))
|
if (len < 6 || strcmp(&path[len - 5], ".conf"))
|
||||||
_d("Skipping %s, not a valid .conf ... ", path);
|
_d("Skipping %s, not a Finit .conf file ... ", path);
|
||||||
else
|
else
|
||||||
parse_conf(path, 1);
|
parse_conf(path, 1);
|
||||||
|
|
||||||
@@ -699,7 +739,13 @@ int conf_reload(void)
|
|||||||
|
|
||||||
/* Mark any reverse deps as chenaged. */
|
/* Mark any reverse deps as chenaged. */
|
||||||
service_update_rdeps();
|
service_update_rdeps();
|
||||||
|
|
||||||
|
/* Set up top-level cgroups */
|
||||||
|
cgroup_config();
|
||||||
done:
|
done:
|
||||||
|
/* Remove all unused top-level cgroups */
|
||||||
|
cgroup_cleanup();
|
||||||
|
|
||||||
/* Drop record of all .conf changes */
|
/* Drop record of all .conf changes */
|
||||||
drop_changes();
|
drop_changes();
|
||||||
|
|
||||||
@@ -792,13 +838,21 @@ int conf_any_change(void)
|
|||||||
|
|
||||||
int conf_changed(char *file)
|
int conf_changed(char *file)
|
||||||
{
|
{
|
||||||
|
int rc = 0;
|
||||||
|
char *rp;
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (conf_find(file))
|
rp = realpath(file, NULL);
|
||||||
return 1;
|
if (!rp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return 0;
|
if (conf_find(rp))
|
||||||
|
rc = 1;
|
||||||
|
free(rp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void conf_cb(uev_t *w, void *arg, int events)
|
static void conf_cb(uev_t *w, void *arg, int events)
|
||||||
@@ -880,7 +934,7 @@ int conf_monitor(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare .conf parser and load all .conf files
|
* Prepare .conf parser and load /etc/finit.conf for global settings
|
||||||
*/
|
*/
|
||||||
int conf_init(uev_ctx_t *ctx)
|
int conf_init(uev_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
@@ -900,6 +954,12 @@ int conf_init(uev_ctx_t *ctx)
|
|||||||
rlim2str(i), strerror(errno));
|
rlim2str(i), strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initialize global rlimits, e.g. for built-in services */
|
||||||
|
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||||
|
|
||||||
|
/* Read global rlimits and global cgroup setup from /etc/finit.conf */
|
||||||
|
parse_conf(FINIT_CONF, 0);
|
||||||
|
|
||||||
/* prepare /etc watcher */
|
/* prepare /etc watcher */
|
||||||
fd = iwatch_init(&iw_conf);
|
fd = iwatch_init(&iw_conf);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
|
|||||||
@@ -24,12 +24,14 @@
|
|||||||
#ifndef FINIT_CONF_H_
|
#ifndef FINIT_CONF_H_
|
||||||
#define FINIT_CONF_H_
|
#define FINIT_CONF_H_
|
||||||
|
|
||||||
|
#include "cgroup.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
extern int logfile_size_max;
|
extern int logfile_size_max;
|
||||||
extern int logfile_count_max;
|
extern int logfile_count_max;
|
||||||
|
|
||||||
extern struct rlimit global_rlimit[];
|
extern struct rlimit global_rlimit[];
|
||||||
|
extern char cgroup_current[];
|
||||||
|
|
||||||
int str2rlim(char *str);
|
int str2rlim(char *str);
|
||||||
char *rlim2str(int rlim);
|
char *rlim2str(int rlim);
|
||||||
|
|||||||
+35
-49
@@ -35,6 +35,7 @@
|
|||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
@@ -244,10 +245,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
|||||||
char name[80];
|
char name[80];
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
/* Detach from initial controlling TTY and become session leader */
|
|
||||||
vhangup();
|
|
||||||
setsid();
|
|
||||||
|
|
||||||
fd = open(tty, O_RDWR);
|
fd = open(tty, O_RDWR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
|
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
|
||||||
@@ -259,6 +256,11 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
|||||||
dup2(fd, STDERR_FILENO);
|
dup2(fd, STDERR_FILENO);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Become session leader and set controlling TTY
|
||||||
|
* to enable Ctrl-C and job control in shell.
|
||||||
|
*/
|
||||||
|
setsid();
|
||||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||||
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
|
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
|
||||||
|
|
||||||
@@ -288,7 +290,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
|||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
sa.sa_handler = SIG_IGN;
|
sa.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGHUP, &sa, NULL);
|
|
||||||
sigaction(SIGINT, &sa, NULL);
|
sigaction(SIGINT, &sa, NULL);
|
||||||
sigaction(SIGQUIT, &sa, NULL);
|
sigaction(SIGQUIT, &sa, NULL);
|
||||||
|
|
||||||
@@ -315,7 +316,7 @@ static int activate_console(int noclear, int nowait)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!noclear)
|
if (!noclear)
|
||||||
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
dprint(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
||||||
|
|
||||||
/* Disable ECHO, XON/OFF while waiting for <CR> */
|
/* Disable ECHO, XON/OFF while waiting for <CR> */
|
||||||
if (!tcgetattr(STDIN_FILENO, &orig)) {
|
if (!tcgetattr(STDIN_FILENO, &orig)) {
|
||||||
@@ -339,16 +340,16 @@ static int activate_console(int noclear, int nowait)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||||
(void)write(STDERR_FILENO, msg, strlen(msg));
|
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
|
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fexist(SYNC_STOPPED))
|
if (fexist(SYNC_STOPPED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||||
(void)write(STDERR_FILENO, cup, strlen(cup));
|
dprint(STDERR_FILENO, cup, strlen(cup));
|
||||||
ret = 1;
|
ret = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -381,62 +382,47 @@ static int activate_console(int noclear, int nowait)
|
|||||||
*/
|
*/
|
||||||
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 pid;
|
speed_t speed;
|
||||||
|
int rc = 1;
|
||||||
|
|
||||||
pid = fork();
|
speed = stty_parse_speed(baud);
|
||||||
if (!pid) {
|
prepare_tty(tty, speed, "tty", rlimit);
|
||||||
speed_t speed;
|
if (activate_console(noclear, nowait)) {
|
||||||
int rc = 1;
|
|
||||||
|
|
||||||
speed = stty_parse_speed(baud);
|
|
||||||
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
|
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
|
||||||
prepare_tty(tty, speed, "tty", rlimit);
|
rc = getty(tty, speed, term, NULL);
|
||||||
if (activate_console(noclear, nowait))
|
|
||||||
rc = getty(tty, speed, term, NULL);
|
|
||||||
|
|
||||||
_exit(rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start external getty as defined by user in .conf file
|
||||||
|
*/
|
||||||
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 pid;
|
int rc = 1;
|
||||||
|
|
||||||
pid = fork();
|
/* Dunno speed, tell stty() to not mess with it */
|
||||||
if (!pid) {
|
prepare_tty(tty, B0, "getty", rlimit);
|
||||||
int rc = 1;
|
if (activate_console(noclear, nowait)) {
|
||||||
|
logit(LOG_INFO, "Starting external getty on %s", tty);
|
||||||
/* Dunno speed, tell stty() to not mess with it */
|
rc = execv(cmd, args);
|
||||||
logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0);
|
|
||||||
prepare_tty(tty, B0, "getty", rlimit);
|
|
||||||
if (activate_console(noclear, nowait))
|
|
||||||
rc = execv(cmd, args);
|
|
||||||
|
|
||||||
vhangup();
|
|
||||||
_exit(rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
vhangup();
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||||
{
|
{
|
||||||
pid_t pid;
|
int rc = 1;
|
||||||
|
|
||||||
pid = fork();
|
prepare_tty(tty, B0, "finitsh", rlimit);
|
||||||
if (!pid) {
|
if (activate_console(noclear, nowait))
|
||||||
int rc = 1;
|
rc = sh(tty);
|
||||||
|
|
||||||
prepare_tty(tty, B0, "finit-sh", rlimit);
|
return rc;
|
||||||
if (activate_console(noclear, nowait))
|
|
||||||
rc = sh(tty);
|
|
||||||
|
|
||||||
_exit(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_parts(char *dir, char *cmd)
|
int run_parts(char *dir, char *cmd)
|
||||||
|
|||||||
+153
-88
@@ -52,6 +52,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
#include "schedule.h"
|
#include "schedule.h"
|
||||||
|
#include "watchdog.h"
|
||||||
|
|
||||||
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 */
|
||||||
@@ -59,6 +60,7 @@ int prevlevel = -1;
|
|||||||
int debug = 0; /* debug mode from kernel cmdline */
|
int debug = 0; /* debug mode from kernel cmdline */
|
||||||
int rescue = 0; /* rescue mode from kernel cmdline */
|
int rescue = 0; /* rescue mode from kernel cmdline */
|
||||||
int single = 0; /* single user mode from kernel cmdline */
|
int single = 0; /* single user mode from kernel cmdline */
|
||||||
|
int bootstrap = 1; /* set while bootrapping (for TTYs) */
|
||||||
char *sdown = NULL;
|
char *sdown = NULL;
|
||||||
char *network = NULL;
|
char *network = NULL;
|
||||||
char *hostname = NULL;
|
char *hostname = NULL;
|
||||||
@@ -184,6 +186,69 @@ static void fs_remount_root(int fsckerr)
|
|||||||
}
|
}
|
||||||
#endif /* SYSROOT */
|
#endif /* SYSROOT */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opinionated file system setup. Checks for critical mount points and
|
||||||
|
* mounts them as most users expect. All file systems are checked with
|
||||||
|
* /proc/mounts before mounting.
|
||||||
|
*
|
||||||
|
* Embedded systems, and other people who want full control, can set up
|
||||||
|
* their system with /etc/fstab, which is handled before this function
|
||||||
|
* is called. For systems like Debian/Ubuntu, who only have / and swap
|
||||||
|
* in their /etc/fstab, this function does all the magic necessary.
|
||||||
|
*/
|
||||||
|
static void fs_finalize(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
if (!fismnt("/dev/shm")) {
|
||||||
|
makedir("/dev/shm", 0777);
|
||||||
|
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern systems use /dev/pts */
|
||||||
|
if (!fismnt("/dev/pts")) {
|
||||||
|
char opts[32];
|
||||||
|
int mode;
|
||||||
|
int gid;
|
||||||
|
|
||||||
|
gid = getgroup("tty");
|
||||||
|
if (gid == -1)
|
||||||
|
gid = 0;
|
||||||
|
|
||||||
|
/* 0600 is default on Debian, use 0620 to get mesg y by default */
|
||||||
|
mode = 0620;
|
||||||
|
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
|
||||||
|
|
||||||
|
makedir("/dev/pts", 0755);
|
||||||
|
mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modern systems use tmpfs for /run. Fallback to /var/run if
|
||||||
|
* /run doesn't exist is handled by the bootmisc plugin. It
|
||||||
|
* also sets up compat symlinks.
|
||||||
|
*
|
||||||
|
* The unconditional mount of /run/lock is for DoS prevention.
|
||||||
|
* To override any of this behavior, add entries to /etc/fstab
|
||||||
|
* for /run (and optionally /run/lock).
|
||||||
|
*/
|
||||||
|
if (fisdir("/run") && !fismnt("/run")) {
|
||||||
|
mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
|
||||||
|
|
||||||
|
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
|
||||||
|
makedir("/run/lock", 1777);
|
||||||
|
mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern systems use tmpfs for /tmp */
|
||||||
|
if (!fismnt("/tmp"))
|
||||||
|
mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
|
||||||
|
}
|
||||||
|
|
||||||
static void fs_mount(void)
|
static void fs_mount(void)
|
||||||
{
|
{
|
||||||
if (!rescue)
|
if (!rescue)
|
||||||
@@ -199,7 +264,9 @@ static void fs_mount(void)
|
|||||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||||
|
|
||||||
run("swapon -ea");
|
run("swapon -ea");
|
||||||
umask(0022);
|
|
||||||
|
_d("Finalize, ensure common file systems are available ...");
|
||||||
|
fs_finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -222,6 +289,9 @@ static void fs_init(void)
|
|||||||
};
|
};
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
/* mask writable bit for g and o */
|
||||||
|
umask(022);
|
||||||
|
|
||||||
for (i = 0; i < NELEMS(fs); i++) {
|
for (i = 0; i < NELEMS(fs); i++) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -231,48 +301,6 @@ static void fs_init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If everything goes south we can use this to give the operator an
|
|
||||||
* emergency shell to debug the problem -- Finit should not crash!
|
|
||||||
*
|
|
||||||
* Note: Only use this for debugging a new Finit setup, don't use
|
|
||||||
* this in production since it gives a root shell to anyone
|
|
||||||
* if Finit crashes.
|
|
||||||
*
|
|
||||||
* This emergency shell steps in to prevent "Aieee, PID 1 crashed"
|
|
||||||
* messages from the kernel, which usually results in a reboot, so
|
|
||||||
* that the operator instead can debug the problem.
|
|
||||||
*/
|
|
||||||
static void emergency_shell(void)
|
|
||||||
{
|
|
||||||
#ifdef EMERGENCY_SHELL
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
if (pid) {
|
|
||||||
while (1) {
|
|
||||||
pid_t id;
|
|
||||||
|
|
||||||
/* Reap 'em (prevents Zombies) */
|
|
||||||
id = waitpid(-1, NULL, WNOHANG);
|
|
||||||
if (id == pid)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Become session leader and set controlling TTY
|
|
||||||
* to enable Ctrl-C and job control in shell.
|
|
||||||
*/
|
|
||||||
setsid();
|
|
||||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
|
||||||
|
|
||||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
|
||||||
}
|
|
||||||
#endif /* EMERGENCY_SHELL */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle bootstrap transition to configured runlevel, start TTYs
|
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||||
@@ -285,20 +313,8 @@ static void emergency_shell(void)
|
|||||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||||
* finalize the bootstrap process by calling this function.
|
* finalize the bootstrap process by calling this function.
|
||||||
*/
|
*/
|
||||||
static void finalize(void)
|
static void finalize(void *unused)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Run startup scripts in the runparts directory, if any.
|
|
||||||
*/
|
|
||||||
if (runparts && fisdir(runparts) && !rescue)
|
|
||||||
run_parts(runparts, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start all tasks/services in the configured runlevel
|
|
||||||
*/
|
|
||||||
_d("Change to default runlevel, start all services ...");
|
|
||||||
service_runlevel(cfglevel);
|
|
||||||
|
|
||||||
/* Clean up bootstrap-only tasks/services that never started */
|
/* Clean up bootstrap-only tasks/services that never started */
|
||||||
_d("Clean up all bootstrap-only tasks/services ...");
|
_d("Clean up all bootstrap-only tasks/services ...");
|
||||||
svc_prune_bootstrap();
|
svc_prune_bootstrap();
|
||||||
@@ -320,9 +336,9 @@ static void finalize(void)
|
|||||||
/* Disable progress output at normal runtime */
|
/* Disable progress output at normal runtime */
|
||||||
enable_progress(0);
|
enable_progress(0);
|
||||||
|
|
||||||
/* Delayed start of TTYs at bootstrap */
|
/* System bootrapped, launch TTYs et al */
|
||||||
_d("Launching all getty services ...");
|
bootstrap = 0;
|
||||||
tty_runlevel();
|
service_step_all(SVC_TYPE_RESPAWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -340,12 +356,17 @@ static void crank_worker(void *unused)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||||
* allowed to complete their work in [S], or timeout, before we call
|
* allowed to complete their work in [S], or timeout, before we switch
|
||||||
* finalize(), should not take more than 120 sec.
|
* to the configured runlevel and call finalize(), should not take more
|
||||||
|
* than 120 sec.
|
||||||
*/
|
*/
|
||||||
static void final_worker(void *work)
|
static void bootstrap_worker(void *work)
|
||||||
{
|
{
|
||||||
static int cnt = 120;
|
static int cnt = 120;
|
||||||
|
static struct wq final = {
|
||||||
|
.cb = finalize,
|
||||||
|
.delay = 10
|
||||||
|
};
|
||||||
|
|
||||||
_d("Step all services ...");
|
_d("Step all services ...");
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
@@ -361,12 +382,36 @@ static void final_worker(void *work)
|
|||||||
else
|
else
|
||||||
_d("Timeout, resuming bootstrap.");
|
_d("Timeout, resuming bootstrap.");
|
||||||
|
|
||||||
finalize();
|
_d("Starting runlevel change finalize ...");
|
||||||
|
schedule_work(&final);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run startup scripts in the runparts directory, if any.
|
||||||
|
*/
|
||||||
|
if (runparts && fisdir(runparts) && !rescue)
|
||||||
|
run_parts(runparts, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start all tasks/services in the configured runlevel
|
||||||
|
*/
|
||||||
|
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
|
||||||
|
service_runlevel(cfglevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int version(int rc)
|
||||||
|
{
|
||||||
|
puts(PACKAGE_STRING);
|
||||||
|
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||||
|
#ifdef PACKAGE_URL
|
||||||
|
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usage(int rc)
|
static int usage(int rc)
|
||||||
{
|
{
|
||||||
printf("Usage: telinit [OPTIONS] [q | Q | 0-9]\n\n"
|
printf("Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
// " -a Ignored, compat SysV init\n"
|
// " -a Ignored, compat SysV init\n"
|
||||||
// " -b Ignored, compat SysV init\n"
|
// " -b Ignored, compat SysV init\n"
|
||||||
@@ -384,12 +429,7 @@ static int usage(int rc)
|
|||||||
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
|
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
|
||||||
" if modified, same as initctl reload or SIGHUP to PID 1\n"
|
" if modified, same as initctl reload or SIGHUP to PID 1\n"
|
||||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||||
"\n");
|
"\n", prognm);
|
||||||
|
|
||||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
|
||||||
#ifdef PACKAGE_URL
|
|
||||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -402,6 +442,7 @@ static int telinit(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
|
progname(argv[0]);
|
||||||
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
|
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'a': case 'b': case 'e': case 's': case 'z':
|
case 'a': case 'b': case 'e': case 's': case 'z':
|
||||||
@@ -411,7 +452,7 @@ static int telinit(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'v': case 'V':
|
case 'v': case 'V':
|
||||||
return puts("v" VERSION) == EOF;
|
return version(0);
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
@@ -423,13 +464,13 @@ static int telinit(int argc, char *argv[])
|
|||||||
int req = (int)argv[optind][0];
|
int req = (int)argv[optind][0];
|
||||||
|
|
||||||
if (isdigit(req))
|
if (isdigit(req))
|
||||||
return systemf("initctl runlevel %c", req);
|
return systemf("initctl -b runlevel %c", req);
|
||||||
|
|
||||||
if (req == 'q' || req == 'Q')
|
if (req == 'q' || req == 'Q')
|
||||||
return systemf("initctl reload");
|
return systemf("initctl -b reload");
|
||||||
|
|
||||||
if (req == 's' || req == 'S')
|
if (req == 's' || req == 'S')
|
||||||
return systemf("initctl runlevel %c", req);
|
return systemf("initctl -b runlevel %c", req);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: add non-pid1 process monitor here
|
/* XXX: add non-pid1 process monitor here
|
||||||
@@ -444,11 +485,12 @@ static int telinit(int argc, char *argv[])
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct wq crank = {
|
struct wq crank = {
|
||||||
.cb = crank_worker
|
.cb = crank_worker,
|
||||||
|
.delay = 10
|
||||||
};
|
};
|
||||||
struct wq final = {
|
struct wq bootstrap = {
|
||||||
.cb = final_worker,
|
.cb = bootstrap_worker,
|
||||||
.delay = 1000
|
.delay = 100
|
||||||
};
|
};
|
||||||
uev_ctx_t loop;
|
uev_ctx_t loop;
|
||||||
|
|
||||||
@@ -479,22 +521,32 @@ int main(int argc, char *argv[])
|
|||||||
ctx = &loop;
|
ctx = &loop;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set PATH, SHELL, PWD, and umask early to something sane
|
* Set PATH, SHELL, and PWD early to something sane
|
||||||
*/
|
*/
|
||||||
setenv("PATH", _PATH_STDPATH, 1);
|
setenv("PATH", _PATH_STDPATH, 1);
|
||||||
setenv("SHELL", _PATH_BSHELL, 1);
|
setenv("SHELL", _PATH_BSHELL, 1);
|
||||||
|
|
||||||
if (chdir("/"))
|
if (chdir("/"))
|
||||||
_pe("Failed cd /");
|
_pe("Failed cd /");
|
||||||
umask(0);
|
|
||||||
|
|
||||||
/* Allow progress, if enabled */
|
|
||||||
enable_progress(1);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of emergency.
|
* In case of emergency.
|
||||||
*/
|
*/
|
||||||
emergency_shell();
|
if (rescue) {
|
||||||
|
char *sulogin[] = {
|
||||||
|
_PATH_SULOGIN,
|
||||||
|
"sulogin",
|
||||||
|
};
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < NELEMS(sulogin); i++) {
|
||||||
|
if (systemf(sulogin[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rescue = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load plugins early, the first hook is in banner(), so we
|
* Load plugins early, the first hook is in banner(), so we
|
||||||
@@ -505,6 +557,7 @@ int main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Hello world.
|
* Hello world.
|
||||||
*/
|
*/
|
||||||
|
enable_progress(1); /* Allow progress, if enabled */
|
||||||
banner();
|
banner();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -515,8 +568,7 @@ int main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Initialize default control groups, if available
|
* Initialize default control groups, if available
|
||||||
*/
|
*/
|
||||||
cgroup_init();
|
cgroup_init(&loop);
|
||||||
|
|
||||||
|
|
||||||
/* Check and mount filesystems. */
|
/* Check and mount filesystems. */
|
||||||
fs_mount();
|
fs_mount();
|
||||||
@@ -534,6 +586,20 @@ int main(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
conf_init(&loop);
|
conf_init(&loop);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start built-in watchdogd as soon as possible, if enabled
|
||||||
|
*/
|
||||||
|
if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
|
||||||
|
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
|
||||||
|
wdog = svc_find_by_nameid("watchdog", "finit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start kernel event daemon as soon as possible, if enabled
|
||||||
|
*/
|
||||||
|
if (whichp(FINIT_LIBPATH_ "/keventd"))
|
||||||
|
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
|
||||||
|
|
||||||
/* Base FS up, enable standard SysV init signals */
|
/* Base FS up, enable standard SysV init signals */
|
||||||
sig_setup(&loop);
|
sig_setup(&loop);
|
||||||
|
|
||||||
@@ -548,13 +614,12 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
_d("Starting initctl API responder ...");
|
_d("Starting initctl API responder ...");
|
||||||
api_init(&loop);
|
api_init(&loop);
|
||||||
umask(022);
|
|
||||||
|
|
||||||
_d("Starting the big state machine ...");
|
_d("Starting the big state machine ...");
|
||||||
schedule_work(&crank);
|
schedule_work(&crank);
|
||||||
|
|
||||||
_d("Starting bootstrap finalize timer ...");
|
_d("Starting bootstrap finalize timer ...");
|
||||||
schedule_work(&final);
|
schedule_work(&bootstrap);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enter main loop to monitor /dev/initctl and services
|
* Enter main loop to monitor /dev/initctl and services
|
||||||
|
|||||||
+7
-4
@@ -30,6 +30,7 @@
|
|||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sysexits.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -46,10 +47,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FINIT_CGPATH
|
#ifndef FINIT_CGPATH
|
||||||
#define FINIT_CGPATH "/sys/fs/cgroup/finit"
|
#define FINIT_CGPATH "/sys/fs/cgroup"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
|
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
|
||||||
|
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
|
||||||
|
|
||||||
#define CMD_SIZE 256
|
#define CMD_SIZE 256
|
||||||
#define LINE_SIZE 1024
|
#define LINE_SIZE 1024
|
||||||
@@ -71,10 +73,10 @@
|
|||||||
/* Finit extensions over std SysV */
|
/* Finit extensions over std SysV */
|
||||||
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
|
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
|
||||||
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
|
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
|
||||||
#define INIT_CMD_START_SVC 10
|
#define INIT_CMD_START_SVC 10 /* START service */
|
||||||
#define INIT_CMD_STOP_SVC 11
|
#define INIT_CMD_STOP_SVC 11
|
||||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||||
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
|
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
|
||||||
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
|
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
|
||||||
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
||||||
#define INIT_CMD_GET_RUNLEVEL 16
|
#define INIT_CMD_GET_RUNLEVEL 16
|
||||||
@@ -105,6 +107,7 @@ extern int prevlevel;
|
|||||||
extern int debug;
|
extern int debug;
|
||||||
extern int rescue;
|
extern int rescue;
|
||||||
extern int single;
|
extern int single;
|
||||||
|
extern int bootstrap;
|
||||||
extern char *rcsd;
|
extern char *rcsd;
|
||||||
extern char *sdown;
|
extern char *sdown;
|
||||||
extern char *network;
|
extern char *network;
|
||||||
|
|||||||
+17
-18
@@ -41,9 +41,6 @@
|
|||||||
#define _PATH_LOGIN "/bin/login"
|
#define _PATH_LOGIN "/bin/login"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read one character from stdin.
|
* Read one character from stdin.
|
||||||
*/
|
*/
|
||||||
@@ -54,7 +51,7 @@ static int readch(char *tty)
|
|||||||
|
|
||||||
st = read(STDIN_FILENO, &ch1, 1);
|
st = read(STDIN_FILENO, &ch1, 1);
|
||||||
if (st == 0) {
|
if (st == 0) {
|
||||||
print("\n");
|
dprint(STDOUT_FILENO, "\n", 0);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,31 +71,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
|||||||
s0 = line;
|
s0 = line;
|
||||||
for (s = line; *s != 0; s++) {
|
for (s = line; *s != 0; s++) {
|
||||||
if (*s == '\\') {
|
if (*s == '\\') {
|
||||||
(void)write(1, s0, s - s0);
|
if ((s - s0) > 0)
|
||||||
|
dprint(STDOUT_FILENO, s0, s - s0);
|
||||||
s0 = s + 2;
|
s0 = s + 2;
|
||||||
switch (*++s) {
|
switch (*++s) {
|
||||||
case 'l':
|
case 'l':
|
||||||
print(tty);
|
dprint(STDOUT_FILENO, tty, 0);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
print(uts->machine);
|
dprint(STDOUT_FILENO, uts->machine, 0);
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
print(uts->nodename);
|
dprint(STDOUT_FILENO, uts->nodename, 0);
|
||||||
break;
|
break;
|
||||||
#ifdef _GNU_SOURCE
|
#ifdef _GNU_SOURCE
|
||||||
case 'o':
|
case 'o':
|
||||||
print(uts->domainname);
|
dprint(STDOUT_FILENO, uts->domainname, 0);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case 'r':
|
case 'r':
|
||||||
print(uts->release);
|
dprint(STDOUT_FILENO, uts->release, 0);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
print(uts->sysname);
|
dprint(STDOUT_FILENO, uts->sysname, 0);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
print(uts->version);
|
dprint(STDOUT_FILENO, uts->version, 0);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
goto leave;
|
goto leave;
|
||||||
@@ -109,7 +107,8 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
|||||||
}
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
(void)write(1, s0, s - s0);
|
if ((s - s0) > 0)
|
||||||
|
dprint(STDOUT_FILENO, s0, s - s0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -159,7 +158,7 @@ static void do_getty(char *tty, char *name, size_t len)
|
|||||||
while ((ch = readch(tty)) != '\n') {
|
while ((ch = readch(tty)) != '\n') {
|
||||||
if (ch == CTRL('U')) {
|
if (ch == CTRL('U')) {
|
||||||
while (np > name) {
|
while (np > name) {
|
||||||
(void)write(1, "\b \b", 3);
|
dprint(STDOUT_FILENO, "\b \b", 3);
|
||||||
np--;
|
np--;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -186,12 +185,12 @@ static int do_login(char *name)
|
|||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
cgroup_user(name);
|
cgroup_user(name, 0);
|
||||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Failed to exec login, should be impossible. Try a starting a
|
* Failed to exec login, should not happen on normal systems.
|
||||||
* fallback shell instead.
|
* Try a starting a rescue shell instead.
|
||||||
*
|
*
|
||||||
* Note: Add /etc/securetty handling.
|
* Note: Add /etc/securetty handling.
|
||||||
*/
|
*/
|
||||||
@@ -215,7 +214,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
|||||||
|
|
||||||
/* Set up TTY, re-enabling ISIG et al. */
|
/* Set up TTY, re-enabling ISIG et al. */
|
||||||
stty(STDIN_FILENO, speed);
|
stty(STDIN_FILENO, speed);
|
||||||
(void)write(STDERR_FILENO, cln, strlen(cln));
|
dprint(STDERR_FILENO, cln, strlen(cln));
|
||||||
|
|
||||||
/* The getty process is responsible for the UTMP login record */
|
/* The getty process is responsible for the UTMP login record */
|
||||||
utmp_set_login(tty, NULL);
|
utmp_set_login(tty, NULL);
|
||||||
|
|||||||
+75
-11
@@ -103,6 +103,9 @@ static void add_console(char *cons)
|
|||||||
if (ptr)
|
if (ptr)
|
||||||
*ptr = 0;
|
*ptr = 0;
|
||||||
|
|
||||||
|
if (!strncmp(cons, "/dev/", 5))
|
||||||
|
cons += 5;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/dev/%s", cons);
|
snprintf(path, sizeof(path), "/dev/%s", cons);
|
||||||
|
|
||||||
/* check for and skip duplicates */
|
/* check for and skip duplicates */
|
||||||
@@ -131,7 +134,7 @@ static void find_active_consoles(void)
|
|||||||
if (!fp) {
|
if (!fp) {
|
||||||
fallback:
|
fallback:
|
||||||
_d("Using fallback /dev/console");
|
_d("Using fallback /dev/console");
|
||||||
add_console("/dev/console");
|
add_console("console");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +154,7 @@ static void find_active_consoles(void)
|
|||||||
void console_init(void)
|
void console_init(void)
|
||||||
{
|
{
|
||||||
find_active_consoles();
|
find_active_consoles();
|
||||||
screen_init();
|
ttinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t cprintf(const char *fmt, ...)
|
ssize_t cprintf(const char *fmt, ...)
|
||||||
@@ -167,7 +170,7 @@ ssize_t cprintf(const char *fmt, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
for (i = 0; i < num_cons; i++)
|
for (i = 0; i < num_cons; i++)
|
||||||
(void)write(fds[i], buf, size);
|
dprint(fds[i], buf, size);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -252,20 +255,20 @@ static char *pad(char *buf, size_t len, char *ch, size_t width)
|
|||||||
|
|
||||||
void print_banner(const char *heading)
|
void print_banner(const char *heading)
|
||||||
{
|
{
|
||||||
char buf[4 * SCREEN_WIDTH];
|
char buf[4 * ttcols];
|
||||||
|
|
||||||
if (progress_style == PROGRESS_SILENT)
|
if (progress_style == PROGRESS_SILENT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
buf[0] = 0;
|
||||||
strlcat(buf, "\r\e[2K", sizeof(buf));
|
strlcat(buf, "\r\e[2K", sizeof(buf));
|
||||||
|
|
||||||
if (progress_style == PROGRESS_CLASSIC) {
|
if (progress_style == PROGRESS_CLASSIC) {
|
||||||
strlcat(buf, "\e[1m", sizeof(buf));
|
strlcat(buf, "\e[1m", sizeof(buf));
|
||||||
strlcat(buf, heading, sizeof(buf));
|
strlcat(buf, heading, sizeof(buf));
|
||||||
pad(buf, sizeof(buf), "=", SCREEN_WIDTH + 10);
|
pad(buf, sizeof(buf), "=", ttcols + 10);
|
||||||
} else {
|
} else {
|
||||||
size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
|
size_t wmax = 80 <= ttcols ? 80 : ttcols;
|
||||||
|
|
||||||
/* • • • Foo System ═══════════════════════════════════════════════ */
|
/* • • • Foo System ═══════════════════════════════════════════════ */
|
||||||
/* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */
|
/* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */
|
||||||
@@ -328,7 +331,7 @@ static char *status(int rc)
|
|||||||
|
|
||||||
void printv(const char *fmt, va_list ap)
|
void printv(const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
char buf[SCREEN_WIDTH];
|
char buf[ttcols];
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
if (!fmt || progress_style == PROGRESS_SILENT)
|
if (!fmt || progress_style == PROGRESS_SILENT)
|
||||||
@@ -336,7 +339,7 @@ void printv(const char *fmt, va_list ap)
|
|||||||
|
|
||||||
delline();
|
delline();
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
buf[0] = 0;
|
||||||
len = print_timestamp(buf, sizeof(buf));
|
len = print_timestamp(buf, sizeof(buf));
|
||||||
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
|
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
|
||||||
|
|
||||||
@@ -410,6 +413,64 @@ int getgroup(char *group)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get current user */
|
||||||
|
int getcuser(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr = "root";
|
||||||
|
|
||||||
|
#ifndef ENABLE_STATIC
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
|
||||||
|
if (pw)
|
||||||
|
ptr = pw->pw_name;
|
||||||
|
#endif
|
||||||
|
if (strlcpy(buf, ptr, len) > len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get current group */
|
||||||
|
int getcgroup(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr = "root";
|
||||||
|
|
||||||
|
#ifndef ENABLE_STATIC
|
||||||
|
struct group *gr = getgrgid(getgid());
|
||||||
|
|
||||||
|
if (gr)
|
||||||
|
ptr = gr->gr_name;
|
||||||
|
#endif
|
||||||
|
if (strlcpy(buf, ptr, len) > len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||||
|
{
|
||||||
|
mode_t omask;
|
||||||
|
int uid, gid;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
omask = umask(0);
|
||||||
|
|
||||||
|
uid = getuser(user, NULL);
|
||||||
|
if (uid >= 0) {
|
||||||
|
gid = getgroup(group);
|
||||||
|
if (gid < 0)
|
||||||
|
gid = 0;
|
||||||
|
|
||||||
|
rc = makedir(dir, mode);
|
||||||
|
if (chown(dir, uid, gid))
|
||||||
|
_pe("Failed chown(%s, %d, %d)", dir, uid, gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
umask(omask);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
void set_hostname(char **hostname)
|
void set_hostname(char **hostname)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@@ -438,8 +499,10 @@ void set_hostname(char **hostname)
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (*hostname)
|
if (*hostname) {
|
||||||
(void)sethostname(*hostname, strlen(*hostname));
|
if (sethostname(*hostname, strlen(*hostname)))
|
||||||
|
_pe("Failed sethostnaem(%s)", *hostname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -477,6 +540,7 @@ void networking(int updown)
|
|||||||
run("ifdown -a");
|
run("ifdown -a");
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
cgroup_service("network", pid, NULL);
|
||||||
|
|
||||||
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
|
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-1
@@ -26,6 +26,7 @@
|
|||||||
#define FINIT_HELPERS_H_
|
#define FINIT_HELPERS_H_
|
||||||
|
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
|
#include <sched.h> /* sched_yield() */
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -70,7 +71,7 @@ void show_progress (pstyle_t style);
|
|||||||
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 sh (char *tty);
|
||||||
|
|
||||||
int stty (int fd, speed_t speed);
|
void stty (int fd, speed_t speed);
|
||||||
speed_t stty_parse_speed(char *baud);
|
speed_t stty_parse_speed(char *baud);
|
||||||
|
|
||||||
void print_banner (const char *heading);
|
void print_banner (const char *heading);
|
||||||
@@ -81,6 +82,12 @@ 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);
|
||||||
|
|
||||||
|
int getcuser (char *buf, size_t len);
|
||||||
|
int getcgroup (char *buf, size_t len);
|
||||||
|
|
||||||
|
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
|
||||||
|
|
||||||
void set_hostname (char **hostname);
|
void set_hostname (char **hostname);
|
||||||
void networking (int updown);
|
void networking (int updown);
|
||||||
|
|
||||||
@@ -103,6 +110,24 @@ static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int dprint(int fd, const char *s, size_t len)
|
||||||
|
{
|
||||||
|
size_t loop = 3;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
len = strlen(s);
|
||||||
|
|
||||||
|
while (loop--) {
|
||||||
|
rc = write(fd, s, len);
|
||||||
|
if (rc == -1 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int ismnt (char *file, char *dir, char *mode);
|
int ismnt (char *file, char *dir, char *mode);
|
||||||
int fismnt (char *dir);
|
int fismnt (char *dir);
|
||||||
|
|
||||||
|
|||||||
+423
-275
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
/* New client tool, replaces old /dev/initctl API and telinit tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2021 Joachim Wiberg <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_INITCTL_H_
|
||||||
|
#define FINIT_INITCTL_H_
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
extern int icreate; /* initctl -c */
|
||||||
|
extern int iforce; /* initctl -f */
|
||||||
|
extern int ionce; /* initctl -1 */
|
||||||
|
extern int heading; /* initctl -t */
|
||||||
|
extern int verbose; /* initctl -v */
|
||||||
|
extern int plain; /* initctl -p */
|
||||||
|
|
||||||
|
extern void print_header(const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif /* FINIT_INITCTL_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
+20
-3
@@ -81,10 +81,10 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!fexist(file)) {
|
if (!fexist(file)) {
|
||||||
_d("No such file or directory, skipping %s", file);
|
_d("skipping %s: no such file or directory", file);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
_d("Adding new watcher for path %s", file);
|
_d("adding new watcher for path %s", file);
|
||||||
|
|
||||||
path = strdup(file);
|
path = strdup(file);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
@@ -94,7 +94,24 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
|||||||
|
|
||||||
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
|
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
|
||||||
if (wd < 0) {
|
if (wd < 0) {
|
||||||
_pe("Failed adding watcher for %s", path);
|
switch (errno) {
|
||||||
|
case EEXIST:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EINVAL:
|
||||||
|
/*
|
||||||
|
* some older kernels, notably 4.19, don't like
|
||||||
|
* inotify on top-level cgroups in cgroup v2
|
||||||
|
*/
|
||||||
|
if (!strncmp(path, "/sys/fs/cgroup", 14))
|
||||||
|
break; /* ignore error */
|
||||||
|
/* fallthrough */
|
||||||
|
|
||||||
|
default:
|
||||||
|
_pe("Failed adding watcher for %s", path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
free(path);
|
free(path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
+278
@@ -0,0 +1,278 @@
|
|||||||
|
/* Listens to kernel events like AC power status and manages sys/ conditions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Joachim Wiberg <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 <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
|
#include "lite/lite.h"
|
||||||
|
|
||||||
|
#include "cond.h"
|
||||||
|
#include "pid.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define _PATH_SYSFS_PWR "/sys/class/power_supply"
|
||||||
|
|
||||||
|
static int num_ac_online;
|
||||||
|
static int num_ac;
|
||||||
|
|
||||||
|
int debug; /* debug in other modules as well */
|
||||||
|
|
||||||
|
void logit(int prio, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
if (!debug)
|
||||||
|
vsyslog(prio, fmt, ap);
|
||||||
|
else {
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
#define panic(fmt, args...) { logit(LOG_CRIT, fmt ":%s", ##args, strerror(errno)); exit(1); }
|
||||||
|
#define warn(fmt, args...) { logit(LOG_WARNING, fmt ":%s", ##args, strerror(errno)); }
|
||||||
|
|
||||||
|
static void sys_cond(char *cond, int set)
|
||||||
|
{
|
||||||
|
char oneshot[256];
|
||||||
|
|
||||||
|
snprintf(oneshot, sizeof(oneshot), "%s/%s", _PATH_CONDSYS, cond);
|
||||||
|
if (set) {
|
||||||
|
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
|
||||||
|
warn("failed asserting sys/%s", cond);
|
||||||
|
} else {
|
||||||
|
if (erase(oneshot) && errno != ENOENT)
|
||||||
|
warn("failed asserting sys/%s", cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fgetln(char *path, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(path, "r");
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!fgets(buf, len, fp)) {
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chomp(buf);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_online(char *online)
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
|
||||||
|
if (!online)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
val = atoi(online);
|
||||||
|
logit(LOG_INFO, "AC %s", val ? "connected" : "disconnected");
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_ac(char *type)
|
||||||
|
{
|
||||||
|
char *types[] = {
|
||||||
|
"Mains",
|
||||||
|
"USB",
|
||||||
|
"BrickID",
|
||||||
|
"Wireless",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; types[i]; i++) {
|
||||||
|
if (!strncmp(type, types[i], strlen(types[i])))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(void)
|
||||||
|
{
|
||||||
|
struct dirent **d = NULL;
|
||||||
|
char *cond_dirs[] = {
|
||||||
|
_PATH_CONDSYS,
|
||||||
|
_PATH_CONDSYS "/pwr",
|
||||||
|
};
|
||||||
|
char path[384];
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
|
||||||
|
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
|
||||||
|
_pe("Failed creating %s condition directory, %s", COND_SYS,
|
||||||
|
cond_dirs[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = scandir(_PATH_SYSFS_PWR, &d, NULL, alphasort);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
char *nm = d[i]->d_name;
|
||||||
|
char buf[10];
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/type", _PATH_SYSFS_PWR, nm);
|
||||||
|
if (!fgetln(path, buf, sizeof(buf)) && is_ac(buf)) {
|
||||||
|
num_ac++;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/online", _PATH_SYSFS_PWR, nm);
|
||||||
|
if (!fgetln(path, buf, sizeof(buf))) {
|
||||||
|
if (check_online(buf))
|
||||||
|
num_ac_online++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(d[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0)
|
||||||
|
free(d);
|
||||||
|
|
||||||
|
/* if any power_supply is online, or none can be found */
|
||||||
|
if (num_ac == 0 || num_ac_online > 0)
|
||||||
|
sys_cond("pwr/ac", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Started by Finit as soon as possible when base filesystem is up,
|
||||||
|
* modules have been probed, or insmodded from /etc/finit.conf, so by
|
||||||
|
* now we should have /sys/class/power_supply/ available for probing.
|
||||||
|
* If none is found we assert /sys/pwr/ac condition anyway, this is what
|
||||||
|
* systemd does (ConditionACPower) and also makes most sense.
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct sockaddr_nl nls = { 0 };
|
||||||
|
struct pollfd pfd;
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
if (!strcmp(argv[1], "-d"))
|
||||||
|
debug = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!debug) {
|
||||||
|
openlog("keventd", LOG_CONS | LOG_PID, LOG_DAEMON);
|
||||||
|
setlogmask(LOG_UPTO(LOG_NOTICE));
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||||
|
if (pfd.fd == -1)
|
||||||
|
panic("failed creating netlink socket");
|
||||||
|
|
||||||
|
nls.nl_family = AF_NETLINK;
|
||||||
|
nls.nl_pid = 0;
|
||||||
|
nls.nl_groups = -1;
|
||||||
|
if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
|
||||||
|
panic("bind failed");
|
||||||
|
|
||||||
|
logit(LOG_DEBUG, "Waiting for events ...");
|
||||||
|
while (-1 != poll(&pfd, 1, -1)) {
|
||||||
|
char *path = NULL;
|
||||||
|
int ac = 0;
|
||||||
|
int i, len;
|
||||||
|
|
||||||
|
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||||
|
if (len == -1) {
|
||||||
|
switch (errno) {
|
||||||
|
case EINTR:
|
||||||
|
continue;
|
||||||
|
case ENOBUFS:
|
||||||
|
warn("lost events");
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
panic("unhandled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip libusb events, focus on kernel events/changes */
|
||||||
|
if (strncmp(buf, "change@", 7))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* XXX: currently limited to monitoring this subsystem */
|
||||||
|
if (!strstr(buf, "power_supply"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < len) {
|
||||||
|
char *line = buf + i;
|
||||||
|
|
||||||
|
logit(LOG_DEBUG, "%s", line);
|
||||||
|
if (!strncmp(line, "DEVPATH=", 8)) {
|
||||||
|
path = &line[8];
|
||||||
|
logit(LOG_DEBUG, "Got path %s", path);
|
||||||
|
} else if (!strncmp(line, "POWER_SUPPLY_TYPE=", 18)) {
|
||||||
|
ac = is_ac(&line[18]);
|
||||||
|
} else if (!strncmp(line, "POWER_SUPPLY_ONLINE=", 20) && ac) {
|
||||||
|
if (check_online(&line[20])) {
|
||||||
|
if (!num_ac_online)
|
||||||
|
sys_cond("pwr/ac", 1);
|
||||||
|
num_ac_online++;
|
||||||
|
} else {
|
||||||
|
if (num_ac_online > 0)
|
||||||
|
num_ac_online--;
|
||||||
|
if (!num_ac_online)
|
||||||
|
sys_cond("pwr/ac", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += strlen(line) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(pfd.fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -50,7 +50,7 @@ void log_exit(void)
|
|||||||
* terminal may have been resized at runtime
|
* terminal may have been resized at runtime
|
||||||
*/
|
*/
|
||||||
if (!debug)
|
if (!debug)
|
||||||
screen_init();
|
ttinit();
|
||||||
|
|
||||||
enable_progress(1);
|
enable_progress(1);
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ void log_debug(void)
|
|||||||
loglevel = LOG_DEBUG;
|
loglevel = LOG_DEBUG;
|
||||||
} else {
|
} else {
|
||||||
loglevel = LOG_NOTICE;
|
loglevel = LOG_NOTICE;
|
||||||
screen_init();
|
ttinit();
|
||||||
}
|
}
|
||||||
log_open();
|
log_open();
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ int logrotate(char *file, int num, off_t sz)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cnt == 2 && fexist(nfile)) {
|
if (cnt == 2 && fexist(nfile)) {
|
||||||
if (systemf("gzip %s", nfile))
|
if (systemf("gzip %s 2>/dev/null", nfile))
|
||||||
continue; /* no gzip, probably */
|
continue; /* no gzip, probably */
|
||||||
|
|
||||||
(void)remove(nfile);
|
(void)remove(nfile);
|
||||||
|
|||||||
@@ -95,18 +95,15 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
|||||||
|
|
||||||
char *pid_file(svc_t *svc)
|
char *pid_file(svc_t *svc)
|
||||||
{
|
{
|
||||||
static char path[256];
|
|
||||||
char fn[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
if (svc->pidfile[0]) {
|
if (svc->pidfile[0]) {
|
||||||
if (svc->pidfile[0] == '!')
|
if (svc->pidfile[0] == '!')
|
||||||
return &svc->pidfile[1];
|
return &svc->pidfile[1];
|
||||||
return svc->pidfile;
|
return svc->pidfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
errno = ENOENT;
|
||||||
|
|
||||||
return pid_runpath(fn, path, sizeof(path));
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t pid_file_read(const char *fn)
|
pid_t pid_file_read(const char *fn)
|
||||||
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
|
|||||||
return fclose(fp);
|
return fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pid_realpath(svc_t *svc, char *file)
|
int pid_file_set(svc_t *svc, char *file, int not)
|
||||||
{
|
{
|
||||||
int not = 0;
|
if (!file) {
|
||||||
|
|
||||||
if (!file)
|
|
||||||
file = pid_file(svc);
|
file = pid_file(svc);
|
||||||
|
if (!file)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (file[0] == '!') {
|
if (file[0] == '!') {
|
||||||
not = 1;
|
not = 1;
|
||||||
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
|
|||||||
|
|
||||||
arg += 4;
|
arg += 4;
|
||||||
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
||||||
return pid_realpath(svc, arg);
|
return pid_file_set(svc, arg, 0);
|
||||||
|
|
||||||
if (arg[0] == '!') {
|
if (arg[0] == '!') {
|
||||||
arg++;
|
arg++;
|
||||||
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
|
|||||||
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
||||||
strlcat(path, ".pid", sizeof(path));
|
strlcat(path, ".pid", sizeof(path));
|
||||||
|
|
||||||
return pid_realpath(svc, path);
|
return pid_file_set(svc, path, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 'pid' arg, no argument following */
|
/* 'pid' arg, no argument following */
|
||||||
if (!strcmp(arg, "pid"))
|
if (!strcmp(arg, "pid"))
|
||||||
return pid_realpath(svc, NULL);
|
return pid_file_set(svc, NULL, 0);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ int pid_alive (pid_t pid);
|
|||||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||||
|
|
||||||
char *pid_file (svc_t *svc);
|
char *pid_file (svc_t *svc);
|
||||||
|
int pid_file_set (svc_t *svc, char *file, int not);
|
||||||
pid_t pid_file_read (const char *fn);
|
pid_t pid_file_read (const char *fn);
|
||||||
int pid_file_create (svc_t *svc);
|
int pid_file_create (svc_t *svc);
|
||||||
int pid_file_parse (svc_t *svc, char *arg);
|
int pid_file_parse (svc_t *svc, char *arg);
|
||||||
|
|||||||
+1
-1
@@ -196,7 +196,7 @@ void plugin_run_hook(hook_point_t no, void *arg)
|
|||||||
|
|
||||||
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 ?: "NIL");
|
||||||
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -206,7 +206,7 @@ int reboot_main(int argc, char *argv[])
|
|||||||
|
|
||||||
case CMD_HALT:
|
case CMD_HALT:
|
||||||
if (do_halt(NULL))
|
if (do_halt(NULL))
|
||||||
do_kill(SIGUSR1, msg);
|
do_kill(SIGUSR2, msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_POWEROFF:
|
case CMD_POWEROFF:
|
||||||
|
|||||||
+1
-2
@@ -1,3 +1,2 @@
|
|||||||
runlevel 1
|
runlevel 1
|
||||||
hostname rescue
|
tty [12345] rescue
|
||||||
tty [12345] @console noclear nologin
|
|
||||||
|
|||||||
+21
-3
@@ -1,4 +1,12 @@
|
|||||||
# sample.conf: template run/task/service stanza for finit
|
# sample.conf: template cgroup/run/task/service stanza for finit
|
||||||
|
#
|
||||||
|
# The top-level cgroups can be defined anywhere, but share the same
|
||||||
|
# namespace. It is up to the user to ensure groups are defined only
|
||||||
|
# once, otherwise the last read .conf wins. See below for assigning
|
||||||
|
# run/task/services to cgroups. The below example creates the cgroup
|
||||||
|
# NAME with the following cpu and memory settings:
|
||||||
|
#
|
||||||
|
# cgroup NAME cpu.weight:1000 mem.max:65
|
||||||
#
|
#
|
||||||
# The one-shot commands 'run' and 'task' are called only once per
|
# The one-shot commands 'run' and 'task' are called only once per
|
||||||
# runlevel. Finit does not restart them when they exit.
|
# runlevel. Finit does not restart them when they exit.
|
||||||
@@ -25,6 +33,16 @@
|
|||||||
# | | `--------------------------------------------------------------- Optional conditions
|
# | | `--------------------------------------------------------------- Optional conditions
|
||||||
# | `---------------------------------------------------------------------- Optional Runlevels
|
# | `---------------------------------------------------------------------- Optional Runlevels
|
||||||
# `------------------------------------------------------------------------------ Monitored application
|
# `------------------------------------------------------------------------------ Monitored application
|
||||||
|
#
|
||||||
|
# Each stanza can also hold a 'cgroup' argument, or be prefixed with
|
||||||
|
# cgroup.NAME to place all following stanzas in the same group. In
|
||||||
|
# the following example, foo and bar share the cgroup foo but baz
|
||||||
|
# runs in its own cgroup baz:
|
||||||
|
#
|
||||||
|
# cgroup.foo
|
||||||
|
# service cgroup:cpu.weight:250,mem.max:655350 foo args -- foo desc
|
||||||
|
# service cgroup:cpu.weight:150,mem.max:655350 bar args -- bar desc
|
||||||
|
# service cgroup.baz:cpu.weight:300 baz args -- baz desc
|
||||||
|
|
||||||
# Debian GNU/Linux: start SSH daemon as soon as basic networking comes up
|
# Debian GNU/Linux: start SSH daemon as soon as basic networking comes up
|
||||||
#service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
|
#service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
|
||||||
@@ -33,11 +51,11 @@
|
|||||||
#task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
|
#task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
|
||||||
|
|
||||||
# Alpine Linux: start SSH daemon with $DROPBEAR_OPTS from /etc/conf.d
|
# Alpine Linux: start SSH daemon with $DROPBEAR_OPTS from /etc/conf.d
|
||||||
#service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
|
#service [2345] cgroup.user env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
|
||||||
|
|
||||||
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
||||||
# Depends on syslogd having started. Redirect any output to log.
|
# Depends on syslogd having started. Redirect any output to log.
|
||||||
#service [2345] <pid/syslogd> log acpid -f -- ACPI daemon
|
#service [2345] <pid/syslogd> cgroup:mem.max:32000,cpu.max:1000 log acpid -f -- ACPI daemon
|
||||||
|
|
||||||
# Start rsyslogd as soon as possible, should always run
|
# Start rsyslogd as soon as possible, should always run
|
||||||
# Provides pid/syslogd condition
|
# Provides pid/syslogd condition
|
||||||
|
|||||||
+65
-20
@@ -30,10 +30,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <lite/lite.h>
|
#include <lite/lite.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "initctl.h"
|
||||||
|
|
||||||
extern int icreate; /* initctl -c */
|
|
||||||
extern int iforce; /* initctl -f */
|
|
||||||
|
|
||||||
|
|
||||||
static int calc_width(char *arr[], size_t len)
|
static int calc_width(char *arr[], size_t len)
|
||||||
@@ -53,13 +50,32 @@ static int calc_width(char *arr[], size_t len)
|
|||||||
|
|
||||||
static void do_list(const char *path)
|
static void do_list(const char *path)
|
||||||
{
|
{
|
||||||
char buf[screen_cols];
|
static int once = 0;
|
||||||
int width, num, prev;
|
int width, num, prev;
|
||||||
glob_t gl;
|
glob_t gl;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (chdir(path))
|
if (chdir(path)) {
|
||||||
|
char *dir, *ptr;
|
||||||
|
|
||||||
|
if (!fexist(path))
|
||||||
|
return;
|
||||||
|
dir = strdupa(path);
|
||||||
|
if (!dir)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ptr = strrchr(dir, '/');
|
||||||
|
if (ptr)
|
||||||
|
*ptr++ = 0;
|
||||||
|
else
|
||||||
|
ptr = dir;
|
||||||
|
|
||||||
|
if (heading)
|
||||||
|
print_header("%s%s ", once ? "\n" : "", dir);
|
||||||
|
puts(ptr);
|
||||||
|
puts("");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (glob("*.conf", 0, NULL, &gl))
|
if (glob("*.conf", 0, NULL, &gl))
|
||||||
return;
|
return;
|
||||||
@@ -67,15 +83,33 @@ static void do_list(const char *path)
|
|||||||
if (gl.gl_pathc <= 0)
|
if (gl.gl_pathc <= 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "%s ", path);
|
if (plain) {
|
||||||
printheader(NULL, buf, 0);
|
if (heading)
|
||||||
|
print_header("%s%s ", once ? "\n" : "", path);
|
||||||
|
once++;
|
||||||
|
|
||||||
|
for (i = 0; i < gl.gl_pathc; i++) {
|
||||||
|
if (!heading) {
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
|
||||||
|
puts(buf);
|
||||||
|
} else
|
||||||
|
puts(gl.gl_pathv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heading)
|
||||||
|
print_header("%s ", path);
|
||||||
|
|
||||||
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
width = calc_width(gl.gl_pathv, gl.gl_pathc);
|
||||||
if (width <= 0)
|
if (width <= 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
num = (screen_cols - 2) / width;
|
num = (ttcols - 2) / width;
|
||||||
if ((num - 1) * 2 + num * width > screen_cols)
|
if ((num - 1) * 2 + num * width > ttcols)
|
||||||
num--;
|
num--;
|
||||||
|
|
||||||
prev = 0;
|
prev = 0;
|
||||||
@@ -120,15 +154,19 @@ int serv_list(char *arg)
|
|||||||
if (fisdir(FINIT_RCSD))
|
if (fisdir(FINIT_RCSD))
|
||||||
do_list(FINIT_RCSD);
|
do_list(FINIT_RCSD);
|
||||||
|
|
||||||
|
if (fexist(FINIT_CONF))
|
||||||
|
do_list(FINIT_CONF);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return path to configuration file for 'name', relative to FINIT_RCSD.
|
* Return path to configuration file for 'name'. This may be any of the
|
||||||
* This may be any of the following, provided sysconfdir is /etc:
|
* following, provided sysconfdir is /etc:
|
||||||
*
|
*
|
||||||
* - /etc/finit.d/$name.conf
|
|
||||||
* - /etc/finit.d/available/$name.conf
|
* - /etc/finit.d/available/$name.conf
|
||||||
|
* - /etc/finit.d/$name.conf
|
||||||
|
* - /etc/finit.conf
|
||||||
*
|
*
|
||||||
* The system *may* have a /etc/finit.d/available/ directory, or it may
|
* The system *may* have a /etc/finit.d/available/ directory, or it may
|
||||||
* just use a plain /etc/finit.d/ -- we do not set policy.
|
* just use a plain /etc/finit.d/ -- we do not set policy.
|
||||||
@@ -140,7 +178,7 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
|||||||
{
|
{
|
||||||
char corr[40];
|
char corr[40];
|
||||||
|
|
||||||
if (!name || !name[0]) {
|
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
|
||||||
strlcpy(path, FINIT_CONF, len);
|
strlcpy(path, FINIT_CONF, len);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -162,6 +200,10 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
|||||||
} else
|
} else
|
||||||
strlcat(path, name, len);
|
strlcat(path, name, len);
|
||||||
|
|
||||||
|
/* fall back to static service unless edit/create */
|
||||||
|
if (!creat && !fexist(path))
|
||||||
|
paste(path, len, FINIT_RCSD, name);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +257,8 @@ int do_disable(char *arg, int check)
|
|||||||
|
|
||||||
if (chdir(FINIT_RCSD))
|
if (chdir(FINIT_RCSD))
|
||||||
err(1, "failed cd %s", FINIT_RCSD);
|
err(1, "failed cd %s", FINIT_RCSD);
|
||||||
(void)chdir("enabled"); /* System *may* have enabled/ dir. */
|
if (chdir("enabled")) /* System *may* have enabled/ dir. */
|
||||||
|
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
|
||||||
|
|
||||||
if (check && stat(arg, &st))
|
if (check && stat(arg, &st))
|
||||||
errx(1, "%s not (an) enabled (service).", arg);
|
errx(1, "%s not (an) enabled (service).", arg);
|
||||||
@@ -300,7 +343,7 @@ static int do_edit(char *arg, int creat)
|
|||||||
if (!fexist(fn)) {
|
if (!fexist(fn)) {
|
||||||
if (!creat) {
|
if (!creat) {
|
||||||
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
|
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
|
||||||
return serv_list("available");
|
return serv_list(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SAMPLE_CONF
|
#ifdef SAMPLE_CONF
|
||||||
@@ -322,8 +365,8 @@ static int do_edit(char *arg, int creat)
|
|||||||
int serv_edit(char *arg)
|
int serv_edit(char *arg)
|
||||||
{
|
{
|
||||||
if (!arg || !arg[0]) {
|
if (!arg || !arg[0]) {
|
||||||
warnx("missing argument to edit, may be one of:");
|
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF))
|
||||||
return serv_list("available");
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return do_edit(arg, icreate);
|
return do_edit(arg, icreate);
|
||||||
@@ -367,8 +410,10 @@ int serv_delete(char *arg)
|
|||||||
char buf[256];
|
char buf[256];
|
||||||
char *fn;
|
char *fn;
|
||||||
|
|
||||||
if (!arg || !arg[0])
|
if (!arg || !arg[0]) {
|
||||||
errx(1, "missing argument to delete");
|
warnx("missing argument to delete, may be one of:");
|
||||||
|
return serv_list("available");
|
||||||
|
}
|
||||||
|
|
||||||
fn = conf(buf, sizeof(buf), arg, 0);
|
fn = conf(buf, sizeof(buf), arg, 0);
|
||||||
if (!fn)
|
if (!fn)
|
||||||
|
|||||||
+450
-140
@@ -25,7 +25,6 @@
|
|||||||
#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/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
@@ -49,8 +48,6 @@
|
|||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
#include "schedule.h"
|
#include "schedule.h"
|
||||||
|
|
||||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
|
||||||
|
|
||||||
static struct wq work = {
|
static struct wq work = {
|
||||||
.cb = service_worker,
|
.cb = service_worker,
|
||||||
};
|
};
|
||||||
@@ -198,6 +195,8 @@ static int lredirect(svc_t *svc)
|
|||||||
char *tag = basename(svc->cmd);
|
char *tag = basename(svc->cmd);
|
||||||
char *prio = "daemon.info";
|
char *prio = "daemon.info";
|
||||||
|
|
||||||
|
sched_yield();
|
||||||
|
|
||||||
fds = open(ptsname(fd), O_RDONLY);
|
fds = open(ptsname(fd), O_RDONLY);
|
||||||
close(fd);
|
close(fd);
|
||||||
if (fds == -1)
|
if (fds == -1)
|
||||||
@@ -207,8 +206,8 @@ static int lredirect(svc_t *svc)
|
|||||||
/* Reset signals */
|
/* Reset signals */
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
if (!whichp(LOGIT_PATH)) {
|
if (!whichp(_PATH_LOGIT)) {
|
||||||
logit(LOG_INFO, LOGIT_PATH " missing, using syslog for %s instead", svc->name);
|
logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
|
||||||
fallback_logger(tag, prio);
|
fallback_logger(tag, prio);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
@@ -219,7 +218,7 @@ static int lredirect(svc_t *svc)
|
|||||||
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
|
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
|
||||||
snprintf(num, sizeof(num), "%d", logfile_count_max);
|
snprintf(num, sizeof(num), "%d", logfile_count_max);
|
||||||
|
|
||||||
execlp(LOGIT_PATH, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
|
execlp(_PATH_LOGIT, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +227,7 @@ static int lredirect(svc_t *svc)
|
|||||||
if (svc->log.prio[0])
|
if (svc->log.prio[0])
|
||||||
prio = svc->log.prio;
|
prio = svc->log.prio;
|
||||||
|
|
||||||
execlp(LOGIT_PATH, "logit", "-t", tag, "-p", prio, NULL);
|
execlp(_PATH_LOGIT, "logit", "-t", tag, "-p", prio, NULL);
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,37 +261,6 @@ static int redirect(svc_t *svc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* non-zero env, no checking if file exists or not
|
|
||||||
*/
|
|
||||||
static char *svc_getenv(svc_t *svc)
|
|
||||||
{
|
|
||||||
int v = 0;
|
|
||||||
|
|
||||||
if (!svc->env[0])
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (svc->env[0] == '-')
|
|
||||||
v = 1;
|
|
||||||
|
|
||||||
return &svc->env[v];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if svc has env, if env file exists returns true
|
|
||||||
* if it doesn't exist, but has '-', also true. if svc
|
|
||||||
* doesn't have env, also true.
|
|
||||||
*/
|
|
||||||
static int check_env(svc_t *svc)
|
|
||||||
{
|
|
||||||
char *env = svc_getenv(svc);
|
|
||||||
|
|
||||||
if (!env)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return fexist(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Source environment file, if it exists
|
* Source environment file, if it exists
|
||||||
* Note: must be called from privsepped child
|
* Note: must be called from privsepped child
|
||||||
@@ -309,7 +277,7 @@ static void source_env(svc_t *svc)
|
|||||||
if (!fn)
|
if (!fn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Warning in service_start() after check_env() */
|
/* Warning in service_start() after svc_checkenv() */
|
||||||
fp = fopen(fn, "r");
|
fp = fopen(fn, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return;
|
return;
|
||||||
@@ -384,6 +352,84 @@ static int is_norespawn(void)
|
|||||||
fexist("/tmp/norespawn");
|
fexist("/tmp/norespawn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* used for process group name, derived from originating filename,
|
||||||
|
* so to group multiple services, place them in the same .conf
|
||||||
|
*/
|
||||||
|
static char *group_name(svc_t *svc, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (!svc->file[0])
|
||||||
|
return svc_ident(svc, buf, len);
|
||||||
|
|
||||||
|
ptr = strrchr(svc->file, '/');
|
||||||
|
if (ptr)
|
||||||
|
ptr++;
|
||||||
|
else
|
||||||
|
ptr = svc->file;
|
||||||
|
|
||||||
|
strlcpy(buf, ptr, len);
|
||||||
|
ptr = strstr(buf, ".conf");
|
||||||
|
if (ptr)
|
||||||
|
*ptr = 0;
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t service_fork(svc_t *svc)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
char *home = NULL;
|
||||||
|
#ifdef ENABLE_STATIC
|
||||||
|
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
|
||||||
|
int gid = 0;
|
||||||
|
#else
|
||||||
|
int uid = getuser(svc->username, &home);
|
||||||
|
int gid = getgroup(svc->group);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sched_yield();
|
||||||
|
|
||||||
|
/* Set configured limits */
|
||||||
|
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||||
|
if (setrlimit(i, &svc->rlimit[i]) == -1)
|
||||||
|
logit(LOG_WARNING,
|
||||||
|
"%s: rlimit: Failed setting %s",
|
||||||
|
svc->cmd, rlim2str(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set desired user+group */
|
||||||
|
if (gid >= 0) {
|
||||||
|
if (setgid(gid))
|
||||||
|
_pe("%s: failed setgid(%d)", svc->cmd, gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uid >= 0) {
|
||||||
|
if (setuid(uid))
|
||||||
|
_pe("%s: failed setuid(%d)", svc->cmd, uid);
|
||||||
|
|
||||||
|
/* Set default path for regular users */
|
||||||
|
if (uid > 0)
|
||||||
|
setenv("PATH", _PATH_DEFPATH, 1);
|
||||||
|
if (home) {
|
||||||
|
setenv("HOME", home, 1);
|
||||||
|
if (chdir(home)) {
|
||||||
|
if (chdir("/"))
|
||||||
|
_pe("%s: failed chdir(%s) and chdir(/)", svc->cmd, home);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Source any environment from env:/path/to/file */
|
||||||
|
source_env(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* service_start - Start service
|
* service_start - Start service
|
||||||
* @svc: Service to start
|
* @svc: Service to start
|
||||||
@@ -395,6 +441,7 @@ static int service_start(svc_t *svc)
|
|||||||
{
|
{
|
||||||
int result = 0, do_progress = 1;
|
int result = 0, do_progress = 1;
|
||||||
sigset_t nmask, omask;
|
sigset_t nmask, omask;
|
||||||
|
char grnam[80];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
@@ -407,17 +454,28 @@ static int service_start(svc_t *svc)
|
|||||||
|
|
||||||
/* Don't try and start service if it doesn't exist. */
|
/* Don't try and start service if it doesn't exist. */
|
||||||
if (!whichp(svc->cmd)) {
|
if (!whichp(svc->cmd)) {
|
||||||
print(1, "%s: does not exist", svc->cmd);
|
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
|
||||||
svc_missing(svc);
|
svc_missing(svc);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
||||||
if (!check_env(svc)) {
|
if (!svc_checkenv(svc)) {
|
||||||
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
|
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
|
||||||
|
svc_missing(svc);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (svc_is_tty(svc) && !svc->notty) {
|
||||||
|
char *dev = tty_canonicalize(svc->dev);
|
||||||
|
|
||||||
|
if (!dev || !tty_exists(dev)) {
|
||||||
|
_d("TTY %s missing or invalid, halting service.", svc->dev);
|
||||||
|
svc_missing(svc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (svc_is_sysv(svc))
|
if (svc_is_sysv(svc))
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
|
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
|
||||||
|
|
||||||
@@ -439,48 +497,10 @@ static int service_start(svc_t *svc)
|
|||||||
sigaddset(&nmask, SIGCHLD);
|
sigaddset(&nmask, SIGCHLD);
|
||||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||||
|
|
||||||
pid = fork();
|
pid = service_fork(svc);
|
||||||
cgroup_service(svc->name, svc->id, pid);
|
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
int status;
|
|
||||||
char *home = NULL;
|
|
||||||
#ifdef ENABLE_STATIC
|
|
||||||
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
|
|
||||||
int gid = 0;
|
|
||||||
#else
|
|
||||||
int uid = getuser(svc->username, &home);
|
|
||||||
int gid = getgroup(svc->group);
|
|
||||||
#endif
|
|
||||||
char *args[MAX_NUM_SVC_ARGS + 1];
|
char *args[MAX_NUM_SVC_ARGS + 1];
|
||||||
|
int status;
|
||||||
/* Set configured limits */
|
|
||||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
|
||||||
if (setrlimit(i, &svc->rlimit[i]) == -1)
|
|
||||||
logit(LOG_WARNING,
|
|
||||||
"%s: rlimit: Failed setting %s",
|
|
||||||
svc->cmd, rlim2str(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set desired user+group */
|
|
||||||
if (gid >= 0)
|
|
||||||
(void)setgid(gid);
|
|
||||||
|
|
||||||
if (uid >= 0) {
|
|
||||||
(void)setuid(uid);
|
|
||||||
|
|
||||||
/* Set default path for regular users */
|
|
||||||
if (uid > 0)
|
|
||||||
setenv("PATH", _PATH_DEFPATH, 1);
|
|
||||||
if (home) {
|
|
||||||
setenv("HOME", home, 1);
|
|
||||||
if (chdir(home))
|
|
||||||
(void)chdir("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Source any environment from env:/path/to/file */
|
|
||||||
source_env(svc);
|
|
||||||
|
|
||||||
if (!svc_is_sysv(svc)) {
|
if (!svc_is_sysv(svc)) {
|
||||||
wordexp_t we = { 0 };
|
wordexp_t we = { 0 };
|
||||||
@@ -544,7 +564,8 @@ static int service_start(svc_t *svc)
|
|||||||
}
|
}
|
||||||
args[i] = NULL;
|
args[i] = NULL;
|
||||||
|
|
||||||
redirect(svc);
|
if (!svc_is_tty(svc))
|
||||||
|
redirect(svc);
|
||||||
sig_unblock();
|
sig_unblock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -562,6 +583,8 @@ static int service_start(svc_t *svc)
|
|||||||
|
|
||||||
if (svc_is_runtask(svc))
|
if (svc_is_runtask(svc))
|
||||||
status = exec_runtask(args[0], &args[1]);
|
status = exec_runtask(args[0], &args[1]);
|
||||||
|
else if (svc_is_tty(svc))
|
||||||
|
status = tty_exec(svc);
|
||||||
else
|
else
|
||||||
status = execvp(args[0], &args[1]);
|
status = execvp(args[0], &args[1]);
|
||||||
|
|
||||||
@@ -579,6 +602,11 @@ static int service_start(svc_t *svc)
|
|||||||
_d("Starting %s %s", svc->cmd, buf);
|
_d("Starting %s %s", svc->cmd, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (svc_is_tty(svc))
|
||||||
|
cgroup_user("getty", pid);
|
||||||
|
else
|
||||||
|
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid, &svc->cgroup);
|
||||||
|
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
|
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
|
||||||
|
|
||||||
svc->pid = pid;
|
svc->pid = pid;
|
||||||
@@ -647,12 +675,19 @@ static void service_cleanup(svc_t *svc)
|
|||||||
{
|
{
|
||||||
char *fn;
|
char *fn;
|
||||||
|
|
||||||
|
/* PID collected, cancel any pending SIGKILL */
|
||||||
|
service_timeout_cancel(svc);
|
||||||
|
|
||||||
fn = pid_file(svc);
|
fn = pid_file(svc);
|
||||||
if (remove(fn) && errno != ENOENT)
|
if (fn && remove(fn) && errno != ENOENT)
|
||||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
||||||
basename(svc->cmd), fn);
|
basename(svc->cmd), fn);
|
||||||
|
|
||||||
/* No longer running, update books. */
|
/* No longer running, update books. */
|
||||||
|
if (svc_is_tty(svc) && svc->pid > 1)
|
||||||
|
utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */
|
||||||
|
|
||||||
|
svc->oldpid = svc->pid;
|
||||||
svc->start_time = svc->pid = 0;
|
svc->start_time = svc->pid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,7 +731,7 @@ static int service_stop(svc_t *svc)
|
|||||||
if (svc->pid > 1) {
|
if (svc->pid > 1) {
|
||||||
/* Kill all children in the same proess group, e.g. logit */
|
/* Kill all children in the same proess group, e.g. logit */
|
||||||
rc = kill(-svc->pid, svc->sighalt);
|
rc = kill(-svc->pid, svc->sighalt);
|
||||||
|
_d("kill(-%d, %d) => rc %d", svc->pid, svc->sighalt, rc);
|
||||||
/* PID lost or forking process never really started */
|
/* PID lost or forking process never really started */
|
||||||
if (rc == -1 && ESRCH == errno)
|
if (rc == -1 && ESRCH == errno)
|
||||||
service_cleanup(svc);
|
service_cleanup(svc);
|
||||||
@@ -863,6 +898,30 @@ static void parse_env(svc_t *svc, char *env)
|
|||||||
strlcpy(svc->env, env, sizeof(svc->env));
|
strlcpy(svc->env, env, sizeof(svc->env));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_cgroup(svc_t *svc, char *cgroup)
|
||||||
|
{
|
||||||
|
char *ptr = cgroup;
|
||||||
|
|
||||||
|
if (!cgroup)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (cgroup[0] == '.') {
|
||||||
|
ptr = strchr(cgroup, ':');
|
||||||
|
if (ptr)
|
||||||
|
*ptr++ = 0;
|
||||||
|
strlcpy(svc->cgroup.name, &cgroup[1], sizeof(svc->cgroup.name));
|
||||||
|
if (!ptr)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(ptr) >= sizeof(svc->cgroup)) {
|
||||||
|
_e("%s: cgroup settings too long (>%d chars)", svc->cmd, sizeof(svc->cgroup));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(svc->cgroup.cfg, ptr, sizeof(svc->cgroup.cfg));
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_sighalt(svc_t *svc, char *arg)
|
static void parse_sighalt(svc_t *svc, char *arg)
|
||||||
{
|
{
|
||||||
int signo;
|
int signo;
|
||||||
@@ -889,6 +948,14 @@ static void parse_killdelay(svc_t *svc, char *delay)
|
|||||||
svc->killdelay = (int)(sec * 1000);
|
svc->killdelay = (int)(sec * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_script(char *type, char *script, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
if (access(script, X_OK))
|
||||||
|
logit(LOG_WARNING, "%s: %s:%s is missing or not executable, skipping.", type, script);
|
||||||
|
else
|
||||||
|
strlcpy(buf, script, len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* name:<name>
|
* name:<name>
|
||||||
*/
|
*/
|
||||||
@@ -904,13 +971,10 @@ static void parse_name(svc_t *svc, char *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
strlcpy(svc->name, name, sizeof(svc->name));
|
strlcpy(svc->name, name, sizeof(svc->name));
|
||||||
|
|
||||||
/* Warn if svc generates same condition as an existing service */
|
|
||||||
svc_validate(svc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* parse_cmdline_args - Update the command line args in the svc struct
|
* Update the command line args in the svc struct
|
||||||
*
|
*
|
||||||
* strtok internal pointer must be positioned at first command line arg
|
* strtok internal pointer must be positioned at first command line arg
|
||||||
* when this function is called.
|
* when this function is called.
|
||||||
@@ -919,23 +983,29 @@ static void parse_name(svc_t *svc, char *arg)
|
|||||||
*/
|
*/
|
||||||
static void parse_cmdline_args(svc_t *svc, char *cmd)
|
static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||||
{
|
{
|
||||||
|
int diff = 0;
|
||||||
char sep = 0;
|
char sep = 0;
|
||||||
char *arg;
|
char *arg;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
if (strcmp(svc->args[i], cmd))
|
||||||
|
diff++;
|
||||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
|
|
||||||
while (i < MAX_NUM_SVC_ARGS)
|
|
||||||
svc->args[i++][0] = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
|
* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
|
||||||
* array to be zero-terminated.
|
* array to be zero-terminated.
|
||||||
*/
|
*/
|
||||||
for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1);) {
|
while ((arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1)) {
|
||||||
|
char prev[sizeof(svc->args[0])] = { 0 };
|
||||||
char ch = arg[0];
|
char ch = arg[0];
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
|
if (!sep) {
|
||||||
|
strlcpy(prev, svc->args[i], sizeof(prev));
|
||||||
|
svc->args[i][0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX: ugly string arg re-concatenation, fixme */
|
/* XXX: ugly string arg re-concatenation, fixme */
|
||||||
if (ch == '"' || ch == '\'')
|
if (ch == '"' || ch == '\'')
|
||||||
sep = ch;
|
sep = ch;
|
||||||
@@ -952,6 +1022,13 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* replace any @console arg with the expanded device name */
|
||||||
|
if (svc_is_tty(svc) && tty_isatcon(svc->args[i]))
|
||||||
|
strlcpy(svc->args[i], svc->dev, sizeof(svc->args[i]));
|
||||||
|
|
||||||
|
if (strcmp(svc->args[i], prev))
|
||||||
|
diff++;
|
||||||
|
|
||||||
sep = 0;
|
sep = 0;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -960,15 +1037,22 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
|||||||
* Clear remaining args in case they were set earlier.
|
* Clear remaining args in case they were set earlier.
|
||||||
* This also zero-terminates the args array.
|
* This also zero-terminates the args array.
|
||||||
*/
|
*/
|
||||||
while (i < MAX_NUM_SVC_ARGS)
|
while (i < MAX_NUM_SVC_ARGS) {
|
||||||
svc->args[i++][0] = 0;
|
if (svc->args[i++][0]) {
|
||||||
#if 0
|
svc->args[i-1][0] = 0;
|
||||||
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
diff++;
|
||||||
if (!svc->args[i][0])
|
}
|
||||||
break;
|
|
||||||
_d("%s ", svc->args[i]);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
/*
|
||||||
|
* Check also for changes to /etc/default/foo, because this
|
||||||
|
* also constitutes changes to command line args.
|
||||||
|
*/
|
||||||
|
diff += conf_changed(svc_getenv(svc));
|
||||||
|
|
||||||
|
if (diff)
|
||||||
|
_d("Modified args for %s detected", cmd);
|
||||||
|
svc->args_dirty = (diff > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1032,7 +1116,10 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||||
char *username = NULL, *log = NULL, *pid = NULL;
|
char *username = NULL, *log = NULL, *pid = NULL;
|
||||||
char *name = NULL, *halt = NULL, *delay = NULL;
|
char *name = NULL, *halt = NULL, *delay = NULL;
|
||||||
char *id = NULL, *env = NULL;
|
char *id = NULL, *env = NULL, *cgroup = NULL;
|
||||||
|
char *pre_script = NULL, *post_script = NULL;
|
||||||
|
struct tty tty = { 0 };
|
||||||
|
char *dev = NULL;
|
||||||
int levels = 0;
|
int levels = 0;
|
||||||
int manual = 0;
|
int manual = 0;
|
||||||
char *line;
|
char *line;
|
||||||
@@ -1043,7 +1130,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
return errno = EINVAL;
|
return errno = EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
line = strdup(cfg);
|
line = strdupa(cfg);
|
||||||
if (!line)
|
if (!line)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -1065,15 +1152,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = strtok(line, " ");
|
cmd = strtok(line, " \t");
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
incomplete:
|
incomplete:
|
||||||
_e("Incomplete service '%s', cannot register", cfg);
|
_e("Incomplete service '%s', cannot register", cfg);
|
||||||
free(line);
|
|
||||||
return errno = ENOENT;
|
return errno = ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cmd) {
|
while (cmd) {
|
||||||
|
if (type == SVC_TYPE_TTY && cmd[0] == '@')
|
||||||
|
break; /* @console */
|
||||||
|
|
||||||
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] */
|
||||||
@@ -1094,8 +1183,16 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
halt = &cmd[5];
|
halt = &cmd[5];
|
||||||
else if (!strncasecmp(cmd, "kill:", 5))
|
else if (!strncasecmp(cmd, "kill:", 5))
|
||||||
delay = &cmd[5];
|
delay = &cmd[5];
|
||||||
|
else if (!strncasecmp(cmd, "pre:", 4))
|
||||||
|
pre_script = &cmd[4];
|
||||||
|
else if (!strncasecmp(cmd, "post:", 5))
|
||||||
|
post_script = &cmd[5];
|
||||||
else if (!strncasecmp(cmd, "env:", 4))
|
else if (!strncasecmp(cmd, "env:", 4))
|
||||||
env = &cmd[4];
|
env = &cmd[4];
|
||||||
|
else if (!strncasecmp(cmd, "cgroup:", 7))
|
||||||
|
cgroup = &cmd[7]; /* only settings */
|
||||||
|
else if (!strncasecmp(cmd, "cgroup.", 7))
|
||||||
|
cgroup = &cmd[6]; /* with group */
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1108,25 +1205,66 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
levels = conf_parse_runlevels(runlevels);
|
levels = conf_parse_runlevels(runlevels);
|
||||||
if (runlevel > 0 && !ISOTHER(levels, 0)) {
|
if (runlevel > 0 && !ISOTHER(levels, 0)) {
|
||||||
_d("Skipping %s, bootstrap is completed.", cmd);
|
_d("Skipping %s, bootstrap is completed.", cmd);
|
||||||
free(line);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
id = "";
|
id = "";
|
||||||
|
|
||||||
svc = svc_find(cmd, id);
|
if (type == SVC_TYPE_TTY) {
|
||||||
|
size_t i, len = 0;
|
||||||
|
|
||||||
|
if (tty_parse_args(cmd, &tty))
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
if (tty.cmd)
|
||||||
|
len += strlen(tty.cmd);
|
||||||
|
else
|
||||||
|
len += 3;
|
||||||
|
|
||||||
|
len += tty.num + 1;
|
||||||
|
for (i = 0; i < tty.num; i++)
|
||||||
|
len += strlen(tty.args[i]) + 1;
|
||||||
|
|
||||||
|
line = alloca(len);
|
||||||
|
if (!line)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
snprintf(line, len, "%s", tty.cmd ?: "tty");
|
||||||
|
for (i = 0; i < tty.num; i++) {
|
||||||
|
strlcat(line, " ", len);
|
||||||
|
strlcat(line, tty.args[i], len);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = strtok(line, " \t");
|
||||||
|
if (!cmd)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
if (tty_isatcon(tty.dev))
|
||||||
|
dev = tty_atcon();
|
||||||
|
else
|
||||||
|
dev = tty.dev;
|
||||||
|
next:
|
||||||
|
svc = svc_find_by_tty(dev);
|
||||||
|
} else
|
||||||
|
svc = svc_find(cmd, id);
|
||||||
|
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_d("Creating new svc for %s id #%s 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);
|
|
||||||
return errno = ENOMEM;
|
return errno = ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == SVC_TYPE_SERVICE && manual)
|
if (type == SVC_TYPE_SERVICE && manual)
|
||||||
svc_stop(svc);
|
svc_stop(svc);
|
||||||
|
} else {
|
||||||
|
/* update type, may have changed from service -> task */
|
||||||
|
svc->type = type;
|
||||||
|
|
||||||
|
/* e.g., if missing cmd or env before */
|
||||||
|
svc_unblock(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always clear svc PID file, for now. See TODO */
|
/* Always clear svc PID file, for now. See TODO */
|
||||||
@@ -1143,24 +1281,71 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
strlcpy(svc->group, ptr, sizeof(svc->group));
|
strlcpy(svc->group, ptr, sizeof(svc->group));
|
||||||
}
|
}
|
||||||
strlcpy(svc->username, username, sizeof(svc->username));
|
strlcpy(svc->username, username, sizeof(svc->username));
|
||||||
|
} else {
|
||||||
|
getcuser(svc->username, sizeof(svc->username));
|
||||||
|
getcgroup(svc->group, sizeof(svc->group));
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_cmdline_args(svc, cmd);
|
|
||||||
|
|
||||||
svc->runlevels = levels;
|
svc->runlevels = levels;
|
||||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
_d("Service %s runlevel 0x%02x", svc->cmd, svc->runlevels);
|
||||||
|
|
||||||
conf_parse_cond(svc, cond);
|
conf_parse_cond(svc, cond);
|
||||||
|
|
||||||
parse_name(svc, name);
|
if (type == SVC_TYPE_TTY) {
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (dev)
|
||||||
|
strlcpy(svc->dev, dev, sizeof(svc->dev));
|
||||||
|
if (tty.baud)
|
||||||
|
strlcpy(svc->baud, tty.baud, sizeof(svc->baud));
|
||||||
|
if (tty.term)
|
||||||
|
strlcpy(svc->term, tty.term, sizeof(svc->term));
|
||||||
|
svc->noclear = tty.noclear;
|
||||||
|
svc->nowait = tty.nowait;
|
||||||
|
svc->nologin = tty.nologin;
|
||||||
|
svc->notty = tty.notty;
|
||||||
|
svc->rescue = tty.rescue;
|
||||||
|
|
||||||
|
/* TTYs cannot be redirected */
|
||||||
|
log = NULL;
|
||||||
|
|
||||||
|
/* Create name:id tuple for identiy, e.g., tty:S0 */
|
||||||
|
ptr = strrchr(svc->dev, '/');
|
||||||
|
if (ptr) {
|
||||||
|
ptr++;
|
||||||
|
if (!strncmp(ptr, "tty", 3))
|
||||||
|
ptr += 3;
|
||||||
|
} else
|
||||||
|
ptr = svc->dev;
|
||||||
|
if (!id || id[0] == 0)
|
||||||
|
id = ptr;
|
||||||
|
strlcpy(svc->name, "tty", sizeof(svc->name));
|
||||||
|
strlcpy(svc->id, id, sizeof(svc->id));
|
||||||
|
} else
|
||||||
|
parse_name(svc, name);
|
||||||
|
|
||||||
|
parse_cmdline_args(svc, cmd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Warn if svc generates same condition (based on name:id)
|
||||||
|
* as an existing service.
|
||||||
|
*/
|
||||||
|
svc_validate(svc);
|
||||||
|
|
||||||
if (halt)
|
if (halt)
|
||||||
parse_sighalt(svc, halt);
|
parse_sighalt(svc, halt);
|
||||||
if (delay)
|
if (delay)
|
||||||
parse_killdelay(svc, delay);
|
parse_killdelay(svc, delay);
|
||||||
|
if (pre_script)
|
||||||
|
parse_script("pre", pre_script, svc->pre_script, sizeof(svc->pre_script));
|
||||||
|
if (post_script)
|
||||||
|
parse_script("post", post_script, svc->post_script, sizeof(svc->post_script));
|
||||||
if (log)
|
if (log)
|
||||||
parse_log(svc, log);
|
parse_log(svc, log);
|
||||||
if (desc)
|
if (desc)
|
||||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||||
|
else if (type == SVC_TYPE_TTY)
|
||||||
|
snprintf(svc->desc, sizeof(svc->desc), "Getty on %s", svc->dev);
|
||||||
if (env)
|
if (env)
|
||||||
parse_env(svc, env);
|
parse_env(svc, env);
|
||||||
if (file)
|
if (file)
|
||||||
@@ -1169,18 +1354,30 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
/* Set configured limits */
|
/* Set configured limits */
|
||||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||||
|
|
||||||
|
/* Seed with currently active group, may be empty */
|
||||||
|
strlcpy(svc->cgroup.name, cgroup_current, sizeof(svc->cgroup.name));
|
||||||
|
if (cgroup)
|
||||||
|
parse_cgroup(svc, cgroup);
|
||||||
|
|
||||||
/* New, recently modified or unchanged ... used on reload. */
|
/* New, recently modified or unchanged ... used on reload. */
|
||||||
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)))
|
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)))
|
||||||
svc_mark_dirty(svc);
|
svc_mark_dirty(svc);
|
||||||
else
|
else
|
||||||
svc_mark_clean(svc);
|
svc_mark_clean(svc);
|
||||||
|
|
||||||
|
svc_enable(svc);
|
||||||
|
|
||||||
/* for finit native services only, e.g. plugins/hotplug.c */
|
/* for finit native services only, e.g. plugins/hotplug.c */
|
||||||
if (!file)
|
if (!file)
|
||||||
svc->protect = 1;
|
svc->protect = 1;
|
||||||
|
|
||||||
/* Free duped line, from above */
|
/* continue expanding any 'tty @console ...' */
|
||||||
free(line);
|
if (tty_isatcon(tty.dev)) {
|
||||||
|
dev = tty_atcon();
|
||||||
|
if (dev)
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1208,36 +1405,47 @@ void service_monitor(pid_t lost, int status)
|
|||||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (tty_respawn(lost))
|
|
||||||
return;
|
|
||||||
|
|
||||||
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);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
|
switch (svc->state) {
|
||||||
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
|
case SVC_CLEANUP_STATE:
|
||||||
svc->status = status;
|
case SVC_SETUP_STATE:
|
||||||
|
_d("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d",
|
||||||
|
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script,
|
||||||
|
lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
|
||||||
|
kill(-svc->pid, SIGKILL);
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
|
||||||
|
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
|
||||||
|
svc->status = status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Forking sysv/services declare themselves with pid:!/path/to/pid.file */
|
/* Forking sysv/services declare themselves with pid:!/path/to/pid.file */
|
||||||
if (svc_is_starting(svc) && svc_is_forking(svc))
|
if (svc_is_starting(svc) && svc_is_forking(svc))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Terminate any children in the same proess group, e.g. logit */
|
||||||
|
kill(-svc->pid, SIGKILL);
|
||||||
|
|
||||||
/* 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) */
|
||||||
if (svc_is_daemon(svc)) {
|
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
|
||||||
service_cleanup(svc);
|
service_cleanup(svc);
|
||||||
} else if (svc_is_runtask(svc)) {
|
} else if (svc_is_runtask(svc)) {
|
||||||
|
/* run/task should run at least once per runlevel */
|
||||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||||
svc->started = 1;
|
svc->started = 1;
|
||||||
else
|
else
|
||||||
svc->started = 0;
|
svc->started = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Terminate any children in the same proess group, e.g. logit */
|
done:
|
||||||
kill(-svc->pid, SIGKILL);
|
|
||||||
|
|
||||||
/* No longer running, update books. */
|
/* No longer running, update books. */
|
||||||
svc->start_time = svc->pid = 0;
|
svc->start_time = svc->pid = 0;
|
||||||
|
|
||||||
@@ -1282,6 +1490,80 @@ void service_update_rdeps(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void service_kill_script(svc_t *svc)
|
||||||
|
{
|
||||||
|
if (svc->pid <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
kill(-svc->pid, SIGKILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void service_pre_script(svc_t *svc)
|
||||||
|
{
|
||||||
|
svc->pid = service_fork(svc);
|
||||||
|
if (svc->pid < 0) {
|
||||||
|
_pe("Failed forking off %s pre-script %s", svc_ident(svc, NULL, 0), svc->pre_script);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->pid == 0) {
|
||||||
|
char *argv[4] = {
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
svc->pre_script,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
|
||||||
|
execvp(_PATH_BSHELL, argv);
|
||||||
|
_exit(EX_OSERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Short hard-coded timeout to prevent locking up Finit */
|
||||||
|
service_timeout_after(svc, svc->killdelay, service_kill_script);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void service_post_script(svc_t *svc)
|
||||||
|
{
|
||||||
|
svc->pid = service_fork(svc);
|
||||||
|
if (svc->pid < 0) {
|
||||||
|
_pe("Failed forking off %s post-script %s", svc_ident(svc, NULL, 0), svc->post_script);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->pid == 0) {
|
||||||
|
char *argv[4] = {
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
svc->post_script,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int rc, sig;
|
||||||
|
|
||||||
|
rc = WEXITSTATUS(svc->status);
|
||||||
|
sig = WTERMSIG(svc->status);
|
||||||
|
|
||||||
|
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
|
||||||
|
|
||||||
|
if (WIFEXITED(svc->status)) {
|
||||||
|
char val[4];
|
||||||
|
|
||||||
|
setenv("EXIT_CODE", "exited", 1);
|
||||||
|
snprintf(val, sizeof(val), "%d", rc & 0xff);
|
||||||
|
setenv("EXIT_STATUS", val, 1);
|
||||||
|
} else if (WIFSIGNALED(svc->status)) {
|
||||||
|
setenv("EXIT_CODE", "signal", 1);
|
||||||
|
setenv("EXIT_STATUS", sig2str(sig), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
execvp(_PATH_BSHELL, argv);
|
||||||
|
_exit(EX_OSERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Short hard-coded timeout to prevent locking up Finit */
|
||||||
|
service_timeout_after(svc, svc->killdelay, service_kill_script);
|
||||||
|
}
|
||||||
|
|
||||||
static void service_retry(svc_t *svc)
|
static void service_retry(svc_t *svc)
|
||||||
{
|
{
|
||||||
int timeout;
|
int timeout;
|
||||||
@@ -1291,12 +1573,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->cmd);
|
if (!svc_is_tty(svc))
|
||||||
|
_d("%s not crashing anymore", svc->cmd);
|
||||||
*restart_cnt = 0;
|
*restart_cnt = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*restart_cnt >= RESPAWN_MAX) {
|
if (*restart_cnt >= SVC_RESPAWN_MAX) {
|
||||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
||||||
svc_ident(svc, NULL, 0));
|
svc_ident(svc, NULL, 0));
|
||||||
svc_crashing(svc);
|
svc_crashing(svc);
|
||||||
@@ -1309,12 +1592,12 @@ static void service_retry(svc_t *svc)
|
|||||||
|
|
||||||
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
|
||||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
|
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
|
||||||
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
|
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
|
||||||
/* Wait 2s for the first 5 respawns, then back off to 5s */
|
/* Wait 2s for the first 5 respawns, then back off to 5s */
|
||||||
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
|
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||||
service_timeout_after(svc, timeout, service_retry);
|
service_timeout_after(svc, timeout, service_retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1325,7 +1608,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
|||||||
*state = new;
|
*state = new;
|
||||||
|
|
||||||
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
|
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
|
||||||
if ((*state == SVC_STOPPING_STATE)) {
|
if (*state == SVC_STOPPING_STATE) {
|
||||||
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
|
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
|
||||||
svc->cmd, svc->killdelay / 1000);
|
svc->cmd, svc->killdelay / 1000);
|
||||||
service_timeout_cancel(svc);
|
service_timeout_cancel(svc);
|
||||||
@@ -1357,8 +1640,13 @@ restart:
|
|||||||
|
|
||||||
switch (svc->state) {
|
switch (svc->state) {
|
||||||
case SVC_HALTED_STATE:
|
case SVC_HALTED_STATE:
|
||||||
if (enabled)
|
if (enabled) {
|
||||||
svc_set_state(svc, SVC_READY_STATE);
|
if (svc_has_pre(svc)) {
|
||||||
|
svc_set_state(svc, SVC_SETUP_STATE);
|
||||||
|
service_pre_script(svc);
|
||||||
|
} else
|
||||||
|
svc_set_state(svc, SVC_READY_STATE);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_DONE_STATE:
|
case SVC_DONE_STATE:
|
||||||
@@ -1368,13 +1656,20 @@ restart:
|
|||||||
|
|
||||||
case SVC_STOPPING_STATE:
|
case SVC_STOPPING_STATE:
|
||||||
if (!svc->pid) {
|
if (!svc->pid) {
|
||||||
/* PID was collected normally, no need to kill it */
|
char cond[MAX_COND_LEN];
|
||||||
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
|
|
||||||
|
_d("%s: stopped, cleaning up timers and conditions ...", svc->cmd);
|
||||||
service_timeout_cancel(svc);
|
service_timeout_cancel(svc);
|
||||||
|
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||||
|
|
||||||
switch (svc->type) {
|
switch (svc->type) {
|
||||||
case SVC_TYPE_SERVICE:
|
case SVC_TYPE_SERVICE:
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
case SVC_TYPE_TTY:
|
||||||
|
if (svc_has_post(svc)) {
|
||||||
|
svc_set_state(svc, SVC_CLEANUP_STATE);
|
||||||
|
service_post_script(svc);
|
||||||
|
} else
|
||||||
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SVC_TYPE_TASK:
|
case SVC_TYPE_TASK:
|
||||||
@@ -1390,6 +1685,16 @@ restart:
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SVC_CLEANUP_STATE:
|
||||||
|
if (!svc->pid)
|
||||||
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVC_SETUP_STATE:
|
||||||
|
if (!svc->pid)
|
||||||
|
svc_set_state(svc, SVC_READY_STATE);
|
||||||
|
break;
|
||||||
|
|
||||||
case SVC_READY_STATE:
|
case SVC_READY_STATE:
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
@@ -1400,6 +1705,10 @@ restart:
|
|||||||
|
|
||||||
err = service_start(svc);
|
err = service_start(svc);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
if (svc_is_missing(svc)) {
|
||||||
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
(*restart_cnt)++;
|
(*restart_cnt)++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1417,7 +1726,7 @@ restart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!svc->pid) {
|
if (!svc->pid) {
|
||||||
if (svc_is_daemon(svc)) {
|
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
|
||||||
svc_restarting(svc);
|
svc_restarting(svc);
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
|
|
||||||
@@ -1455,7 +1764,9 @@ restart:
|
|||||||
|
|
||||||
case COND_ON:
|
case COND_ON:
|
||||||
if (svc_is_changed(svc)) {
|
if (svc_is_changed(svc)) {
|
||||||
if (svc->sighup) {
|
if (svc_nohup(svc))
|
||||||
|
service_stop(svc);
|
||||||
|
else {
|
||||||
/*
|
/*
|
||||||
* wait until all processes have been
|
* wait until all processes have been
|
||||||
* stopped before continuing...
|
* stopped before continuing...
|
||||||
@@ -1463,8 +1774,7 @@ restart:
|
|||||||
if (sm_is_in_teardown(&sm))
|
if (sm_is_in_teardown(&sm))
|
||||||
break;
|
break;
|
||||||
service_restart(svc);
|
service_restart(svc);
|
||||||
} else
|
}
|
||||||
service_stop(svc);
|
|
||||||
|
|
||||||
svc_mark_clean(svc);
|
svc_mark_clean(svc);
|
||||||
}
|
}
|
||||||
@@ -1535,7 +1845,7 @@ void service_step_all(int types)
|
|||||||
|
|
||||||
void service_worker(void *unused)
|
void service_worker(void *unused)
|
||||||
{
|
{
|
||||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
|
service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,21 +31,24 @@
|
|||||||
* does on USR1 with their FIFO/D-Bus.
|
* does on USR1 with their FIFO/D-Bus.
|
||||||
*
|
*
|
||||||
* SIGUSR1
|
* SIGUSR1
|
||||||
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
* Restarts API (initctl) socket, like SysV init and systemd does
|
||||||
* processes, and unmounts all file systems. Then tells kernel to
|
* on USR1 with their FIFO/D-Bus.
|
||||||
* halt. Most people these days want SIGUSR2 though.
|
|
||||||
*
|
*
|
||||||
* (SysV init and systemd use this to re-open their FIFO/D-Bus.)
|
* Finit <= 4.0 used to perform a system halt on USR1. This caused
|
||||||
|
* some quite nasty problems on systems with both systemd/sysvinit
|
||||||
|
* installed alongside finit. For compatibility reasons Finit 4.1
|
||||||
|
* changed to partial SIGHUP (api_exit/init).
|
||||||
*
|
*
|
||||||
* SIGUSR2
|
* SIGUSR2
|
||||||
* Like SIGUSR1, but tell kernel to power-off the system, if ACPI
|
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
||||||
* or similar exists to actually do this. If the kernel fails
|
* processes, and unmounts all file systems. Then tells kernel to
|
||||||
* power-off, Finit falls back to halt.
|
* power-off the system, if ACPI or similar exists to actually do
|
||||||
|
* this. If the kernel fails power-off, Finit falls back to halt.
|
||||||
*
|
*
|
||||||
* (SysV init N/A, systemd dumps its internal state to log.)
|
* (SysV init N/A, systemd dumps its internal state to log.)
|
||||||
*
|
*
|
||||||
* SIGTERM
|
* SIGTERM
|
||||||
* Like SIGUSR1, but tell kernel to reboot the system when done.
|
* Like SIGUSR2, but tell kernel to reboot the system when done.
|
||||||
*
|
*
|
||||||
* (SysV init N/A, systemd rexecutes itself.)
|
* (SysV init N/A, systemd rexecutes itself.)
|
||||||
*
|
*
|
||||||
@@ -138,7 +141,9 @@ static struct sigmap {
|
|||||||
/* Nonstandard signals found in all modern POSIX systems
|
/* Nonstandard signals found in all modern POSIX systems
|
||||||
(including both BSD and Linux). */
|
(including both BSD and Linux). */
|
||||||
{ SIGWINCH, "SIGWINCH" },
|
{ SIGWINCH, "SIGWINCH" },
|
||||||
|
#ifdef SIGSTKFLT
|
||||||
{ SIGSTKFLT, "SIGSTKFLT" },
|
{ SIGSTKFLT, "SIGSTKFLT" },
|
||||||
|
#endif
|
||||||
{ SIGPWR, "SIGPWR" },
|
{ SIGPWR, "SIGPWR" },
|
||||||
|
|
||||||
/* Archaic names for compatibility. */
|
/* Archaic names for compatibility. */
|
||||||
@@ -192,10 +197,10 @@ void do_kill(int signo)
|
|||||||
if (fgets(file, sizeof(file), fp)) {
|
if (fgets(file, sizeof(file), fp)) {
|
||||||
if (strstr(file, "gdbserver"))
|
if (strstr(file, "gdbserver"))
|
||||||
_d("Skipping %s ...", file);
|
_d("Skipping %s ...", file);
|
||||||
else if (file[0] != '@')
|
else if (file[0] == '@')
|
||||||
kill(pid, signo);
|
|
||||||
else
|
|
||||||
_d("Skipping %s ...", &file[1]);
|
_d("Skipping %s ...", &file[1]);
|
||||||
|
else
|
||||||
|
kill(pid, signo);
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
@@ -214,11 +219,11 @@ void do_shutdown(shutop_t op)
|
|||||||
utmp_set_halt();
|
utmp_set_halt();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tell all remaining non-monitored processes to exit, give them
|
* Tell remaining non-monitored processes to exit, give them
|
||||||
* some time to exit gracefully, 2 sec is customary.
|
* time to exit gracefully, 2 sec was customary, we go for 1.
|
||||||
*/
|
*/
|
||||||
do_kill(SIGTERM);
|
do_kill(SIGTERM);
|
||||||
do_sleep(2);
|
do_sleep(1);
|
||||||
do_kill(SIGKILL);
|
do_kill(SIGKILL);
|
||||||
|
|
||||||
/* Exit plugins and API gracefully */
|
/* Exit plugins and API gracefully */
|
||||||
@@ -265,7 +270,7 @@ 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 (wdog) {
|
if (wdog && wdog->pid > 1) {
|
||||||
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 */
|
||||||
@@ -274,15 +279,15 @@ void do_shutdown(shutop_t op)
|
|||||||
do_sleep(1);
|
do_sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_e("Rebooting ...");
|
_d("Rebooting ...");
|
||||||
reboot(RB_AUTOBOOT);
|
reboot(RB_AUTOBOOT);
|
||||||
} else if (op == SHUT_OFF) {
|
} else if (op == SHUT_OFF) {
|
||||||
_e("Powering down ...");
|
_d("Powering down ...");
|
||||||
reboot(RB_POWER_OFF);
|
reboot(RB_POWER_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Also fallback if any of the other two fails */
|
/* Also fallback if any of the other two fails */
|
||||||
_e("Halting ...");
|
_d("Halting ...");
|
||||||
reboot(RB_HALT_SYSTEM);
|
reboot(RB_HALT_SYSTEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +326,7 @@ static void sigint_cb(uev_t *w, void *arg, int events)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SIGUSR1: BusyBox style halt
|
* SIGUSR1: SysV init/systemd API socket restart
|
||||||
*/
|
*/
|
||||||
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
@@ -331,8 +336,9 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
halt = SHUT_HALT;
|
/* Restart initctl API domain socket, similar to systemd/SysV init */
|
||||||
service_runlevel(0);
|
api_exit();
|
||||||
|
api_init(w->ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -515,10 +521,14 @@ void sig_setup(uev_ctx_t *ctx)
|
|||||||
erase(SYNC_SHUTDOWN);
|
erase(SYNC_SHUTDOWN);
|
||||||
erase(SYNC_STOPPED);
|
erase(SYNC_STOPPED);
|
||||||
|
|
||||||
/* Standard SysV init calls ctrl-alt-delete handler */
|
/*
|
||||||
|
* Standard SysV init calls ctrl-alt-delete handler
|
||||||
|
* We need to disable kernel default so it sends us SIGINT
|
||||||
|
*/
|
||||||
|
reboot(RB_DISABLE_CAD);
|
||||||
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
|
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
|
||||||
|
|
||||||
/* BusyBox init style signals for halt, power-off and reboot. */
|
/* BusyBox/SysV init style signals for halt, power-off and reboot. */
|
||||||
uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
|
uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
|
||||||
uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
|
uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
|
||||||
uev_signal_init(ctx, &sigpwr_watcher, sigusr2_cb, NULL, SIGPWR);
|
uev_signal_init(ctx, &sigpwr_watcher, sigusr2_cb, NULL, SIGPWR);
|
||||||
|
|||||||
@@ -156,9 +156,11 @@ restart:
|
|||||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||||
nologin();
|
nologin();
|
||||||
|
|
||||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
if (runlevel != 0 && runlevel != 6) {
|
||||||
if (conf_any_change())
|
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||||
conf_reload();
|
if (conf_any_change())
|
||||||
|
conf_reload();
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset once flag of runtasks */
|
/* Reset once flag of runtasks */
|
||||||
service_runtask_clean();
|
service_runtask_clean();
|
||||||
@@ -204,10 +206,6 @@ restart:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No TTYs run at bootstrap, they have a delayed start. */
|
|
||||||
if (prevlevel > 0)
|
|
||||||
tty_runlevel();
|
|
||||||
|
|
||||||
sm->state = SM_RUNNING_STATE;
|
sm->state = SM_RUNNING_STATE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -222,8 +220,7 @@ restart:
|
|||||||
_d("Stopping 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);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
tty_reload(NULL);
|
|
||||||
|
|
||||||
sm->state = SM_RELOAD_WAIT_STATE;
|
sm->state = SM_RELOAD_WAIT_STATE;
|
||||||
break;
|
break;
|
||||||
@@ -240,16 +237,17 @@ restart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
sm->in_teardown = 0;
|
sm->in_teardown = 0;
|
||||||
/* Cleanup stale services */
|
|
||||||
svc_clean_dynamic(service_unregister);
|
|
||||||
|
|
||||||
_d("Starting services after reconf ...");
|
_d("Starting services after reconf ...");
|
||||||
service_step_all(SVC_TYPE_SERVICE);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|
||||||
|
/* Cleanup stale services */
|
||||||
|
svc_clean_dynamic(service_unregister);
|
||||||
|
|
||||||
_d("Calling reconf hooks ...");
|
_d("Calling reconf hooks ...");
|
||||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||||
|
|
||||||
service_step_all(SVC_TYPE_SERVICE);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
_d("Reconfiguration done");
|
_d("Reconfiguration done");
|
||||||
|
|
||||||
sm->state = SM_RUNNING_STATE;
|
sm->state = SM_RUNNING_STATE;
|
||||||
|
|||||||
+6
-2
@@ -27,6 +27,8 @@
|
|||||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include <helpers.h>
|
||||||
|
|
||||||
/* B0 means; keep kernel default */
|
/* B0 means; keep kernel default */
|
||||||
speed_t stty_parse_speed(char *baud)
|
speed_t stty_parse_speed(char *baud)
|
||||||
{
|
{
|
||||||
@@ -61,10 +63,12 @@ speed_t stty_parse_speed(char *baud)
|
|||||||
{ 1152000, B1152000 },
|
{ 1152000, B1152000 },
|
||||||
{ 1500000, B1500000 },
|
{ 1500000, B1500000 },
|
||||||
{ 2000000, B2000000 },
|
{ 2000000, B2000000 },
|
||||||
|
#ifdef B4000000
|
||||||
{ 2500000, B2500000 },
|
{ 2500000, B2500000 },
|
||||||
{ 3000000, B3000000 },
|
{ 3000000, B3000000 },
|
||||||
{ 3500000, B3500000 },
|
{ 3500000, B3500000 },
|
||||||
{ 4000000, B4000000 },
|
{ 4000000, B4000000 },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!baud || !baud[0])
|
if (!baud || !baud[0])
|
||||||
@@ -121,10 +125,10 @@ void stty(int fd, speed_t speed)
|
|||||||
tcsetattr(fd, TCSAFLUSH, &term);
|
tcsetattr(fd, TCSAFLUSH, &term);
|
||||||
|
|
||||||
/* Show cursor again, if it was hidden previously */
|
/* Show cursor again, if it was hidden previously */
|
||||||
write(fd, "\033[?25h", 6);
|
dprint(fd, "\033[?25h", 6);
|
||||||
|
|
||||||
/* Enable line wrap, if disabled previously */
|
/* Enable line wrap, if disabled previously */
|
||||||
write(fd, "\033[7h", 4);
|
dprint(fd, "\033[7h", 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+213
@@ -0,0 +1,213 @@
|
|||||||
|
/* poor man's sulogin, may be statically linked
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Joachim Wiberg <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 <crypt.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <paths.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
/* getpwnam() cannot be used when statically linked */
|
||||||
|
static int get_passwd(struct passwd *pw)
|
||||||
|
{
|
||||||
|
char *ptr, *tmp;
|
||||||
|
char buf[256];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen("/etc/passwd", "r");
|
||||||
|
if (!fp)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
while ((tmp = fgets(buf, sizeof(buf), fp))) {
|
||||||
|
if (!strstr(buf, ":0:0:"))
|
||||||
|
continue; /* not uid 0 */
|
||||||
|
|
||||||
|
ptr = strsep(&tmp, ":");
|
||||||
|
if (!ptr)
|
||||||
|
break;
|
||||||
|
pw->pw_name = strdup(ptr);
|
||||||
|
|
||||||
|
ptr = strsep(&tmp, ":");
|
||||||
|
if (!ptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* x : password in /etc/shadow, see passwd(5) */
|
||||||
|
if (ptr[0] != 'x')
|
||||||
|
pw->pw_passwd = strdup(ptr);
|
||||||
|
|
||||||
|
break; /* done, check it */
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!pw->pw_name)
|
||||||
|
return 1; /* no uid 0 here */
|
||||||
|
if (pw->pw_passwd)
|
||||||
|
return 0; /* passwd in passwd */
|
||||||
|
|
||||||
|
fp = fopen("/etc/shadow", "r");
|
||||||
|
if (!fp) {
|
||||||
|
if (pw->pw_name)
|
||||||
|
free(pw->pw_name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((tmp = fgets(buf, sizeof(buf), fp))) {
|
||||||
|
ptr = strsep(&tmp, ":");
|
||||||
|
if (!ptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!strcmp(tmp, pw->pw_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ptr = strsep(&tmp, ":");
|
||||||
|
if (!ptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pw->pw_passwd = strdup(ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!pw->pw_passwd) {
|
||||||
|
if (pw->pw_name)
|
||||||
|
free(pw->pw_name);
|
||||||
|
return 1; /* nopass => nologin */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; /* passwd in shadow */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freepw(struct passwd *pw)
|
||||||
|
{
|
||||||
|
if (pw->pw_passwd) {
|
||||||
|
free(pw->pw_passwd);
|
||||||
|
pw->pw_passwd = NULL;
|
||||||
|
}
|
||||||
|
free(pw->pw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sh(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Become session leader and set controlling TTY
|
||||||
|
* to enable Ctrl-C and job control in shell.
|
||||||
|
*/
|
||||||
|
setsid();
|
||||||
|
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||||
|
setenv("PS1", "# ", 1);
|
||||||
|
|
||||||
|
return execl(_PATH_BSHELL, "-sh", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct termios old, raw;
|
||||||
|
struct passwd pw = { 0 };
|
||||||
|
int rc = 1, ch;
|
||||||
|
|
||||||
|
if (get_passwd(&pw))
|
||||||
|
err(1, "reading credentials for UID 0");
|
||||||
|
|
||||||
|
switch (pw.pw_passwd[0]) {
|
||||||
|
case '*': case '!': /* login disabled, allow */
|
||||||
|
printf("The %s account is locked, allowing entry.\n", pw.pw_name);
|
||||||
|
/* fallthrough */
|
||||||
|
case 0: /* no password at all? */
|
||||||
|
printf("Press enter for maintenance (Ctrl-D to continue) ");
|
||||||
|
fflush(stdout);
|
||||||
|
ch = getchar();
|
||||||
|
if (ch == 4 || ch == EOF) { /* Ctrl-D */
|
||||||
|
rc = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
goto nopass;
|
||||||
|
|
||||||
|
case '$': /* md5/shaN or similar, original DES not supported */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcgetattr(1, &old) < 0)
|
||||||
|
goto done; /* failed reading tty */
|
||||||
|
|
||||||
|
raw = old;
|
||||||
|
raw.c_lflag &= ~ICANON;
|
||||||
|
raw.c_lflag &= ~ECHO;
|
||||||
|
raw.c_cc[VMIN] = 1;
|
||||||
|
raw.c_cc[VTIME] = 0;
|
||||||
|
if (tcsetattr(1, TCSANOW, &raw) < 0) {
|
||||||
|
tcsetattr(1, TCSANOW, &old);
|
||||||
|
goto done; /* failed setting raw/pw mode */
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
char pwd[256]; /* reasonable max? */
|
||||||
|
size_t i = 0;
|
||||||
|
char *passwd;
|
||||||
|
|
||||||
|
printf("Give %s password for maintenance (Ctrl-D to continue): ", pw.pw_name);
|
||||||
|
fflush(stdout);
|
||||||
|
do {
|
||||||
|
ch = getchar();
|
||||||
|
if (ch == '\n' || ch == '\r')
|
||||||
|
break;
|
||||||
|
if (ch == 4 || ch == EOF) { /* Ctrl-D */
|
||||||
|
rc = 0;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pwd[i++] = ch;
|
||||||
|
} while (i + 1 < sizeof(pwd));
|
||||||
|
pwd[i] = 0;
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
passwd = crypt(pwd, pw.pw_passwd);
|
||||||
|
if (passwd && !strcmp(passwd, pw.pw_passwd)) {
|
||||||
|
tcsetattr(1, TCSANOW, &old); /* restore tty */
|
||||||
|
nopass:
|
||||||
|
freepw(&pw);
|
||||||
|
|
||||||
|
return sh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
tcsetattr(1, TCSANOW, &old); /* restore tty */
|
||||||
|
puts("");
|
||||||
|
done:
|
||||||
|
freepw(&pw);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -44,6 +44,36 @@ static int jobcounter = 1;
|
|||||||
static TAILQ_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 TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before gc removal of svc, make sure we don't clear an active
|
||||||
|
* condition of a new instance of the svc.
|
||||||
|
*/
|
||||||
|
static void maybe_clear_cond(svc_t *svc)
|
||||||
|
{
|
||||||
|
char ident[MAX_IDENT_LEN];
|
||||||
|
char cond[MAX_COND_LEN];
|
||||||
|
svc_t *iter = NULL;
|
||||||
|
svc_t *s;
|
||||||
|
|
||||||
|
mkcond(svc, cond, sizeof(cond));
|
||||||
|
svc_ident(svc, ident, sizeof(ident));
|
||||||
|
|
||||||
|
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
|
||||||
|
char c[MAX_COND_LEN];
|
||||||
|
|
||||||
|
mkcond(s, c, sizeof(c));
|
||||||
|
if (!string_compare(cond, c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_d("Not clearing cond %s from gc svc %s, provided by new active service %s",
|
||||||
|
cond, ident, svc_ident(s, NULL, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Cleaning out %s, clearing any conditions ...", svc->name);
|
||||||
|
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||||
|
}
|
||||||
|
|
||||||
static void svc_gc(void *arg)
|
static void svc_gc(void *arg)
|
||||||
{
|
{
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
@@ -52,7 +82,6 @@ static void svc_gc(void *arg)
|
|||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
||||||
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
|
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
|
||||||
char cond[MAX_COND_LEN];
|
|
||||||
int msec;
|
int msec;
|
||||||
|
|
||||||
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
|
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
|
||||||
@@ -61,8 +90,7 @@ static void svc_gc(void *arg)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
TAILQ_REMOVE(&gc_list, svc, link);
|
TAILQ_REMOVE(&gc_list, svc, link);
|
||||||
_d("Cleaning out %s, clearing any conditions ...", svc->name);
|
maybe_clear_cond(svc);
|
||||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
|
||||||
free(svc);
|
free(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +114,7 @@ svc_t *svc_new(char *cmd, char *id, int type)
|
|||||||
|
|
||||||
/* Find first job n:o if registering multiple instances */
|
/* Find first job n:o if registering multiple instances */
|
||||||
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)) {
|
if (cmd && !strcmp(svc->cmd, cmd)) {
|
||||||
job = svc->job;
|
job = svc->job;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -102,13 +130,17 @@ svc_t *svc_new(char *cmd, char *id, int type)
|
|||||||
svc->job = job;
|
svc->job = job;
|
||||||
if (id && id[0])
|
if (id && id[0])
|
||||||
strlcpy(svc->id, id, sizeof(svc->id));
|
strlcpy(svc->id, id, sizeof(svc->id));
|
||||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
if (cmd)
|
||||||
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
|
||||||
/* Default description, if missing */
|
/* Default description, if missing */
|
||||||
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
||||||
|
|
||||||
/* Default HALT signal to send */
|
/* Default HALT signal to send */
|
||||||
svc->sighalt = SIGTERM;
|
if (svc_is_tty(svc))
|
||||||
|
svc->sighalt = SIGHUP;
|
||||||
|
else
|
||||||
|
svc->sighalt = SIGTERM;
|
||||||
|
|
||||||
/* Default delay between SIGTERM and SIGKILL */
|
/* Default delay between SIGTERM and SIGKILL */
|
||||||
svc->killdelay = SVC_TERM_TIMEOUT;
|
svc->killdelay = SVC_TERM_TIMEOUT;
|
||||||
@@ -160,6 +192,8 @@ void svc_validate(svc_t *svc)
|
|||||||
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
|
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
|
||||||
char c[MAX_COND_LEN];
|
char c[MAX_COND_LEN];
|
||||||
|
|
||||||
|
if (s->removed)
|
||||||
|
continue;
|
||||||
if (s == svc)
|
if (s == svc)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -299,7 +333,7 @@ svc_t *svc_stop_completed(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->state == SVC_STOPPING_STATE)
|
if (svc->state == SVC_STOPPING_STATE && svc->pid > 1)
|
||||||
return svc;
|
return svc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +428,26 @@ svc_t *svc_find_by_nameid(char *name, char *id)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
svc_t *svc_find_by_tty(char *dev)
|
||||||
|
{
|
||||||
|
svc_t *svc, *iter = NULL;
|
||||||
|
|
||||||
|
/* rescue (notty) shells have no device node */
|
||||||
|
if (!dev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
if (!svc_is_tty(svc))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!strcmp(dev, svc->dev))
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_find_by_plidfile - Find a service by its PID file
|
* svc_find_by_plidfile - Find a service by its PID file
|
||||||
* @fn: PID file, can be absolute path or relative to /run
|
* @fn: PID file, can be absolute path or relative to /run
|
||||||
@@ -415,8 +469,20 @@ svc_t *svc_find_by_pidfile(char *fn)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
pid = pid_file_read(fn);
|
pid = pid_file_read(fn);
|
||||||
if (pid == -1)
|
if (pid <= 0) {
|
||||||
|
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||||
|
int v = 0;
|
||||||
|
|
||||||
|
if (svc->pidfile[0] == '!')
|
||||||
|
v = 1;
|
||||||
|
if (strcmp(&svc->pidfile[v], fn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return 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->pid != pid)
|
if (svc->pid != pid)
|
||||||
@@ -446,7 +512,7 @@ void svc_mark_dynamic(void)
|
|||||||
if (svc->protect)
|
if (svc->protect)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
*((int *)&svc->dirty) = -1;
|
*((int *)&svc->removed) = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,6 +524,12 @@ void svc_mark_dirty(svc_t *svc)
|
|||||||
void svc_mark_clean(svc_t *svc)
|
void svc_mark_clean(svc_t *svc)
|
||||||
{
|
{
|
||||||
*((int *)&svc->dirty) = 0;
|
*((int *)&svc->dirty) = 0;
|
||||||
|
*((int *)&svc->args_dirty) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void svc_enable(svc_t *svc)
|
||||||
|
{
|
||||||
|
*((int *)&svc->removed) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -472,7 +544,7 @@ 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->removed && cb)
|
||||||
cb(svc);
|
cb(svc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,6 +604,12 @@ int svc_enabled(svc_t *svc)
|
|||||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (svc_is_missing(svc))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (svc_is_tty(svc) && bootstrap)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,7 +680,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
|||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
_d("Got token:'%s'", input);
|
_d("Got token:'%s'", input);
|
||||||
token = sanitize(input, len);
|
token = sanitize(input, strlen(input) + 1);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
_d("Sanitation of token:'%s' failed", input);
|
_d("Sanitation of token:'%s' failed", input);
|
||||||
goto next;
|
goto next;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||||
#include <uev/uev.h>
|
#include <uev/uev.h>
|
||||||
|
|
||||||
|
#include "cgroup.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
typedef int svc_cmd_t;
|
typedef int svc_cmd_t;
|
||||||
@@ -41,16 +42,20 @@ typedef enum {
|
|||||||
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
|
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
|
||||||
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
|
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
|
||||||
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
|
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
|
||||||
|
SVC_TYPE_TTY = 8, /* Like service, but dedicated to TTYs */
|
||||||
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
|
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
|
||||||
} svc_type_t;
|
} svc_type_t;
|
||||||
|
|
||||||
#define SVC_TYPE_ANY (-1)
|
#define SVC_TYPE_ANY (-1)
|
||||||
|
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_TTY)
|
||||||
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV)
|
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||||
SVC_DONE_STATE, /* Task/Run job has been run */
|
SVC_DONE_STATE, /* Task/Run job has been run */
|
||||||
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
||||||
|
SVC_CLEANUP_STATE, /* Running post: script */
|
||||||
|
SVC_SETUP_STATE, /* Running pre: script */
|
||||||
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||||
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
||||||
SVC_RUNNING_STATE, /* Process running */
|
SVC_RUNNING_STATE, /* Process running */
|
||||||
@@ -77,6 +82,9 @@ typedef enum {
|
|||||||
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
|
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
|
||||||
#define SVC_TERM_TIMEOUT 3000
|
#define SVC_TERM_TIMEOUT 3000
|
||||||
|
|
||||||
|
/* Prevent endless respawn of faulty services. */
|
||||||
|
#define SVC_RESPAWN_MAX 10
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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.
|
||||||
@@ -91,11 +99,12 @@ typedef struct svc {
|
|||||||
|
|
||||||
/* Limits and scoping */
|
/* Limits and scoping */
|
||||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||||
|
struct cgroup cgroup;
|
||||||
|
|
||||||
/* Service details */
|
/* Service details */
|
||||||
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
|
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
|
||||||
int killdelay; /* Delay in msec before sending SIGKILL */
|
int killdelay; /* Delay in msec before sending SIGKILL */
|
||||||
pid_t pid;
|
pid_t oldpid, pid;
|
||||||
char pidfile[256];
|
char pidfile[256];
|
||||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||||
int started; /* Set for run/task/sysv to track if started */
|
int started; /* Set for run/task/sysv to track if started */
|
||||||
@@ -103,7 +112,8 @@ typedef struct svc {
|
|||||||
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, ... */
|
svc_type_t type; /* Service, run, task, ... */
|
||||||
int protect; /* Services like dbus-daemon & udev by Finit */
|
int protect; /* Services like dbus-daemon & udev by Finit */
|
||||||
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
const int dirty; /* 0: unmodified, 1: modified */
|
||||||
|
const int removed;
|
||||||
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 :) */
|
||||||
@@ -119,15 +129,29 @@ typedef struct svc {
|
|||||||
char once; /* run/task, (at least) once per runlevel */
|
char once; /* run/task, (at least) once per runlevel */
|
||||||
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
||||||
|
|
||||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
union {
|
||||||
struct {
|
/* services we redirect stdout/stderr to syslog (not TTYs!) */
|
||||||
char enabled;
|
struct {
|
||||||
char null;
|
char enabled;
|
||||||
char console;
|
char null;
|
||||||
char file[64];
|
char console;
|
||||||
char prio[20];
|
char file[64];
|
||||||
char ident[20];
|
char prio[20];
|
||||||
} log;
|
char ident[20];
|
||||||
|
} log;
|
||||||
|
|
||||||
|
/* Only for TTY type services */
|
||||||
|
struct {
|
||||||
|
char dev[32];
|
||||||
|
char baud[10];
|
||||||
|
char term[10];
|
||||||
|
char noclear;
|
||||||
|
char nowait;
|
||||||
|
char nologin;
|
||||||
|
char notty;
|
||||||
|
char rescue;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/* Identity */
|
/* Identity */
|
||||||
char username[MAX_USER_LEN];
|
char username[MAX_USER_LEN];
|
||||||
@@ -136,8 +160,11 @@ typedef struct svc {
|
|||||||
/* Command, arguments and service description */
|
/* Command, arguments and service description */
|
||||||
char cmd[MAX_ARG_LEN];
|
char cmd[MAX_ARG_LEN];
|
||||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||||
|
int args_dirty;
|
||||||
char desc[MAX_STR_LEN];
|
char desc[MAX_STR_LEN];
|
||||||
char env[MAX_ARG_LEN];
|
char env[MAX_ARG_LEN];
|
||||||
|
char pre_script[MAX_ARG_LEN];
|
||||||
|
char post_script[MAX_ARG_LEN];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to forcefully kill services that won't shutdown on
|
* Used to forcefully kill services that won't shutdown on
|
||||||
@@ -158,6 +185,7 @@ 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, char *id);
|
svc_t *svc_find_by_jobid (int job, char *id);
|
||||||
svc_t *svc_find_by_nameid (char *name, char *id);
|
svc_t *svc_find_by_nameid (char *name, char *id);
|
||||||
|
svc_t *svc_find_by_tty (char *dev);
|
||||||
svc_t *svc_find_by_pidfile (char *fn);
|
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);
|
||||||
@@ -176,30 +204,37 @@ void svc_clean_dynamic (void (*cb)(svc_t *));
|
|||||||
int svc_clean_bootstrap (svc_t *svc);
|
int svc_clean_bootstrap (svc_t *svc);
|
||||||
void svc_prune_bootstrap (void);
|
void svc_prune_bootstrap (void);
|
||||||
|
|
||||||
|
void svc_enable (svc_t *svc);
|
||||||
int svc_enabled (svc_t *svc);
|
int svc_enabled (svc_t *svc);
|
||||||
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 *, char *));
|
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
|
||||||
|
|
||||||
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_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
|
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
|
||||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == svc->type; }
|
||||||
|
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);}
|
||||||
static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; }
|
static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; }
|
||||||
|
|
||||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
|
||||||
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
|
||||||
|
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
|
||||||
|
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
|
||||||
|
|
||||||
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||||
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||||
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
|
||||||
|
static inline int svc_is_running (svc_t *svc) { return svc && svc->state == SVC_RUNNING_STATE; }
|
||||||
|
|
||||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
static inline int svc_is_removed (svc_t *svc) { return svc && svc->removed; }
|
||||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||||
|
|
||||||
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
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 int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||||
|
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
|
||||||
|
|
||||||
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
|
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) { if (svc) svc->block = SVC_BLOCK_USER; }
|
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||||
@@ -218,6 +253,7 @@ static inline int svc_has_cond(svc_t *svc)
|
|||||||
case SVC_TYPE_SERVICE:
|
case SVC_TYPE_SERVICE:
|
||||||
case SVC_TYPE_TASK:
|
case SVC_TYPE_TASK:
|
||||||
case SVC_TYPE_RUN:
|
case SVC_TYPE_RUN:
|
||||||
|
case SVC_TYPE_TTY:
|
||||||
case SVC_TYPE_SYSV:
|
case SVC_TYPE_SYSV:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -272,6 +308,12 @@ static inline char *svc_status(svc_t *svc)
|
|||||||
case SVC_WAITING_STATE:
|
case SVC_WAITING_STATE:
|
||||||
return "waiting";
|
return "waiting";
|
||||||
|
|
||||||
|
case SVC_CLEANUP_STATE:
|
||||||
|
return "cleanup";
|
||||||
|
|
||||||
|
case SVC_SETUP_STATE:
|
||||||
|
return "setup";
|
||||||
|
|
||||||
case SVC_READY_STATE:
|
case SVC_READY_STATE:
|
||||||
return "ready";
|
return "ready";
|
||||||
|
|
||||||
@@ -347,6 +389,37 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* non-zero env, no checking if file exists or not
|
||||||
|
*/
|
||||||
|
static inline char *svc_getenv(svc_t *svc)
|
||||||
|
{
|
||||||
|
int v = 0;
|
||||||
|
|
||||||
|
if (!svc->env[0])
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (svc->env[0] == '-')
|
||||||
|
v = 1;
|
||||||
|
|
||||||
|
return &svc->env[v];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if svc has env, if env file exists returns true
|
||||||
|
* if it doesn't exist, but has '-', also true. if svc
|
||||||
|
* doesn't have env, also true.
|
||||||
|
*/
|
||||||
|
static inline int svc_checkenv(svc_t *svc)
|
||||||
|
{
|
||||||
|
char *env = svc_getenv(svc);
|
||||||
|
|
||||||
|
if (!env || svc->env[0] == '-')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return fexist(env);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* FINIT_SVC_H_ */
|
#endif /* FINIT_SVC_H_ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -35,24 +35,22 @@
|
|||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "service.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
#ifdef FALLBACK_SHELL
|
char *tty_canonicalize(char *dev)
|
||||||
static pid_t fallback = 0;
|
|
||||||
#endif
|
|
||||||
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
|
|
||||||
|
|
||||||
static char *canonicalize(char *tty)
|
|
||||||
{
|
{
|
||||||
struct stat st;
|
|
||||||
static char path[80];
|
static char path[80];
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
if (!tty)
|
if (!dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (tty_isatcon(dev))
|
||||||
|
return dev;
|
||||||
|
|
||||||
strlcpy(path, tty, sizeof(path));
|
strlcpy(path, dev, 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))) {
|
||||||
unavailable:
|
unavailable:
|
||||||
@@ -60,7 +58,7 @@ static char *canonicalize(char *tty)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty);
|
snprintf(path, sizeof(path), "%s%s", _PATH_DEV, dev);
|
||||||
if (stat(path, &st))
|
if (stat(path, &st))
|
||||||
goto unavailable;
|
goto unavailable;
|
||||||
}
|
}
|
||||||
@@ -71,37 +69,65 @@ static char *canonicalize(char *tty)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_mark(void)
|
/*
|
||||||
|
* The @console syntax is a wildcard to match the system console(s) given
|
||||||
|
* on the kernel cmdline. As such it can match multiple, or even none.
|
||||||
|
*/
|
||||||
|
int tty_isatcon(char *dev)
|
||||||
{
|
{
|
||||||
struct tty *tty;
|
return dev && !strcmp(dev, "@console");
|
||||||
|
|
||||||
LIST_FOREACH(tty, &tty_list, link)
|
|
||||||
tty->dirty = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_sweep(void)
|
/*
|
||||||
|
* Returns console TTYs known by the kernel, from cmdline
|
||||||
|
*/
|
||||||
|
char *tty_atcon(void)
|
||||||
{
|
{
|
||||||
struct tty *tty, *tmp;
|
static char *buf = NULL;
|
||||||
|
static char *save;
|
||||||
|
char *ptr = NULL;
|
||||||
|
char *dev;
|
||||||
|
|
||||||
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
|
if (!buf) {
|
||||||
if (!tty->dirty)
|
FILE *fp;
|
||||||
continue;
|
|
||||||
|
|
||||||
_d("TTY %s dirty, stopping ...", tty->name);
|
fp = fopen("/sys/class/tty/console/active", "r");
|
||||||
tty_stop(tty);
|
if (!fp) {
|
||||||
|
_e("Cannot find system console, is sysfs not mounted?");
|
||||||
if (tty->dirty == -1) {
|
errno = ENOENT;
|
||||||
_d("TTY %s removed, cleaning up.", tty->name);
|
return NULL;
|
||||||
tty_unregister(tty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf = malloc(512);
|
||||||
|
if (!buf) {
|
||||||
|
_pe("Failed allocating memory for @console");
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fgets(buf, 512, fp)) {
|
||||||
|
fclose(fp);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
ptr = chomp(buf);
|
||||||
|
_d("consoles: %s", ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev = strtok_r(ptr, " \t", &save);
|
||||||
|
if (!dev) {
|
||||||
|
done:
|
||||||
|
free(buf);
|
||||||
|
return buf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_register - Register a getty on a device
|
* tty_parse_args - Parse cmdline args for a tty
|
||||||
* @line: Configuration, text after initial "tty"
|
* @cmd: command or tty
|
||||||
* @rlimit: Limits for this service/run/task, may be global limits
|
|
||||||
* @file: The file name TTY was loaded from
|
|
||||||
*
|
*
|
||||||
* A Finit tty line can use the internal getty implementation or an
|
* A Finit tty line can use the internal getty implementation or an
|
||||||
* external one, like the BusyBox getty for instance. This function
|
* external one, like the BusyBox getty for instance. This function
|
||||||
@@ -119,66 +145,61 @@ 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_parse_args(char *cmd, struct tty *tty)
|
||||||
{
|
{
|
||||||
struct tty *entry;
|
char *dev = NULL;
|
||||||
size_t i, num = 0;
|
size_t i;
|
||||||
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256];
|
|
||||||
char *dev = NULL, *baud = NULL;
|
|
||||||
char *runlevels = NULL, *term = NULL;
|
|
||||||
int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0;
|
|
||||||
|
|
||||||
if (!line) {
|
do {
|
||||||
_e("Missing argument");
|
if (!strcmp(cmd, "noclear"))
|
||||||
return errno = EINVAL;
|
tty->noclear = 1;
|
||||||
}
|
else if (!strcmp(cmd, "nowait"))
|
||||||
|
tty->nowait = 1;
|
||||||
/*
|
else if (!strcmp(cmd, "nologin"))
|
||||||
* Split line in separate arguments. For an external getty
|
tty->nologin = 1;
|
||||||
* this is used with execv(), for the built-in it simplifies
|
else if (!strcmp(cmd, "notty"))
|
||||||
* further translation.
|
tty->notty = 1; /* for board bringup */
|
||||||
*/
|
else if (!strcmp(cmd, "rescue"))
|
||||||
tok = strtok(line, " \t");
|
tty->rescue = 1; /* for rescue shells */
|
||||||
while (tok && num < NELEMS(args)) {
|
else if (!access(cmd, X_OK))
|
||||||
if (!strcmp(tok, "noclear"))
|
tty->cmd = cmd;
|
||||||
noclear = 1;
|
|
||||||
else if (!strcmp(tok, "nowait"))
|
|
||||||
nowait = 1;
|
|
||||||
else if (!strcmp(tok, "nologin"))
|
|
||||||
nologin = 1;
|
|
||||||
else
|
else
|
||||||
args[num++] = tok;
|
tty->args[tty->num++] = cmd;
|
||||||
|
|
||||||
tok = strtok(NULL, " \t");
|
cmd = strtok(NULL, " \t");
|
||||||
}
|
} while (cmd && tty->num < NELEMS(tty->args));
|
||||||
|
|
||||||
|
/* rescue shells are always notty */
|
||||||
|
if (tty->rescue)
|
||||||
|
tty->notty = 1;
|
||||||
|
|
||||||
|
/* skip /dev probe, we just want a bríngup shell */
|
||||||
|
if (tty->notty)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Iterate over all args */
|
/* Iterate over all args */
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < tty->num; i++) {
|
||||||
/*
|
/*
|
||||||
* First, figure out if built-in or external getty
|
* First, figure out if built-in or external getty
|
||||||
* tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in
|
* tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in
|
||||||
* tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external
|
* tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external
|
||||||
*/
|
*/
|
||||||
if ((!cmd && !dev) || (cmd && !dev)) {
|
if (!dev) {
|
||||||
if (args[i][0] == '[')
|
if (!strcmp(tty->args[i], "@console"))
|
||||||
runlevels = line;
|
dev = tty->args[i];
|
||||||
if (!strcmp(args[i], "@console"))
|
if (!strncmp(tty->args[i], "/dev", 4))
|
||||||
dev = args[i];
|
dev = tty->args[i];
|
||||||
if (!strncmp(args[i], "/dev", 4))
|
if (!strncmp(tty->args[i], "tty", 3) || !strcmp(tty->args[i], "console"))
|
||||||
dev = args[i];
|
dev = tty->args[i];
|
||||||
if (!strncmp(args[i], "tty", 3) || !strcmp(args[i], "console"))
|
|
||||||
dev = args[i];
|
|
||||||
if (!access(args[i], X_OK))
|
|
||||||
cmd = args[i];
|
|
||||||
|
|
||||||
/* The first arg must be one of the above */
|
/* The first arg must be one of the above */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Built-in getty args */
|
/* Built-in getty args */
|
||||||
if (!cmd && dev) {
|
if (!tty->cmd && dev) {
|
||||||
if (isdigit(args[i][0])) {
|
if (isdigit(tty->args[i][0])) {
|
||||||
baud = args[i];
|
tty->baud = tty->args[i];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,369 +207,97 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
|||||||
* Last arg, if not anything else, is the value
|
* Last arg, if not anything else, is the value
|
||||||
* to be used for the TERM environment variable.
|
* to be used for the TERM environment variable.
|
||||||
*/
|
*/
|
||||||
if (i + 1 == num)
|
if (i + 1 == tty->num)
|
||||||
term = args[i];
|
tty->term = tty->args[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dev) {
|
tty->dev = tty_canonicalize(dev);
|
||||||
error:
|
if (!tty->dev) {
|
||||||
_e("Incomplete or non-existing TTY device given, cannot register.");
|
_e("Incomplete or non-existing TTY device given, cannot register.");
|
||||||
return errno = EINVAL;
|
return errno = EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auto-detect serial console, for embedded devices mostly */
|
_d("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in",
|
||||||
if (!strcmp(dev, "@console")) {
|
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
|
||||||
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 */
|
|
||||||
dev = canonicalize(dev);
|
|
||||||
if (!dev)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
entry = tty_find(dev);
|
|
||||||
if (!entry) {
|
|
||||||
entry = calloc(1, sizeof(*entry));
|
|
||||||
if (!entry)
|
|
||||||
return errno = ENOMEM;
|
|
||||||
insert = 1;
|
|
||||||
} else {
|
|
||||||
if (entry->cmd) {
|
|
||||||
free(entry->cmd);
|
|
||||||
entry->cmd = NULL;
|
|
||||||
for (i = 0; i < TTY_MAX_ARGS; i++) {
|
|
||||||
if (entry->args[i])
|
|
||||||
free(entry->args[i]);
|
|
||||||
entry->args[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
insert = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
strlcpy(entry->name, dev, sizeof(entry->name));
|
|
||||||
strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud));
|
|
||||||
strlcpy(entry->term, term ? term : "", sizeof(entry->term));
|
|
||||||
entry->noclear = noclear;
|
|
||||||
entry->nowait = nowait;
|
|
||||||
entry->nologin = nologin;
|
|
||||||
entry->runlevels = conf_parse_runlevels(runlevels);
|
|
||||||
|
|
||||||
/* External getty */
|
|
||||||
if (cmd) {
|
|
||||||
int j = 0;
|
|
||||||
|
|
||||||
tok = strrchr(cmd, '/');
|
|
||||||
if (!tok)
|
|
||||||
tok = cmd;
|
|
||||||
else
|
|
||||||
tok++;
|
|
||||||
entry->cmd = strdup(cmd);
|
|
||||||
args[1] = strdup(tok);
|
|
||||||
|
|
||||||
for (i = 1; i < num; i++) {
|
|
||||||
char *arg = args[i];
|
|
||||||
|
|
||||||
/* Replace @console with actual device */
|
|
||||||
if (arg && !strcmp(arg, "@console"))
|
|
||||||
arg = dev;
|
|
||||||
if (arg)
|
|
||||||
entry->args[j++] = strdup(arg);
|
|
||||||
}
|
|
||||||
entry->args[++j] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s",
|
|
||||||
cmd ? "external" : "built-in", dev, baud ?: "NULL", term ?: "N/A", runlevels ?: "[2-5]");
|
|
||||||
|
|
||||||
if (insert)
|
|
||||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
|
||||||
|
|
||||||
/* Register configured limits */
|
|
||||||
memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit));
|
|
||||||
|
|
||||||
if (file && conf_changed(file))
|
|
||||||
entry->dirty = 1; /* Modified, restart */
|
|
||||||
else
|
|
||||||
entry->dirty = 0; /* Not modified */
|
|
||||||
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
|
|
||||||
|
|
||||||
if (atcon) {
|
|
||||||
dev = strtok(NULL, " \t");
|
|
||||||
if (dev) {
|
|
||||||
_d("Found another @console: %s", dev);
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tty_unregister(struct tty *tty)
|
int tty_exists(char *dev)
|
||||||
{
|
{
|
||||||
if (!tty) {
|
|
||||||
_e("Missing argument");
|
|
||||||
return errno = EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
LIST_REMOVE(tty, link);
|
|
||||||
|
|
||||||
if (tty->cmd) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
free(tty->cmd);
|
|
||||||
for (i = 0; i < TTY_MAX_ARGS; i++) {
|
|
||||||
if (tty->args[i])
|
|
||||||
free(tty->args[i]);
|
|
||||||
tty->args[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(tty);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tty *tty_find(char *dev)
|
|
||||||
{
|
|
||||||
struct tty *entry;
|
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
|
||||||
if (!strcmp(dev, entry->name))
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t tty_num(void)
|
|
||||||
{
|
|
||||||
size_t num = 0;
|
|
||||||
struct tty *entry;
|
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link)
|
|
||||||
num++;
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t tty_num_active(void)
|
|
||||||
{
|
|
||||||
size_t num = 0;
|
|
||||||
struct tty *entry;
|
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
|
||||||
if (entry->pid)
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tty *tty_find_by_pid(pid_t pid)
|
|
||||||
{
|
|
||||||
struct tty *entry;
|
|
||||||
|
|
||||||
LIST_FOREACH(entry, &tty_list, link) {
|
|
||||||
if (entry->pid == pid)
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tty_exist(char *dev)
|
|
||||||
{
|
|
||||||
int fd, result;
|
|
||||||
struct termios c;
|
struct termios c;
|
||||||
|
int fd, rc;
|
||||||
|
|
||||||
fd = open(dev, O_RDWR);
|
fd = open(dev, O_RDWR);
|
||||||
if (-1 == fd)
|
if (-1 == fd)
|
||||||
return 1;
|
return 0;
|
||||||
|
|
||||||
/* XXX: Add check for errno == EIO? */
|
/* XXX: Add check for errno == EIO? */
|
||||||
result = tcgetattr(fd, &c);
|
rc = tcgetattr(fd, &c);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
return result;
|
return rc == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tty_start(struct tty *tty)
|
int tty_exec(svc_t *svc)
|
||||||
{
|
{
|
||||||
|
char *args[MAX_NUM_SVC_ARGS];
|
||||||
char *dev;
|
char *dev;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
if (tty->pid) {
|
/* try to protect system with sulogin, fall back to root shell */
|
||||||
_d("%s: TTY already active", tty->name);
|
if (svc->notty) {
|
||||||
return;
|
if (svc->rescue) {
|
||||||
|
/* check if bundled one is available */
|
||||||
|
if (whichp(_PATH_SULOGIN))
|
||||||
|
return execl(_PATH_SULOGIN, _PATH_SULOGIN, NULL);
|
||||||
|
|
||||||
|
/* util-linux or busybox, no args for compat */
|
||||||
|
if (whichp("sulogin"))
|
||||||
|
return execlp("sulogin", "sulogin", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Become session leader and set controlling TTY
|
||||||
|
* to enable Ctrl-C and job control in shell.
|
||||||
|
*/
|
||||||
|
setsid();
|
||||||
|
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||||
|
|
||||||
|
prctl(PR_SET_NAME, "finitsh", 0, 0, 0);
|
||||||
|
return execl(_PATH_BSHELL, "-sh", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = canonicalize(tty->name);
|
dev = tty_canonicalize(svc->dev);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
_d("%s: Cannot find TTY device: %s", tty->name, strerror(errno));
|
_d("%s: Cannot find TTY device: %s", svc->dev, strerror(errno));
|
||||||
return;
|
return EX_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tty_exist(dev)) {
|
if (!tty_exists(dev)) {
|
||||||
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
|
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
|
||||||
return;
|
return EX_OSFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tty->nologin) {
|
if (svc->nologin) {
|
||||||
_d("%s: Starting /bin/sh ...", dev);
|
_d("%s: Starting /bin/sh ...", dev);
|
||||||
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
|
return run_sh(dev, svc->noclear, svc->nowait, svc->rlimit);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
|
_d("%s: Starting %sgetty ...", dev, !svc->cmd ? "built-in " : "");
|
||||||
if (!tty->cmd)
|
if (!strcmp(svc->cmd, "tty"))
|
||||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
return run_getty(dev, svc->baud, svc->term, svc->noclear, svc->nowait, svc->rlimit);
|
||||||
else
|
|
||||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tty_stop(struct tty *tty)
|
for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
||||||
{
|
if (!svc->args[i][0])
|
||||||
if (!tty->pid)
|
break;
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
args[j++] = svc->args[i];
|
||||||
* XXX: TTY handling should be refactored to regular services,
|
|
||||||
* XXX: that way we could rely on the state machine to properly
|
|
||||||
* XXX: send SIGTERM, wait for max 2 sec to collect PID before
|
|
||||||
* XXX: sending SIGKILL.
|
|
||||||
*/
|
|
||||||
_d("Stopping TTY %s", tty->name);
|
|
||||||
kill(tty->pid, SIGKILL);
|
|
||||||
waitpid(tty->pid, NULL, 0);
|
|
||||||
tty->pid = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tty_enabled(struct tty *tty)
|
|
||||||
{
|
|
||||||
if (!tty)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (ISSET(tty->runlevels, runlevel))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fallback shell if no TTYs are active
|
|
||||||
*/
|
|
||||||
int tty_fallback(pid_t lost)
|
|
||||||
{
|
|
||||||
#ifdef FALLBACK_SHELL
|
|
||||||
if (lost == 1) {
|
|
||||||
if (fallback) {
|
|
||||||
kill(fallback, SIGKILL);
|
|
||||||
fallback = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
args[j++] = NULL;
|
||||||
|
|
||||||
if (fallback != lost || tty_num_active())
|
return run_getty2(dev, svc->cmd, args, svc->noclear, svc->nowait, svc->rlimit);
|
||||||
return 0;
|
|
||||||
|
|
||||||
fallback = fork();
|
|
||||||
if (fallback)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Become session leader and set controlling TTY
|
|
||||||
* to enable Ctrl-C and job control in shell.
|
|
||||||
*/
|
|
||||||
setsid();
|
|
||||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
|
||||||
|
|
||||||
prctl(PR_SET_NAME, "finitsh", 0, 0, 0);
|
|
||||||
_exit(execl(_PATH_BSHELL, _PATH_BSHELL, NULL));
|
|
||||||
#else
|
|
||||||
(void)lost;
|
|
||||||
#endif /* FALLBACK_SHELL */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tty_action(struct tty *tty)
|
|
||||||
{
|
|
||||||
if (!tty_enabled(tty))
|
|
||||||
tty_stop(tty);
|
|
||||||
else
|
|
||||||
tty_start(tty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TTY monitor, called by service_monitor()
|
|
||||||
*/
|
|
||||||
int tty_respawn(pid_t pid)
|
|
||||||
{
|
|
||||||
struct tty *tty = tty_find_by_pid(pid);
|
|
||||||
|
|
||||||
if (!tty)
|
|
||||||
return tty_fallback(pid);
|
|
||||||
|
|
||||||
/* Set DEAD_PROCESS UTMP entry */
|
|
||||||
utmp_set_dead(pid);
|
|
||||||
|
|
||||||
/* Clear PID to be able to respawn it. */
|
|
||||||
tty->pid = 0;
|
|
||||||
tty_action(tty);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Called after reload of /etc/finit.d/, stop/start TTYs
|
|
||||||
*/
|
|
||||||
void tty_reload(char *dev)
|
|
||||||
{
|
|
||||||
struct tty *tty;
|
|
||||||
|
|
||||||
if (dev) {
|
|
||||||
tty = tty_find(dev);
|
|
||||||
if (!tty) {
|
|
||||||
logit(LOG_WARNING, "No TTY registered for %s", dev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tty_action(tty);
|
|
||||||
tty->dirty = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tty_sweep();
|
|
||||||
|
|
||||||
LIST_FOREACH(tty, &tty_list, link) {
|
|
||||||
tty_action(tty);
|
|
||||||
tty->dirty = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
|
||||||
void tty_runlevel(void)
|
|
||||||
{
|
|
||||||
struct tty *tty;
|
|
||||||
|
|
||||||
LIST_FOREACH(tty, &tty_list, link)
|
|
||||||
tty_action(tty);
|
|
||||||
|
|
||||||
/* Start fallback shell if enabled && no TTYs */
|
|
||||||
tty_fallback(tty_num_active() > 0 ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,51 +25,33 @@
|
|||||||
#ifndef FINIT_TTY_H_
|
#ifndef FINIT_TTY_H_
|
||||||
#define FINIT_TTY_H_
|
#define FINIT_TTY_H_
|
||||||
|
|
||||||
#include <limits.h>
|
#include <stdio.h>
|
||||||
#include <sys/resource.h>
|
#include "svc.h"
|
||||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
|
||||||
|
|
||||||
#define TTY_MAX_ARGS 16
|
|
||||||
|
|
||||||
struct tty {
|
struct tty {
|
||||||
LIST_ENTRY(tty) link;
|
char *cmd;
|
||||||
|
char *args[MAX_NUM_SVC_ARGS];
|
||||||
|
size_t num;
|
||||||
|
|
||||||
char name[42];
|
char *dev;
|
||||||
char baud[10];
|
char *baud;
|
||||||
char term[10];
|
char *term;
|
||||||
int noclear;
|
char noclear;
|
||||||
int nowait;
|
char nowait;
|
||||||
int nologin;
|
char nologin;
|
||||||
int runlevels;
|
char notty;
|
||||||
|
char rescue;
|
||||||
char *cmd; /* NULL when running built-in getty */
|
|
||||||
char *args[TTY_MAX_ARGS];
|
|
||||||
|
|
||||||
int pid;
|
|
||||||
|
|
||||||
/* Limits and scoping */
|
|
||||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
|
||||||
|
|
||||||
/* Set if modified => reloaded, or -1 when marked for removal */
|
|
||||||
int dirty;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void tty_mark (void);
|
char *tty_canonicalize (char *dev);
|
||||||
void tty_sweep (void);
|
|
||||||
|
|
||||||
int tty_register (char *line, struct rlimit rlimit[], char *file);
|
int tty_isatcon (char *dev);
|
||||||
int tty_unregister (struct tty *tty);
|
char *tty_atcon (void);
|
||||||
|
|
||||||
struct tty *tty_find (char *dev);
|
int tty_parse_args (char *cmdline, struct tty *tty);
|
||||||
size_t tty_num (void);
|
|
||||||
size_t tty_num_active (void);
|
int tty_exists (char *dev);
|
||||||
struct tty *tty_find_by_pid (pid_t pid);
|
int tty_exec (svc_t *tty);
|
||||||
void tty_start (struct tty *tty);
|
|
||||||
void tty_stop (struct tty *tty);
|
|
||||||
int tty_enabled (struct tty *tty);
|
|
||||||
int tty_respawn (pid_t pid);
|
|
||||||
void tty_reload (char *dev);
|
|
||||||
void tty_runlevel (void);
|
|
||||||
|
|
||||||
#endif /* FINIT_TTY_H_ */
|
#endif /* FINIT_TTY_H_ */
|
||||||
|
|
||||||
|
|||||||
+286
-37
@@ -26,7 +26,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#ifdef HAVE_TERMIOS_H
|
#ifdef HAVE_TERMIOS_H
|
||||||
# include <termios.h>
|
# include <termios.h>
|
||||||
@@ -41,9 +40,96 @@
|
|||||||
#include <lite/lite.h> /* strlcat() */
|
#include <lite/lite.h> /* strlcat() */
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
int screen_rows = 24;
|
#ifdef HAVE_TERMIOS_H
|
||||||
int screen_cols = 80;
|
/*
|
||||||
char *prognm = NULL;
|
* This flag is used on *BSD when calling tcsetattr() to prevent it
|
||||||
|
* from changing speed, duplex, parity. GNU says we should use the
|
||||||
|
* CIGNORE flag to c_cflag, but that doesn't exist so ... we rely on
|
||||||
|
* our initial tcgetattr() and prey that nothing changes on the TTY
|
||||||
|
* before we exit and restore with tcsetattr()
|
||||||
|
*/
|
||||||
|
#ifndef TCSASOFT
|
||||||
|
#define TCSASOFT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct termios ttold;
|
||||||
|
static struct termios ttnew;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ttrows = 24;
|
||||||
|
int ttcols = 80;
|
||||||
|
char *prognm = NULL;
|
||||||
|
|
||||||
|
static char *signames[] = {
|
||||||
|
"",
|
||||||
|
"HUP", /* 1 */
|
||||||
|
"INT",
|
||||||
|
"QUIT",
|
||||||
|
"ILL",
|
||||||
|
"TRAP",
|
||||||
|
"ABRT",
|
||||||
|
"BUS",
|
||||||
|
"FPE", /* 8 */
|
||||||
|
"KILL",
|
||||||
|
"USR1",
|
||||||
|
"SEGV",
|
||||||
|
"USR2",
|
||||||
|
"PIPE",
|
||||||
|
"ALRM",
|
||||||
|
"TERM",
|
||||||
|
"STKFLT", /* 16 */
|
||||||
|
"CHLD",
|
||||||
|
"CONT",
|
||||||
|
"STOP",
|
||||||
|
"TSTP",
|
||||||
|
"TTIN",
|
||||||
|
"TTOU",
|
||||||
|
"URG",
|
||||||
|
"XCPU", /* 24 */
|
||||||
|
"XFSZ",
|
||||||
|
"VTALRM",
|
||||||
|
"PROF",
|
||||||
|
"WINCH",
|
||||||
|
"IO",
|
||||||
|
"PWR",
|
||||||
|
"SYS",
|
||||||
|
};
|
||||||
|
|
||||||
|
/* https://freedesktop.org/software/systemd/man/systemd.exec.html#id-1.20.8 */
|
||||||
|
static char *exitcodes[] = {
|
||||||
|
"SUCCESS", /* 0: Std C exit OK */
|
||||||
|
"FAILURE", /* 1: Std C exit FAIL */
|
||||||
|
/* 2-7: LSB init scripts (usually) */
|
||||||
|
"INVALIDARGUMENT", /* 2: Invalid or excess args */
|
||||||
|
"NOTIMPLEMENTED", /* 3: Unimplemented feature, e.g. 'reload' */
|
||||||
|
"NOPERMISSION", /* 4: Insufficient privilege */
|
||||||
|
"NOTINSTALLED", /* 5: Program is not installed */
|
||||||
|
"NOTCONFIGURED", /* 6: Program is not configured */
|
||||||
|
"NOTRUNNING", /* 7: Program is not running */
|
||||||
|
"", "", "", "", "", "", "", "", /* 8-63: Not standardized */
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 32-47 */
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 48-63 */
|
||||||
|
/* 64-78: BSD, from sysexit.h */
|
||||||
|
"USAGE", /* 64: Command line usage error */
|
||||||
|
"DATAERR", /* 65: data format error */
|
||||||
|
"NOINPUT", /* 66: cannot open input */
|
||||||
|
"NOUSER", /* 67: addressee unknown */
|
||||||
|
"NOHOST", /* 68: host name unknown */
|
||||||
|
"UNAVAILABLE", /* 69: service unavailable */
|
||||||
|
"SOFTWARE", /* 70: internal software error */
|
||||||
|
"OSERR", /* 71: system error (e.g., can't fork) */
|
||||||
|
"OSFILE", /* 72: critical OS file missing */
|
||||||
|
"CANTCREAT", /* 73: can't create (user) output file */
|
||||||
|
"IOERR", /* 74: input/output error */
|
||||||
|
"TEMPFAIL", /* 75: temp failure; user is invited to retry */
|
||||||
|
"PROTOCOL", /* 76: remote error in protocol */
|
||||||
|
"NOPERM", /* 77: permission denied */
|
||||||
|
"CONFIG", /* 78: configuration error */
|
||||||
|
/* 79-199: Not standardized, typically 127 == -1 */
|
||||||
|
/* >= 200: reserved (LSB), used by systemd */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
char *progname(char *arg0)
|
char *progname(char *arg0)
|
||||||
{
|
{
|
||||||
@@ -56,28 +142,89 @@ char *progname(char *arg0)
|
|||||||
return prognm;
|
return prognm;
|
||||||
}
|
}
|
||||||
|
|
||||||
int echo(char *file, int append, char *fmt, ...)
|
char *str(char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
static char buf[32];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fnread(char *buf, size_t len, char *fmt, ...)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
if (!file)
|
va_start(ap, fmt);
|
||||||
fp = stdout;
|
vsnprintf(path, sizeof(path), fmt, ap);
|
||||||
else
|
va_end(ap);
|
||||||
fp = fopen(file, append ? "a" : "w");
|
|
||||||
|
if (!buf || !len) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(path, &st))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return (ssize_t)st.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(path, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (fmt) {
|
len = fread(buf, sizeof(char), len, fp);
|
||||||
va_start(ap, fmt);
|
fclose(fp);
|
||||||
vfprintf(fp, fmt, ap);
|
|
||||||
va_end(ap);
|
return (int)len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fnwrite(char *value, char *fmt, ...)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
va_list ap;
|
||||||
|
FILE *fp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(path, sizeof(path), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
fp = fopen(path, "w");
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
/* echo(1) always adds a newline */
|
/* echo(1) always adds a newline */
|
||||||
fprintf(fp, "\n");
|
if (fputs(value, fp) == EOF || fputs("\n", fp) == EOF)
|
||||||
if (file)
|
rc = -1;
|
||||||
fclose(fp);
|
if (fclose(fp) == EOF)
|
||||||
|
rc = -1;
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fngetint(char *path, int *val)
|
||||||
|
{
|
||||||
|
char buf[10];
|
||||||
|
|
||||||
|
if (fnread(buf, sizeof(buf), path) <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
*val = strtoul(chomp(buf), NULL, 0);
|
||||||
|
if (errno)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -121,6 +268,29 @@ int strtobytes(char *arg)
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *sig2str(int sig)
|
||||||
|
{
|
||||||
|
static char signame[8];
|
||||||
|
|
||||||
|
if (sig < 1 || sig >= (int)NELEMS(signames))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
snprintf(signame, sizeof(signame), "/%s", signames[sig]);
|
||||||
|
return signame;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *code2str(int code)
|
||||||
|
{
|
||||||
|
static char codename[20];
|
||||||
|
|
||||||
|
if (code < 0 || code >= (int)NELEMS(exitcodes))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
snprintf(codename, sizeof(codename), "/%s", exitcodes[code]);
|
||||||
|
return codename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void do_sleep(unsigned int sec)
|
void do_sleep(unsigned int sec)
|
||||||
{
|
{
|
||||||
struct timespec requested_sleep = {
|
struct timespec requested_sleep = {
|
||||||
@@ -182,6 +352,32 @@ char *uptime(long secs, char *buf, size_t len)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *memsz(uint64_t sz, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int gb, mb, kb, b;
|
||||||
|
|
||||||
|
if (!sz) {
|
||||||
|
strlcpy(buf, "--.--", len);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb = sz / (1024 * 1024 * 1024);
|
||||||
|
sz = sz % (1024 * 1024 * 1024);
|
||||||
|
mb = sz / (1024 * 1024);
|
||||||
|
sz = sz % (1024 * 1024);
|
||||||
|
kb = sz / (1024);
|
||||||
|
b = sz % (1024);
|
||||||
|
|
||||||
|
if (gb)
|
||||||
|
snprintf(buf, len, "%d.%dG", gb, mb / 102);
|
||||||
|
else if (mb)
|
||||||
|
snprintf(buf, len, "%d.%dM", mb, kb / 102);
|
||||||
|
else
|
||||||
|
snprintf(buf, len, "%d.%dk", kb, b / 102);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify string argument is NUL terminated
|
* Verify string argument is NUL terminated
|
||||||
* Verify string is a JOB[:ID], JOB and ID
|
* Verify string is a JOB[:ID], JOB and ID
|
||||||
@@ -193,7 +389,7 @@ char *sanitize(char *arg, size_t len)
|
|||||||
regex_t preg;
|
regex_t preg;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (strlen(arg) > len)
|
if (!memchr(arg, 0, len))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
|
if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
|
||||||
@@ -207,39 +403,43 @@ char *sanitize(char *arg, size_t len)
|
|||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS_H
|
||||||
/*
|
/*
|
||||||
* Called by initctl, and by finit at boot and shutdown, to
|
* Called by initctl, and by finit at boot and shutdown, to
|
||||||
* (re)initialize the screen size for print() et al.
|
* (re)initialize the screen size for print() et al.
|
||||||
*/
|
*/
|
||||||
int screen_init(void)
|
int ttinit(void)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_TERMIOS_H
|
|
||||||
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
|
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
|
||||||
struct winsize ws = { 0 };
|
struct winsize ws = { 0 };
|
||||||
struct termios tc, saved;
|
struct termios tc, saved;
|
||||||
|
int cached = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Basic TTY init, CLOCaL is important or TIOCWINSZ will block
|
* Basic TTY init, CLOCaL is important or TIOCWINSZ will block
|
||||||
* until DCD is asserted, and we won't ever get it.
|
* until DCD is asserted, and we won't ever get it.
|
||||||
*/
|
*/
|
||||||
tcgetattr(STDERR_FILENO, &tc);
|
if (!tcgetattr(STDERR_FILENO, &tc)) {
|
||||||
saved = tc;
|
saved = tc;
|
||||||
tc.c_cflag |= (CLOCAL | CREAD);
|
cached = 1;
|
||||||
tc.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
|
||||||
tcsetattr(STDERR_FILENO, TCSANOW, &tc);
|
tc.c_cflag |= (CLOCAL | CREAD);
|
||||||
|
tc.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||||
|
tcsetattr(STDERR_FILENO, TCSANOW, &tc);
|
||||||
|
}
|
||||||
|
|
||||||
/* 1. Try TIOCWINSZ to query window size from kernel */
|
/* 1. Try TIOCWINSZ to query window size from kernel */
|
||||||
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
|
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
|
||||||
screen_rows = ws.ws_row;
|
ttrows = ws.ws_row;
|
||||||
screen_cols = ws.ws_col;
|
ttcols = ws.ws_col;
|
||||||
|
|
||||||
/* Likely doesn't work in PID 1 after kernel starts us ... */
|
/* Likely doesn't work in PID 1 after kernel starts us ... */
|
||||||
if (!ws.ws_row && !ws.ws_col)
|
if (!ws.ws_row && !ws.ws_col)
|
||||||
goto fallback;
|
goto fallback;
|
||||||
} else if (!isatty(STDOUT_FILENO)) {
|
} else if (!isatty(STDOUT_FILENO)) {
|
||||||
/* 2. We may be running under watch(1) */
|
/* 2. We may be running under watch(1) */
|
||||||
screen_cols = atonum(getenv("COLUMNS"));
|
ttcols = atonum(getenv("COLUMNS"));
|
||||||
screen_rows = atonum(getenv("LINES"));
|
ttrows = atonum(getenv("LINES"));
|
||||||
} else {
|
} else {
|
||||||
fallback:
|
fallback:
|
||||||
/* 3. ANSI goto + query cursor position trick as fallback */
|
/* 3. ANSI goto + query cursor position trick as fallback */
|
||||||
@@ -249,8 +449,8 @@ int screen_init(void)
|
|||||||
int row, col;
|
int row, col;
|
||||||
|
|
||||||
if (scanf("\e[%d;%dR", &row, &col) == 2) {
|
if (scanf("\e[%d;%dR", &row, &col) == 2) {
|
||||||
screen_cols = col;
|
ttcols = col;
|
||||||
screen_rows = row;
|
ttrows = row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,18 +458,67 @@ int screen_init(void)
|
|||||||
fprintf(stderr, "\e8");
|
fprintf(stderr, "\e8");
|
||||||
}
|
}
|
||||||
|
|
||||||
tcsetattr(STDERR_FILENO, TCSANOW, &saved);
|
if (cached)
|
||||||
|
tcsetattr(STDERR_FILENO, TCSANOW, &saved);
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
if (screen_cols <= 0)
|
if (ttcols <= 0)
|
||||||
screen_cols = 80;
|
ttcols = 80;
|
||||||
if (screen_rows <= 0)
|
if (ttrows <= 0)
|
||||||
screen_rows = 24;
|
ttrows = 24;
|
||||||
#endif
|
|
||||||
|
|
||||||
return screen_cols;
|
return ttcols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function sets the terminal to RAW mode, as defined for the current
|
||||||
|
* shell. This is called both by ttopen() above and by spawncli() to
|
||||||
|
* get the current terminal settings and then change them to what
|
||||||
|
* mg expects. Thus, tty changes done while spawncli() is in effect
|
||||||
|
* will be reflected in mg.
|
||||||
|
*/
|
||||||
|
int ttraw(void)
|
||||||
|
{
|
||||||
|
if (tcgetattr(0, &ttold) == -1) {
|
||||||
|
fprintf(stderr, "%s: failed querying tty attrs: %s\n",
|
||||||
|
prognm, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)memcpy(&ttnew, &ttold, sizeof(ttnew));
|
||||||
|
/* Set terminal to 'raw' mode and ignore a 'break' */
|
||||||
|
ttnew.c_cc[VMIN] = 1;
|
||||||
|
ttnew.c_cc[VTIME] = 0;
|
||||||
|
ttnew.c_iflag |= IGNBRK;
|
||||||
|
ttnew.c_iflag &= ~(BRKINT | PARMRK | INLCR | IGNCR | ICRNL | IXON);
|
||||||
|
ttnew.c_oflag &= ~OPOST;
|
||||||
|
ttnew.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
|
||||||
|
|
||||||
|
if (tcsetattr(0, TCSASOFT | TCSADRAIN, &ttnew) == -1) {
|
||||||
|
fprintf(stderr, "%s: failed setting tty in raw mode: %s\n",
|
||||||
|
prognm, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function restores all terminal settings to their default values,
|
||||||
|
* in anticipation of exiting or suspending the editor.
|
||||||
|
*/
|
||||||
|
int ttcooked(void)
|
||||||
|
{
|
||||||
|
if (tcsetattr(0, TCSASOFT | TCSADRAIN, &ttold) == -1) {
|
||||||
|
fprintf(stderr, "%s: failed restoring tty to cooked mode: %s\n",
|
||||||
|
prognm, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_TERMIOS_H */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
+38
-7
@@ -25,9 +25,10 @@
|
|||||||
#define FINIT_UTIL_H_
|
#define FINIT_UTIL_H_
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef HAVE_TERMIOS_H /* for screen_width() */
|
#ifdef HAVE_TERMIOS_H
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <sys/reboot.h>
|
#include <sys/reboot.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -37,25 +38,55 @@
|
|||||||
#define RB_SW_SUSPEND 0xd000fce2
|
#define RB_SW_SUSPEND 0xd000fce2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int screen_rows;
|
extern int debug; /* defined in finit or initctl */
|
||||||
extern int screen_cols;
|
extern int ttrows;
|
||||||
|
extern int ttcols;
|
||||||
extern char *prognm;
|
extern char *prognm;
|
||||||
|
|
||||||
#define SCREEN_WIDTH screen_cols
|
/* Define for printheader() from conio.h, used in initctl */
|
||||||
|
#define SCREEN_WIDTH ttcols
|
||||||
#include <lite/conio.h>
|
#include <lite/conio.h>
|
||||||
|
|
||||||
char *progname (char *arg0);
|
char *progname (char *arg0);
|
||||||
int echo (char *file, int append, char *fmt, ...);
|
|
||||||
|
char *str (char *fmt, ...);
|
||||||
|
int fnread (char *buf, size_t len, char *fmt, ...);
|
||||||
|
int fnwrite (char *value, char *fmt, ...);
|
||||||
|
int fngetint (char *path, int *val);
|
||||||
|
|
||||||
int has_utmp (void);
|
int has_utmp (void);
|
||||||
int strtobytes (char *arg);
|
int strtobytes (char *arg);
|
||||||
void do_sleep (unsigned int sec);
|
char *sig2str (int sig);
|
||||||
|
char *code2str (int code);
|
||||||
|
|
||||||
|
void do_sleep (unsigned int sec);
|
||||||
long jiffies (void);
|
long jiffies (void);
|
||||||
char *uptime (long secs, char *buf, size_t len);
|
char *uptime (long secs, char *buf, size_t len);
|
||||||
|
char *memsz (uint64_t sz, char *buf, size_t len);
|
||||||
|
|
||||||
char *sanitize (char *arg, size_t len);
|
char *sanitize (char *arg, size_t len);
|
||||||
|
|
||||||
int screen_init (void);
|
#ifdef HAVE_TERMIOS_H
|
||||||
|
int ttinit (void);
|
||||||
|
int ttraw (void);
|
||||||
|
int ttcooked (void);
|
||||||
|
#else
|
||||||
|
#define ttinit() ttcols
|
||||||
|
#define ttraw() 0
|
||||||
|
#define ttcooked() 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void dbg(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (!debug)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
static inline char *strterm(char *str, size_t len)
|
static inline char *strterm(char *str, size_t len)
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user