mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
250
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ccfea59cb3 | ||
|
|
7b315364ca | ||
|
|
e9e28ece1c | ||
|
|
c1f9b4e1f6 | ||
|
|
6ebb23e0f0 | ||
|
|
026c229005 | ||
|
|
4d05bf9359 | ||
|
|
289247dab9 | ||
|
|
e44dfde54e | ||
|
|
35b200d3a9 | ||
|
|
5badf4d376 | ||
|
|
24f274c126 | ||
|
|
1abd43384b | ||
|
|
70a1acc210 | ||
|
|
6af0446490 | ||
|
|
ecaf111a4b | ||
|
|
32ec25b070 | ||
|
|
305ad302f2 | ||
|
|
d6390244ad | ||
|
|
790a316607 | ||
|
|
77aa941e84 | ||
|
|
e0a65f67c9 | ||
|
|
3ed291789c | ||
|
|
774a24cdd8 | ||
|
|
6011f91f22 | ||
|
|
07a364e570 | ||
|
|
5b9d99011b | ||
|
|
f4f773b4c7 | ||
|
|
9d949c4186 | ||
|
|
1434561a46 | ||
|
|
cf3d3f6f8e | ||
|
|
621da582c2 | ||
|
|
9167d9255d | ||
|
|
c71bafd495 | ||
|
|
b3b7a33252 | ||
|
|
d1efc60c84 | ||
|
|
9a24dfd98d | ||
|
|
0e3fe5e3bf | ||
|
|
224f42c344 | ||
|
|
e60eb94810 | ||
|
|
464bc831a5 | ||
|
|
e84911fe52 | ||
|
|
4bcfd606b4 | ||
|
|
fe27ed17eb | ||
|
|
b3ea23ed26 |
+10
-13
@@ -5,9 +5,6 @@ language: c
|
||||
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
|
||||
sudo: false
|
||||
|
||||
# Use non-docker builds atm. to install libuEv and libite from source
|
||||
#sudo: true
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
compiler:
|
||||
- gcc
|
||||
@@ -30,7 +27,7 @@ addons:
|
||||
name: "troglobit/finit"
|
||||
description: "finit | Fast & Extensible init for Linux"
|
||||
notification_email: troglobit@gmail.com
|
||||
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
|
||||
build_command_prepend: "./autogen.sh && ./configure --prefix= --disable-doc --disable-contrib --with-watchdog"
|
||||
build_command: "make"
|
||||
branch_pattern: dev
|
||||
|
||||
@@ -45,20 +42,20 @@ install:
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --prefix=/tmp --enable-static
|
||||
- ./configure --prefix= --enable-static
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
|
||||
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog
|
||||
- cat config.log
|
||||
- make clean
|
||||
- make V=1 -j5
|
||||
- make install-strip
|
||||
- tree /tmp
|
||||
- ldd /tmp/sbin/finit
|
||||
- size /tmp/sbin/finit
|
||||
- size /tmp/sbin/initctl
|
||||
- size /tmp/sbin/reboot
|
||||
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
|
||||
- make install-strip DESTDIR=/tmp/finit
|
||||
- tree /tmp/finit
|
||||
- ldd /tmp/finit/sbin/finit
|
||||
- size /tmp/finit/sbin/finit
|
||||
- size /tmp/finit/sbin/initctl
|
||||
- size /tmp/finit/sbin/reboot
|
||||
- LD_LIBRARY_PATH=/tmp/lib /tmp/finit/sbin/initctl -h
|
||||
- unshare -V
|
||||
- nsenter -V
|
||||
- chroot --version
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.0][UNRELEASED] - 2021-02-21
|
||||
[4.0][UNRELEASED] - 2021-03-xx
|
||||
------------------------------
|
||||
|
||||
This release became v4.0, and not v3.2, because of incompatible changes
|
||||
@@ -18,6 +18,9 @@ make sure to read the whole changelog when upgrading.
|
||||
with a fallback to sending signals as per traditional SysV init. The
|
||||
`-f` (force) flag remains, where `reboot(2)` is called directly
|
||||
* 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,
|
||||
use an external inetd, like xinetd, instead. A pull request for a
|
||||
stand-alone inetd, like watchdogd and getty, is most welcome!
|
||||
|
||||
@@ -26,6 +26,25 @@ Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
|
||||
was reverse engineered from the [EeePC fastinit][] over ten years ago
|
||||
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
|
||||
|
||||

|
||||
*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
|
||||
|
||||
Focus is on small and embedded systems, although Finit is fully usable
|
||||
on server and desktop systems as well. For working examples, see the
|
||||
[contrib/](contrib/) section with tutorials for:
|
||||
@@ -34,9 +53,6 @@ on server and desktop systems as well. For working examples, see the
|
||||
* [Alpine Linux](contrib/alpine/), and
|
||||
* [Debian GNU/Linux](contrib/debian/)
|
||||
|
||||

|
||||
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
@@ -58,10 +74,10 @@ runlevel 2
|
||||
log size=100k count=4
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
service [S12345] watchdogd -L -f -- System watchdog daemon
|
||||
service [S12345] syslogd -n -b 3 -D -- System log daemon
|
||||
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
|
||||
service [2345] lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
|
||||
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon
|
||||
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
|
||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||
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
|
||||
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||
@@ -76,24 +92,24 @@ service :80 [2345] merecat -n -p 80 /var/www -- 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
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
||||
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
|
||||
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd -- Preparing console
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
|
||||
# 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/tty2 linux
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty3 linux
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
|
||||
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
|
||||
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
|
||||
|
||||
# Use built-in getty for serial port and USB serial
|
||||
tty [12345] /dev/ttyAMA0 noclear nowait
|
||||
tty [12345] /dev/ttyUSB0 noclear
|
||||
#tty [12345] /dev/ttyAMA0 noclear nowait
|
||||
#tty [12345] /dev/ttyUSB0 noclear
|
||||
|
||||
# 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
|
||||
@@ -232,8 +248,11 @@ For more information, see [doc/plugins.md](doc/plugins.md).
|
||||
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
|
||||
registering any changes to `.conf` files. To activate a change the user
|
||||
must call `initctl reload`, which reloads all modified files, stops any
|
||||
removed services, starts new ones, and restarts any modified ones, with
|
||||
SIGHUP if the process supports it.
|
||||
removed services, starts new ones, and restarts any modified ones. If the
|
||||
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
|
||||
unnecessary overhead, which can be removed at build-time using:
|
||||
@@ -241,6 +260,46 @@ unnecessary overhead, which can be removed at build-time using:
|
||||
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
|
||||
------------------------
|
||||
|
||||
@@ -348,11 +407,18 @@ Finit also implements a modern API to query status, and start/stop
|
||||
services, called `initctl`. Unlike `telinit` the `initctl` tool does
|
||||
not return until the given command has fully completed.
|
||||
|
||||
```shell
|
||||
~ $ initctl -h
|
||||
```
|
||||
alpine:~# initctl help
|
||||
Usage: initctl [OPTIONS] [COMMAND]
|
||||
|
||||
Options:
|
||||
-b, --batch Batch mode, no screen size probing
|
||||
-c, --create Create missing paths (and files) as needed
|
||||
-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
|
||||
-v, --verbose Verbose output
|
||||
-h, --help This help text
|
||||
|
||||
@@ -360,29 +426,42 @@ Commands:
|
||||
debug Toggle Finit (daemon) debug
|
||||
help This help text
|
||||
version Show Finit version
|
||||
|
||||
list List all .conf in /etc/finit.d/
|
||||
enable <CONF> Enable .conf in /etc/finit.d/available/
|
||||
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
|
||||
touch <CONF> Mark .conf in /etc/finit.d/ for reload
|
||||
reload Reload *.conf in /etc/finit.d/ (activates changes)
|
||||
|
||||
cond show Show condition status
|
||||
|
||||
ls | list List all .conf in /etc/finit.d
|
||||
create <CONF> Create .conf in /etc/finit.d/available
|
||||
delete <CONF> Delete .conf in /etc/finit.d/available
|
||||
show <CONF> Show .conf in /etc/finit.d/available
|
||||
edit <CONF> Edit .conf in /etc/finit.d/available
|
||||
touch <CONF> Change .conf in /etc/finit.d/available
|
||||
enable <CONF> Enable .conf in /etc/finit.d/available
|
||||
disable <CONF> Disable .conf in /etc/finit.d/enabled
|
||||
reload Reload *.conf in /etc/finit.d (activate changes)
|
||||
|
||||
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
|
||||
|
||||
|
||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
||||
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
||||
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
||||
status <JOB|NAME>[:ID] Show service status, by job# or name
|
||||
status | show Show status of services, default command
|
||||
|
||||
start <NAME>[:ID] Start service by name, with optional ID
|
||||
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
|
||||
status <NAME>[:ID] Show service status, by name
|
||||
status Show status of services, default command
|
||||
|
||||
cgroup List cgroup config overview
|
||||
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
|
||||
reboot Reboot system
|
||||
halt Halt system
|
||||
poweroff Halt and power off system
|
||||
|
||||
utmp show Raw dump of UTMP/WTMP db
|
||||
suspend Suspend system
|
||||
|
||||
Bug report address: https://github.com/troglobit/finit/issues
|
||||
Project homepage: https://troglobit.com/finit.html
|
||||
```
|
||||
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
@@ -390,22 +469,73 @@ file must be used to tell Finit to stop and start it on `reload` and
|
||||
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
|
||||
these will also affect how a service is maintained.
|
||||
|
||||
**Note:** even though it is possible to start services not belonging in
|
||||
the current runlevel these services will not be respawned automatically
|
||||
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.
|
||||
> **Note:** even though it is possible to start services not belonging in
|
||||
> the current runlevel these services will not be respawned automatically
|
||||
> 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.
|
||||
|
||||
```shell
|
||||
~ $ initctl status -v
|
||||
1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
|
||||
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
|
||||
3 running 478 [S12345] /sbin/klogd -n
|
||||
4:1 inetd 0 [2345] internal time allow *:37,!eth0
|
||||
4:2 inetd 0 [2345] internal time allow eth0:3737
|
||||
5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
|
||||
5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
|
||||
6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
|
||||
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
|
||||
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 -p
|
||||
PID IDENT STATUS RUNLEVELS DESCRIPTION
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] ACPI daemon
|
||||
1509 crond running [--2345----] Cron daemon
|
||||
1489 dropbear running [--2345----] Dropbear SSH daemon
|
||||
1511 klogd running [S12345----] Kernel log daemon
|
||||
1512 ntpd running [--2345----] NTP daemon
|
||||
1473 syslogd running [S12345----] Syslog daemon
|
||||
|
||||
alpine:~# initctl -pv
|
||||
PID IDENT STATUS RUNLEVELS COMMAND
|
||||
======================================================================
|
||||
1506 acpid running [--2345----] acpid -f
|
||||
1509 crond running [--2345----] crond -f -S $CRON_OPTS
|
||||
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
|
||||
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
|
||||
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
|
||||
1473 syslogd running [S12345----] syslogd -n
|
||||
```
|
||||
|
||||
The environment variables to each of the services above are read from,
|
||||
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
@@ -473,6 +603,6 @@ and proposed extensions.
|
||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||
[Travis]: https://travis-ci.org/troglobit/finit
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
|
||||
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
||||
|
||||
+20
-22
@@ -1,4 +1,4 @@
|
||||
AC_INIT(Finit, 4.0-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 4.0-rc4, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
@@ -32,7 +32,7 @@ AC_PROG_MKDIR_P
|
||||
|
||||
# Configuration.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([fstaby.h termios.h])
|
||||
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
|
||||
AC_CHECK_FUNCS([strstr getopt getfsenty])
|
||||
|
||||
# Check for uint[8,16,32]_t
|
||||
@@ -53,21 +53,13 @@ AC_ARG_ENABLE(emergency_shell,
|
||||
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,
|
||||
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
|
||||
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,
|
||||
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
|
||||
enable_redirect_output=no])
|
||||
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
||||
enable_redirect=yes])
|
||||
|
||||
AC_ARG_ENABLE(logit,
|
||||
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
|
||||
@@ -128,21 +120,19 @@ 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]),
|
||||
[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],[watchdog=])
|
||||
|
||||
### 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"], [
|
||||
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"], [
|
||||
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
|
||||
|
||||
@@ -153,6 +143,8 @@ AS_IF([test "x$enable_logit" != "xno"], [
|
||||
AS_IF([test "x$enable_logrotate" != "xno"], [
|
||||
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
|
||||
|
||||
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
|
||||
|
||||
### With features ##############################################################################
|
||||
AS_IF([test "x$with_config" != "xno"], [
|
||||
AS_IF([test "x$conf" = "xyes"], [
|
||||
@@ -202,9 +194,15 @@ AS_IF([test "x$with_random_seed" != "xno"], [
|
||||
AC_EXPAND_DIR(random_path, "$random_seed")
|
||||
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])])
|
||||
|
||||
# Control build with automake flags
|
||||
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(CONTRIB, [test "x$enable_contrib" = "xyes"])
|
||||
@@ -251,11 +249,11 @@ Behavior:
|
||||
Optional features:
|
||||
Install doc/..........: $enable_doc
|
||||
Install contrib/......: $enable_contrib
|
||||
Built-in watchdogd....: $enable_watchdog
|
||||
Built-in watchdogd....: $with_watchdog $watchdog
|
||||
Built-in logrotate....: $enable_logrotate
|
||||
Scripting tool logit..: $enable_logit
|
||||
Emergency shell.......: $enable_emergency_shell
|
||||
Fallback shell........: $enable_fallback_shell
|
||||
Redirect output.......: $enable_redirect
|
||||
Default hostname......: $hostname
|
||||
Default runlevel......: $runlevel
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
|
||||
--prefix=/usr --exec-prefix= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--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"
|
||||
|
||||
echo
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
docsdir := @docdir@/contrib/alpine/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
|
||||
lxdm.conf modules.conf ntpd.conf klogd.conf syslog.conf \
|
||||
watchdog.conf
|
||||
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf
|
||||
EXTRA_DIST = $(docs_DATA)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Consoles to start getty on when system is up
|
||||
tty [12345] /dev/tty1 noclear nowait
|
||||
tty [2345] /dev/tty2 noclear nowait
|
||||
tty [2345] /dev/tty3 noclear nowait
|
||||
tty [12345] /dev/ttyS0 noclear
|
||||
tty [12345] /dev/tty1 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 mousedev
|
||||
#module 8139cp
|
||||
module softdog
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# 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
|
||||
|
||||
+29
-2
@@ -8,6 +8,18 @@ module evdev
|
||||
module loop
|
||||
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 2
|
||||
|
||||
@@ -16,7 +28,7 @@ network service networking start
|
||||
|
||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||
log size=100k count=4
|
||||
log size:100k count:4
|
||||
|
||||
# Virtual consoles to start getty on
|
||||
tty /dev/tty1
|
||||
@@ -30,9 +42,24 @@ tty /dev/tty3
|
||||
#run [2] /etc/init.d/networking start -- Start networking
|
||||
|
||||
# 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] 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
|
||||
# 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= \
|
||||
--sysconfdir=/etc --localstatedir=/var \
|
||||
--enable-dbus-plugin --enable-x11-common-plugin \
|
||||
--enable-alsa-utils-plugin --enable-watchdog \
|
||||
--enable-alsa-utils-plugin --with-watchdog \
|
||||
--with-heading="Void Linux"
|
||||
|
||||
echo
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
docsdir := @docdir@/contrib/void/finit.d/available
|
||||
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
|
||||
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
|
||||
sshd.conf syslogd.conf uuidd.conf
|
||||
keymap.conf lxdm.conf modules.conf ntpd.conf \
|
||||
sshd.conf syslog.conf uuidd.conf
|
||||
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 mousedev
|
||||
#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
|
||||
+4
-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
|
||||
from the very terse one-liner format currently used
|
||||
* 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
|
||||
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,
|
||||
@@ -29,7 +26,10 @@ Some of these are also registered in the issue tracker on GitHub.
|
||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||
* Write man pages for finit and `finit.conf`, steal from the excellent
|
||||
`pimd` man pages ...
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown
|
||||
* 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.
|
||||
`initctl show --json`
|
||||
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ Bootstrap
|
||||
24. Call 4th level hooks, `HOOK_SVC_UP`
|
||||
25. Call `/etc/rc.local`, if it exists and is an executable shell script
|
||||
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
|
||||
`/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"
|
||||
```
|
||||
|
||||
To debug startup issues, in particular issues with getty/login, try
|
||||
`configure --enable-fallback-shell`. When no TTYs are detected, and
|
||||
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
|
||||
and would leave your production system potentially wide open.
|
||||
To debug startup issues, in particular issues with getty/login, add
|
||||
the following to your Finit .conf file:
|
||||
|
||||
tty [12345789] notty noclear
|
||||
|
||||
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
|
||||
kernel usually reboots: `configure --enable-emergency-shell`. However,
|
||||
the behavior of Finit is severely limited when this is enabled, so use
|
||||
it only for debugging start up issues when Finit crashes.
|
||||
|
||||
**NOTE:** Neither of these two configure options should be enabled on
|
||||
production systems since they can potentially give a user root access.
|
||||
**NOTE:** Neither of these options should be enabled on production
|
||||
systems since they can potentially give a user root access.
|
||||
|
||||
|
||||
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
|
||||
|
||||
+27
-1
@@ -45,7 +45,31 @@ being created.
|
||||
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
|
||||
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"
|
||||
> or recreate their PID file on `SIGHUP`.
|
||||
|
||||
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
|
||||
|
||||
Built-in Conditions
|
||||
-------------------
|
||||
|
||||
+104
-10
@@ -2,7 +2,9 @@ Configuration
|
||||
=============
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service Environment](#service-environment)
|
||||
* [Service Wrapper Scripts](#service-wrapper-scripts)
|
||||
* [Cgroups](#cgroups)
|
||||
* [Configuration File Syntax](#configuration-file-syntax)
|
||||
* [Hostname](#hostname)
|
||||
* [Kernel Modules](#kernel-modules)
|
||||
@@ -128,6 +130,67 @@ example employs a wrapper script in `/etc/start.d`.
|
||||
> 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
|
||||
-------------------------
|
||||
|
||||
@@ -333,6 +396,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
|
||||
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
|
||||
|
||||
@@ -353,6 +438,7 @@ you will block Finit.
|
||||
|
||||
> **Note:** only read and executed in runlevel S (bootstrap).
|
||||
|
||||
|
||||
### Including Finit Configs
|
||||
|
||||
**Syntax:** `include <CONF>`
|
||||
@@ -377,8 +463,8 @@ size limit is reached.
|
||||
|
||||
### TTYs and Consoles
|
||||
|
||||
**Syntax:** `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
|
||||
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
|
||||
|
||||
The first variant of this option uses the built-in getty on the given
|
||||
TTY device DEV, in the given runlevels. The DEV may be the special
|
||||
@@ -386,6 +472,11 @@ keyword `@console`, or `console`, useful on embedded systems.
|
||||
|
||||
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:**
|
||||
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||
@@ -423,16 +514,22 @@ uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
|
||||
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
|
||||
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:**
|
||||
|
||||
tty [12345] @console noclear vt220
|
||||
|
||||
On really bare bones systems Finit offers a fallback shell, which
|
||||
should not be enabled on production systems since. This because it
|
||||
may give a user root access without having to log in. However, for
|
||||
bringup and system debugging it can come in handy:
|
||||
On really bare bones systems Finit can give you a shell prompt as
|
||||
soon as bootstrap is done, without opening any device node:
|
||||
|
||||
configure --enable-fallback-shell
|
||||
tty [12345789] notty noclear
|
||||
|
||||
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:
|
||||
|
||||
@@ -483,9 +580,6 @@ Log rotation is controlled using the global `log` setting.
|
||||
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
|
||||
Worth noting is that conditions is allowed for all these stanzas. For a
|
||||
detailed description, see the [Conditions](conditions.md) document.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ The current state depends on the two following conditions:
|
||||
|
||||
A service may be stopped, or blocked, for several reasons:
|
||||
|
||||
- The user has manually stopped the service using `initctl stop JOB`
|
||||
- The user has manually stopped the service using `initctl stop NAME`
|
||||
- The program exits immediately. I.e. keeps crashing (make sure to use
|
||||
the 'run this service in the foreground' command line option)
|
||||
- The binary is missing in the filesystem
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 138 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
+2
-2
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
|
||||
|
||||
if STATIC
|
||||
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 tty.c urandom.c usr.c
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
libplug_la_SOURCES += alsa-utils.c
|
||||
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
|
||||
endif
|
||||
|
||||
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 tty.la urandom.la usr.la
|
||||
|
||||
if BUILD_ALSA_UTILS_PLUGIN
|
||||
pkglib_LTLIBRARIES += alsa-utils.la
|
||||
|
||||
+31
-39
@@ -65,7 +65,7 @@ static int is_tmpfs(char *path)
|
||||
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)
|
||||
return 1;
|
||||
@@ -76,8 +76,14 @@ static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct
|
||||
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[] = {
|
||||
"/tmp/",
|
||||
@@ -90,7 +96,7 @@ static void bootclean(void)
|
||||
if (is_tmpfs(dir[i]))
|
||||
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)
|
||||
{
|
||||
int uid, gid;
|
||||
mode_t prev;
|
||||
|
||||
/* Cleanup stale files, if any still linger on. */
|
||||
bootclean();
|
||||
prev = umask(0);
|
||||
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
umask(0);
|
||||
makedir("/var/cache", 0755);
|
||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/games", 0755);
|
||||
@@ -133,19 +137,21 @@ static void setup(void *arg)
|
||||
makedir("/var/tmp", 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
|
||||
*/
|
||||
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/log/wtmp", 0644, 0, gid); /* Login history */
|
||||
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
|
||||
@@ -165,39 +171,25 @@ static void setup(void *arg)
|
||||
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
|
||||
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
|
||||
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
|
||||
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
|
||||
makedir("/var/run/frr", 0755); /* Needed by FRR */
|
||||
makedir("/var/log/frr", 0755); /* Needed by FRR */
|
||||
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
|
||||
mksubsys("/var/run/frr", 0755, "frr", "frr");
|
||||
mksubsys("/var/log/frr", 0755, "frr", "frr");
|
||||
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"))
|
||||
symlink("../proc/self/mounts", "/etc/mtab");
|
||||
|
||||
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
|
||||
uid = getuser("uuidd", NULL);
|
||||
if (uid >= 0) {
|
||||
gid = getgroup("uuidd");
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
makedir("/var/run/uuidd", 0755);
|
||||
chown("/var/run/uuidd", uid, gid);
|
||||
}
|
||||
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.hook[HOOK_MOUNT_POST] = { .cb = clean },
|
||||
.hook[HOOK_BASEFS_UP] = { .cb = setup },
|
||||
.depends = { "pidfile" },
|
||||
};
|
||||
|
||||
|
||||
+13
-11
@@ -32,13 +32,16 @@
|
||||
#include "conf.h"
|
||||
|
||||
#define DAEMON "dbus-daemon"
|
||||
#define ARGS "--nofork --system"
|
||||
#define ARGS "--nofork --system --syslog-only"
|
||||
#define DESC "D-Bus message bus daemon"
|
||||
|
||||
#define DAEMONUSER "messagebus"
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
char *cmd;
|
||||
char line[256];
|
||||
mode_t prev;
|
||||
char *cmd;
|
||||
|
||||
cmd = which(DAEMON);
|
||||
if (!cmd) {
|
||||
@@ -46,29 +49,28 @@ static void setup(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
umask(0);
|
||||
prev =umask(0);
|
||||
|
||||
makedir("/var/run/dbus", 0755);
|
||||
makedir("/var/lock/subsys", 0755);
|
||||
makedir("/var/lock/subsys/messagebus", 0755);
|
||||
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
|
||||
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 */
|
||||
erase("/var/run/dbus/pid");
|
||||
remove("/var/run/dbus/pid");
|
||||
|
||||
/* 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] @%s:%s %s %s -- %s",
|
||||
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.name = __FILE__,
|
||||
.hook[HOOK_NETWORK_UP] = {
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
|
||||
@@ -90,6 +90,7 @@ static plugin_t plugin = {
|
||||
.hook[HOOK_BASEFS_UP] = {
|
||||
.cb = setup
|
||||
},
|
||||
.depends = { "bootmisc" },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+75
-40
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h> /* IFNAMSIZ */
|
||||
#include <sys/socket.h>
|
||||
@@ -48,7 +49,7 @@ static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_route(struct nlmsghdr *nlmsg)
|
||||
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
struct rtmsg *r;
|
||||
struct rtattr *a;
|
||||
@@ -63,8 +64,14 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
||||
r = NLMSG_DATA(nlmsg);
|
||||
a = RTM_RTA(r);
|
||||
la = RTM_PAYLOAD(nlmsg);
|
||||
if (la >= len) {
|
||||
_e("Packet too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
while (RTA_OK(a, la)) {
|
||||
void *data = RTA_DATA(a);
|
||||
|
||||
switch (a->rta_type) {
|
||||
case RTA_GATEWAY:
|
||||
gw = *((int *)data);
|
||||
@@ -105,7 +112,27 @@ static void net_cond_set(char *ifname, char *cond, int set)
|
||||
cond_clear(msg);
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg)
|
||||
static int validate_ifname(const char *ifname)
|
||||
{
|
||||
if (!ifname || !ifname[0])
|
||||
return 1;
|
||||
|
||||
if (strlen(ifname) >= IFNAMSIZ)
|
||||
return 1;
|
||||
|
||||
if (strstr(ifname, ".."))
|
||||
return 1;
|
||||
|
||||
while (*ifname) {
|
||||
if (*ifname == '/' || isspace(*ifname))
|
||||
return 1;
|
||||
ifname++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
|
||||
{
|
||||
int la;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
@@ -120,53 +147,61 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
||||
i = NLMSG_DATA(nlmsg);
|
||||
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
|
||||
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
|
||||
if (la >= len) {
|
||||
_e("Packet too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
while (RTA_OK(a, la)) {
|
||||
if (a->rta_type == IFLA_IFNAME) {
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
switch (nlmsg->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
/*
|
||||
* New interface has appeared, or interface flags has changed.
|
||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||
*/
|
||||
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||
net_cond_set(ifname, "exist", 1);
|
||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||
break;
|
||||
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) {
|
||||
if (a->rta_type != IFLA_IFNAME)
|
||||
continue;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has disappeared, not link down ... */
|
||||
_d("%s: Delete link", ifname);
|
||||
net_cond_set(ifname, "exist", 0);
|
||||
net_cond_set(ifname, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
_d("%s: New Address", ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
_d("%s: Deconfig Address", ifname);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
|
||||
if (validate_ifname(ifname)) {
|
||||
_d("Invalid interface name '%s', skipping ...", ifname);
|
||||
continue;
|
||||
}
|
||||
|
||||
a = RTA_NEXT(a, la);
|
||||
switch (nlmsg->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
/*
|
||||
* New interface has appeared, or interface flags has changed.
|
||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||
*/
|
||||
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
|
||||
net_cond_set(ifname, "exist", 1);
|
||||
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
|
||||
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
/* NOTE: Interface has disappeared, not link down ... */
|
||||
_d("%s: Delete link", ifname);
|
||||
net_cond_set(ifname, "exist", 0);
|
||||
net_cond_set(ifname, "up", 0);
|
||||
net_cond_set(ifname, "running", 0);
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
_d("%s: New Address", ifname);
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
_d("%s: Deconfig Address", ifname);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nl_callback(void *arg, int sd, int events)
|
||||
{
|
||||
ssize_t len;
|
||||
static char buf[4096];
|
||||
struct nlmsghdr *nh;
|
||||
ssize_t len;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
len = recv(sd, buf, sizeof(buf), 0);
|
||||
@@ -179,9 +214,9 @@ static void nl_callback(void *arg, int sd, int events)
|
||||
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
|
||||
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
|
||||
nl_route(nh);
|
||||
nl_route(nh, len);
|
||||
else
|
||||
nl_link(nh);
|
||||
nl_link(nh, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-17
@@ -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)
|
||||
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
svc_t *svc;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
|
||||
return;
|
||||
|
||||
_d("path: %s, mask: %08x", fn, mask);
|
||||
|
||||
svc = svc_find_by_pidfile(fn);
|
||||
if (!svc) {
|
||||
_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);
|
||||
|
||||
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);
|
||||
if (!svc_has_pidfile(svc))
|
||||
pid_file_set(svc, fn, 1);
|
||||
|
||||
if (svc_is_forking(svc)) {
|
||||
pid_t pid;
|
||||
|
||||
pid = pid_file_read(pid_file(svc));
|
||||
_d("Forking service %s changed PID from %d to %d",
|
||||
svc->cmd, svc->pid, pid);
|
||||
svc->pid = pid;
|
||||
if (pid == -1) {
|
||||
cond_clear(cond);
|
||||
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);
|
||||
@@ -151,7 +161,8 @@ static void pidfile_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, off;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
@@ -160,15 +171,19 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
||||
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;
|
||||
|
||||
_d("name %s, event: 0x%08x", ev->name, ev->mask);
|
||||
|
||||
/* Find base path for this event */
|
||||
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
|
||||
if (!iwp)
|
||||
@@ -179,12 +194,7 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & IN_DELETE) {
|
||||
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
|
||||
pidfile_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
}
|
||||
@@ -226,8 +236,14 @@ static void pidfile_reconf(void *arg)
|
||||
|
||||
static void pidfile_init(void *arg)
|
||||
{
|
||||
char piddir[MAX_ARG_LEN];
|
||||
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.
|
||||
* and/or /run, with proper symlinks etc. We depend on bootmisc
|
||||
@@ -235,6 +251,7 @@ static void pidfile_init(void *arg)
|
||||
*/
|
||||
path = realpath(_PATH_VARRUN, NULL);
|
||||
if (!path) {
|
||||
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
|
||||
path = realpath("/run", NULL);
|
||||
if (!path) {
|
||||
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
|
||||
@@ -244,6 +261,8 @@ static void pidfile_init(void *arg)
|
||||
|
||||
if (pidfile_add_path(&iw_pidfile, path))
|
||||
iwatch_exit(&iw_pidfile);
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+46
-25
@@ -32,13 +32,14 @@
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "service.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 = {
|
||||
.io = {
|
||||
.cb = watcher,
|
||||
.cb = tty_watcher,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
@@ -53,33 +54,53 @@ static void setup(void)
|
||||
_pe("Failed starting TTY watcher");
|
||||
}
|
||||
|
||||
static void watcher(void *arg, int fd, int events)
|
||||
static void do_tty(char *tty, size_t len, int creat)
|
||||
{
|
||||
char buf[EVENT_SIZE];
|
||||
int len = 0;
|
||||
char name[len + 6];
|
||||
svc_t *svc;
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||
char name[notified->len + 6];
|
||||
struct tty *entry;
|
||||
|
||||
if (-1 == len) {
|
||||
if (errno == EINVAL)
|
||||
setup();
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
break; /* Likely EAGAIN */
|
||||
snprintf(name, sizeof(name), "/dev/%s", tty);
|
||||
svc = svc_find_by_tty(name);
|
||||
if (svc) {
|
||||
if (svc_is_blocked(svc) && creat) {
|
||||
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
|
||||
svc_start(svc);
|
||||
} else if (svc->pid) {
|
||||
_d("%s: missing ...", svc_ident(svc, NULL, 0));
|
||||
svc_missing(svc);
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(entry)) {
|
||||
if (notified->mask & IN_CREATE)
|
||||
tty_start(entry);
|
||||
else if (entry->pid)
|
||||
tty_stop(entry);
|
||||
}
|
||||
service_step_all(SVC_TYPE_TTY);
|
||||
}
|
||||
}
|
||||
|
||||
static void tty_watcher(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;
|
||||
|
||||
if (!ev->mask)
|
||||
continue;
|
||||
|
||||
_d("tty %s, event: 0x%08x", ev->name, ev->mask);
|
||||
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-4
@@ -37,10 +37,11 @@ static void setup(void *arg)
|
||||
#ifdef RANDOMSEED
|
||||
if (!fexist(RANDOMSEED)) {
|
||||
int ret = 1;
|
||||
mode_t prev;
|
||||
FILE *fp;
|
||||
|
||||
print_desc("Bootstrapping random seed", NULL);
|
||||
umask(077);
|
||||
prev = umask(077);
|
||||
fp = fopen(RANDOMSEED, "w");
|
||||
if (fp) {
|
||||
int iter = 128;
|
||||
@@ -57,7 +58,7 @@ static void setup(void *arg)
|
||||
ret = fclose(fp);
|
||||
}
|
||||
print_result(ret);
|
||||
umask(0);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
print_desc("Initializing random number generator", NULL);
|
||||
@@ -68,10 +69,14 @@ static void setup(void *arg)
|
||||
static void save(void *arg)
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
umask(077);
|
||||
mode_t prev;
|
||||
|
||||
prev = umask(077);
|
||||
|
||||
print_desc("Saving random seed", NULL);
|
||||
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
|
||||
umask(0);
|
||||
|
||||
umask(prev);
|
||||
#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:
|
||||
*/
|
||||
@@ -32,13 +32,14 @@
|
||||
|
||||
static void setup(void *arg)
|
||||
{
|
||||
const char *username = "nobody";
|
||||
char line[LINE_SIZE];
|
||||
mode_t prev;
|
||||
#ifdef PAM_CONSOLE
|
||||
int fd;
|
||||
#endif
|
||||
char line[LINE_SIZE];
|
||||
const char *username = "nobody";
|
||||
|
||||
umask(0);
|
||||
prev = umask(0);
|
||||
|
||||
makedir("/var/run/console", 01777);
|
||||
snprintf(line, sizeof(line), "/var/run/console/%s", username);
|
||||
@@ -58,7 +59,7 @@ static void setup(void *arg)
|
||||
if (whichp("restorecon"))
|
||||
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
|
||||
|
||||
umask(022);
|
||||
umask(prev);
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
|
||||
+20
-17
@@ -1,6 +1,5 @@
|
||||
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
EXTRA_DIST = rescue.conf sample.conf
|
||||
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
if STATIC
|
||||
AM_CPPFLAGS += -DENABLE_STATIC
|
||||
AM_LDFLAGS = -static-libtool-libs
|
||||
@@ -8,20 +7,21 @@ else
|
||||
AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_SCRIPTS = cgreaper.sh
|
||||
|
||||
sbin_PROGRAMS = finit initctl
|
||||
pkglibexec_PROGRAMS =
|
||||
finit_pkglibdir = $(pkglibdir)
|
||||
finit_pkglib_SCRIPTS = rescue.conf sample.conf
|
||||
|
||||
if WATCHDOGD
|
||||
pkglibexec_PROGRAMS = watchdogd
|
||||
pkglibexec_PROGRAMS += watchdogd
|
||||
endif
|
||||
|
||||
if LOGIT
|
||||
pkglibexec_PROGRAMS = logit
|
||||
logit_SOURCES = logit.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
pkglibexec_PROGRAMS += logit
|
||||
logit_SOURCES = logit.c logrotate.c
|
||||
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
logit_CFLAGS += $(lite_CFLAGS)
|
||||
logit_LDADD = $(lite_LIBS)
|
||||
endif
|
||||
|
||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
@@ -43,6 +43,10 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
tty.c tty.h \
|
||||
util.c util.h \
|
||||
utmp-api.c utmp-api.h
|
||||
if LOGROTATE
|
||||
finit_SOURCES += logrotate.c
|
||||
endif
|
||||
|
||||
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
@@ -54,20 +58,19 @@ else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
|
||||
initctl_SOURCES = initctl.c client.c client.h \
|
||||
reboot.c serv.c serv.h svc.h \
|
||||
cond.c cond.h util.c util.h
|
||||
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
|
||||
client.c client.h cond.c cond.h reboot.c \
|
||||
serv.c serv.h svc.h util.c util.h
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS)
|
||||
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
|
||||
INIT_LNKS = init
|
||||
INIT_LNKS = init telinit
|
||||
REBOOT_LNKS = reboot shutdown halt poweroff suspend
|
||||
|
||||
if WATCHDOGD
|
||||
watchdogd_SOURCES = watchdog.c watchdog.h
|
||||
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
#log.c log.h
|
||||
endif
|
||||
|
||||
# Hook in install to add finit and reboot symlink(s)
|
||||
|
||||
@@ -74,7 +74,24 @@ static int start(svc_t *svc)
|
||||
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)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
svc_start(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reload(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
@@ -88,9 +105,10 @@ static int restart(svc_t *svc)
|
||||
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_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_reload (char *buf, size_t len) { return call(reload, buf, len); }
|
||||
|
||||
static char query_buf[368];
|
||||
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)
|
||||
{
|
||||
memset(query_buf, 0, sizeof(query_buf));
|
||||
query_buf[0] = 0;
|
||||
if (svc_parse_jobstr(buf, len, NULL, missing)) {
|
||||
memcpy(buf, query_buf, len);
|
||||
return 1;
|
||||
@@ -237,7 +255,7 @@ ev_t ev_list[] = {
|
||||
|
||||
static void send_svc(int sd, svc_t *svc)
|
||||
{
|
||||
svc_t empty;
|
||||
svc_t empty = { 0 };
|
||||
size_t len;
|
||||
|
||||
if (!svc) {
|
||||
@@ -252,10 +270,10 @@ static void send_svc(int sd, svc_t *svc)
|
||||
|
||||
static void api_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
int sd, lvl;
|
||||
svc_t *svc;
|
||||
static svc_t *iter = NULL;
|
||||
struct init_request rq;
|
||||
int sd, lvl;
|
||||
svc_t *svc;
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
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));
|
||||
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:
|
||||
_d("stop %s", rq.data);
|
||||
strterm(rq.data, sizeof(rq.data));
|
||||
result = do_stop(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
_d("restart %s", rq.data);
|
||||
case INIT_CMD_RELOAD_SVC:
|
||||
_d("reload %s", 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;
|
||||
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable and allow Finit to collect bundled watchdog */
|
||||
if (wdog) {
|
||||
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
|
||||
stop(wdog);
|
||||
if (wdog && wdog != svc) {
|
||||
char name[32];
|
||||
|
||||
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) {
|
||||
wdog->protect = 0;
|
||||
wdog->runlevels = 0;
|
||||
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
|
||||
stop(wdog);
|
||||
svc_del(wdog);
|
||||
}
|
||||
}
|
||||
wdog = svc;
|
||||
break;
|
||||
|
||||
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
|
||||
* simultaneous client connections, but will
|
||||
@@ -444,7 +474,7 @@ int api_init(uev_ctx_t *ctx)
|
||||
};
|
||||
|
||||
_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) {
|
||||
_pe("Failed starting external API socket");
|
||||
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
|
||||
+427
-80
@@ -25,111 +25,458 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysinfo.h> /* get_nprocs_conf() */
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "finit.h"
|
||||
#include "iwatch.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static int cg_init = 0;
|
||||
struct cg {
|
||||
TAILQ_ENTRY(cg) link;
|
||||
|
||||
/*
|
||||
* Called by Finit at early boot to mount initial cgroups
|
||||
*/
|
||||
void cgroup_init(void)
|
||||
char *name; /* top-level group name */
|
||||
char *cfg; /* kernel settings */
|
||||
|
||||
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 buf[80];
|
||||
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
|
||||
char *val;
|
||||
|
||||
fp = fopen("/proc/cgroups", "r");
|
||||
if (!fp) {
|
||||
_d("No cgroup support");
|
||||
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
|
||||
if (!path || !ctrl) {
|
||||
_e("Missing path or controller, skipping!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
|
||||
_d("Failed mounting cgroups");
|
||||
goto fail;
|
||||
if (!prop) {
|
||||
prop = strchr(ctrl, '.');
|
||||
if (!prop) {
|
||||
_e("Invalid cgroup ctrl syntax: %s", ctrl);
|
||||
return;
|
||||
}
|
||||
|
||||
*prop++ = 0;
|
||||
}
|
||||
|
||||
/* Skip first line, header */
|
||||
fgets(buf, sizeof(buf), fp);
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char *cgroup;
|
||||
char rc[80];
|
||||
val = strchr(prop, ':');
|
||||
if (!val) {
|
||||
_e("Missing cgroup ctrl value, prop %s", prop);
|
||||
return;
|
||||
}
|
||||
*val++ = 0;
|
||||
|
||||
cgroup = strtok(buf, "\t ");
|
||||
if (!cgroup)
|
||||
continue;
|
||||
|
||||
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);
|
||||
/* disallow sneaky relative paths */
|
||||
if (strstr(ctrl, "..") || strstr(prop, "..")) {
|
||||
_e("Possible security violation; '..' not allowed in cgroup config!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Default cgroups for process monitoring */
|
||||
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
|
||||
_pe("Failed mounting Finit cgroup hierarchy");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set up reaper and enable callbacks */
|
||||
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
|
||||
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
|
||||
|
||||
/* Default groups, PID 1, services, and user/login processes */
|
||||
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
|
||||
goto fail;
|
||||
|
||||
/* Move ourselves to init */
|
||||
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
|
||||
|
||||
/* We have signal, main screen turn on! */
|
||||
cg_init = 1;
|
||||
fail:
|
||||
fclose(fp);
|
||||
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
|
||||
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
|
||||
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
|
||||
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) {
|
||||
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||
if (pid < 0 || pid == 1) {
|
||||
errno = EINVAL;
|
||||
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");
|
||||
|
||||
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_
|
||||
#define FINIT_CGROUP_H_
|
||||
|
||||
void cgroup_init (void);
|
||||
#include <uev/uev.h>
|
||||
|
||||
int cgroup_user (char *name);
|
||||
int cgroup_service (char *cmd, char *id, int pid);
|
||||
struct cgroup {
|
||||
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_ */
|
||||
|
||||
+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:
|
||||
warnx("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.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
@@ -36,19 +38,14 @@ int client_connect(void)
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (-1 == sd)
|
||||
goto error;
|
||||
err(1, "Failed creating UNIX domain socket");
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||
close(sd);
|
||||
goto error;
|
||||
}
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
err(1, "Failed connecting to finit");
|
||||
|
||||
return sd;
|
||||
error:
|
||||
perror("Failed connecting to finit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int client_disconnect(void)
|
||||
@@ -68,27 +65,44 @@ int client_disconnect(void)
|
||||
|
||||
int client_send(struct init_request *rq, ssize_t len)
|
||||
{
|
||||
struct pollfd pfd = { 0 };
|
||||
int sd, result = 255;
|
||||
|
||||
sd = client_connect();
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (write(sd, rq, len) != len)
|
||||
goto error;
|
||||
pfd.fd = sd;
|
||||
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)
|
||||
goto error;
|
||||
if (write(sd, rq, len) != len) {
|
||||
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)
|
||||
result = 1;
|
||||
else
|
||||
result = 0;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
exit:
|
||||
client_disconnect();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+20
-37
@@ -99,13 +99,14 @@ static void cond_bump_reconf(void)
|
||||
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.
|
||||
*/
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
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)
|
||||
@@ -119,7 +120,7 @@ static int cond_checkpath(const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
if (mkpath(dir, 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 1;
|
||||
}
|
||||
@@ -127,22 +128,22 @@ static int cond_checkpath(const char *path)
|
||||
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;
|
||||
|
||||
_d("%s", path);
|
||||
_d("%s <= %d", path, next);
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen) {
|
||||
_e("Unable to read configuration generation (%s)", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
old = cond_get_path(path);
|
||||
prev = cond_get_path(path);
|
||||
|
||||
switch (new) {
|
||||
switch (next) {
|
||||
case COND_ON:
|
||||
if (cond_checkpath(path))
|
||||
return 0;
|
||||
@@ -159,30 +160,11 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new != old;
|
||||
return next != prev;
|
||||
}
|
||||
|
||||
/* Has condition in configuration and cond is allowed? */
|
||||
static int svc_has_cond(svc_t *svc)
|
||||
{
|
||||
if (!svc->cond[0])
|
||||
return 0;
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_SYSV:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cond_update(const char *name)
|
||||
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
|
||||
void cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
@@ -221,7 +203,8 @@ void cond_set_oneshot(const char *name)
|
||||
if (cond_checkpath(path))
|
||||
return;
|
||||
|
||||
symlink(COND_RECONF, path);
|
||||
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
|
||||
_pe("Failed creating onshot cond %s", name);
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
@@ -254,13 +237,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
if (tflg != FTW_F)
|
||||
return 0;
|
||||
|
||||
nm = strstr((char *)fpath, COND_DIR);
|
||||
nm = strstr((char *)fpath, COND_BASE);
|
||||
if (!nm) {
|
||||
_e("Incorrect condtion path %s, cannot reassert", fpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
nm += sizeof(COND_DIR);
|
||||
nm += strlen(COND_BASE);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
|
||||
@@ -281,8 +264,8 @@ void cond_init(void)
|
||||
{
|
||||
char path[MAX_ARG_LEN];
|
||||
|
||||
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", _PATH_COND);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -45,15 +45,15 @@ const char *cond_path(const char *name)
|
||||
static char path[256];
|
||||
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));
|
||||
}
|
||||
|
||||
unsigned int cond_get_gen(const char *file)
|
||||
{
|
||||
unsigned int gen;
|
||||
char *ptr, path[256];
|
||||
unsigned int gen;
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
|
||||
{
|
||||
int cgen, rgen;
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
cgen = cond_get_gen(path);
|
||||
if (!cgen)
|
||||
return COND_OFF;
|
||||
|
||||
rgen = cond_get_gen(_PATH_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -4,9 +4,14 @@
|
||||
#include <paths.h>
|
||||
#include <svc.h>
|
||||
|
||||
#define COND_DIR "finit/cond"
|
||||
#define COND_PATH _PATH_VARRUN COND_DIR
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
#define COND_BASE "finit/cond"
|
||||
#define COND_USR "usr/"
|
||||
#define COND_PID "pid/"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
|
||||
#define _PATH_CONDUSR _PATH_COND COND_USR
|
||||
#define _PATH_CONDPID _PATH_COND COND_PID
|
||||
#define _PATH_RECONF _PATH_COND "reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
COND_OFF = 0,
|
||||
@@ -23,6 +28,7 @@ enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (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);
|
||||
void cond_set (const char *name);
|
||||
void cond_set_oneshot (const char *name);
|
||||
|
||||
+107
-32
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
@@ -47,8 +48,11 @@
|
||||
int logfile_size_max = 200000; /* 200 kB */
|
||||
int logfile_count_max = 5;
|
||||
|
||||
struct rlimit initial_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 {
|
||||
TAILQ_ENTRY(conf_change) link;
|
||||
char *name;
|
||||
@@ -78,14 +82,14 @@ static void hide_args(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
int get_bool(char *ptr, int default_value)
|
||||
int get_bool(char *arg, int default_value)
|
||||
{
|
||||
if (!ptr)
|
||||
if (!arg)
|
||||
goto fallback;
|
||||
|
||||
if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
|
||||
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
|
||||
return 1;
|
||||
if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
|
||||
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
|
||||
return 0;
|
||||
fallback:
|
||||
return default_value;
|
||||
@@ -97,19 +101,24 @@ fallback:
|
||||
*/
|
||||
static void parse_finit_opts(char *opt)
|
||||
{
|
||||
char *ptr;
|
||||
char *arg;
|
||||
|
||||
ptr = strchr(opt, '=');
|
||||
if (ptr)
|
||||
*ptr++ = 0;
|
||||
arg = strchr(opt, '=');
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
if (string_compare(opt, "debug")) {
|
||||
debug = get_bool(ptr, 1);
|
||||
debug = get_bool(arg, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status_style")) {
|
||||
if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
|
||||
if (!arg) {
|
||||
_e("status_style option requires an argument, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(arg, "old") || string_compare(arg, "classic"))
|
||||
show_progress(PROGRESS_CLASSIC);
|
||||
else
|
||||
show_progress(PROGRESS_MODERN);
|
||||
@@ -117,7 +126,7 @@ static void parse_finit_opts(char *opt)
|
||||
}
|
||||
|
||||
if (string_compare(opt, "show_status")) {
|
||||
show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
|
||||
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -346,7 +355,7 @@ char *lim2str(struct rlimit *rlim)
|
||||
char tmp[25];
|
||||
static char buf[42];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 0;
|
||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
@@ -417,6 +426,29 @@ 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)
|
||||
{
|
||||
char cmd[CMD_SIZE];
|
||||
@@ -546,9 +578,21 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
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 */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x), rlimit, file);
|
||||
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -574,9 +618,11 @@ static int parse_conf(char *file, int is_rcsd)
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
/* Prepare default limits for each service in /etc/finit.d/ */
|
||||
if (is_rcsd)
|
||||
/* Prepare default limits and group for each service in /etc/finit.d/ */
|
||||
if (is_rcsd) {
|
||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||
cgroup_current[0] = 0;
|
||||
}
|
||||
|
||||
_d("*** Parsing %s", file);
|
||||
while (!feof(fp)) {
|
||||
@@ -616,16 +662,13 @@ int conf_reload(void)
|
||||
daylight, timezone, tzname[0], tzname[1]);
|
||||
|
||||
/* Mark and sweep */
|
||||
cgroup_mark_all();
|
||||
svc_mark_dynamic();
|
||||
tty_mark();
|
||||
|
||||
/*
|
||||
* Get current global limits, which may be overridden from both
|
||||
* finit.conf, for Finit and its services like getty+watchdogd,
|
||||
* and *.conf in finit.d/, for each service(s) listed there.
|
||||
* Reset global rlimit to bootstrap values from conf_init().
|
||||
*/
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
|
||||
|
||||
if (rescue) {
|
||||
int rc;
|
||||
@@ -634,7 +677,7 @@ int conf_reload(void)
|
||||
/* If rescue.conf is missing, fall back to a root shell */
|
||||
rc = parse_conf(RESCUE_CONF, 0);
|
||||
if (rc)
|
||||
tty_register(line, global_rlimit, NULL);
|
||||
service_register(SVC_TYPE_TTY, line, global_rlimit, NULL);
|
||||
|
||||
print(rc, "Entering rescue mode");
|
||||
goto done;
|
||||
@@ -674,7 +717,7 @@ int conf_reload(void)
|
||||
|
||||
/* Check for dangling symlinks */
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
path = rp = realpath(path, NULL);
|
||||
rp = realpath(path, NULL);
|
||||
if (!rp) {
|
||||
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
|
||||
continue;
|
||||
@@ -684,7 +727,7 @@ int conf_reload(void)
|
||||
/* Check that file ends with '.conf' */
|
||||
len = strlen(path);
|
||||
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
|
||||
parse_conf(path, 1);
|
||||
|
||||
@@ -694,7 +737,15 @@ int conf_reload(void)
|
||||
|
||||
globfree(&gl);
|
||||
|
||||
/* Mark any reverse deps as chenaged. */
|
||||
service_update_rdeps();
|
||||
|
||||
/* Set up top-level cgroups */
|
||||
cgroup_config();
|
||||
done:
|
||||
/* Remove all unused top-level cgroups */
|
||||
cgroup_cleanup();
|
||||
|
||||
/* Drop record of all .conf changes */
|
||||
drop_changes();
|
||||
|
||||
@@ -787,20 +838,29 @@ int conf_any_change(void)
|
||||
|
||||
int conf_changed(char *file)
|
||||
{
|
||||
int rc = 0;
|
||||
char *rp;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
if (conf_find(file))
|
||||
return 1;
|
||||
rp = realpath(file, NULL);
|
||||
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 char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, off;
|
||||
ssize_t sz;
|
||||
size_t off;
|
||||
|
||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
@@ -809,10 +869,16 @@ static void conf_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
|
||||
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
|
||||
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;
|
||||
|
||||
@@ -868,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)
|
||||
{
|
||||
@@ -882,8 +948,17 @@ int conf_init(uev_ctx_t *ctx)
|
||||
* finit.conf, for Finit and its services like getty+watchdogd,
|
||||
* and *.conf in finit.d/, for each service(s) listed there.
|
||||
*/
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (getrlimit(i, &initial_rlimit[i]))
|
||||
logit(LOG_WARNING, "rlimit: Failed reading setting %s: %s",
|
||||
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 */
|
||||
fd = iwatch_init(&iw_conf);
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
#ifndef FINIT_CONF_H_
|
||||
#define FINIT_CONF_H_
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "svc.h"
|
||||
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
|
||||
extern struct rlimit global_rlimit[];
|
||||
extern char cgroup_current[];
|
||||
|
||||
int str2rlim(char *str);
|
||||
char *rlim2str(int rlim);
|
||||
|
||||
+35
-49
@@ -35,6 +35,7 @@
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cgroup.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
@@ -244,10 +245,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
|
||||
char name[80];
|
||||
int fd;
|
||||
|
||||
/* Detach from initial controlling TTY and become session leader */
|
||||
vhangup();
|
||||
setsid();
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0) {
|
||||
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);
|
||||
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)
|
||||
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);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
|
||||
@@ -315,7 +316,7 @@ static int activate_console(int noclear, int nowait)
|
||||
return 1;
|
||||
|
||||
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> */
|
||||
if (!tcgetattr(STDIN_FILENO, &orig)) {
|
||||
@@ -339,16 +340,16 @@ static int activate_console(int noclear, int nowait)
|
||||
continue;
|
||||
}
|
||||
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, msg, strlen(msg));
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, msg, strlen(msg));
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, cup, strlen(cup));
|
||||
dprint(STDERR_FILENO, clr, strlen(clr));
|
||||
dprint(STDERR_FILENO, cup, strlen(cup));
|
||||
ret = 1;
|
||||
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 pid;
|
||||
speed_t speed;
|
||||
int rc = 1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
speed_t speed;
|
||||
int rc = 1;
|
||||
|
||||
speed = stty_parse_speed(baud);
|
||||
speed = stty_parse_speed(baud);
|
||||
prepare_tty(tty, speed, "tty", rlimit);
|
||||
if (activate_console(noclear, nowait)) {
|
||||
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
|
||||
prepare_tty(tty, speed, "tty", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = getty(tty, speed, term, NULL);
|
||||
|
||||
_exit(rc);
|
||||
rc = getty(tty, speed, term, NULL);
|
||||
}
|
||||
|
||||
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 pid;
|
||||
int rc = 1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
int rc = 1;
|
||||
|
||||
/* Dunno speed, tell stty() to not mess with it */
|
||||
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);
|
||||
/* Dunno speed, tell stty() to not mess with it */
|
||||
prepare_tty(tty, B0, "getty", rlimit);
|
||||
if (activate_console(noclear, nowait)) {
|
||||
logit(LOG_INFO, "Starting external getty on %s", tty);
|
||||
rc = execv(cmd, args);
|
||||
}
|
||||
|
||||
return pid;
|
||||
vhangup();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
pid_t pid;
|
||||
int rc = 1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
int rc = 1;
|
||||
prepare_tty(tty, B0, "finitsh", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = sh(tty);
|
||||
|
||||
prepare_tty(tty, B0, "finit-sh", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
rc = sh(tty);
|
||||
|
||||
_exit(rc);
|
||||
}
|
||||
|
||||
return pid;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int run_parts(char *dir, char *cmd)
|
||||
|
||||
+82
-52
@@ -59,6 +59,7 @@ int prevlevel = -1;
|
||||
int debug = 0; /* debug mode from kernel cmdline */
|
||||
int rescue = 0; /* rescue 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 *network = NULL;
|
||||
char *hostname = NULL;
|
||||
@@ -179,7 +180,8 @@ static void fs_remount_root(int fsckerr)
|
||||
* XXX: Untested, in the initramfs age we should
|
||||
* probably use switch_root instead.
|
||||
*/
|
||||
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
|
||||
_pe("Failed %s / MS_MOVE");
|
||||
}
|
||||
#endif /* SYSROOT */
|
||||
|
||||
@@ -198,7 +200,6 @@ static void fs_mount(void)
|
||||
plugin_run_hooks(HOOK_MOUNT_POST);
|
||||
|
||||
run("swapon -ea");
|
||||
umask(0022);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -206,6 +207,9 @@ static void fs_mount(void)
|
||||
* for early multi-console, and /sys for the cgroups support. Any
|
||||
* occurrence of these file systems in /etc/fstab will replace these
|
||||
* mounts later in fs_mount()
|
||||
*
|
||||
* Ignore any mount errors with EBUSY, kernel likely alread mounted
|
||||
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
|
||||
*/
|
||||
static void fs_init(void)
|
||||
{
|
||||
@@ -218,8 +222,16 @@ static void fs_init(void)
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(fs); i++)
|
||||
mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
/* mask writable bit for g and o */
|
||||
umask(022);
|
||||
|
||||
for (i = 0; i < NELEMS(fs); i++) {
|
||||
int rc;
|
||||
|
||||
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
|
||||
if (rc && errno != EBUSY)
|
||||
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -276,20 +288,8 @@ static void emergency_shell(void)
|
||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||
* 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 */
|
||||
_d("Clean up all bootstrap-only tasks/services ...");
|
||||
svc_prune_bootstrap();
|
||||
@@ -311,9 +311,9 @@ static void finalize(void)
|
||||
/* Disable progress output at normal runtime */
|
||||
enable_progress(0);
|
||||
|
||||
/* Delayed start of TTYs at bootstrap */
|
||||
_d("Launching all getty services ...");
|
||||
tty_runlevel();
|
||||
/* System bootrapped, launch TTYs et al */
|
||||
bootstrap = 0;
|
||||
service_step_all(SVC_TYPE_RESPAWN);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -331,12 +331,17 @@ static void crank_worker(void *unused)
|
||||
|
||||
/*
|
||||
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
|
||||
* allowed to complete their work in [S], or timeout, before we call
|
||||
* finalize(), should not take more than 120 sec.
|
||||
* allowed to complete their work in [S], or timeout, before we switch
|
||||
* 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 struct wq final = {
|
||||
.cb = finalize,
|
||||
.delay = 10
|
||||
};
|
||||
|
||||
_d("Step all services ...");
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
@@ -352,12 +357,36 @@ static void final_worker(void *work)
|
||||
else
|
||||
_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)
|
||||
{
|
||||
printf("Usage: telinit [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
printf("Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
"Options:\n"
|
||||
// " -a Ignored, compat SysV init\n"
|
||||
// " -b Ignored, compat SysV init\n"
|
||||
@@ -375,12 +404,7 @@ static int usage(int rc)
|
||||
" 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"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
"\n");
|
||||
|
||||
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
|
||||
#ifdef PACKAGE_URL
|
||||
printf("Project homepage: %s\n", PACKAGE_URL);
|
||||
#endif
|
||||
"\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -393,6 +417,7 @@ static int telinit(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
progname(argv[0]);
|
||||
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
|
||||
switch(c) {
|
||||
case 'a': case 'b': case 'e': case 's': case 'z':
|
||||
@@ -402,7 +427,7 @@ static int telinit(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'v': case 'V':
|
||||
return puts("v" VERSION) == EOF;
|
||||
return version(0);
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
@@ -414,13 +439,13 @@ static int telinit(int argc, char *argv[])
|
||||
int req = (int)argv[optind][0];
|
||||
|
||||
if (isdigit(req))
|
||||
return systemf("initctl runlevel %c", req);
|
||||
return systemf("initctl -b runlevel %c", req);
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return systemf("initctl reload");
|
||||
return systemf("initctl -b reload");
|
||||
|
||||
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
|
||||
@@ -435,11 +460,12 @@ static int telinit(int argc, char *argv[])
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct wq crank = {
|
||||
.cb = crank_worker
|
||||
.cb = crank_worker,
|
||||
.delay = 10
|
||||
};
|
||||
struct wq final = {
|
||||
.cb = final_worker,
|
||||
.delay = 1000
|
||||
struct wq bootstrap = {
|
||||
.cb = bootstrap_worker,
|
||||
.delay = 100
|
||||
};
|
||||
uev_ctx_t loop;
|
||||
|
||||
@@ -470,17 +496,13 @@ int main(int argc, char *argv[])
|
||||
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("SHELL", _PATH_BSHELL, 1);
|
||||
|
||||
chdir("/");
|
||||
umask(0);
|
||||
|
||||
/* Set up canvas */
|
||||
if (!rescue && !debug)
|
||||
screen_init();
|
||||
if (chdir("/"))
|
||||
_pe("Failed cd /");
|
||||
|
||||
/* Allow progress, if enabled */
|
||||
enable_progress(1);
|
||||
@@ -490,8 +512,10 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
emergency_shell();
|
||||
|
||||
/* Load plugins early, the first hook is in banner(), so we
|
||||
* need plugins loaded before calling it. */
|
||||
/*
|
||||
* Load plugins early, the first hook is in banner(), so we
|
||||
* need plugins loaded before calling it.
|
||||
*/
|
||||
plugin_init(&loop);
|
||||
|
||||
/*
|
||||
@@ -507,8 +531,7 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
* Initialize default control groups, if available
|
||||
*/
|
||||
cgroup_init();
|
||||
|
||||
cgroup_init(&loop);
|
||||
|
||||
/* Check and mount filesystems. */
|
||||
fs_mount();
|
||||
@@ -526,6 +549,14 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
conf_init(&loop);
|
||||
|
||||
/*
|
||||
* Start built-in watchdogd as soon as possible, if enabled
|
||||
*/
|
||||
if (which(FINIT_LIBPATH_ "/watchdogd")) {
|
||||
service_register(SVC_TYPE_SERVICE, "[123456789] name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
|
||||
wdog = svc_find_by_nameid("watchdog", "finit");
|
||||
}
|
||||
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
@@ -540,13 +571,12 @@ int main(int argc, char *argv[])
|
||||
|
||||
_d("Starting initctl API responder ...");
|
||||
api_init(&loop);
|
||||
umask(022);
|
||||
|
||||
_d("Starting the big state machine ...");
|
||||
schedule_work(&crank);
|
||||
|
||||
_d("Starting bootstrap finalize timer ...");
|
||||
schedule_work(&final);
|
||||
schedule_work(&bootstrap);
|
||||
|
||||
/*
|
||||
* Enter main loop to monitor /dev/initctl and services
|
||||
|
||||
+10
-4
@@ -30,6 +30,7 @@
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
@@ -38,11 +39,15 @@
|
||||
|
||||
/* just in case */
|
||||
#ifndef _PATH_STDPATH
|
||||
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
|
||||
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_VARRUN
|
||||
#define _PATH_VARRUN "/var/run/"
|
||||
#define _PATH_VARRUN "/var/run/"
|
||||
#endif
|
||||
|
||||
#ifndef FINIT_CGPATH
|
||||
#define FINIT_CGPATH "/sys/fs/cgroup"
|
||||
#endif
|
||||
|
||||
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
|
||||
@@ -67,10 +72,10 @@
|
||||
/* Finit extensions over std SysV */
|
||||
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
|
||||
#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_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_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
@@ -101,6 +106,7 @@ extern int prevlevel;
|
||||
extern int debug;
|
||||
extern int rescue;
|
||||
extern int single;
|
||||
extern int bootstrap;
|
||||
extern char *rcsd;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
|
||||
+15
-16
@@ -41,9 +41,6 @@
|
||||
#define _PATH_LOGIN "/bin/login"
|
||||
#endif
|
||||
|
||||
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
|
||||
|
||||
|
||||
/*
|
||||
* Read one character from stdin.
|
||||
*/
|
||||
@@ -54,7 +51,7 @@ static int readch(char *tty)
|
||||
|
||||
st = read(STDIN_FILENO, &ch1, 1);
|
||||
if (st == 0) {
|
||||
print("\n");
|
||||
dprint(STDOUT_FILENO, "\n", 0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
@@ -74,31 +71,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
s0 = line;
|
||||
for (s = line; *s != 0; s++) {
|
||||
if (*s == '\\') {
|
||||
(void)write(1, s0, s - s0);
|
||||
if ((s - s0) > 0)
|
||||
dprint(STDOUT_FILENO, s0, s - s0);
|
||||
s0 = s + 2;
|
||||
switch (*++s) {
|
||||
case 'l':
|
||||
print(tty);
|
||||
dprint(STDOUT_FILENO, tty, 0);
|
||||
break;
|
||||
case 'm':
|
||||
print(uts->machine);
|
||||
dprint(STDOUT_FILENO, uts->machine, 0);
|
||||
break;
|
||||
case 'n':
|
||||
print(uts->nodename);
|
||||
dprint(STDOUT_FILENO, uts->nodename, 0);
|
||||
break;
|
||||
#ifdef _GNU_SOURCE
|
||||
case 'o':
|
||||
print(uts->domainname);
|
||||
dprint(STDOUT_FILENO, uts->domainname, 0);
|
||||
break;
|
||||
#endif
|
||||
case 'r':
|
||||
print(uts->release);
|
||||
dprint(STDOUT_FILENO, uts->release, 0);
|
||||
break;
|
||||
case 's':
|
||||
print(uts->sysname);
|
||||
dprint(STDOUT_FILENO, uts->sysname, 0);
|
||||
break;
|
||||
case 'v':
|
||||
print(uts->version);
|
||||
dprint(STDOUT_FILENO, uts->version, 0);
|
||||
break;
|
||||
case 0:
|
||||
goto leave;
|
||||
@@ -109,7 +107,8 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
}
|
||||
|
||||
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') {
|
||||
if (ch == CTRL('U')) {
|
||||
while (np > name) {
|
||||
(void)write(1, "\b \b", 3);
|
||||
dprint(STDOUT_FILENO, "\b \b", 3);
|
||||
np--;
|
||||
}
|
||||
continue;
|
||||
@@ -186,7 +185,7 @@ static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
cgroup_user(name);
|
||||
cgroup_user(name, 0);
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
@@ -215,7 +214,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
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 */
|
||||
utmp_set_login(tty, NULL);
|
||||
|
||||
+88
-16
@@ -103,6 +103,9 @@ static void add_console(char *cons)
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
if (!strncmp(cons, "/dev/", 5))
|
||||
cons += 5;
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s", cons);
|
||||
|
||||
/* check for and skip duplicates */
|
||||
@@ -121,7 +124,7 @@ static void add_console(char *cons)
|
||||
num_cons++;
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
static void find_active_consoles(void)
|
||||
{
|
||||
char *cons, *ptr, *tok;
|
||||
char buf[512];
|
||||
@@ -131,7 +134,7 @@ void console_init(void)
|
||||
if (!fp) {
|
||||
fallback:
|
||||
_d("Using fallback /dev/console");
|
||||
add_console("/dev/console");
|
||||
add_console("console");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,6 +151,12 @@ void console_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
{
|
||||
find_active_consoles();
|
||||
ttinit();
|
||||
}
|
||||
|
||||
ssize_t cprintf(const char *fmt, ...)
|
||||
{
|
||||
const size_t len = strlen(fmt) * 2;
|
||||
@@ -161,7 +170,7 @@ ssize_t cprintf(const char *fmt, ...)
|
||||
va_end(ap);
|
||||
|
||||
for (i = 0; i < num_cons; i++)
|
||||
(void)write(fds[i], buf, size);
|
||||
dprint(fds[i], buf, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -246,20 +255,20 @@ static char *pad(char *buf, size_t len, char *ch, size_t width)
|
||||
|
||||
void print_banner(const char *heading)
|
||||
{
|
||||
char buf[4 * SCREEN_WIDTH];
|
||||
char buf[4 * ttcols];
|
||||
|
||||
if (progress_style == PROGRESS_SILENT)
|
||||
return;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 0;
|
||||
strlcat(buf, "\r\e[2K", sizeof(buf));
|
||||
|
||||
if (progress_style == PROGRESS_CLASSIC) {
|
||||
strlcat(buf, "\e[1m", sizeof(buf));
|
||||
strlcat(buf, heading, sizeof(buf));
|
||||
pad(buf, sizeof(buf), "=", SCREEN_WIDTH + 10);
|
||||
pad(buf, sizeof(buf), "=", ttcols + 10);
|
||||
} else {
|
||||
size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
|
||||
size_t wmax = 80 <= ttcols ? 80 : ttcols;
|
||||
|
||||
/* • • • Foo System ═══════════════════════════════════════════════ */
|
||||
/* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */
|
||||
@@ -322,7 +331,7 @@ static char *status(int rc)
|
||||
|
||||
void printv(const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[SCREEN_WIDTH];
|
||||
char buf[ttcols];
|
||||
size_t len;
|
||||
|
||||
if (!fmt || progress_style == PROGRESS_SILENT)
|
||||
@@ -330,7 +339,7 @@ void printv(const char *fmt, va_list ap)
|
||||
|
||||
delline();
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 0;
|
||||
len = print_timestamp(buf, sizeof(buf));
|
||||
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
|
||||
|
||||
@@ -404,6 +413,64 @@ int getgroup(char *group)
|
||||
#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)
|
||||
{
|
||||
FILE *fp;
|
||||
@@ -426,14 +493,16 @@ void set_hostname(char **hostname)
|
||||
return;
|
||||
}
|
||||
|
||||
fgets(*hostname, st.st_size, fp);
|
||||
chomp(*hostname);
|
||||
if (fgets(*hostname, st.st_size, fp))
|
||||
chomp(*hostname);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
done:
|
||||
if (*hostname)
|
||||
sethostname(*hostname, strlen(*hostname));
|
||||
if (*hostname) {
|
||||
if (sethostname(*hostname, strlen(*hostname)))
|
||||
_pe("Failed sethostnaem(%s)", *hostname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -471,6 +540,7 @@ void networking(int updown)
|
||||
run("ifdown -a");
|
||||
_exit(0);
|
||||
}
|
||||
cgroup_service("network", pid, NULL);
|
||||
|
||||
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
|
||||
}
|
||||
@@ -557,10 +627,12 @@ int ismnt(char *file, char *dir, char *mode)
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, dir)) {
|
||||
if (mode && hasopt(mnt->mnt_opts, mode))
|
||||
found = 1;
|
||||
else
|
||||
if (mode) {
|
||||
if (hasopt(mnt->mnt_opts, mode))
|
||||
found = 1;
|
||||
} else
|
||||
found = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-1
@@ -26,6 +26,7 @@
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <mntent.h>
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <stdarg.h>
|
||||
#include <sys/resource.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 sh (char *tty);
|
||||
|
||||
int stty (int fd, speed_t speed);
|
||||
void stty (int fd, speed_t speed);
|
||||
speed_t stty_parse_speed(char *baud);
|
||||
|
||||
void print_banner (const char *heading);
|
||||
@@ -81,6 +82,12 @@ int print_result (int fail);
|
||||
|
||||
int getuser (char *username, char **home);
|
||||
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 networking (int updown);
|
||||
|
||||
@@ -103,6 +110,24 @@ static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
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 fismnt (char *dir);
|
||||
|
||||
|
||||
+458
-257
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:
|
||||
*/
|
||||
+4
-3
@@ -81,10 +81,10 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||
return -1;
|
||||
|
||||
if (!fexist(file)) {
|
||||
_d("No such file or directory, skipping %s", file);
|
||||
_d("skipping %s: no such file or directory", file);
|
||||
return 0;
|
||||
}
|
||||
_d("Adding new watcher for path %s", file);
|
||||
_d("adding new watcher for path %s", file);
|
||||
|
||||
path = strdup(file);
|
||||
if (!path) {
|
||||
@@ -94,7 +94,8 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
|
||||
|
||||
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
|
||||
if (wd < 0) {
|
||||
_pe("Failed adding watcher for %s", path);
|
||||
if (errno != EEXIST)
|
||||
_pe("Failed adding watcher for %s", path);
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <lite/lite.h>
|
||||
@@ -50,15 +49,13 @@ void log_exit(void)
|
||||
* Unless in debug mode at shutdown, Reinitialize screen,
|
||||
* terminal may have been resized at runtime
|
||||
*/
|
||||
if (debug)
|
||||
screen_exit();
|
||||
else
|
||||
screen_init();
|
||||
if (!debug)
|
||||
ttinit();
|
||||
|
||||
enable_progress(1);
|
||||
}
|
||||
|
||||
void log_open(void)
|
||||
static void log_open(void)
|
||||
{
|
||||
int opts = LOG_PID;
|
||||
|
||||
@@ -79,10 +76,9 @@ void log_debug(void)
|
||||
|
||||
if (debug) {
|
||||
loglevel = LOG_DEBUG;
|
||||
screen_exit();
|
||||
} else {
|
||||
loglevel = LOG_NOTICE;
|
||||
screen_init();
|
||||
ttinit();
|
||||
}
|
||||
log_open();
|
||||
|
||||
@@ -103,11 +99,12 @@ void logit(int prio, const char *fmt, ...)
|
||||
if (up || fexist("/dev/log")) {
|
||||
if (!up)
|
||||
log_open();
|
||||
|
||||
vsyslog(prio, fmt, ap);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!debug && prio > loglevel)
|
||||
if (prio > loglevel)
|
||||
goto done;
|
||||
|
||||
fp = fopen("/dev/kmsg", "w");
|
||||
|
||||
+2
-65
@@ -30,75 +30,12 @@
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
|
||||
extern int logrotate(char *file, int num, off_t sz);
|
||||
|
||||
|
||||
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
static int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
|
||||
if (cnt == 2 && !access(nfile, F_OK)) {
|
||||
size_t len = 5 + strlen(nfile) + 1;
|
||||
char cmd[len];
|
||||
|
||||
snprintf(cmd, len, "gzip %s", nfile);
|
||||
system(cmd);
|
||||
|
||||
remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
if (rename(file, nfile))
|
||||
(void)truncate(file, 0);
|
||||
else
|
||||
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
if (truncate(file, 0))
|
||||
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checksz(FILE *fp, off_t sz)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2018-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 <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
if (mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid)) {
|
||||
syslog(LOG_ERR, "Failed recreating %s: %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
if (rename(ofile, nfile) && errno != ENOENT)
|
||||
syslog(LOG_ERR, "Failed logrotate %s: %s",
|
||||
ofile, strerror(errno));
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
if (rename(ofile, nfile) && errno != ENOENT) {
|
||||
syslog(LOG_ERR, "Failed logrotate %s: %s",
|
||||
ofile, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cnt == 2 && fexist(nfile)) {
|
||||
if (systemf("gzip %s 2>/dev/null", nfile))
|
||||
continue; /* no gzip, probably */
|
||||
|
||||
(void)remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
if (rename(file, nfile))
|
||||
goto fallback;
|
||||
recreate(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
fallback:
|
||||
if (truncate(file, 0))
|
||||
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
|
||||
file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -95,18 +95,15 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
|
||||
char *pid_file(svc_t *svc)
|
||||
{
|
||||
static char path[256];
|
||||
char fn[MAX_ARG_LEN];
|
||||
|
||||
if (svc->pidfile[0]) {
|
||||
if (svc->pidfile[0] == '!')
|
||||
return &svc->pidfile[1];
|
||||
return svc->pidfile;
|
||||
}
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
|
||||
errno = ENOENT;
|
||||
|
||||
return pid_runpath(fn, path, sizeof(path));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid_t pid_file_read(const char *fn)
|
||||
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
|
||||
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);
|
||||
if (!file)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file[0] == '!') {
|
||||
not = 1;
|
||||
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
|
||||
|
||||
arg += 4;
|
||||
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
|
||||
return pid_realpath(svc, arg);
|
||||
return pid_file_set(svc, arg, 0);
|
||||
|
||||
if (arg[0] == '!') {
|
||||
arg++;
|
||||
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
|
||||
if (len > 4 && strcmp(&path[len - 4], ".pid"))
|
||||
strlcat(path, ".pid", sizeof(path));
|
||||
|
||||
return pid_realpath(svc, path);
|
||||
return pid_file_set(svc, path, 0);
|
||||
}
|
||||
|
||||
/* 'pid' arg, no argument following */
|
||||
if (!strcmp(arg, "pid"))
|
||||
return pid_realpath(svc, NULL);
|
||||
return pid_file_set(svc, NULL, 0);
|
||||
|
||||
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_file (svc_t *svc);
|
||||
int pid_file_set (svc_t *svc, char *file, int not);
|
||||
pid_t pid_file_read (const char *fn);
|
||||
int pid_file_create (svc_t *svc);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+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
|
||||
# runlevel. Finit does not restart them when they exit.
|
||||
@@ -25,6 +33,16 @@
|
||||
# | | `--------------------------------------------------------------- Optional conditions
|
||||
# | `---------------------------------------------------------------------- Optional Runlevels
|
||||
# `------------------------------------------------------------------------------ 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
|
||||
#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
|
||||
|
||||
# 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)
|
||||
# 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
|
||||
# Provides pid/syslogd condition
|
||||
|
||||
+87
-20
@@ -30,10 +30,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
extern int icreate; /* initctl -c */
|
||||
extern int iforce; /* initctl -f */
|
||||
#include "initctl.h"
|
||||
|
||||
|
||||
static int calc_width(char *arr[], size_t len)
|
||||
@@ -53,27 +50,66 @@ static int calc_width(char *arr[], size_t len)
|
||||
|
||||
static void do_list(const char *path)
|
||||
{
|
||||
char buf[screen_cols];
|
||||
static int once = 0;
|
||||
int width, num, prev;
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (glob("*.conf", 0, NULL, &gl))
|
||||
return;
|
||||
|
||||
if (gl.gl_pathc <= 0)
|
||||
goto done;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s ", path);
|
||||
printheader(NULL, buf, 0);
|
||||
if (plain) {
|
||||
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);
|
||||
if (width <= 0)
|
||||
goto done;
|
||||
|
||||
num = (screen_cols - 2) / width;
|
||||
if ((num - 1) * 2 + num * width > screen_cols)
|
||||
num = (ttcols - 2) / width;
|
||||
if ((num - 1) * 2 + num * width > ttcols)
|
||||
num--;
|
||||
|
||||
prev = 0;
|
||||
@@ -118,15 +154,19 @@ int serv_list(char *arg)
|
||||
if (fisdir(FINIT_RCSD))
|
||||
do_list(FINIT_RCSD);
|
||||
|
||||
if (fexist(FINIT_CONF))
|
||||
do_list(FINIT_CONF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return path to configuration file for 'name', relative to FINIT_RCSD.
|
||||
* This may be any of the following, provided sysconfdir is /etc:
|
||||
* Return path to configuration file for 'name'. This may be any of the
|
||||
* following, provided sysconfdir is /etc:
|
||||
*
|
||||
* - /etc/finit.d/$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
|
||||
* just use a plain /etc/finit.d/ -- we do not set policy.
|
||||
@@ -138,6 +178,11 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
||||
{
|
||||
char corr[40];
|
||||
|
||||
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
|
||||
strlcpy(path, FINIT_CONF, len);
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!strstr(name, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", name);
|
||||
name = corr;
|
||||
@@ -155,6 +200,10 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
||||
} else
|
||||
strlcat(path, name, len);
|
||||
|
||||
/* fall back to static service unless edit/create */
|
||||
if (!creat && !fexist(path))
|
||||
paste(path, len, FINIT_RCSD, name);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -208,7 +257,8 @@ int do_disable(char *arg, int check)
|
||||
|
||||
if (chdir(FINIT_RCSD))
|
||||
err(1, "failed cd %s", FINIT_RCSD);
|
||||
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))
|
||||
errx(1, "%s not (an) enabled (service).", arg);
|
||||
@@ -250,6 +300,20 @@ int serv_touch(char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serv_show(char *arg)
|
||||
{
|
||||
char path[256];
|
||||
char *fn;
|
||||
|
||||
fn = conf(path, sizeof(path), arg, 0);
|
||||
if (!fexist(fn)) {
|
||||
warnx("Cannot find %s", arg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return systemf("cat %s", fn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to open an editor for the given file, if creat is given we
|
||||
* create a new file based on /lib/finit/sample.conf
|
||||
@@ -279,7 +343,7 @@ static int do_edit(char *arg, int creat)
|
||||
if (!fexist(fn)) {
|
||||
if (!creat) {
|
||||
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
|
||||
@@ -301,8 +365,8 @@ static int do_edit(char *arg, int creat)
|
||||
int serv_edit(char *arg)
|
||||
{
|
||||
if (!arg || !arg[0]) {
|
||||
warnx("missing argument to edit, may be one of:");
|
||||
return serv_list("available");
|
||||
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return do_edit(arg, icreate);
|
||||
@@ -346,8 +410,10 @@ int serv_delete(char *arg)
|
||||
char buf[256];
|
||||
char *fn;
|
||||
|
||||
if (!arg || !arg[0])
|
||||
errx(1, "missing argument to delete");
|
||||
if (!arg || !arg[0]) {
|
||||
warnx("missing argument to delete, may be one of:");
|
||||
return serv_list("available");
|
||||
}
|
||||
|
||||
fn = conf(buf, sizeof(buf), arg, 0);
|
||||
if (!fn)
|
||||
@@ -358,7 +424,8 @@ int serv_delete(char *arg)
|
||||
|
||||
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
|
||||
do_disable(arg, 0);
|
||||
remove(fn);
|
||||
if (remove(fn))
|
||||
err(1, "Failed removing %s", fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -28,6 +28,7 @@ int serv_list (char *arg);
|
||||
int serv_enable (char *arg);
|
||||
int serv_disable (char *arg);
|
||||
int serv_touch (char *arg);
|
||||
int serv_show (char *arg);
|
||||
int serv_edit (char *arg);
|
||||
int serv_creat (char *arg);
|
||||
int serv_delete (char *arg);
|
||||
|
||||
+470
-135
@@ -25,7 +25,6 @@
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isblank() */
|
||||
#include <sched.h> /* sched_yield() */
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/resource.h>
|
||||
@@ -49,8 +48,6 @@
|
||||
#include "utmp-api.h"
|
||||
#include "schedule.h"
|
||||
|
||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||
|
||||
static struct wq work = {
|
||||
.cb = service_worker,
|
||||
};
|
||||
@@ -198,6 +195,8 @@ static int lredirect(svc_t *svc)
|
||||
char *tag = basename(svc->cmd);
|
||||
char *prio = "daemon.info";
|
||||
|
||||
sched_yield();
|
||||
|
||||
fds = open(ptsname(fd), O_RDONLY);
|
||||
close(fd);
|
||||
if (fds == -1)
|
||||
@@ -262,37 +261,6 @@ static int redirect(svc_t *svc)
|
||||
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
|
||||
* Note: must be called from privsepped child
|
||||
@@ -309,7 +277,7 @@ static void source_env(svc_t *svc)
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
/* Warning in service_start() after check_env() */
|
||||
/* Warning in service_start() after svc_checkenv() */
|
||||
fp = fopen(fn, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
@@ -384,6 +352,84 @@ static int is_norespawn(void)
|
||||
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
|
||||
* @svc: Service to start
|
||||
@@ -395,6 +441,7 @@ static int service_start(svc_t *svc)
|
||||
{
|
||||
int result = 0, do_progress = 1;
|
||||
sigset_t nmask, omask;
|
||||
char grnam[80];
|
||||
pid_t pid;
|
||||
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. */
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Unlike systemd we do not allow starting service if env is missing, unless - */
|
||||
if (!check_env(svc)) {
|
||||
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
|
||||
if (!svc_checkenv(svc)) {
|
||||
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
|
||||
svc_missing(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (svc_is_tty(svc)) {
|
||||
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))
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
|
||||
|
||||
@@ -439,47 +497,10 @@ static int service_start(svc_t *svc)
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
|
||||
pid = fork();
|
||||
cgroup_service(svc->name, svc->id, pid);
|
||||
|
||||
pid = service_fork(svc);
|
||||
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];
|
||||
|
||||
/* 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)
|
||||
setgid(gid);
|
||||
|
||||
if (uid >= 0) {
|
||||
setuid(uid);
|
||||
|
||||
/* Set default path for regular users */
|
||||
if (uid > 0)
|
||||
setenv("PATH", _PATH_DEFPATH, 1);
|
||||
if (home) {
|
||||
setenv("HOME", home, 1);
|
||||
chdir(home);
|
||||
}
|
||||
}
|
||||
|
||||
/* Source any environment from env:/path/to/file */
|
||||
source_env(svc);
|
||||
int status;
|
||||
|
||||
if (!svc_is_sysv(svc)) {
|
||||
wordexp_t we = { 0 };
|
||||
@@ -543,7 +564,8 @@ static int service_start(svc_t *svc)
|
||||
}
|
||||
args[i] = NULL;
|
||||
|
||||
redirect(svc);
|
||||
if (!svc_is_tty(svc))
|
||||
redirect(svc);
|
||||
sig_unblock();
|
||||
|
||||
/*
|
||||
@@ -561,6 +583,8 @@ static int service_start(svc_t *svc)
|
||||
|
||||
if (svc_is_runtask(svc))
|
||||
status = exec_runtask(args[0], &args[1]);
|
||||
else if (svc_is_tty(svc))
|
||||
status = tty_exec(svc);
|
||||
else
|
||||
status = execvp(args[0], &args[1]);
|
||||
|
||||
@@ -578,6 +602,11 @@ static int service_start(svc_t *svc)
|
||||
_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);
|
||||
|
||||
svc->pid = pid;
|
||||
@@ -646,12 +675,19 @@ static void service_cleanup(svc_t *svc)
|
||||
{
|
||||
char *fn;
|
||||
|
||||
/* PID collected, cancel any pending SIGKILL */
|
||||
service_timeout_cancel(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",
|
||||
basename(svc->cmd), fn);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -695,7 +731,7 @@ static int service_stop(svc_t *svc)
|
||||
if (svc->pid > 1) {
|
||||
/* Kill all children in the same proess group, e.g. logit */
|
||||
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 */
|
||||
if (rc == -1 && ESRCH == errno)
|
||||
service_cleanup(svc);
|
||||
@@ -862,6 +898,30 @@ static void parse_env(svc_t *svc, char *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)
|
||||
{
|
||||
int signo;
|
||||
@@ -888,6 +948,14 @@ static void parse_killdelay(svc_t *svc, char *delay)
|
||||
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>
|
||||
*/
|
||||
@@ -903,13 +971,10 @@ static void parse_name(svc_t *svc, char *arg)
|
||||
}
|
||||
|
||||
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
|
||||
* when this function is called.
|
||||
@@ -918,23 +983,29 @@ static void parse_name(svc_t *svc, char *arg)
|
||||
*/
|
||||
static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
{
|
||||
int diff = 0;
|
||||
char sep = 0;
|
||||
char *arg;
|
||||
int i = 0;
|
||||
|
||||
if (strcmp(svc->args[i], cmd))
|
||||
diff++;
|
||||
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
|
||||
* 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];
|
||||
size_t len;
|
||||
|
||||
if (!sep) {
|
||||
strlcpy(prev, svc->args[i], sizeof(prev));
|
||||
svc->args[i][0] = 0;
|
||||
}
|
||||
|
||||
/* XXX: ugly string arg re-concatenation, fixme */
|
||||
if (ch == '"' || ch == '\'')
|
||||
sep = ch;
|
||||
@@ -951,6 +1022,13 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
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;
|
||||
i++;
|
||||
}
|
||||
@@ -959,15 +1037,16 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
|
||||
* Clear remaining args in case they were set earlier.
|
||||
* This also zero-terminates the args array.
|
||||
*/
|
||||
while (i < MAX_NUM_SVC_ARGS)
|
||||
svc->args[i++][0] = 0;
|
||||
#if 0
|
||||
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
||||
if (!svc->args[i][0])
|
||||
break;
|
||||
_d("%s ", svc->args[i]);
|
||||
while (i < MAX_NUM_SVC_ARGS) {
|
||||
if (svc->args[i++][0]) {
|
||||
svc->args[i-1][0] = 0;
|
||||
diff++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (diff)
|
||||
_d("Modified args for %s detected", cmd);
|
||||
svc->args_dirty = (diff > 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1031,7 +1110,10 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||
char *username = NULL, *log = NULL, *pid = 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 manual = 0;
|
||||
char *line;
|
||||
@@ -1042,7 +1124,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
line = strdup(cfg);
|
||||
line = strdupa(cfg);
|
||||
if (!line)
|
||||
return 1;
|
||||
|
||||
@@ -1064,15 +1146,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = strtok(line, " ");
|
||||
cmd = strtok(line, " \t");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete service '%s', cannot register", cfg);
|
||||
free(line);
|
||||
return errno = ENOENT;
|
||||
}
|
||||
|
||||
while (cmd) {
|
||||
if (type == SVC_TYPE_TTY && cmd[0] == '@')
|
||||
break; /* @console */
|
||||
|
||||
if (cmd[0] == '@') /* @username[:group] */
|
||||
username = &cmd[1];
|
||||
else if (cmd[0] == '[') /* [runlevels] */
|
||||
@@ -1093,8 +1177,16 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
halt = &cmd[5];
|
||||
else if (!strncasecmp(cmd, "kill:", 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))
|
||||
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
|
||||
break;
|
||||
|
||||
@@ -1107,25 +1199,63 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
levels = conf_parse_runlevels(runlevels);
|
||||
if (runlevel > 0 && !ISOTHER(levels, 0)) {
|
||||
_d("Skipping %s, bootstrap is completed.", cmd);
|
||||
free(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!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) {
|
||||
_d("Creating new svc for %s id #%s type %d", cmd, id, type);
|
||||
svc = svc_new(cmd, id, type);
|
||||
if (!svc) {
|
||||
_e("Out of memory, cannot register service %s", cmd);
|
||||
free(line);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
if (type == SVC_TYPE_SERVICE && manual)
|
||||
svc_stop(svc);
|
||||
} else {
|
||||
/* e.g., if missing cmd or env before */
|
||||
svc_unblock(svc);
|
||||
}
|
||||
|
||||
/* Always clear svc PID file, for now. See TODO */
|
||||
@@ -1142,42 +1272,102 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
strlcpy(svc->group, ptr, sizeof(svc->group));
|
||||
}
|
||||
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;
|
||||
_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);
|
||||
|
||||
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;
|
||||
|
||||
/* 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)
|
||||
parse_sighalt(svc, halt);
|
||||
if (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)
|
||||
parse_log(svc, log);
|
||||
if (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)
|
||||
parse_env(svc, env);
|
||||
if (file)
|
||||
strlcpy(svc->file, file, sizeof(svc->file));
|
||||
|
||||
/* Set configured limits */
|
||||
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. */
|
||||
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)))
|
||||
svc_mark_dirty(svc);
|
||||
else
|
||||
svc_mark_clean(svc);
|
||||
|
||||
svc_enable(svc);
|
||||
|
||||
/* for finit native services only, e.g. plugins/hotplug.c */
|
||||
if (!file)
|
||||
svc->protect = 1;
|
||||
|
||||
/* Free duped line, from above */
|
||||
free(line);
|
||||
/* continue expanding any 'tty @console ...' */
|
||||
if (tty_isatcon(tty.dev)) {
|
||||
dev = tty_atcon();
|
||||
if (dev)
|
||||
goto next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1205,36 +1395,47 @@ void service_monitor(pid_t lost, int status)
|
||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||
return;
|
||||
|
||||
if (tty_respawn(lost))
|
||||
return;
|
||||
|
||||
svc = svc_find_by_pid(lost);
|
||||
if (!svc) {
|
||||
_d("collected unknown PID %d", lost);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
|
||||
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
|
||||
svc->status = status;
|
||||
switch (svc->state) {
|
||||
case SVC_CLEANUP_STATE:
|
||||
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 */
|
||||
if (svc_is_starting(svc) && svc_is_forking(svc))
|
||||
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) */
|
||||
if (svc_is_daemon(svc)) {
|
||||
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
|
||||
service_cleanup(svc);
|
||||
} else if (svc_is_runtask(svc)) {
|
||||
/* run/task should run at least once per runlevel */
|
||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||
svc->started = 1;
|
||||
else
|
||||
svc->started = 0;
|
||||
}
|
||||
|
||||
/* Terminate any children in the same proess group, e.g. logit */
|
||||
kill(-svc->pid, SIGKILL);
|
||||
|
||||
done:
|
||||
/* No longer running, update books. */
|
||||
svc->start_time = svc->pid = 0;
|
||||
|
||||
@@ -1247,6 +1448,112 @@ void service_monitor(pid_t lost, int status)
|
||||
sm_step(&sm);
|
||||
}
|
||||
|
||||
static void svc_mark_affected(char *cond)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_has_cond(svc))
|
||||
continue;
|
||||
|
||||
if (cond_affects(cond, svc->cond))
|
||||
svc_mark_dirty(svc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on conf_reload() to update service reverse dependencies.
|
||||
* E.g., if ospfd depends on zebra and the zebra Finit conf has
|
||||
* changed, we need to mark the ospfd Finit conf as changed too.
|
||||
*/
|
||||
void service_update_rdeps(void)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
if (!svc_is_changed(svc))
|
||||
continue;
|
||||
|
||||
svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int timeout;
|
||||
@@ -1256,12 +1563,13 @@ static void service_retry(svc_t *svc)
|
||||
|
||||
if (svc->state != SVC_HALTED_STATE ||
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*restart_cnt >= RESPAWN_MAX) {
|
||||
if (*restart_cnt >= SVC_RESPAWN_MAX) {
|
||||
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
|
||||
svc_ident(svc, NULL, 0));
|
||||
svc_crashing(svc);
|
||||
@@ -1274,12 +1582,12 @@ static void service_retry(svc_t *svc)
|
||||
|
||||
_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)",
|
||||
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);
|
||||
service_step(svc);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@@ -1290,7 +1598,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
||||
*state = new;
|
||||
|
||||
/* 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 ...",
|
||||
svc->cmd, svc->killdelay / 1000);
|
||||
service_timeout_cancel(svc);
|
||||
@@ -1322,8 +1630,13 @@ restart:
|
||||
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
if (enabled)
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
if (enabled) {
|
||||
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;
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
@@ -1333,13 +1646,20 @@ restart:
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
if (!svc->pid) {
|
||||
/* PID was collected normally, no need to kill it */
|
||||
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
_d("%s: stopped, cleaning up timers and conditions ...", svc->cmd);
|
||||
service_timeout_cancel(svc);
|
||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||
|
||||
switch (svc->type) {
|
||||
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;
|
||||
|
||||
case SVC_TYPE_TASK:
|
||||
@@ -1355,6 +1675,16 @@ restart:
|
||||
}
|
||||
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:
|
||||
if (!enabled) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
@@ -1365,6 +1695,10 @@ restart:
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
if (svc_is_missing(svc)) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
break;
|
||||
}
|
||||
(*restart_cnt)++;
|
||||
break;
|
||||
}
|
||||
@@ -1382,7 +1716,7 @@ restart:
|
||||
}
|
||||
|
||||
if (!svc->pid) {
|
||||
if (svc_is_daemon(svc)) {
|
||||
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
|
||||
svc_restarting(svc);
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
|
||||
@@ -1420,7 +1754,9 @@ restart:
|
||||
|
||||
case COND_ON:
|
||||
if (svc_is_changed(svc)) {
|
||||
if (svc->sighup) {
|
||||
if (svc_nohup(svc))
|
||||
service_stop(svc);
|
||||
else {
|
||||
/*
|
||||
* wait until all processes have been
|
||||
* stopped before continuing...
|
||||
@@ -1428,8 +1764,7 @@ restart:
|
||||
if (sm_is_in_teardown(&sm))
|
||||
break;
|
||||
service_restart(svc);
|
||||
} else
|
||||
service_stop(svc);
|
||||
}
|
||||
|
||||
svc_mark_clean(svc);
|
||||
}
|
||||
@@ -1500,7 +1835,7 @@ void service_step_all(int types)
|
||||
|
||||
void service_worker(void *unused)
|
||||
{
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
|
||||
service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ void service_unregister (svc_t *svc);
|
||||
|
||||
void service_runtask_clean (void);
|
||||
void service_reload_dynamic (void);
|
||||
void service_update_rdeps (void);
|
||||
|
||||
int service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
@@ -138,7 +138,9 @@ static struct sigmap {
|
||||
/* Nonstandard signals found in all modern POSIX systems
|
||||
(including both BSD and Linux). */
|
||||
{ SIGWINCH, "SIGWINCH" },
|
||||
#ifdef SIGSTKFLT
|
||||
{ SIGSTKFLT, "SIGSTKFLT" },
|
||||
#endif
|
||||
{ SIGPWR, "SIGPWR" },
|
||||
|
||||
/* Archaic names for compatibility. */
|
||||
@@ -192,10 +194,10 @@ void do_kill(int signo)
|
||||
if (fgets(file, sizeof(file), fp)) {
|
||||
if (strstr(file, "gdbserver"))
|
||||
_d("Skipping %s ...", file);
|
||||
else if (file[0] != '@')
|
||||
kill(pid, signo);
|
||||
else
|
||||
else if (file[0] == '@')
|
||||
_d("Skipping %s ...", &file[1]);
|
||||
else
|
||||
kill(pid, signo);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -214,11 +216,11 @@ void do_shutdown(shutop_t op)
|
||||
utmp_set_halt();
|
||||
|
||||
/*
|
||||
* Tell all remaining non-monitored processes to exit, give them
|
||||
* some time to exit gracefully, 2 sec is customary.
|
||||
* Tell remaining non-monitored processes to exit, give them
|
||||
* time to exit gracefully, 2 sec was customary, we go for 1.
|
||||
*/
|
||||
do_kill(SIGTERM);
|
||||
do_sleep(2);
|
||||
do_sleep(1);
|
||||
do_kill(SIGKILL);
|
||||
|
||||
/* Exit plugins and API gracefully */
|
||||
@@ -265,7 +267,7 @@ void do_shutdown(shutop_t op)
|
||||
|
||||
/* Reboot via watchdog or kernel, or shutdown? */
|
||||
if (op == SHUT_REBOOT) {
|
||||
if (wdog) {
|
||||
if (wdog && wdog->pid > 1) {
|
||||
int timeout = 10;
|
||||
|
||||
/* Wait here until the WDT reboots, or timeout with fallback */
|
||||
@@ -274,15 +276,15 @@ void do_shutdown(shutop_t op)
|
||||
do_sleep(1);
|
||||
}
|
||||
|
||||
_e("Rebooting ...");
|
||||
_d("Rebooting ...");
|
||||
reboot(RB_AUTOBOOT);
|
||||
} else if (op == SHUT_OFF) {
|
||||
_e("Powering down ...");
|
||||
_d("Powering down ...");
|
||||
reboot(RB_POWER_OFF);
|
||||
}
|
||||
|
||||
/* Also fallback if any of the other two fails */
|
||||
_e("Halting ...");
|
||||
_d("Halting ...");
|
||||
reboot(RB_HALT_SYSTEM);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,9 +156,11 @@ restart:
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
nologin();
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
if (runlevel != 0 && runlevel != 6) {
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (conf_any_change())
|
||||
conf_reload();
|
||||
}
|
||||
|
||||
/* Reset once flag of runtasks */
|
||||
service_runtask_clean();
|
||||
@@ -204,10 +206,6 @@ restart:
|
||||
break;
|
||||
}
|
||||
|
||||
/* No TTYs run at bootstrap, they have a delayed start. */
|
||||
if (prevlevel > 0)
|
||||
tty_runlevel();
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
@@ -222,8 +220,7 @@ restart:
|
||||
_d("Stopping services not allowed after reconf ...");
|
||||
sm->in_teardown = 1;
|
||||
cond_reload();
|
||||
service_step_all(SVC_TYPE_SERVICE);
|
||||
tty_reload(NULL);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
sm->state = SM_RELOAD_WAIT_STATE;
|
||||
break;
|
||||
@@ -240,16 +237,17 @@ restart:
|
||||
}
|
||||
|
||||
sm->in_teardown = 0;
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
_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 ...");
|
||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||
|
||||
service_step_all(SVC_TYPE_SERVICE);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
_d("Reconfiguration done");
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
|
||||
+6
-2
@@ -27,6 +27,8 @@
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
|
||||
#include <helpers.h>
|
||||
|
||||
/* B0 means; keep kernel default */
|
||||
speed_t stty_parse_speed(char *baud)
|
||||
{
|
||||
@@ -61,10 +63,12 @@ speed_t stty_parse_speed(char *baud)
|
||||
{ 1152000, B1152000 },
|
||||
{ 1500000, B1500000 },
|
||||
{ 2000000, B2000000 },
|
||||
#ifdef B4000000
|
||||
{ 2500000, B2500000 },
|
||||
{ 3000000, B3000000 },
|
||||
{ 3500000, B3500000 },
|
||||
{ 4000000, B4000000 },
|
||||
#endif
|
||||
};
|
||||
|
||||
if (!baud || !baud[0])
|
||||
@@ -121,10 +125,10 @@ void stty(int fd, speed_t speed)
|
||||
tcsetattr(fd, TCSAFLUSH, &term);
|
||||
|
||||
/* Show cursor again, if it was hidden previously */
|
||||
write(fd, "\033[?25h", 6);
|
||||
dprint(fd, "\033[?25h", 6);
|
||||
|
||||
/* Enable line wrap, if disabled previously */
|
||||
write(fd, "\033[7h", 4);
|
||||
dprint(fd, "\033[7h", 4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,36 @@ static int jobcounter = 1;
|
||||
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_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)
|
||||
{
|
||||
struct timespec now;
|
||||
@@ -52,7 +82,6 @@ static void svc_gc(void *arg)
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
||||
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
|
||||
char cond[MAX_COND_LEN];
|
||||
int msec;
|
||||
|
||||
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
|
||||
@@ -61,8 +90,7 @@ static void svc_gc(void *arg)
|
||||
continue;
|
||||
|
||||
TAILQ_REMOVE(&gc_list, svc, link);
|
||||
_d("Cleaning out %s, clearing any conditions ...", svc->name);
|
||||
cond_clear(mkcond(svc, cond, sizeof(cond)));
|
||||
maybe_clear_cond(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 */
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@@ -102,13 +130,17 @@ svc_t *svc_new(char *cmd, char *id, int type)
|
||||
svc->job = job;
|
||||
if (id && id[0])
|
||||
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 */
|
||||
strlcpy(svc->desc, svc->name, sizeof(svc->desc));
|
||||
|
||||
/* 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 */
|
||||
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)) {
|
||||
char c[MAX_COND_LEN];
|
||||
|
||||
if (s->removed)
|
||||
continue;
|
||||
if (s == svc)
|
||||
continue;
|
||||
|
||||
@@ -299,7 +333,7 @@ svc_t *svc_stop_completed(void)
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -394,6 +428,26 @@ svc_t *svc_find_by_nameid(char *name, char *id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
svc_t *svc_find_by_tty(char *dev)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
/* this is valid for fallback (notty) shells */
|
||||
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
|
||||
* @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 = 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;
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->pid != pid)
|
||||
@@ -446,7 +512,7 @@ void svc_mark_dynamic(void)
|
||||
if (svc->protect)
|
||||
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)
|
||||
{
|
||||
*((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;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->dirty == -1 && cb)
|
||||
if (svc->removed && cb)
|
||||
cb(svc);
|
||||
}
|
||||
}
|
||||
@@ -532,6 +604,12 @@ int svc_enabled(svc_t *svc)
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||
return 0;
|
||||
|
||||
if (svc_is_missing(svc))
|
||||
return 0;
|
||||
|
||||
if (svc_is_tty(svc) && bootstrap)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -602,7 +680,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
char *ptr;
|
||||
|
||||
_d("Got token:'%s'", input);
|
||||
token = sanitize(input, len);
|
||||
token = sanitize(input, strlen(input) + 1);
|
||||
if (!token) {
|
||||
_d("Sanitation of token:'%s' failed", input);
|
||||
goto next;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#include <uev/uev.h>
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "helpers.h"
|
||||
|
||||
typedef int svc_cmd_t;
|
||||
@@ -41,16 +42,20 @@ typedef enum {
|
||||
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
|
||||
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
|
||||
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_t;
|
||||
|
||||
#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)
|
||||
|
||||
typedef enum {
|
||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||
SVC_DONE_STATE, /* Task/Run job has been run */
|
||||
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_READY_STATE, /* Enabled but condition not satisfied */
|
||||
SVC_RUNNING_STATE, /* Process running */
|
||||
@@ -77,6 +82,9 @@ typedef enum {
|
||||
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
|
||||
#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
|
||||
* of issuing an initctl call. E.g.
|
||||
@@ -86,17 +94,17 @@ typedef enum {
|
||||
typedef struct svc {
|
||||
TAILQ_ENTRY(svc) link;
|
||||
|
||||
/* Instance specifics */
|
||||
int job; /* JOB: */
|
||||
char id[MAX_ID_LEN]; /* :ID */
|
||||
/* Origin of service */
|
||||
char file[MAX_ARG_LEN];
|
||||
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
struct cgroup cgroup;
|
||||
|
||||
/* Service details */
|
||||
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
|
||||
int killdelay; /* Delay in msec before sending SIGKILL */
|
||||
pid_t pid;
|
||||
pid_t oldpid, pid;
|
||||
char pidfile[256];
|
||||
long start_time; /* Start time, as seconds since boot, from sysinfo() */
|
||||
int started; /* Set for run/task/sysv to track if started */
|
||||
@@ -104,27 +112,45 @@ typedef struct svc {
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type; /* Service, run, task, ... */
|
||||
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 runlevels;
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
svc_block_t block; /* Reason that this service is currently stopped */
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
/* Instance specifics */
|
||||
int job; /* For intenal use only, canonical ref is NAME:ID */
|
||||
char name[MAX_ARG_LEN];
|
||||
char id[MAX_ID_LEN]; /* :ID */
|
||||
|
||||
/* Counters */
|
||||
char once; /* run/task, (at least) once per runlevel */
|
||||
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
||||
|
||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||
struct {
|
||||
char enabled;
|
||||
char null;
|
||||
char console;
|
||||
char file[64];
|
||||
char prio[20];
|
||||
char ident[20];
|
||||
} log;
|
||||
union {
|
||||
/* services we redirect stdout/stderr to syslog (not TTYs!) */
|
||||
struct {
|
||||
char enabled;
|
||||
char null;
|
||||
char console;
|
||||
char file[64];
|
||||
char prio[20];
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
/* Identity */
|
||||
char username[MAX_USER_LEN];
|
||||
@@ -133,8 +159,11 @@ typedef struct svc {
|
||||
/* Command, arguments and service description */
|
||||
char cmd[MAX_ARG_LEN];
|
||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||
int args_dirty;
|
||||
char desc[MAX_STR_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
|
||||
@@ -155,6 +184,7 @@ svc_t *svc_find (char *cmd, char *id);
|
||||
svc_t *svc_find_by_pid (pid_t pid);
|
||||
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_tty (char *dev);
|
||||
svc_t *svc_find_by_pidfile (char *fn);
|
||||
|
||||
svc_t *svc_iterator (svc_t **iter, int first);
|
||||
@@ -173,30 +203,37 @@ void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||
int svc_clean_bootstrap (svc_t *svc);
|
||||
void svc_prune_bootstrap (void);
|
||||
|
||||
void svc_enable (svc_t *svc);
|
||||
int svc_enabled (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 *));
|
||||
|
||||
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_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & 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_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_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_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_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_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_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_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_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline 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; }
|
||||
#define svc_start(svc) svc_unblock(svc)
|
||||
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
|
||||
@@ -205,6 +242,27 @@ static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOC
|
||||
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
|
||||
|
||||
/* Has condition in configuration and cond is allowed? */
|
||||
static inline int svc_has_cond(svc_t *svc)
|
||||
{
|
||||
if (!svc->cond[0])
|
||||
return 0;
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_TTY:
|
||||
case SVC_TYPE_SYSV:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline char *svc_status(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
@@ -249,6 +307,12 @@ static inline char *svc_status(svc_t *svc)
|
||||
case SVC_WAITING_STATE:
|
||||
return "waiting";
|
||||
|
||||
case SVC_CLEANUP_STATE:
|
||||
return "cleanup";
|
||||
|
||||
case SVC_SETUP_STATE:
|
||||
return "setup";
|
||||
|
||||
case SVC_READY_STATE:
|
||||
return "ready";
|
||||
|
||||
@@ -324,6 +388,37 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
|
||||
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_ */
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,24 +35,22 @@
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#ifdef FALLBACK_SHELL
|
||||
static pid_t fallback = 0;
|
||||
#endif
|
||||
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
|
||||
|
||||
static char *canonicalize(char *tty)
|
||||
char *tty_canonicalize(char *dev)
|
||||
{
|
||||
struct stat st;
|
||||
static char path[80];
|
||||
struct stat st;
|
||||
|
||||
if (!tty)
|
||||
if (!dev)
|
||||
return NULL;
|
||||
if (tty_isatcon(dev))
|
||||
return dev;
|
||||
|
||||
strlcpy(path, tty, sizeof(path));
|
||||
strlcpy(path, dev, sizeof(path));
|
||||
if (stat(path, &st)) {
|
||||
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
|
||||
unavailable:
|
||||
@@ -60,7 +58,7 @@ static char *canonicalize(char *tty)
|
||||
return path;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty);
|
||||
snprintf(path, sizeof(path), "%s%s", _PATH_DEV, dev);
|
||||
if (stat(path, &st))
|
||||
goto unavailable;
|
||||
}
|
||||
@@ -71,37 +69,65 @@ static char *canonicalize(char *tty)
|
||||
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;
|
||||
|
||||
LIST_FOREACH(tty, &tty_list, link)
|
||||
tty->dirty = -1;
|
||||
return dev && !strcmp(dev, "@console");
|
||||
}
|
||||
|
||||
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 (!tty->dirty)
|
||||
continue;
|
||||
if (!buf) {
|
||||
FILE *fp;
|
||||
|
||||
_d("TTY %s dirty, stopping ...", tty->name);
|
||||
tty_stop(tty);
|
||||
|
||||
if (tty->dirty == -1) {
|
||||
_d("TTY %s removed, cleaning up.", tty->name);
|
||||
tty_unregister(tty);
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
_e("Cannot find system console, is sysfs not mounted?");
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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
|
||||
* @line: Configuration, text after initial "tty"
|
||||
* @rlimit: Limits for this service/run/task, may be global limits
|
||||
* @file: The file name TTY was loaded from
|
||||
* tty_parse_args - Parse cmdline args for a tty
|
||||
* @cmd: command or tty
|
||||
*
|
||||
* A Finit tty line can use the internal getty implementation or an
|
||||
* external one, like the BusyBox getty for instance. This function
|
||||
@@ -119,66 +145,55 @@ void tty_sweep(void)
|
||||
* Different getty implementations prefer the TTY device argument in
|
||||
* 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;
|
||||
size_t i, num = 0;
|
||||
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;
|
||||
char *dev = NULL;
|
||||
size_t i;
|
||||
|
||||
if (!line) {
|
||||
_e("Missing argument");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split line in separate arguments. For an external getty
|
||||
* this is used with execv(), for the built-in it simplifies
|
||||
* further translation.
|
||||
*/
|
||||
tok = strtok(line, " \t");
|
||||
while (tok && num < NELEMS(args)) {
|
||||
if (!strcmp(tok, "noclear"))
|
||||
noclear = 1;
|
||||
else if (!strcmp(tok, "nowait"))
|
||||
nowait = 1;
|
||||
else if (!strcmp(tok, "nologin"))
|
||||
nologin = 1;
|
||||
do {
|
||||
if (!strcmp(cmd, "noclear"))
|
||||
tty->noclear = 1;
|
||||
else if (!strcmp(cmd, "nowait"))
|
||||
tty->nowait = 1;
|
||||
else if (!strcmp(cmd, "nologin"))
|
||||
tty->nologin = 1;
|
||||
else if (!strcmp(cmd, "notty"))
|
||||
tty->notty = 1; /* for fallback shell */
|
||||
else if (!access(cmd, X_OK))
|
||||
tty->cmd = cmd;
|
||||
else
|
||||
args[num++] = tok;
|
||||
tty->args[tty->num++] = cmd;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
}
|
||||
cmd = strtok(NULL, " \t");
|
||||
} while (cmd && tty->num < NELEMS(tty->args));
|
||||
|
||||
/* skip /dev probe, we just want a bríngup shell */
|
||||
if (tty->notty)
|
||||
return 0;
|
||||
|
||||
/* 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
|
||||
* tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in
|
||||
* tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external
|
||||
*/
|
||||
if ((!cmd && !dev) || (cmd && !dev)) {
|
||||
if (args[i][0] == '[')
|
||||
runlevels = line;
|
||||
if (!strcmp(args[i], "@console"))
|
||||
dev = args[i];
|
||||
if (!strncmp(args[i], "/dev", 4))
|
||||
dev = args[i];
|
||||
if (!strncmp(args[i], "tty", 3) || !strcmp(args[i], "console"))
|
||||
dev = args[i];
|
||||
if (!access(args[i], X_OK))
|
||||
cmd = args[i];
|
||||
if (!dev) {
|
||||
if (!strcmp(tty->args[i], "@console"))
|
||||
dev = tty->args[i];
|
||||
if (!strncmp(tty->args[i], "/dev", 4))
|
||||
dev = tty->args[i];
|
||||
if (!strncmp(tty->args[i], "tty", 3) || !strcmp(tty->args[i], "console"))
|
||||
dev = tty->args[i];
|
||||
|
||||
/* The first arg must be one of the above */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Built-in getty args */
|
||||
if (!cmd && dev) {
|
||||
if (isdigit(args[i][0])) {
|
||||
baud = args[i];
|
||||
if (!tty->cmd && dev) {
|
||||
if (isdigit(tty->args[i][0])) {
|
||||
tty->baud = tty->args[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -186,369 +201,86 @@ int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
* Last arg, if not anything else, is the value
|
||||
* to be used for the TERM environment variable.
|
||||
*/
|
||||
if (i + 1 == num)
|
||||
term = args[i];
|
||||
if (i + 1 == tty->num)
|
||||
tty->term = tty->args[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev) {
|
||||
error:
|
||||
tty->dev = tty_canonicalize(dev);
|
||||
if (!tty->dev) {
|
||||
_e("Incomplete or non-existing TTY device given, cannot register.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
/* Auto-detect serial console, for embedded devices mostly */
|
||||
if (!strcmp(dev, "@console")) {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
_e("Cannot find system console, is sysfs not mounted?");
|
||||
return errno = ENOENT;
|
||||
}
|
||||
|
||||
atcon = 1;
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
dev = strtok(chomp(buf), " \t");
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
again:
|
||||
/* Ensure all getty (built-in + external) are registered with absolute path */
|
||||
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;
|
||||
}
|
||||
}
|
||||
_d("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in",
|
||||
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tty_unregister(struct tty *tty)
|
||||
{
|
||||
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 tty_exists(char *dev)
|
||||
{
|
||||
int fd, result;
|
||||
struct termios c;
|
||||
|
||||
fd = open(dev, O_RDWR);
|
||||
if (-1 == fd)
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
/* XXX: Add check for errno == EIO? */
|
||||
result = tcgetattr(fd, &c);
|
||||
close(fd);
|
||||
|
||||
return result;
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
void tty_start(struct tty *tty)
|
||||
int tty_exec(svc_t *tty)
|
||||
{
|
||||
char *args[MAX_NUM_SVC_ARGS];
|
||||
char *dev;
|
||||
int i, j;
|
||||
|
||||
if (tty->pid) {
|
||||
_d("%s: TTY already active", tty->name);
|
||||
return;
|
||||
if (tty->notty) {
|
||||
/*
|
||||
* 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, _PATH_BSHELL, NULL);
|
||||
}
|
||||
|
||||
dev = canonicalize(tty->name);
|
||||
dev = tty_canonicalize(tty->dev);
|
||||
if (!dev) {
|
||||
_d("%s: Cannot find TTY device: %s", tty->name, strerror(errno));
|
||||
return;
|
||||
_d("%s: Cannot find TTY device: %s", tty->dev, strerror(errno));
|
||||
return EX_CONFIG;
|
||||
}
|
||||
|
||||
if (tty_exist(dev)) {
|
||||
if (!tty_exists(dev)) {
|
||||
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
|
||||
return;
|
||||
return EX_OSFILE;
|
||||
}
|
||||
|
||||
if (tty->nologin) {
|
||||
_d("%s: Starting /bin/sh ...", dev);
|
||||
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
|
||||
return;
|
||||
return run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
|
||||
}
|
||||
|
||||
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
|
||||
if (!tty->cmd)
|
||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||
else
|
||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
||||
}
|
||||
if (!strcmp(tty->cmd, "tty"))
|
||||
return run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||
|
||||
void tty_stop(struct tty *tty)
|
||||
{
|
||||
if (!tty->pid)
|
||||
return;
|
||||
for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) {
|
||||
if (!tty->args[i][0])
|
||||
break;
|
||||
|
||||
/*
|
||||
* 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++] = tty->args[i];
|
||||
}
|
||||
args[j++] = NULL;
|
||||
|
||||
if (fallback != lost || tty_num_active())
|
||||
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);
|
||||
return run_getty2(dev, tty->cmd, args, tty->noclear, tty->nowait, tty->rlimit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,52 +25,32 @@
|
||||
#ifndef FINIT_TTY_H_
|
||||
#define FINIT_TTY_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/resource.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#define TTY_MAX_ARGS 16
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||
#include <stdio.h>
|
||||
#include "svc.h"
|
||||
|
||||
struct tty {
|
||||
LIST_ENTRY(tty) link;
|
||||
char *cmd;
|
||||
char *args[MAX_NUM_SVC_ARGS];
|
||||
size_t num;
|
||||
|
||||
char name[42];
|
||||
char baud[10];
|
||||
char term[10];
|
||||
int noclear;
|
||||
int nowait;
|
||||
int nologin;
|
||||
int runlevels;
|
||||
|
||||
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;
|
||||
char *dev;
|
||||
char *baud;
|
||||
char *term;
|
||||
char noclear;
|
||||
char nowait;
|
||||
char nologin;
|
||||
char notty;
|
||||
};
|
||||
|
||||
void tty_mark (void);
|
||||
void tty_sweep (void);
|
||||
char *tty_canonicalize (char *dev);
|
||||
|
||||
int tty_register (char *line, struct rlimit rlimit[], char *file);
|
||||
int tty_unregister (struct tty *tty);
|
||||
int tty_isatcon (char *dev);
|
||||
char *tty_atcon (void);
|
||||
|
||||
struct tty *tty_find (char *dev);
|
||||
size_t tty_num (void);
|
||||
size_t tty_num_active (void);
|
||||
struct tty *tty_find_by_pid (pid_t pid);
|
||||
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);
|
||||
int tty_parse_args (char *cmdline, struct tty *tty);
|
||||
|
||||
int tty_exists (char *dev);
|
||||
int tty_exec (svc_t *tty);
|
||||
|
||||
#endif /* FINIT_TTY_H_ */
|
||||
|
||||
|
||||
+283
-31
@@ -26,18 +26,110 @@
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <lite/lite.h> /* strlcat() */
|
||||
#include "util.h"
|
||||
|
||||
int screen_rows = 24;
|
||||
int screen_cols = 80;
|
||||
char *prognm = NULL;
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
@@ -50,30 +142,47 @@ char *progname(char *arg0)
|
||||
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 fnwrite(char *value, char *fmt, ...)
|
||||
{
|
||||
char path[256];
|
||||
va_list ap;
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (!file)
|
||||
fp = stdout;
|
||||
else
|
||||
fp = fopen(file, append ? "a" : "w");
|
||||
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;
|
||||
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fp, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* echo(1) always adds a newline */
|
||||
fprintf(fp, "\n");
|
||||
if (file)
|
||||
fclose(fp);
|
||||
if (fputs(value, fp) == EOF || fputs("\n", fp) == EOF)
|
||||
rc = -1;
|
||||
if (fclose(fp) == EOF)
|
||||
rc = -1;
|
||||
else
|
||||
rc = 0;
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -115,6 +224,29 @@ int strtobytes(char *arg)
|
||||
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)
|
||||
{
|
||||
struct timespec requested_sleep = {
|
||||
@@ -176,6 +308,32 @@ char *uptime(long secs, char *buf, size_t len)
|
||||
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 is a JOB[:ID], JOB and ID
|
||||
@@ -187,7 +345,7 @@ char *sanitize(char *arg, size_t len)
|
||||
regex_t preg;
|
||||
int rc;
|
||||
|
||||
if (strlen(arg) > len)
|
||||
if (!memchr(arg, 0, len))
|
||||
return NULL;
|
||||
|
||||
if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
|
||||
@@ -201,28 +359,122 @@ char *sanitize(char *arg, size_t len)
|
||||
return arg;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
/*
|
||||
* Called at boot, and shutdown, to (re)initialize the
|
||||
* screen size for print() et al.
|
||||
* Called by initctl, and by finit at boot and shutdown, to
|
||||
* (re)initialize the screen size for print() et al.
|
||||
*/
|
||||
void screen_init(void)
|
||||
int ttinit(void)
|
||||
{
|
||||
if (!isatty(STDOUT_FILENO))
|
||||
return;
|
||||
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
|
||||
struct winsize ws = { 0 };
|
||||
struct termios tc, saved;
|
||||
int cached = 0;
|
||||
|
||||
initscr(&screen_rows, &screen_cols);
|
||||
/*
|
||||
* Basic TTY init, CLOCaL is important or TIOCWINSZ will block
|
||||
* until DCD is asserted, and we won't ever get it.
|
||||
*/
|
||||
if (!tcgetattr(STDERR_FILENO, &tc)) {
|
||||
saved = tc;
|
||||
cached = 1;
|
||||
|
||||
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 */
|
||||
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
|
||||
ttrows = ws.ws_row;
|
||||
ttcols = ws.ws_col;
|
||||
|
||||
/* Likely doesn't work in PID 1 after kernel starts us ... */
|
||||
if (!ws.ws_row && !ws.ws_col)
|
||||
goto fallback;
|
||||
} else if (!isatty(STDOUT_FILENO)) {
|
||||
/* 2. We may be running under watch(1) */
|
||||
ttcols = atonum(getenv("COLUMNS"));
|
||||
ttrows = atonum(getenv("LINES"));
|
||||
} else {
|
||||
fallback:
|
||||
/* 3. ANSI goto + query cursor position trick as fallback */
|
||||
fprintf(stderr, "\e7\e[r\e[999;999H\e[6n");
|
||||
|
||||
if (poll(&fd, 1, 300) > 0) {
|
||||
int row, col;
|
||||
|
||||
if (scanf("\e[%d;%dR", &row, &col) == 2) {
|
||||
ttcols = col;
|
||||
ttrows = row;
|
||||
}
|
||||
}
|
||||
|
||||
/* Jump back to where we started (\e7) */
|
||||
fprintf(stderr, "\e8");
|
||||
}
|
||||
|
||||
if (cached)
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &saved);
|
||||
|
||||
/* Sanity check */
|
||||
if (ttcols <= 0)
|
||||
ttcols = 80;
|
||||
if (ttrows <= 0)
|
||||
ttrows = 24;
|
||||
|
||||
return ttcols;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when debug mode is enabled to revert back
|
||||
* to old-school safe defaults.
|
||||
* 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.
|
||||
*/
|
||||
void screen_exit(void)
|
||||
int ttraw(void)
|
||||
{
|
||||
screen_rows = 24;
|
||||
screen_cols = 80;
|
||||
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:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
+36
-9
@@ -25,9 +25,10 @@
|
||||
#define FINIT_UTIL_H_
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_TERMIOS_H /* for screen_width() */
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
@@ -37,27 +38,53 @@
|
||||
#define RB_SW_SUSPEND 0xd000fce2
|
||||
#endif
|
||||
|
||||
extern int screen_rows;
|
||||
extern int screen_cols;
|
||||
extern int debug; /* defined in finit or initctl */
|
||||
extern int ttrows;
|
||||
extern int ttcols;
|
||||
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>
|
||||
|
||||
char *progname (char *arg0);
|
||||
int echo (char *file, int append, char *fmt, ...);
|
||||
|
||||
char *str (char *fmt, ...);
|
||||
int fnwrite (char *value, char *fmt, ...);
|
||||
|
||||
int has_utmp (void);
|
||||
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);
|
||||
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);
|
||||
|
||||
void screen_init (void);
|
||||
void screen_exit (void);
|
||||
int screen_width (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)
|
||||
{
|
||||
|
||||
+5
-61
@@ -35,6 +35,9 @@
|
||||
#define MAX_NO 5
|
||||
#define MAX_SZ 100 * 1024
|
||||
|
||||
extern int logrotate(char *file, int num, off_t sz);
|
||||
|
||||
|
||||
static void utmp_strncpy(char *dst, const char *src, size_t dlen)
|
||||
{
|
||||
size_t i;
|
||||
@@ -46,65 +49,6 @@ static void utmp_strncpy(char *dst, const char *src, size_t dlen)
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
#ifdef LOGROTATE_ENABLED
|
||||
/*
|
||||
* This function triggers a log rotates of @file when size >= @sz bytes
|
||||
* At most @num old versions are kept and by default it starts gzipping
|
||||
* .2 and older log files. If gzip is not available in $PATH then @num
|
||||
* files are kept uncompressed.
|
||||
*/
|
||||
static int logrotate(char *file, int num, off_t sz)
|
||||
{
|
||||
int cnt;
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st))
|
||||
return 1;
|
||||
|
||||
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
|
||||
if (num > 0) {
|
||||
size_t len = strlen(file) + 10 + 1;
|
||||
char ofile[len];
|
||||
char nfile[len];
|
||||
|
||||
/* First age zipped log files */
|
||||
for (cnt = num; cnt > 2; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d.gz", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
}
|
||||
|
||||
for (cnt = num; cnt > 0; cnt--) {
|
||||
snprintf(ofile, len, "%s.%d", file, cnt - 1);
|
||||
snprintf(nfile, len, "%s.%d", file, cnt);
|
||||
|
||||
/* May fail because ofile doesn't exist yet, ignore. */
|
||||
(void)rename(ofile, nfile);
|
||||
|
||||
if (cnt == 2 && fexist(nfile)) {
|
||||
size_t len = 5 + strlen(nfile) + 1;
|
||||
char cmd[len];
|
||||
|
||||
snprintf(cmd, len, "gzip %s", nfile);
|
||||
run(cmd);
|
||||
|
||||
remove(nfile);
|
||||
}
|
||||
}
|
||||
|
||||
(void)rename(file, nfile);
|
||||
create(file, st.st_mode, st.st_uid, st.st_gid);
|
||||
} else {
|
||||
truncate(file, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* LOGROTATE_ENABLED */
|
||||
|
||||
/*
|
||||
* Rotate /var/log/wtmp (+ btmp?) and /run/utmp
|
||||
*
|
||||
@@ -194,13 +138,13 @@ static int set_getty(int type, char *tty, char *id, char *user)
|
||||
return 0;
|
||||
|
||||
if (!tty)
|
||||
line = "";
|
||||
line = NULL;
|
||||
else if (!strncmp(tty, "/dev/", 5))
|
||||
line = tty + 5;
|
||||
else
|
||||
line = tty;
|
||||
|
||||
if (!id) {
|
||||
if (!id && line) {
|
||||
id = line;
|
||||
if (!strncmp(id, "pts/", 4))
|
||||
id += 4;
|
||||
|
||||
+18
-14
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -58,7 +60,7 @@ static int init(char *progname, char *devnode)
|
||||
signal(SIGPWR, sighandler);
|
||||
|
||||
openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON);
|
||||
syslog(LOG_INFO, "Finit v%s basic watchdogd starting ...", VERSION);
|
||||
syslog(LOG_INFO, "Finit v%s watchdog %s starting ...", VERSION, devnode);
|
||||
|
||||
fd = open(devnode, O_WRONLY);
|
||||
if (fd == -1)
|
||||
@@ -89,36 +91,38 @@ static int loop(int fd, int timeout)
|
||||
if (handover) {
|
||||
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
return !write(fd, "V", 1);
|
||||
}
|
||||
if (write(fd, "V", 1) == -1)
|
||||
return EX_IOERR;
|
||||
} else
|
||||
syslog(LOG_ALERT, "System going down ...");
|
||||
|
||||
return 0;
|
||||
return EX_OK;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd, ret;
|
||||
int fd, rc = EX_OK;
|
||||
|
||||
fd = init(argv[0], WDT_DEVNODE);
|
||||
if (fd == -1) {
|
||||
if (ENOENT != errno)
|
||||
syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||
return 1;
|
||||
syslog(LOG_CRIT, "Failed connecting to %s: %s", WDT_DEVNODE, strerror(errno));
|
||||
rc = EX_OSFILE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = loop(fd, WDT_TIMEOUT);
|
||||
rc = loop(fd, WDT_TIMEOUT);
|
||||
while (!handover) {
|
||||
syslog(LOG_ALERT, "System going down ...");
|
||||
|
||||
/* Waiting for SIGTERM ... */
|
||||
/* Waiting for SIGTERM or WDT reset ... */
|
||||
sleep(1);
|
||||
|
||||
/* Set lowest possible timeout on SIGTERM */
|
||||
/* Set lowest possible timeout on SIGPWR */
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
|
||||
}
|
||||
close(fd);
|
||||
done:
|
||||
closelog();
|
||||
|
||||
return ret;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-6
@@ -23,15 +23,11 @@
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#ifndef WDT_DEVNODE
|
||||
#define WDT_DEVNODE "/dev/watchdog"
|
||||
#endif
|
||||
#define WDT_TIMEOUT 30
|
||||
|
||||
#ifdef BUILTIN_WATCHDOG
|
||||
int watchdog(char *progname);
|
||||
#else
|
||||
#define watchdog(progname) 0
|
||||
#endif /* BUILTIN_WATCHDOG */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
+3
-4
@@ -3,7 +3,6 @@ EXTRA_DIST += tenv/root.mk tenv/busybox-x86_64.md5
|
||||
EXTRA_DIST += tenv/chrootsetup.sh
|
||||
EXTRA_DIST += setup-root.sh
|
||||
EXTRA_DIST += common/service.conf common/service.sh
|
||||
EXTRA_DIST += checkself.sh
|
||||
EXTRA_DIST += add-remove-dynamic-service.sh
|
||||
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
||||
EXTRA_DIST += start-stop-service.sh
|
||||
@@ -16,9 +15,9 @@ AM_TESTS_ENVIRONMENT += top_builddir=$(top_builddir);
|
||||
AM_TESTS_ENVIRONMENT += export top_builddir;
|
||||
|
||||
checkself.sh:
|
||||
echo '#!/bin/sh' > $@
|
||||
echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(EXTRA_DIST)))) >> $@
|
||||
chmod +x $@
|
||||
@echo '#!/bin/sh' > $@
|
||||
@echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(EXTRA_DIST)))) >> $@
|
||||
@chmod +x $@
|
||||
|
||||
TEST_EXTENSIONS = .sh
|
||||
TESTS = checkself.sh
|
||||
|
||||
Reference in New Issue
Block a user