Compare commits

..
Author SHA1 Message Date
Jacques de Laval 50c99762f1 Add run target for running Finit as a stand alone unprivileged process
It could be very useful, during development, to be able to run Finit on a
host system without having to provision a full VM. We have all the building
blocks for easily containerizing Finit - in the test framework - so adding
a "run" target that does the orchestration makes sense.

Signed-off-by: Jacques de Laval <jacques@de-laval.se>
2021-03-05 01:39:42 +01:00
Jacques de Laval 067043b298 test: Use STDOUT of parent process instead of a pipe as tty for finit
Signed-off-by: Jacques de Laval <jacques@de-laval.se>
2021-03-05 01:24:31 +01:00
86 changed files with 1778 additions and 4380 deletions

No files matched your search

+13 -10
View File
@@ -5,6 +5,9 @@ language: c
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/ # Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false sudo: false
# Use non-docker builds atm. to install libuEv and libite from source
#sudo: true
# Test build with both GCC and Clang (LLVM) # Test build with both GCC and Clang (LLVM)
compiler: compiler:
- gcc - gcc
@@ -27,7 +30,7 @@ addons:
name: "troglobit/finit" name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux" description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --prefix= --disable-doc --disable-contrib --with-watchdog" build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
build_command: "make" build_command: "make"
branch_pattern: dev branch_pattern: dev
@@ -42,20 +45,20 @@ install:
# Custom build script for the time being, no "make test" yet # Custom build script for the time being, no "make test" yet
script: script:
- ./autogen.sh - ./autogen.sh
- ./configure --prefix= --enable-static - ./configure --prefix=/tmp --enable-static
- make clean - make clean
- make V=1 -j5 - make V=1 -j5
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog - ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
- cat config.log - cat config.log
- make clean - make clean
- make V=1 -j5 - make V=1 -j5
- make install-strip DESTDIR=/tmp/finit - make install-strip
- tree /tmp/finit - tree /tmp
- ldd /tmp/finit/sbin/finit - ldd /tmp/sbin/finit
- size /tmp/finit/sbin/finit - size /tmp/sbin/finit
- size /tmp/finit/sbin/initctl - size /tmp/sbin/initctl
- size /tmp/finit/sbin/reboot - size /tmp/sbin/reboot
- LD_LIBRARY_PATH=/tmp/lib /tmp/finit/sbin/initctl -h - LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
- unshare -V - unshare -V
- nsenter -V - nsenter -V
- chroot --version - chroot --version
+1 -4
View File
@@ -4,7 +4,7 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.0][UNRELEASED] - 2021-03-xx [4.0][UNRELEASED] - 2021-02-21
------------------------------ ------------------------------
This release became v4.0, and not v3.2, because of incompatible changes This release became v4.0, and not v3.2, because of incompatible changes
@@ -18,9 +18,6 @@ make sure to read the whole changelog when upgrading.
with a fallback to sending signals as per traditional SysV init. The with a fallback to sending signals as per traditional SysV init. The
`-f` (force) flag remains, where `reboot(2)` is called directly `-f` (force) flag remains, where `reboot(2)` is called directly
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷 * Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
* The `inictl cond set|clear COND` have changed completely. Constrained
to a flat `<usr/...>` namespace and automatically activated by a new
`usr.so` plugin that checks services for usr condition changes
* Removed built-in inetd super server. If you need this functionality, * Removed built-in inetd super server. If you need this functionality,
use an external inetd, like xinetd, instead. A pull request for a use an external inetd, like xinetd, instead. A pull request for a
stand-alone inetd, like watchdogd and getty, is most welcome! stand-alone inetd, like watchdogd and getty, is most welcome!
+16
View File
@@ -29,3 +29,19 @@ release: distcheck
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \ printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \ printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done done
run:
srcdir=@srcdir@/test \
top_builddir=@top_builddir@ \
TENV_ROOT=$(tenv_root) \
@srcdir@/test/setup-root.sh
mkdir -p $(dir $(tenv_root)/@FINIT_CONF@)
echo 'tty [12345] @console linux noclear nologin' > $(tenv_root)/@FINIT_CONF@
-TENV_ROOT=$(tenv_root) \
TESTENV_PATH=@sbindir@:/bin \
FINIT_RCSD=@FINIT_RCSD@ \
test/tenv/start.sh finit
run : tenv_root ?= @abs_top_builddir@/run-rootfs
+53 -183
View File
@@ -26,25 +26,6 @@ Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
was reverse engineered from the [EeePC fastinit][] over ten years ago was reverse engineered from the [EeePC fastinit][] over ten years ago
by [Claudio Matsuoka][] — "gaps filled with frog DNA …" by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
![Alpine screenshot](img/finit4-screenshot.png)
*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 Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section with tutorials for: [contrib/](contrib/) section with tutorials for:
@@ -53,6 +34,9 @@ on server and desktop systems as well. For working examples, see the
* [Alpine Linux](contrib/alpine/), and * [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/) * [Debian GNU/Linux](contrib/debian/)
![Alpine screenshot](img/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
Example Example
------- -------
@@ -74,10 +58,10 @@ runlevel 2
log size=100k count=4 log size=100k count=4
# Services to be monitored and respawned as needed # Services to be monitored and respawned as needed
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon service [S12345] watchdogd -L -f -- System watchdog daemon
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon service [S12345] syslogd -n -b 3 -D -- System log daemon
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab) service [2345] lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
# The BusyBox ntpd does not use syslog when running in the foreground # The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The # So we use this trick to redirect stdout/stderr to a log file. The
@@ -92,24 +76,24 @@ service :80 [2345] merecat -n -p 80 /var/www -- Web server
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
# Alternative method instead of below runparts, can also use /etc/rc.local # Alternative method instead of below runparts, can also use /etc/rc.local
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap #task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon #task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd -- Preparing console #task [S] /etc/init.d/kbd start -- Preparing console
# Run start scripts from this directory # Run start scripts from this directory
# runparts /etc/start.d # runparts /etc/start.d
# Virtual consoles run BusyBox getty, keep kernel default speed # Virtual consoles run BusyBox getty, keep kernel default speed
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear tty [12345] /sbin/getty -L 0 /dev/tty1 linux
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear tty [12345] /sbin/getty -L 0 /dev/tty2 linux
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear tty [12345] /sbin/getty -L 0 /dev/tty3 linux
# Use built-in getty for serial port and USB serial # Use built-in getty for serial port and USB serial
#tty [12345] /dev/ttyAMA0 noclear nowait tty [12345] /dev/ttyAMA0 noclear nowait
#tty [12345] /dev/ttyUSB0 noclear tty [12345] /dev/ttyUSB0 noclear
# Just give me a shell, I need to debug this embedded system! # Just give me a shell, I need to debug this embedded system!
#tty [12345] console noclear nologin tty [12345] @console noclear nologin
``` ```
The `service` stanza, as well as `task`, `run` and others are described The `service` stanza, as well as `task`, `run` and others are described
@@ -248,11 +232,8 @@ For more information, see [doc/plugins.md](doc/plugins.md).
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/` By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
registering any changes to `.conf` files. To activate a change the user registering any changes to `.conf` files. To activate a change the user
must call `initctl reload`, which reloads all modified files, stops any must call `initctl reload`, which reloads all modified files, stops any
removed services, starts new ones, and restarts any modified ones. If the removed services, starts new ones, and restarts any modified ones, with
command line arguments of a service have changed, the process will be SIGHUP if the process supports it.
terminated and then started again with the updated arguments. If the arguments
have not been modified and the process supports SIGHUP, the process will
receive a SIGHUP rather than being terminated and started.
For some use-cases the extra step of calling `initctl reload` creates an For some use-cases the extra step of calling `initctl reload` creates an
unnecessary overhead, which can be removed at build-time using: unnecessary overhead, which can be removed at build-time using:
@@ -260,46 +241,6 @@ unnecessary overhead, which can be removed at build-time using:
configure --enable-auto-reload configure --enable-auto-reload
**Cgroups**
Finit supports cgroups v2 and comes with the following default groups
in which services and user sessions are placed in:
/sys/fs/cgroup
|-- init/ # cpu.weight:100
|-- system/ # cpu.weight:9800
`-- user/ # cpu.weight:100
Finit itself and its helper scripts and services are placed in the
top-level leaf-node group `init/`, which also is _reserved_.
All run/task/service/sysv processes are placed in their own sub-group
in `system/`. The name of each sub-group is taken from the respective
`.conf` file from `/etc/finit.d`.
All getty/tty processes are placed in their own sub-group in `user/`.
The name of each sub-group is taken from the username.
A fourth group also exists, the `root` group. It is also _reserved_ and
primarily intended for RT tasks. If you have RT tasks they need to be
declared as such in their service stanza like this:
service [...] <...> cgroup.root /path/to/foo args -- description
or
cgroup.root
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
See [doc/config.md](doc/config.md#cgroups) for more information, e.g.,
how to configure per-group limits.
The `initctl` tool has three commands to help debug and optimize the
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
commands for details.
Runparts & /etc/rc.local Runparts & /etc/rc.local
------------------------ ------------------------
@@ -407,18 +348,11 @@ Finit also implements a modern API to query status, and start/stop
services, called `initctl`. Unlike `telinit` the `initctl` tool does services, called `initctl`. Unlike `telinit` the `initctl` tool does
not return until the given command has fully completed. not return until the given command has fully completed.
``` ```shell
alpine:~# initctl help ~ $ initctl -h
Usage: initctl [OPTIONS] [COMMAND] Usage: initctl [OPTIONS] [COMMAND]
Options: 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 -v, --verbose Verbose output
-h, --help This help text -h, --help This help text
@@ -426,42 +360,29 @@ Commands:
debug Toggle Finit (daemon) debug debug Toggle Finit (daemon) debug
help This help text help This help text
version Show Finit version version Show Finit version
ls | list List all .conf in /etc/finit.d list List all .conf in /etc/finit.d/
create <CONF> Create .conf in /etc/finit.d/available enable <CONF> Enable .conf in /etc/finit.d/available/
delete <CONF> Delete .conf in /etc/finit.d/available disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
show <CONF> Show .conf in /etc/finit.d/available touch <CONF> Mark .conf in /etc/finit.d/ for reload
edit <CONF> Edit .conf in /etc/finit.d/available reload Reload *.conf in /etc/finit.d/ (activates changes)
touch <CONF> Change .conf in /etc/finit.d/available
enable <CONF> Enable .conf in /etc/finit.d/available cond show Show condition status
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 cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog log [NAME] Show ten last Finit, or NAME, messages from syslog
start <NAME>[:ID] Start service by name, with optional ID start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <NAME>[:ID] Stop/Pause a running service by name stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
reload <NAME>[:ID] Reload service by name (SIGHUP or restart) restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
restart <NAME>[:ID] Restart (stop/start) service by name status <JOB|NAME>[:ID] Show service status, by job# or name
status <NAME>[:ID] Show service status, by name status | show Show status of services, default command
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 runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system reboot Reboot system
halt Halt system halt Halt system
poweroff Halt and power off system poweroff Halt and power off system
suspend Suspend system
utmp show Raw dump of UTMP/WTMP db
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 For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -469,73 +390,22 @@ file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md), `runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
these will also affect how a service is maintained. these will also affect how a service is maintained.
> **Note:** even though it is possible to start services not belonging in **Note:** even though it is possible to start services not belonging in
> the current runlevel these services will not be respawned automatically the current runlevel these services will not be respawned automatically
> by Finit if they exit (crash). Hence, if the runlevel is 2, the below by Finit if they exit (crash). Hence, if the runlevel is 2, the below
> Dropbear SSH service will not be restarted if it is killed or exits. Dropbear SSH service will not be restarted if it is killed or exits.
The `status` command is the default, it displays a quick overview of all ```shell
monitored run/task/services. Here we call `initctl -p`, suitable for ~ $ initctl status -v
scripting and documentation: 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
alpine:~# initctl -p 4:1 inetd 0 [2345] internal time allow *:37,!eth0
PID IDENT STATUS RUNLEVELS DESCRIPTION 4:2 inetd 0 [2345] internal time allow eth0:3737
====================================================================== 5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
1506 acpid running [--2345----] ACPI daemon 5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
1509 crond running [--2345----] Cron daemon 6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
1489 dropbear running [--2345----] Dropbear SSH daemon 6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
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
``` ```
@@ -603,6 +473,6 @@ and proposed extensions.
[License]: https://en.wikipedia.org/wiki/MIT_License [License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg [License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[Travis]: https://travis-ci.org/troglobit/finit [Travis]: https://travis-ci.org/troglobit/finit
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master [Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545 [Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg [Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+26 -21
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0-rc4, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html) AC_INIT(Finit, 4.0-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
@@ -32,7 +32,7 @@ AC_PROG_MKDIR_P
# Configuration. # Configuration.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h]) AC_CHECK_HEADERS([fstaby.h termios.h])
AC_CHECK_FUNCS([strstr getopt getfsenty]) AC_CHECK_FUNCS([strstr getopt getfsenty])
# Check for uint[8,16,32]_t # Check for uint[8,16,32]_t
@@ -53,13 +53,21 @@ AC_ARG_ENABLE(emergency_shell,
Use only for debugging very early boot problems with Finit]),,[ Use only for debugging very early boot problems with Finit]),,[
enable_emergency_shell=no]) enable_emergency_shell=no])
AC_ARG_ENABLE(fallback_shell,
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
enable_fallback_shell=no])
AC_ARG_ENABLE(auto_reload, AC_ARG_ENABLE(auto_reload,
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[ AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
enable_auto_reload=no]) enable_auto_reload=no])
AC_ARG_ENABLE(watchdog,
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_watchdog=no])
AC_ARG_ENABLE(redirect, AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[ AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
enable_redirect=yes]) enable_redirect_output=no])
AC_ARG_ENABLE(logit, AC_ARG_ENABLE(logit,
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[ AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
@@ -120,19 +128,21 @@ AC_ARG_WITH(random-seed,
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]), AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
[random_seed=$withval], [random_seed=yes]) [random_seed=$withval], [random_seed=yes])
AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval],[watchdog=])
### Enable features ########################################################################### ### Enable features ###########################################################################
# Create config.h from selected features and fallback defaults # Create config.h from selected features and fallback defautls
AS_IF([test "x$enable_emergency_shell" = "xyes"], [ 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.])]) AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
AS_IF([test "x$enable_auto_reload" = "xyes"], [ AS_IF([test "x$enable_auto_reload" = "xyes"], [
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])]) AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
AS_IF([test "x$enable_watchdog" = "xyes"], [
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
AS_IF([test "x$enable_redirect" = "xyes"], [ AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])]) AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
@@ -143,8 +153,6 @@ AS_IF([test "x$enable_logit" != "xno"], [
AS_IF([test "x$enable_logrotate" != "xno"], [ AS_IF([test "x$enable_logrotate" != "xno"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])]) AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ############################################################################## ### With features ##############################################################################
AS_IF([test "x$with_config" != "xno"], [ AS_IF([test "x$with_config" != "xno"], [
AS_IF([test "x$conf" = "xyes"], [ AS_IF([test "x$conf" = "xyes"], [
@@ -194,15 +202,9 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_EXPAND_DIR(random_path, "$random_seed") AC_EXPAND_DIR(random_path, "$random_seed")
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])]) AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
AS_IF([test "x$with_watchdog" != "xno"], [
AS_IF([test "x$watchdog" = "xyes"], [
watchdog=/dev/watchdog])
with_watchdog=yes
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])])
# Control build with automake flags # Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"]) AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"]) AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"]) AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"]) AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"]) AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
@@ -222,6 +224,9 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf") AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create]) AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
AC_EXPAND_DIR(path_stdpath, "$sbindir:$bindir:/usr/sbin:/usr/bin:/sbin:/bin")
AC_DEFINE_UNQUOTED(_PATH_STDPATH, "$path_stdpath", [Default PATH])
# Generate all files # Generate all files
AC_OUTPUT AC_OUTPUT
@@ -230,7 +235,7 @@ cat <<EOF
------------------ Summary ------------------ ------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION $PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $prefix Prefix................: $prefix
Exec prefix...........: $eprefix Exec prefix...........: `eval echo $exec_prefix`
Sysconfdir............: `eval echo $sysconfdir` Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir` Localstatedir.........: `eval echo $localstatedir`
Finit config file.....: $conf_path Finit config file.....: $conf_path
@@ -249,11 +254,11 @@ Behavior:
Optional features: Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib Install contrib/......: $enable_contrib
Built-in watchdogd....: $with_watchdog $watchdog Built-in watchdogd....: $enable_watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Scripting tool logit..: $enable_logit Scripting tool logit..: $enable_logit
Emergency shell.......: $enable_emergency_shell Emergency shell.......: $enable_emergency_shell
Redirect output.......: $enable_redirect Fallback shell........: $enable_fallback_shell
Default hostname......: $hostname Default hostname......: $hostname
Default runlevel......: $runlevel Default runlevel......: $runlevel
+1 -1
View File
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--prefix=/usr --exec-prefix= \ --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --enable-watchdog \
--with-heading="Alpine Linux 3.13" --with-heading="Alpine Linux 3.13"
echo echo
+2 -1
View File
@@ -1,4 +1,5 @@
docsdir := @docdir@/contrib/alpine/finit.d/available docsdir := @docdir@/contrib/alpine/finit.d/available
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \ docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf lxdm.conf modules.conf ntpd.conf klogd.conf syslog.conf \
watchdog.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
+3 -4
View File
@@ -1,5 +1,4 @@
# Consoles to start getty on when system is up # Consoles to start getty on when system is up
tty [12345] /dev/ttyS0 noclear tty [12345] /dev/tty1 noclear nowait
tty [12345] /dev/tty1 noclear nowait tty [2345] /dev/tty2 noclear nowait
tty [2345] /dev/tty2 noclear nowait tty [2345] /dev/tty3 noclear nowait
tty [2345] /dev/tty3 noclear nowait
@@ -0,0 +1,2 @@
# 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,4 +4,3 @@
#module evdev #module evdev
#module mousedev #module mousedev
#module 8139cp #module 8139cp
module softdog
+1 -3
View File
@@ -1,4 +1,2 @@
# Start syslogd as soon as possible, should always run # Start syslogd as soon as possible, should always run
# Start klogd as soon as possible, but after syslogd service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
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
+2 -29
View File
@@ -8,18 +8,6 @@ module evdev
module loop module loop
module psmouse module psmouse
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9700 cpu.max:50000
# Example extra cgroup
cgroup maint cpu.weight:100
# Runlevel to start after bootstrap, runlevel 'S' # Runlevel to start after bootstrap, runlevel 'S'
runlevel 2 runlevel 2
@@ -28,7 +16,7 @@ network service networking start
# Max file size for each log file: 100 kiB, rotate max 4 copies: # Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz # log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size:100k count:4 log size=100k count=4
# Virtual consoles to start getty on # Virtual consoles to start getty on
tty /dev/tty1 tty /dev/tty1
@@ -42,24 +30,9 @@ tty /dev/tty3
#run [2] /etc/init.d/networking start -- Start networking #run [2] /etc/init.d/networking start -- Start networking
# Services to be monitored and respawned as needed # Services to be monitored and respawned as needed
# klgod and syslogd are placed in the maint cgroup
cgroup.maint
service [2345] <pid/syslogd> klogd -n -- Kernel logging server service [2345] <pid/syslogd> klogd -n -- Kernel logging server
service [2345] syslogd -n -- Syslog server service [2345] syslogd -n -- Syslog server
service [3] gdm -- GNOME Display Manager
# gdm in the system cgroup (default when read from it's own .conf file)
cgroup.system
service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
# Start SSH daemon with opts from /etc/default/ssh (if available), move
# to user cgroup with cpu.weight:250. Default for services is system
# group. Default weight in user group is 100 (above). Max length of
# cgroup argument is (currently) 63 chars.
#
# Notice alternative syntax where current cgroup is only set for this
# particular service.
service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
# The BusyBox ntpd does not use syslog when running in the foreground # The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The # So we use this trick to redirect stdout/stderr to a log file. The
+1 -1
View File
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--prefix=/usr --exec-prefix= \ --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --enable-watchdog \
--with-heading="Void Linux" --with-heading="Void Linux"
echo echo
+2 -2
View File
@@ -1,5 +1,5 @@
docsdir := @docdir@/contrib/void/finit.d/available docsdir := @docdir@/contrib/void/finit.d/available
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \ docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
keymap.conf lxdm.conf modules.conf ntpd.conf \ keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
sshd.conf syslog.conf uuidd.conf sshd.conf syslogd.conf uuidd.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
@@ -0,0 +1,2 @@
# Start klogd as soon as possible, should always run
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
@@ -4,4 +4,3 @@
#module evdev #module evdev
#module mousedev #module mousedev
#module 8139cp #module 8139cp
module softdog
@@ -1,4 +0,0 @@
# 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
@@ -0,0 +1,2 @@
# Start syslogd as soon as possible, should always run
service [S12345] syslogd -n -- Syslog daemon
+4 -4
View File
@@ -19,6 +19,9 @@ Some of these are also registered in the issue tracker on GitHub.
* Add new (complimentary) .svc file format to slowly migrate away * Add new (complimentary) .svc file format to slowly migrate away
from the very terse one-liner format currently used from the very terse one-liner format currently used
* Allow running as non-pid1 => read .conf and RCSD form cmdline * Allow running as non-pid1 => read .conf and RCSD form cmdline
* When a process dies, and Finit does not restart it, we should collect
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
`initctl status <process>` is called to debug the issue.
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task * Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest, * Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
@@ -26,10 +29,7 @@ Some of these are also registered in the issue tracker on GitHub.
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling. e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Write man pages for finit and `finit.conf`, steal from the excellent * Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ... `pimd` man pages ...
* Add support for timed shutdown in Finit, including cancelled shutdown: * Add support for timed shutdown in Finit, including cancelled shutdown
shutdown -h 03:35 "foobar was here"
* Add support for JSON output, or similar, from `initctl show`, e.g. * Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json` `initctl show --json`
+1 -1
View File
@@ -34,7 +34,7 @@ Bootstrap
24. Call 4th level hooks, `HOOK_SVC_UP` 24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script 25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP` 26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs 27. Start all configured TTYs, or a fallback shell on `/dev/console`
In (19) and (22) tasks and services defined in `/etc/finit.conf` and In (19) and (22) tasks and services defined in `/etc/finit.conf` and
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task` `/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
+7 -10
View File
@@ -153,22 +153,19 @@ kernel messages and add `debug` to enable Finit debug messages.
append="init=/sbin/finit debug" append="init=/sbin/finit debug"
``` ```
To debug startup issues, in particular issues with getty/login, add To debug startup issues, in particular issues with getty/login, try
the following to your Finit .conf file: `configure --enable-fallback-shell`. When no TTYs are detected, and
Finit is configured with this option, Finit will try to start a bare
tty [12345789] notty noclear `/bin/sh` on the boot console. Remember, this is only for debugging
and would leave your production system potentially wide open.
The `notty` option ensures reusing the stdin/stdout set up by the
kernel. Remember, this is only for debugging and would leave your
production system potentially wide open.
There is also a rescue shell available, in case Finit crashes and the There is also a rescue shell available, in case Finit crashes and the
kernel usually reboots: `configure --enable-emergency-shell`. However, kernel usually reboots: `configure --enable-emergency-shell`. However,
the behavior of Finit is severely limited when this is enabled, so use the behavior of Finit is severely limited when this is enabled, so use
it only for debugging start up issues when Finit crashes. it only for debugging start up issues when Finit crashes.
**NOTE:** Neither of these options should be enabled on production **NOTE:** Neither of these two configure options should be enabled on
systems since they can potentially give a user root access. production systems since they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz [1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
+1 -27
View File
@@ -45,31 +45,7 @@ being created.
Triggering Triggering
---------- ----------
Conditions are mainly triggered (asserted) by built-in plugins, e.g., Conditions can only be triggered by built-in plugins.
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands:
initctl cond set foo
initctl cond clear foo
The purpose of user-defined conditions is to be able to start or stop
services, or run/task jobs, on external site-dependent stimuli.
Example:
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
For convenience, prefixing with `usr/` is allowed, but any other slashes
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
the same as above, can also be achieved like this:
initctl cond set usr/foo
initctl cond clear usr/foo
Conditions retain their current state until the next reconfiguration or Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the runlevel change. At that point all set conditions transition into the
@@ -81,8 +57,6 @@ unchanged) state of it.
> **Note:** For `pid/` conditions it is expected that services "touch" > **Note:** For `pid/` conditions it is expected that services "touch"
> or recreate their PID file on `SIGHUP`. > or recreate their PID file on `SIGHUP`.
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions Built-in Conditions
------------------- -------------------
+10 -104
View File
@@ -2,9 +2,7 @@ Configuration
============= =============
* [Introduction](#introduction) * [Introduction](#introduction)
* [Service Environment](#service-environment)
* [Service Wrapper Scripts](#service-wrapper-scripts) * [Service Wrapper Scripts](#service-wrapper-scripts)
* [Cgroups](#cgroups)
* [Configuration File Syntax](#configuration-file-syntax) * [Configuration File Syntax](#configuration-file-syntax)
* [Hostname](#hostname) * [Hostname](#hostname)
* [Kernel Modules](#kernel-modules) * [Kernel Modules](#kernel-modules)
@@ -130,67 +128,6 @@ example employs a wrapper script in `/etc/start.d`.
> instead of sending SIGHUP at restart/reload events. > instead of sending SIGHUP at restart/reload events.
Cgroups
-------
There are three major cgroup configuration directives:
1. Global top-level group: init, system, user, or a custom group
2. Selecting a top-level group for a set of run/task/services
3. Per run/task/service limits
Top-level group configuration.
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9800
Adding an extra cgroup `maint/` will require you to adjust the weight of
the above three. We leave `init/` and `user /` as-is reducing weight of
`system/` to 9700.
cgroup system cpu.weight:9700
# Example extra cgroup 'maint'
cgroup maint cpu.weight:100
By default, the `system/` cgroup is selected for almost everything. The
`init/` cgroup is reserved for PID 1 itself and its closest relatives.
The `user/` cgroup is for local TTY logins spawned by getty.
To select a different top-level cgroup, e.g. `maint/`, one can either
define it for a group of run/task/service directives in a `.conf` or per
each stanza:
cgroup.maint
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
or
service [...] <...> cgroup.maint /path/to/foo args -- description
The latter form also allows per-stanza limits on the form:
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
Notice the comma separation and the `mem.` exception to the rule: every
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is not filtering,
except for expanding the shorthand `mem.` to `memory.`, if the file is
not available, either the cgroup controller is not available in your
Linux kernel, or the name is misspelled.
> Linux cgroups and details surrounding values are not explained in the
> Finit documentation. The Linux admin-guide cover this well:
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
Configuration File Syntax Configuration File Syntax
------------------------- -------------------------
@@ -396,28 +333,6 @@ process using a different signal, use the option `halt:SIGNAL`, e.g.,
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
sending `SIGKILL`. sending `SIGKILL`.
Services support `pre:script` and `post:script` actions as well. These
run as the same `@USER:GROUP` as the service itself, with any `env:file`
sourced. The scripts must use an absolute path, but are executed from
the `$HOME` of the given user. The scripts are not called with any
argument (currently), but both get the `SERVICE_IDENT=foo` environment
variable set. Here `foo` denotes the identity of the service, which if
there are multiple services named `foo`, may be `foo:1`, or any unique
identifier specified in the .conf file. The `post:script` is called
with an additional set of environment variables:
- `EXIT_CODE=[exited,signal]`: set to one of `exited` or `signal`
- `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from
the program, if it exited normally, or the signal name (`HUP`,
`TERM`, etc.) if it exited due to signal
The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
> **Note:** the `pre:script` _must_ be idempotent, because a service
> can transition between READY and HALTED states any number
> of times before going to RUNNING.
### Run-parts Scripts ### Run-parts Scripts
@@ -438,7 +353,6 @@ you will block Finit.
> **Note:** only read and executed in runlevel S (bootstrap). > **Note:** only read and executed in runlevel S (bootstrap).
### Including Finit Configs ### Including Finit Configs
**Syntax:** `include <CONF>` **Syntax:** `include <CONF>`
@@ -463,8 +377,8 @@ size limit is reached.
### TTYs and Consoles ### TTYs and Consoles
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]` **Syntax:** `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]` `tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
The first variant of this option uses the built-in getty on the given The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. The DEV may be the special TTY device DEV, in the given runlevels. The DEV may be the special
@@ -472,11 +386,6 @@ keyword `@console`, or `console`, useful on embedded systems.
The default baud rate is 0, i.e., keep kernel default. The default baud rate is 0, i.e., keep kernel default.
> The `tty` stanza inherits runlevel, condition (and other feature)
> parsing from the `service` stanza. So TTYs can run in one or many
> runlevels and depend on any condition supported by Finit. This is
> useful e.g. to depend on `<pid/elogind>` before starting a TTY.
**Example:** **Example:**
tty [12345] /dev/ttyAMA0 115200 noclear vt220 tty [12345] /dev/ttyAMA0 115200 noclear vt220
@@ -514,22 +423,16 @@ uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`. The speed it out by querying sysfs: `/sys/class/tty/console/active`. The speed
can be omitted to keep the kernel default. can be omitted to keep the kernel default.
> Most systems get by fine by just using `console`, which will evaluate
> to `/dev/console`. If you have to use `@console` to get any output,
> you may have some issue with your kernel config.
**Example:** **Example:**
tty [12345] @console noclear vt220 tty [12345] @console noclear vt220
On really bare bones systems Finit can give you a shell prompt as On really bare bones systems Finit offers a fallback shell, which
soon as bootstrap is done, without opening any device node: should not be enabled on production systems since. This because it
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 may give a user root access without having to log in. However, for
board bringup and system debugging it can come in handy. bringup and system debugging it can come in handy:
configure --enable-fallback-shell
One can also use the `service` stanza to start a stand-alone shell: One can also use the `service` stanza to start a stand-alone shell:
@@ -580,6 +483,9 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
Limitations Limitations
----------- -----------
+1 -1
View File
@@ -18,7 +18,7 @@ The current state depends on the two following conditions:
A service may be stopped, or blocked, for several reasons: A service may be stopped, or blocked, for several reasons:
- The user has manually stopped the service using `initctl stop NAME` - The user has manually stopped the service using `initctl stop JOB`
- The program exits immediately. I.e. keeps crashing (make sure to use - The program exits immediately. I.e. keeps crashing (make sure to use
the 'run this service in the foreground' command line option) the 'run this service in the foreground' command line option)
- The binary is missing in the filesystem - The binary is missing in the filesystem
Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

+2 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c libplug_la_SOURCES += alsa-utils.c
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
endif endif
else else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la pkglib_LTLIBRARIES += alsa-utils.la
+39 -31
View File
@@ -65,7 +65,7 @@ static int is_tmpfs(char *path)
return tmpfs; return tmpfs;
} }
static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw) static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{ {
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
@@ -76,14 +76,8 @@ static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct
return 0; return 0;
} }
/* /* We can safely skip tmpfs, nothing to clean from previous boot there */
* Cleanup stale files from previous boot, if any still linger on. static void bootclean(void)
* Some systems, e.g. Alpine Linux, still have a persistent /run and
* /tmp, i.e. not tmpfs.
*
* We can safely skip tmpfs, nothing to clean there.
*/
static void clean(void *arg)
{ {
char *dir[] = { char *dir[] = {
"/tmp/", "/tmp/",
@@ -96,7 +90,7 @@ static void clean(void *arg)
if (is_tmpfs(dir[i])) if (is_tmpfs(dir[i]))
continue; continue;
nftw(dir[i], bootclean, 20, FTW_DEPTH); nftw(dir[i], do_clean, 20, FTW_DEPTH);
} }
} }
@@ -105,11 +99,13 @@ static void clean(void *arg)
*/ */
static void setup(void *arg) static void setup(void *arg)
{ {
mode_t prev; int uid, gid;
prev = umask(0); /* Cleanup stale files, if any still linger on. */
bootclean();
_d("Setting up FHS structure in /var ..."); _d("Setting up FHS structure in /var ...");
umask(0);
makedir("/var/cache", 0755); makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */ makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755); makedir("/var/games", 0755);
@@ -137,21 +133,19 @@ static void setup(void *arg)
makedir("/var/tmp", 0755); makedir("/var/tmp", 0755);
makedir("/var/empty", 0755); makedir("/var/empty", 0755);
/*
* If /etc/group or "utmp" group is missing,
* default to "root"/"wheel" group
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
/* /*
* UTMP actually needs multiple db files * UTMP actually needs multiple db files
*/ */
if (has_utmp()) { if (has_utmp()) {
int gid;
/*
* If /etc/group or "utmp" group is missing, default to
* "root", or "wheel", group.
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */ create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */ create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */ create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
@@ -171,25 +165,39 @@ static void setup(void *arg)
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */ makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */ makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */ makedir("/var/run/pluto", 0755); /* Needed by Openswan */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga"); makedir("/var/run/quagga", 0755); /* Needed by Quagga */
mksubsys("/var/log/quagga", 0755, "quagga", "quagga"); makedir("/var/log/quagga", 0755); /* Needed by Quagga */
mksubsys("/var/run/frr", 0755, "frr", "frr"); makedir("/var/run/frr", 0755); /* Needed by FRR */
mksubsys("/var/log/frr", 0755, "frr", "frr"); makedir("/var/log/frr", 0755); /* Needed by FRR */
makedir("/var/run/sshd", 01755); /* OpenSSH */ makedir("/var/run/sshd", 01755); /* OpenSSH */
gid = getgroup("tty");
if (gid < 0)
gid = 0;
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid);
if (!fexist("/etc/mtab")) if (!fexist("/etc/mtab"))
symlink("../proc/self/mounts", "/etc/mtab"); symlink("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */ /* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd"); 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);
}
umask(prev); umask(022);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_MOUNT_POST] = { .cb = clean }, .hook[HOOK_BASEFS_UP] = {
.hook[HOOK_BASEFS_UP] = { .cb = setup }, .cb = setup
},
.depends = { "pidfile" }, .depends = { "pidfile" },
}; };
+11 -13
View File
@@ -32,16 +32,13 @@
#include "conf.h" #include "conf.h"
#define DAEMON "dbus-daemon" #define DAEMON "dbus-daemon"
#define ARGS "--nofork --system --syslog-only" #define ARGS "--nofork --system"
#define DESC "D-Bus message bus daemon" #define DESC "D-Bus message bus daemon"
#define DAEMONUSER "messagebus"
static void setup(void *arg) static void setup(void *arg)
{ {
char line[256];
mode_t prev;
char *cmd; char *cmd;
char line[256];
cmd = which(DAEMON); cmd = which(DAEMON);
if (!cmd) { if (!cmd) {
@@ -49,28 +46,29 @@ static void setup(void *arg)
return; return;
} }
prev =umask(0); umask(0);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER); makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
if (whichp("dbus-uuidgen")) if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus"); run("dbus-uuidgen --ensure");
/* Clean up from any previous pre-bootstrap run */ /* Clean up from any previous pre-bootstrap run */
remove("/var/run/dbus/pid"); erase("/var/run/dbus/pid");
/* Register service with Finit */ /* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] @%s:%s %s %s -- %s", snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL)) if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON); _pe("Failed registering %s", DAEMON);
free(cmd); free(cmd);
umask(prev); umask(022);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_NETWORK_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", }, .depends = { "bootmisc", },
-1
View File
@@ -90,7 +90,6 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc" },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+40 -75
View File
@@ -17,7 +17,6 @@
*/ */
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <net/if.h> /* IFNAMSIZ */ #include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h> #include <sys/socket.h>
@@ -49,7 +48,7 @@ static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
return 0; return 0;
} }
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len) static void nl_route(struct nlmsghdr *nlmsg)
{ {
struct rtmsg *r; struct rtmsg *r;
struct rtattr *a; struct rtattr *a;
@@ -64,14 +63,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
r = NLMSG_DATA(nlmsg); r = NLMSG_DATA(nlmsg);
a = RTM_RTA(r); a = RTM_RTA(r);
la = RTM_PAYLOAD(nlmsg); la = RTM_PAYLOAD(nlmsg);
if (la >= len) {
_e("Packet too large!");
return;
}
while (RTA_OK(a, la)) { while (RTA_OK(a, la)) {
void *data = RTA_DATA(a); void *data = RTA_DATA(a);
switch (a->rta_type) { switch (a->rta_type) {
case RTA_GATEWAY: case RTA_GATEWAY:
gw = *((int *)data); gw = *((int *)data);
@@ -112,27 +105,7 @@ static void net_cond_set(char *ifname, char *cond, int set)
cond_clear(msg); cond_clear(msg);
} }
static int validate_ifname(const char *ifname) static void nl_link(struct nlmsghdr *nlmsg)
{
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; int la;
char ifname[IFNAMSIZ + 1]; char ifname[IFNAMSIZ + 1];
@@ -147,61 +120,53 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
i = NLMSG_DATA(nlmsg); i = NLMSG_DATA(nlmsg);
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg))); a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg)); la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
if (la >= len) {
_e("Packet too large!");
return;
}
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) { while (RTA_OK(a, la)) {
if (a->rta_type != IFLA_IFNAME) if (a->rta_type == IFLA_IFNAME) {
continue; 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;
strlcpy(ifname, RTA_DATA(a), sizeof(ifname)); case RTM_DELLINK:
if (validate_ifname(ifname)) { /* NOTE: Interface has disappeared, not link down ... */
_d("Invalid interface name '%s', skipping ...", ifname); _d("%s: Delete link", ifname);
continue; 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;
}
} }
switch (nlmsg->nlmsg_type) { a = RTA_NEXT(a, la);
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) static void nl_callback(void *arg, int sd, int events)
{ {
ssize_t len;
static char buf[4096]; static char buf[4096];
struct nlmsghdr *nh; struct nlmsghdr *nh;
ssize_t len;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
len = recv(sd, buf, sizeof(buf), 0); len = recv(sd, buf, sizeof(buf), 0);
@@ -214,9 +179,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)) { 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); //_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE) if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
nl_route(nh, len); nl_route(nh);
else else
nl_link(nh, len); nl_link(nh);
} }
} }
+17 -36
View File
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
} }
} }
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE); return iwatch_add(iw, path, IN_ONLYDIR);
} }
static void pidfile_update_conds(char *dir, char *name, uint32_t mask) static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
svc_t *svc; svc_t *svc;
paste(fn, sizeof(fn), dir, name); paste(fn, sizeof(fn), dir, name);
_d("path: %s, mask: %08x", fn, mask);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0)) if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return; return;
_d("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn); svc = svc_find_by_pidfile(fn);
if (!svc) { if (!svc) {
_d("No matching svc for %s", fn); _d("No matching svc for %s", fn);
@@ -82,25 +82,15 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid); _d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc); svc_started(svc);
if (!svc_has_pidfile(svc))
pid_file_set(svc, fn, 1);
if (svc_is_forking(svc)) { if (svc_is_forking(svc)) {
pid_t pid; pid_t pid;
pid = pid_file_read(pid_file(svc)); pid = pid_file_read(pid_file(svc));
if (pid == -1) { _d("Forking service %s changed PID from %d to %d",
cond_clear(cond); svc->cmd, svc->pid, pid);
return; svc->pid = pid;
}
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
} }
cond_set(cond); cond_set(cond);
@@ -161,8 +151,7 @@ static void pidfile_callback(void *arg, int fd, int events)
{ {
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev; struct inotify_event *ev;
ssize_t sz; ssize_t sz, off;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
@@ -171,19 +160,15 @@ static void pidfile_callback(void *arg, int fd, int events)
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) { for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp; struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off]; ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask) if (!ev->mask)
continue; continue;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
/* Find base path for this event */ /* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd); iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
if (!iwp) if (!iwp)
@@ -194,7 +179,12 @@ static void pidfile_callback(void *arg, int fd, int events)
continue; continue;
} }
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) 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))
pidfile_update_conds(iwp->path, ev->name, ev->mask); pidfile_update_conds(iwp->path, ev->name, ev->mask);
} }
} }
@@ -236,14 +226,8 @@ static void pidfile_reconf(void *arg)
static void pidfile_init(void *arg) static void pidfile_init(void *arg)
{ {
char piddir[MAX_ARG_LEN];
char *path; char *path;
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
return;
}
/* /*
* The bootmisc plugin is responsible for setting up /var/run. * The bootmisc plugin is responsible for setting up /var/run.
* and/or /run, with proper symlinks etc. We depend on bootmisc * and/or /run, with proper symlinks etc. We depend on bootmisc
@@ -251,7 +235,6 @@ static void pidfile_init(void *arg)
*/ */
path = realpath(_PATH_VARRUN, NULL); path = realpath(_PATH_VARRUN, NULL);
if (!path) { if (!path) {
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL); path = realpath("/run", NULL);
if (!path) { if (!path) {
_e("System does not have %s or /run, aborting.", _PATH_VARRUN); _e("System does not have %s or /run, aborting.", _PATH_VARRUN);
@@ -261,8 +244,6 @@ static void pidfile_init(void *arg)
if (pidfile_add_path(&iw_pidfile, path)) if (pidfile_add_path(&iw_pidfile, path))
iwatch_exit(&iw_pidfile); iwatch_exit(&iw_pidfile);
free(path);
} }
/* /*
+25 -46
View File
@@ -32,14 +32,13 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "service.h"
#include "tty.h" #include "tty.h"
static void tty_watcher(void *arg, int fd, int events); static void watcher(void *arg, int fd, int events);
static plugin_t plugin = { static plugin_t plugin = {
.io = { .io = {
.cb = tty_watcher, .cb = watcher,
.flags = PLUGIN_IO_READ, .flags = PLUGIN_IO_READ,
}, },
}; };
@@ -54,53 +53,33 @@ static void setup(void)
_pe("Failed starting TTY watcher"); _pe("Failed starting TTY watcher");
} }
static void do_tty(char *tty, size_t len, int creat) static void watcher(void *arg, int fd, int events)
{ {
char name[len + 6]; char buf[EVENT_SIZE];
svc_t *svc; int len = 0;
snprintf(name, sizeof(name), "/dev/%s", tty); while ((len = read(fd, buf, sizeof(buf)))) {
svc = svc_find_by_tty(name); struct inotify_event *notified = (struct inotify_event *)buf;
if (svc) { char name[notified->len + 6];
if (svc_is_blocked(svc) && creat) { struct tty *entry;
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
svc_start(svc); if (-1 == len) {
} else if (svc->pid) { if (errno == EINVAL)
_d("%s: missing ...", svc_ident(svc, NULL, 0)); setup();
svc_missing(svc); if (errno == EINTR)
continue;
break; /* Likely EAGAIN */
} }
service_step_all(SVC_TYPE_TTY); snprintf(name, sizeof(name), "/dev/%s", notified->name);
} entry = tty_find(name);
} if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE)
static void tty_watcher(void *arg, int fd, int events) tty_start(entry);
{ else if (entry->pid)
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; tty_stop(entry);
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);
} }
} }
+4 -9
View File
@@ -37,11 +37,10 @@ static void setup(void *arg)
#ifdef RANDOMSEED #ifdef RANDOMSEED
if (!fexist(RANDOMSEED)) { if (!fexist(RANDOMSEED)) {
int ret = 1; int ret = 1;
mode_t prev;
FILE *fp; FILE *fp;
print_desc("Bootstrapping random seed", NULL); print_desc("Bootstrapping random seed", NULL);
prev = umask(077); umask(077);
fp = fopen(RANDOMSEED, "w"); fp = fopen(RANDOMSEED, "w");
if (fp) { if (fp) {
int iter = 128; int iter = 128;
@@ -58,7 +57,7 @@ static void setup(void *arg)
ret = fclose(fp); ret = fclose(fp);
} }
print_result(ret); print_result(ret);
umask(prev); umask(0);
} }
print_desc("Initializing random number generator", NULL); print_desc("Initializing random number generator", NULL);
@@ -69,14 +68,10 @@ static void setup(void *arg)
static void save(void *arg) static void save(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
mode_t prev; umask(077);
prev = umask(077);
print_desc("Saving random seed", NULL); print_desc("Saving random seed", NULL);
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0)); print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
umask(0);
umask(prev);
#endif #endif
} }
-133
View File
@@ -1,133 +0,0 @@
/* 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:
*/
+4 -5
View File
@@ -32,14 +32,13 @@
static void setup(void *arg) static void setup(void *arg)
{ {
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE #ifdef PAM_CONSOLE
int fd; int fd;
#endif #endif
char line[LINE_SIZE];
const char *username = "nobody";
prev = umask(0); umask(0);
makedir("/var/run/console", 01777); makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username); snprintf(line, sizeof(line), "/var/run/console/%s", username);
@@ -59,7 +58,7 @@ static void setup(void *arg)
if (whichp("restorecon")) if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix"); run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
umask(prev); umask(022);
} }
static plugin_t plugin = { static plugin_t plugin = {
+17 -20
View File
@@ -1,5 +1,6 @@
EXTRA_DIST = rescue.conf sample.conf EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC if STATIC
AM_CPPFLAGS += -DENABLE_STATIC AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs AM_LDFLAGS = -static-libtool-libs
@@ -7,21 +8,20 @@ else
AM_LDFLAGS = -export-dynamic AM_LDFLAGS = -export-dynamic
endif endif
sbin_PROGRAMS = finit initctl sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = pkglibexec_SCRIPTS = cgreaper.sh
finit_pkglibdir = $(pkglibdir) finit_pkglibdir = $(pkglibdir)
finit_pkglib_SCRIPTS = rescue.conf sample.conf finit_pkglib_SCRIPTS = rescue.conf sample.conf
if WATCHDOGD if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd pkglibexec_PROGRAMS = watchdogd
endif endif
if LOGIT if LOGIT
pkglibexec_PROGRAMS += logit pkglibexec_PROGRAMS = logit
logit_SOURCES = logit.c logrotate.c logit_SOURCES = logit.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS)
logit_LDADD = $(lite_LIBS)
endif endif
finit_SOURCES = api.c cgroup.c cgroup.h \ finit_SOURCES = api.c cgroup.c cgroup.h \
@@ -43,10 +43,6 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
tty.c tty.h \ tty.c tty.h \
util.c util.h \ util.c util.h \
utmp-api.c utmp-api.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 pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
@@ -58,19 +54,20 @@ else
finit_LDADD += -ldl finit_LDADD += -ldl
endif endif
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \ initctl_SOURCES = initctl.c client.c client.h \
client.c client.h cond.c cond.h reboot.c \ reboot.c serv.c serv.h svc.h \
serv.c serv.h svc.h util.c util.h cond.c cond.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) initctl_CFLAGS += $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS) $(uev_LIBS) initctl_LDADD = $(lite_LIBS)
INIT_LNKS = init telinit INIT_LNKS = init
REBOOT_LNKS = reboot shutdown halt poweroff suspend REBOOT_LNKS = reboot shutdown halt poweroff suspend
if WATCHDOGD if WATCHDOGD
watchdogd_SOURCES = watchdog.c watchdog.h watchdogd_SOURCES = watchdog.c watchdog.h
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
#log.c log.h
endif endif
# Hook in install to add finit and reboot symlink(s) # Hook in install to add finit and reboot symlink(s)
+17 -47
View File
@@ -74,24 +74,7 @@ static int start(svc_t *svc)
return 0; return 0;
} }
/*
* NOTE: this does not wait for svc to be stopped first, that is the
* responsibility of initctl to do. Otherwise we'd block PID 1,
* or introduce some nasty race conditions.
*/
static int restart(svc_t *svc) static int restart(svc_t *svc)
{
if (!svc)
return 1;
svc_mark_dirty(svc);
svc_start(svc);
service_step(svc);
return 0;
}
static int reload(svc_t *svc)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -105,10 +88,9 @@ static int reload(svc_t *svc)
return 0; return 0;
} }
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_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_restart(char *buf, size_t len) { return call(restart, buf, len); } static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
static char query_buf[368]; static char query_buf[368];
static int missing(char *job, char *id) static int missing(char *job, char *id)
@@ -128,7 +110,7 @@ static int missing(char *job, char *id)
static int do_query(char *buf, size_t len) static int do_query(char *buf, size_t len)
{ {
query_buf[0] = 0; memset(query_buf, 0, sizeof(query_buf));
if (svc_parse_jobstr(buf, len, NULL, missing)) { if (svc_parse_jobstr(buf, len, NULL, missing)) {
memcpy(buf, query_buf, len); memcpy(buf, query_buf, len);
return 1; return 1;
@@ -255,7 +237,7 @@ ev_t ev_list[] = {
static void send_svc(int sd, svc_t *svc) static void send_svc(int sd, svc_t *svc)
{ {
svc_t empty = { 0 }; svc_t empty;
size_t len; size_t len;
if (!svc) { if (!svc) {
@@ -270,10 +252,10 @@ static void send_svc(int sd, svc_t *svc)
static void api_cb(uev_t *w, void *arg, int events) static void api_cb(uev_t *w, void *arg, int events)
{ {
static svc_t *iter = NULL;
struct init_request rq;
int sd, lvl; int sd, lvl;
svc_t *svc; svc_t *svc;
static svc_t *iter = NULL;
struct init_request rq;
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
@@ -345,22 +327,16 @@ static void api_cb(uev_t *w, void *arg, int events)
result = do_start(rq.data, sizeof(rq.data)); result = do_start(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data));
break;
case INIT_CMD_STOP_SVC: case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data); _d("stop %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_stop(rq.data, sizeof(rq.data)); result = do_stop(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RELOAD_SVC: case INIT_CMD_RESTART_SVC:
_d("reload %s", rq.data); _d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_reload(rq.data, sizeof(rq.data)); result = do_restart(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_GET_RUNLEVEL: case INIT_CMD_GET_RUNLEVEL:
@@ -388,30 +364,24 @@ static void api_cb(uev_t *w, void *arg, int events)
} }
_e("Request to hand-over wdog ... to PID %d", rq.runlevel); _e("Request to hand-over wdog ... to PID %d", rq.runlevel);
svc = svc_find_by_pid(rq.runlevel); if (!svc_find_by_pid(rq.runlevel)) {
if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel); logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
break; break;
} }
if (wdog && wdog != svc) { /* Disable and allow Finit to collect bundled watchdog */
char name[32]; if (wdog) {
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
svc_ident(svc, name, sizeof(name)); stop(wdog);
logit(LOG_NOTICE, "Handing over wdog ctrl from %s[%d] to %s[%d]",
svc_ident(wdog, NULL, 0), wdog->pid, name, svc->pid);
if (wdog->protect) { if (wdog->protect) {
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog."); wdog->protect = 0;
stop(wdog); wdog->runlevels = 0;
svc_del(wdog);
} }
} }
wdog = svc;
break; break;
case INIT_CMD_SVC_ITER: case INIT_CMD_SVC_ITER:
// _d("svc iter, first: %d", rq.runlevel); _d("svc iter, first: %d", rq.runlevel);
/* /*
* XXX: This severly limits the number of * XXX: This severly limits the number of
* simultaneous client connections, but will * simultaneous client connections, but will
@@ -474,7 +444,7 @@ int api_init(uev_ctx_t *ctx)
}; };
_d("Setting up external API socket ..."); _d("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
_pe("Failed starting external API socket"); _pe("Failed starting external API socket");
return 1; return 1;
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
# Called by kernel when last child in cgroup exists
BASE=/sys/fs/cgroup/finit
PROC=`basename $1`
DIR="$BASE$1"
/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir $DIR
exit 0
+79 -426
View File
@@ -25,458 +25,111 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <lite/lite.h> #include <lite/lite.h>
#include <lite/queue.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */
#include "cgroup.h"
#include "finit.h"
#include "iwatch.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
struct cg { static int cg_init = 0;
TAILQ_ENTRY(cg) link;
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)
{
char *val;
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
if (!path || !ctrl) {
_e("Missing path or controller, skipping!");
return;
}
if (!prop) {
prop = strchr(ctrl, '.');
if (!prop) {
_e("Invalid cgroup ctrl syntax: %s", ctrl);
return;
}
*prop++ = 0;
}
val = strchr(prop, ':');
if (!val) {
_e("Missing cgroup ctrl value, prop %s", prop);
return;
}
*val++ = 0;
/* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) {
_e("Possible security violation; '..' not allowed in cgroup config!");
return;
}
_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);
}
/*
* 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];
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
if (pid < 0 || pid == 1) {
errno = EINVAL;
return 1;
}
/* 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 * Called by Finit at early boot to mount initial cgroups
*/ */
void cgroup_init(uev_ctx_t *ctx) void cgroup_init(void)
{ {
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char buf[80];
FILE *fp; FILE *fp;
int fd; char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { fp = fopen("/proc/cgroups", "r");
_pe("Failed mounting cgroup v2");
return;
}
/* Find available controllers */
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
if (!fp) { if (!fp) {
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers"); _d("No cgroup support");
return; return;
} }
if (fgets(buf, sizeof(buf), fp)) { if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
_d("Failed mounting cgroups");
goto fail;
}
/* Skip first line, header */
fgets(buf, sizeof(buf), fp);
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup; char *cgroup;
char rc[80];
cgroup = strtok(chomp(buf), "\t "); cgroup = strtok(buf, "\t ");
while (cgroup) { if (!cgroup)
append_ctrl(cgroup); continue;
cgroup = strtok(NULL, "\t ");
} snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
if (mkdir(rc, 0755) && EEXIST != errno)
continue;
if (mount("cgroup", rc, "cgroup", opts, cgroup))
_d("Failed mounting %s cgroup on %s", cgroup, rc);
} }
/* Default cgroups for process monitoring */
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); fclose(fp);
}
/* Enable all controllers */ static int move_pid(char *group, char *name, char *id, int pid)
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) {
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control"); char path[256];
/* Default (protected) groups, PID 1, services, and user/login processes */ snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
cgroup_add("init", "cpu.weight:100", 1); if (mkdir(path, 0755) && errno != EEXIST)
cgroup_add("system", "cpu.weight:9800", 1); return 1;
cgroup_add("user", "cpu.weight:100", 1);
cgroup_config();
/* Move ourselves to init (best effort, otherwise run in 'root' group */ strlcat(path, "/cgroup.procs", sizeof(path));
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs"))
_pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
/* prepare cgroup.events watcher */ return echo(path, 0, "%d", pid);
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"); int cgroup_user(char *name)
close(fd); {
if (!cg_init)
return 0;
return move_pid("finit/user", name, "0", getpid());
}
int cgroup_service(char *nm, char *id, int pid)
{
if (!cg_init)
return 0;
if (pid <= 0) {
errno = EINVAL;
return 1;
} }
return move_pid("finit/system", nm, id, pid);
} }
/** /**
+3 -17
View File
@@ -24,23 +24,9 @@
#ifndef FINIT_CGROUP_H_ #ifndef FINIT_CGROUP_H_
#define FINIT_CGROUP_H_ #define FINIT_CGROUP_H_
#include <uev/uev.h> void cgroup_init (void);
struct cgroup { int cgroup_user (char *name);
char name[16]; int cgroup_service (char *cmd, char *id, int pid);
char cfg[128];
};
void cgroup_mark_all(void);
void cgroup_cleanup (void);
int cgroup_add (char *name, char *cfg, int protected);
int cgroup_del (char *dir);
void cgroup_config (void);
void cgroup_init (uev_ctx_t *ctx);
int cgroup_user (char *name, int pid);
int cgroup_service (char *name, int pid, struct cgroup *cg);
#endif /* FINIT_CGROUP_H_ */ #endif /* FINIT_CGROUP_H_ */
-654
View File
@@ -1,654 +0,0 @@
/* 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:
*/
-77
View File
@@ -1,77 +0,0 @@
/* 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:
*/
+16 -30
View File
@@ -21,9 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <err.h>
#include <errno.h> #include <errno.h>
#include <poll.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
@@ -38,14 +36,19 @@ int client_connect(void)
.sun_path = INIT_SOCKET, .sun_path = INIT_SOCKET,
}; };
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0); sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd) if (-1 == sd)
err(1, "Failed creating UNIX domain socket"); goto error;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
err(1, "Failed connecting to finit"); close(sd);
goto error;
}
return sd; return sd;
error:
perror("Failed connecting to finit");
return -1;
} }
int client_disconnect(void) int client_disconnect(void)
@@ -65,44 +68,27 @@ int client_disconnect(void)
int client_send(struct init_request *rq, ssize_t len) int client_send(struct init_request *rq, ssize_t len)
{ {
struct pollfd pfd = { 0 };
int sd, result = 255; int sd, result = 255;
sd = client_connect(); sd = client_connect();
if (-1 == sd) if (-1 == sd)
return -1; return -1;
pfd.fd = sd; if (write(sd, rq, len) != len)
pfd.events = POLLOUT; goto error;
if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for Finit, errno %d", errno);
goto exit;
}
if (write(sd, rq, len) != len) { if (read(sd, rq, len) != len)
warn("Failed communicating with Finit, errno %d", errno); goto error;
goto exit;
}
pfd.fd = sd;
pfd.events = POLLIN;
if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for reply from Finit, errno %d", errno);
goto exit;
}
if (read(sd, rq, len) != len) {
warn("Failed reading reply from Finit, errno %d", errno);
goto exit;
}
if (rq->cmd == INIT_CMD_NACK) if (rq->cmd == INIT_CMD_NACK)
result = 1; result = 1;
else else
result = 0; result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit: exit:
client_disconnect(); client_disconnect();
return result; return result;
} }
+37 -20
View File
@@ -99,14 +99,13 @@ static void cond_bump_reconf(void)
unsigned int rgen; unsigned int rgen;
/* /*
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0 * If %COND_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want. * meaning that rgen++ is always what we want.
*/ */
rgen = cond_get_gen(_PATH_RECONF); rgen = cond_get_gen(COND_RECONF);
rgen++; rgen++;
if (cond_set_gen(_PATH_RECONF, rgen)) cond_set_gen(COND_RECONF, rgen);
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen);
} }
static int cond_checkpath(const char *path) static int cond_checkpath(const char *path)
@@ -120,7 +119,7 @@ static int cond_checkpath(const char *path)
return 1; return 1;
} }
if (mkpath(dir, 0755) && errno != EEXIST) { if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path); _pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1; return 1;
} }
@@ -128,22 +127,22 @@ static int cond_checkpath(const char *path)
return 0; return 0;
} }
int cond_set_path(const char *path, enum cond_state next) int cond_set_path(const char *path, enum cond_state new)
{ {
enum cond_state prev; enum cond_state old;
unsigned int rgen; unsigned int rgen;
_d("%s <= %d", path, next); _d("%s", path);
rgen = cond_get_gen(_PATH_RECONF); rgen = cond_get_gen(COND_RECONF);
if (!rgen) { if (!rgen) {
_e("Unable to read configuration generation (%s)", path); _e("Unable to read configuration generation (%s)", path);
return -1; return -1;
} }
prev = cond_get_path(path); old = cond_get_path(path);
switch (next) { switch (new) {
case COND_ON: case COND_ON:
if (cond_checkpath(path)) if (cond_checkpath(path))
return 0; return 0;
@@ -160,11 +159,30 @@ int cond_set_path(const char *path, enum cond_state next)
return 0; return 0;
} }
return next != prev; return new != old;
} }
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */ /* Has condition in configuration and cond is allowed? */
void cond_update(const char *name) 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)
{ {
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
@@ -203,8 +221,7 @@ void cond_set_oneshot(const char *name)
if (cond_checkpath(path)) if (cond_checkpath(path))
return; return;
if (symlink(_PATH_RECONF, path) && errno != EEXIST) symlink(COND_RECONF, path);
_pe("Failed creating onshot cond %s", name);
cond_update(name); cond_update(name);
} }
@@ -237,13 +254,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F) if (tflg != FTW_F)
return 0; return 0;
nm = strstr((char *)fpath, COND_BASE); nm = strstr((char *)fpath, COND_DIR);
if (!nm) { if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath); _e("Incorrect condtion path %s, cannot reassert", fpath);
return 1; return 1;
} }
nm += strlen(COND_BASE); nm += sizeof(COND_DIR);
_d("Reasserting %s => %s", fpath, nm); _d("Reasserting %s => %s", fpath, nm);
cond_set(nm); cond_set(nm);
@@ -264,8 +281,8 @@ void cond_init(void)
{ {
char path[MAX_ARG_LEN]; char path[MAX_ARG_LEN];
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) { if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND); _pe("Failed creating condition base directory '%s'", COND_PATH);
return; return;
} }
+6 -6
View File
@@ -45,15 +45,15 @@ const char *cond_path(const char *name)
static char path[256]; static char path[256];
char tmp[MAX_ARG_LEN]; char tmp[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name); snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
return pid_runpath(tmp, path, sizeof(path)); return pid_runpath(tmp, path, sizeof(path));
} }
unsigned int cond_get_gen(const char *file) unsigned int cond_get_gen(const char *file)
{ {
char *ptr, path[256];
unsigned int gen; unsigned int gen;
char *ptr, path[256];
FILE *fp; FILE *fp;
int ret; int ret;
@@ -74,12 +74,12 @@ enum cond_state cond_get_path(const char *path)
{ {
int cgen, rgen; int cgen, rgen;
cgen = cond_get_gen(path); rgen = cond_get_gen(COND_RECONF);
if (!cgen) if (!rgen)
return COND_OFF; return COND_OFF;
rgen = cond_get_gen(_PATH_RECONF); cgen = cond_get_gen(path);
if (!rgen) if (!cgen)
return COND_OFF; return COND_OFF;
return (cgen == rgen) ? COND_ON : COND_FLUX; return (cgen == rgen) ? COND_ON : COND_FLUX;
+3 -9
View File
@@ -4,14 +4,9 @@
#include <paths.h> #include <paths.h>
#include <svc.h> #include <svc.h>
#define COND_BASE "finit/cond" #define COND_DIR "finit/cond"
#define COND_USR "usr/" #define COND_PATH _PATH_VARRUN COND_DIR
#define COND_PID "pid/" #define COND_RECONF COND_PATH "/reconf"
#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 { typedef enum cond_state {
COND_OFF = 0, COND_OFF = 0,
@@ -28,7 +23,6 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names); enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names); int cond_affects (const char *name, const char *names);
void cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new); int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name); void cond_set (const char *name);
void cond_set_oneshot (const char *name); void cond_set_oneshot (const char *name);
+32 -107
View File
@@ -23,7 +23,6 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
#include <sys/inotify.h> #include <sys/inotify.h>
@@ -48,11 +47,8 @@
int logfile_size_max = 200000; /* 200 kB */ int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5; int logfile_count_max = 5;
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
struct rlimit global_rlimit[RLIMIT_NLIMITS]; struct rlimit global_rlimit[RLIMIT_NLIMITS];
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
struct conf_change { struct conf_change {
TAILQ_ENTRY(conf_change) link; TAILQ_ENTRY(conf_change) link;
char *name; char *name;
@@ -82,14 +78,14 @@ static void hide_args(int argc, char *argv[])
} }
} }
int get_bool(char *arg, int default_value) int get_bool(char *ptr, int default_value)
{ {
if (!arg) if (!ptr)
goto fallback; goto fallback;
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1")) if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
return 1; return 1;
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0")) if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
return 0; return 0;
fallback: fallback:
return default_value; return default_value;
@@ -101,24 +97,19 @@ fallback:
*/ */
static void parse_finit_opts(char *opt) static void parse_finit_opts(char *opt)
{ {
char *arg; char *ptr;
arg = strchr(opt, '='); ptr = strchr(opt, '=');
if (arg) if (ptr)
*arg++ = 0; *ptr++ = 0;
if (string_compare(opt, "debug")) { if (string_compare(opt, "debug")) {
debug = get_bool(arg, 1); debug = get_bool(ptr, 1);
return; return;
} }
if (string_compare(opt, "status_style")) { if (string_compare(opt, "status_style")) {
if (!arg) { if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
_e("status_style option requires an argument, skipping.");
return;
}
if (string_compare(arg, "old") || string_compare(arg, "classic"))
show_progress(PROGRESS_CLASSIC); show_progress(PROGRESS_CLASSIC);
else else
show_progress(PROGRESS_MODERN); show_progress(PROGRESS_MODERN);
@@ -126,7 +117,7 @@ static void parse_finit_opts(char *opt)
} }
if (string_compare(opt, "show_status")) { if (string_compare(opt, "show_status")) {
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT); show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
return; return;
} }
} }
@@ -355,7 +346,7 @@ char *lim2str(struct rlimit *rlim)
char tmp[25]; char tmp[25];
static char buf[42]; static char buf[42];
buf[0] = 0; memset(buf, 0, sizeof(buf));
if (RLIM_INFINITY == rlim->rlim_cur) if (RLIM_INFINITY == rlim->rlim_cur)
snprintf(tmp, sizeof(tmp), "unlimited, "); snprintf(tmp, sizeof(tmp), "unlimited, ");
else else
@@ -426,29 +417,6 @@ error:
logit(LOG_WARNING, "rlimit: parse error"); logit(LOG_WARNING, "rlimit: parse error");
} }
/* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */
static void conf_parse_cgroup(char *line)
{
char config[strlen(line) + 1];
char *ptr, *name;
name = strtok(line, " \t");
if (!name)
return;
if (strstr(name, "..") || strchr(name, '/'))
return; /* illegal */
config[0] = 0;
while ((ptr = strtok(NULL, " \t"))) {
if (config[0])
strlcat(config, ",", sizeof(config));
strlcat(config, ptr, sizeof(config));
}
cgroup_add(name, config, 0);
}
static void parse_static(char *line, int is_rcsd) static void parse_static(char *line, int is_rcsd)
{ {
char cmd[CMD_SIZE]; char cmd[CMD_SIZE];
@@ -578,21 +546,9 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return; return;
} }
/* Read contrl group limits */
if (MATCH_CMD(line, "cgroup ", x)) {
conf_parse_cgroup(x);
return;
}
/* Set current cgroup for the following services/run/tasks */
if (MATCH_CMD(line, "cgroup.", x)) {
strlcpy(cgroup_current, x, sizeof(cgroup_current));
return;
}
/* Regular or serial TTYs to run getty */ /* Regular or serial TTYs to run getty */
if (MATCH_CMD(line, "tty ", x)) { if (MATCH_CMD(line, "tty ", x)) {
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file); tty_register(strip_line(x), rlimit, file);
return; return;
} }
} }
@@ -618,11 +574,9 @@ static int parse_conf(char *file, int is_rcsd)
if (!fp) if (!fp)
return 1; return 1;
/* Prepare default limits and group for each service in /etc/finit.d/ */ /* Prepare default limits for each service in /etc/finit.d/ */
if (is_rcsd) { if (is_rcsd)
memcpy(rlimit, global_rlimit, sizeof(rlimit)); memcpy(rlimit, global_rlimit, sizeof(rlimit));
cgroup_current[0] = 0;
}
_d("*** Parsing %s", file); _d("*** Parsing %s", file);
while (!feof(fp)) { while (!feof(fp)) {
@@ -662,13 +616,16 @@ int conf_reload(void)
daylight, timezone, tzname[0], tzname[1]); daylight, timezone, tzname[0], tzname[1]);
/* Mark and sweep */ /* Mark and sweep */
cgroup_mark_all();
svc_mark_dynamic(); svc_mark_dynamic();
tty_mark();
/* /*
* Reset global rlimit to bootstrap values from conf_init(). * 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.
*/ */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit)); for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
if (rescue) { if (rescue) {
int rc; int rc;
@@ -677,7 +634,7 @@ int conf_reload(void)
/* If rescue.conf is missing, fall back to a root shell */ /* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF, 0); rc = parse_conf(RESCUE_CONF, 0);
if (rc) if (rc)
service_register(SVC_TYPE_TTY, line, global_rlimit, NULL); tty_register(line, global_rlimit, NULL);
print(rc, "Entering rescue mode"); print(rc, "Entering rescue mode");
goto done; goto done;
@@ -717,7 +674,7 @@ int conf_reload(void)
/* Check for dangling symlinks */ /* Check for dangling symlinks */
if (S_ISLNK(st.st_mode)) { if (S_ISLNK(st.st_mode)) {
rp = realpath(path, NULL); path = rp = realpath(path, NULL);
if (!rp) { if (!rp) {
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno)); logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
continue; continue;
@@ -727,7 +684,7 @@ int conf_reload(void)
/* Check that file ends with '.conf' */ /* Check that file ends with '.conf' */
len = strlen(path); len = strlen(path);
if (len < 6 || strcmp(&path[len - 5], ".conf")) if (len < 6 || strcmp(&path[len - 5], ".conf"))
_d("Skipping %s, not a Finit .conf file ... ", path); _d("Skipping %s, not a valid .conf ... ", path);
else else
parse_conf(path, 1); parse_conf(path, 1);
@@ -737,15 +694,7 @@ int conf_reload(void)
globfree(&gl); globfree(&gl);
/* Mark any reverse deps as chenaged. */
service_update_rdeps();
/* Set up top-level cgroups */
cgroup_config();
done: done:
/* Remove all unused top-level cgroups */
cgroup_cleanup();
/* Drop record of all .conf changes */ /* Drop record of all .conf changes */
drop_changes(); drop_changes();
@@ -838,29 +787,20 @@ int conf_any_change(void)
int conf_changed(char *file) int conf_changed(char *file)
{ {
int rc = 0;
char *rp;
if (!file) if (!file)
return 0; return 0;
rp = realpath(file, NULL); if (conf_find(file))
if (!rp) return 1;
return 0;
if (conf_find(rp)) return 0;
rc = 1;
free(rp);
return rc;
} }
static void conf_cb(uev_t *w, void *arg, int events) static void conf_cb(uev_t *w, void *arg, int events)
{ {
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev; struct inotify_event *ev;
ssize_t sz; ssize_t sz, off;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
@@ -869,16 +809,10 @@ static void conf_cb(uev_t *w, void *arg, int events)
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) { for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp; struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off]; ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask) if (!ev->mask)
continue; continue;
@@ -934,7 +868,7 @@ int conf_monitor(void)
} }
/* /*
* Prepare .conf parser and load /etc/finit.conf for global settings * Prepare .conf parser and load all .conf files
*/ */
int conf_init(uev_ctx_t *ctx) int conf_init(uev_ctx_t *ctx)
{ {
@@ -948,17 +882,8 @@ int conf_init(uev_ctx_t *ctx)
* finit.conf, for Finit and its services like getty+watchdogd, * finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there. * and *.conf in finit.d/, for each service(s) listed there.
*/ */
for (int i = 0; i < RLIMIT_NLIMITS; i++) { for (int i = 0; i < RLIMIT_NLIMITS; i++)
if (getrlimit(i, &initial_rlimit[i])) getrlimit(i, &global_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 */ /* prepare /etc watcher */
fd = iwatch_init(&iw_conf); fd = iwatch_init(&iw_conf);
-2
View File
@@ -24,14 +24,12 @@
#ifndef FINIT_CONF_H_ #ifndef FINIT_CONF_H_
#define FINIT_CONF_H_ #define FINIT_CONF_H_
#include "cgroup.h"
#include "svc.h" #include "svc.h"
extern int logfile_size_max; extern int logfile_size_max;
extern int logfile_count_max; extern int logfile_count_max;
extern struct rlimit global_rlimit[]; extern struct rlimit global_rlimit[];
extern char cgroup_current[];
int str2rlim(char *str); int str2rlim(char *str);
char *rlim2str(int rlim); char *rlim2str(int rlim);
+49 -35
View File
@@ -35,7 +35,6 @@
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "cgroup.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
#include "sig.h" #include "sig.h"
@@ -245,6 +244,10 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
char name[80]; char name[80];
int fd; int fd;
/* Detach from initial controlling TTY and become session leader */
vhangup();
setsid();
fd = open(tty, O_RDWR); fd = open(tty, O_RDWR);
if (fd < 0) { if (fd < 0) {
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno)); logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
@@ -256,11 +259,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
close(fd); close(fd);
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno)); logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
@@ -290,6 +288,7 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN; sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL); sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL);
@@ -316,7 +315,7 @@ static int activate_console(int noclear, int nowait)
return 1; return 1;
if (!noclear) if (!noclear)
dprint(STDERR_FILENO, "\e[r\e[H\e[J", 9); (void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
/* Disable ECHO, XON/OFF while waiting for <CR> */ /* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &orig)) { if (!tcgetattr(STDIN_FILENO, &orig)) {
@@ -340,16 +339,16 @@ static int activate_console(int noclear, int nowait)
continue; continue;
} }
dprint(STDERR_FILENO, clr, strlen(clr)); (void)write(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg)); (void)write(STDERR_FILENO, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r') while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
continue; continue;
if (fexist(SYNC_STOPPED)) if (fexist(SYNC_STOPPED))
continue; continue;
dprint(STDERR_FILENO, clr, strlen(clr)); (void)write(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, cup, strlen(cup)); (void)write(STDERR_FILENO, cup, strlen(cup));
ret = 1; ret = 1;
break; break;
} }
@@ -382,47 +381,62 @@ static int activate_console(int noclear, int nowait)
*/ */
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]) pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
{ {
speed_t speed; pid_t pid;
int rc = 1;
speed = stty_parse_speed(baud); pid = fork();
prepare_tty(tty, speed, "tty", rlimit); if (!pid) {
if (activate_console(noclear, nowait)) { speed_t speed;
int rc = 1;
speed = stty_parse_speed(baud);
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed); logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
rc = getty(tty, speed, term, NULL); prepare_tty(tty, speed, "tty", rlimit);
if (activate_console(noclear, nowait))
rc = getty(tty, speed, term, NULL);
_exit(rc);
} }
return rc; return pid;
} }
/*
* Start external getty as defined by user in .conf file
*/
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]) pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{ {
int rc = 1; pid_t pid;
/* Dunno speed, tell stty() to not mess with it */ pid = fork();
prepare_tty(tty, B0, "getty", rlimit); if (!pid) {
if (activate_console(noclear, nowait)) { int rc = 1;
logit(LOG_INFO, "Starting external getty on %s", tty);
rc = execv(cmd, args); /* 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);
} }
vhangup(); return pid;
return rc;
} }
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[]) pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
{ {
int rc = 1; pid_t pid;
prepare_tty(tty, B0, "finitsh", rlimit); pid = fork();
if (activate_console(noclear, nowait)) if (!pid) {
rc = sh(tty); int rc = 1;
return rc; prepare_tty(tty, B0, "finit-sh", rlimit);
if (activate_console(noclear, nowait))
rc = sh(tty);
_exit(rc);
}
return pid;
} }
int run_parts(char *dir, char *cmd) int run_parts(char *dir, char *cmd)
+52 -82
View File
@@ -59,7 +59,6 @@ int prevlevel = -1;
int debug = 0; /* debug mode from kernel cmdline */ int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */ int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */ int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootrapping (for TTYs) */
char *sdown = NULL; char *sdown = NULL;
char *network = NULL; char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
@@ -180,8 +179,7 @@ static void fs_remount_root(int fsckerr)
* XXX: Untested, in the initramfs age we should * XXX: Untested, in the initramfs age we should
* probably use switch_root instead. * probably use switch_root instead.
*/ */
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL)) rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
_pe("Failed %s / MS_MOVE");
} }
#endif /* SYSROOT */ #endif /* SYSROOT */
@@ -200,6 +198,7 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST); plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea"); run("swapon -ea");
umask(0022);
} }
/* /*
@@ -207,9 +206,6 @@ static void fs_mount(void)
* for early multi-console, and /sys for the cgroups support. Any * for early multi-console, and /sys for the cgroups support. Any
* occurrence of these file systems in /etc/fstab will replace these * occurrence of these file systems in /etc/fstab will replace these
* mounts later in fs_mount() * 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) static void fs_init(void)
{ {
@@ -222,16 +218,8 @@ static void fs_init(void)
}; };
size_t i; size_t i;
/* mask writable bit for g and o */ for (i = 0; i < NELEMS(fs); i++)
umask(022); mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
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);
}
} }
/* /*
@@ -288,8 +276,20 @@ static void emergency_shell(void)
* finit.conf, or *.conf in finit.d/, run to completion before we * finit.conf, or *.conf in finit.d/, run to completion before we
* finalize the bootstrap process by calling this function. * finalize the bootstrap process by calling this function.
*/ */
static void finalize(void *unused) static void finalize(void)
{ {
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel, start all services ...");
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */ /* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ..."); _d("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap(); svc_prune_bootstrap();
@@ -311,9 +311,9 @@ static void finalize(void *unused)
/* Disable progress output at normal runtime */ /* Disable progress output at normal runtime */
enable_progress(0); enable_progress(0);
/* System bootrapped, launch TTYs et al */ /* Delayed start of TTYs at bootstrap */
bootstrap = 0; _d("Launching all getty services ...");
service_step_all(SVC_TYPE_RESPAWN); tty_runlevel();
} }
/* /*
@@ -331,17 +331,12 @@ static void crank_worker(void *unused)
/* /*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be * Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
* allowed to complete their work in [S], or timeout, before we switch * allowed to complete their work in [S], or timeout, before we call
* to the configured runlevel and call finalize(), should not take more * finalize(), should not take more than 120 sec.
* than 120 sec.
*/ */
static void bootstrap_worker(void *work) static void final_worker(void *work)
{ {
static int cnt = 120; static int cnt = 120;
static struct wq final = {
.cb = finalize,
.delay = 10
};
_d("Step all services ..."); _d("Step all services ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -357,36 +352,12 @@ static void bootstrap_worker(void *work)
else else
_d("Timeout, resuming bootstrap."); _d("Timeout, resuming bootstrap.");
_d("Starting runlevel change finalize ..."); finalize();
schedule_work(&final);
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(cfglevel);
}
static int version(int rc)
{
puts(PACKAGE_STRING);
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc;
} }
static int usage(int rc) static int usage(int rc)
{ {
printf("Usage: %s [OPTIONS] [q | Q | 0-9]\n\n" printf("Usage: telinit [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n" "Options:\n"
// " -a Ignored, compat SysV init\n" // " -a Ignored, compat SysV init\n"
// " -b Ignored, compat SysV init\n" // " -b Ignored, compat SysV init\n"
@@ -404,7 +375,12 @@ static int usage(int rc)
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n" " q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
" if modified, same as initctl reload or SIGHUP to PID 1\n" " if modified, same as initctl reload or SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n" " 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm); "\n");
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc; return rc;
} }
@@ -417,7 +393,6 @@ static int telinit(int argc, char *argv[])
{ {
int c; int c;
progname(argv[0]);
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) { while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
switch(c) { switch(c) {
case 'a': case 'b': case 'e': case 's': case 'z': case 'a': case 'b': case 'e': case 's': case 'z':
@@ -427,7 +402,7 @@ static int telinit(int argc, char *argv[])
break; break;
case 'v': case 'V': case 'v': case 'V':
return version(0); return puts("v" VERSION) == EOF;
case 'h': case 'h':
case '?': case '?':
@@ -439,13 +414,13 @@ static int telinit(int argc, char *argv[])
int req = (int)argv[optind][0]; int req = (int)argv[optind][0];
if (isdigit(req)) if (isdigit(req))
return systemf("initctl -b runlevel %c", req); return systemf("initctl runlevel %c", req);
if (req == 'q' || req == 'Q') if (req == 'q' || req == 'Q')
return systemf("initctl -b reload"); return systemf("initctl reload");
if (req == 's' || req == 'S') if (req == 's' || req == 'S')
return systemf("initctl -b runlevel %c", req); return systemf("initctl runlevel %c", req);
} }
/* XXX: add non-pid1 process monitor here /* XXX: add non-pid1 process monitor here
@@ -460,12 +435,11 @@ static int telinit(int argc, char *argv[])
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct wq crank = { struct wq crank = {
.cb = crank_worker, .cb = crank_worker
.delay = 10
}; };
struct wq bootstrap = { struct wq final = {
.cb = bootstrap_worker, .cb = final_worker,
.delay = 100 .delay = 1000
}; };
uev_ctx_t loop; uev_ctx_t loop;
@@ -496,13 +470,17 @@ int main(int argc, char *argv[])
ctx = &loop; ctx = &loop;
/* /*
* Set PATH, SHELL, and PWD early to something sane * Set PATH, SHELL, PWD, and umask early to something sane
*/ */
setenv("PATH", _PATH_STDPATH, 1); setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1); setenv("SHELL", _PATH_BSHELL, 1);
if (chdir("/")) chdir("/");
_pe("Failed cd /"); umask(0);
/* Set up canvas */
if (!rescue && !debug)
screen_init();
/* Allow progress, if enabled */ /* Allow progress, if enabled */
enable_progress(1); enable_progress(1);
@@ -512,10 +490,8 @@ int main(int argc, char *argv[])
*/ */
emergency_shell(); emergency_shell();
/* /* Load plugins early, the first hook is in banner(), so we
* Load plugins early, the first hook is in banner(), so we * need plugins loaded before calling it. */
* need plugins loaded before calling it.
*/
plugin_init(&loop); plugin_init(&loop);
/* /*
@@ -531,7 +507,8 @@ int main(int argc, char *argv[])
/* /*
* Initialize default control groups, if available * Initialize default control groups, if available
*/ */
cgroup_init(&loop); cgroup_init();
/* Check and mount filesystems. */ /* Check and mount filesystems. */
fs_mount(); fs_mount();
@@ -549,14 +526,6 @@ int main(int argc, char *argv[])
*/ */
conf_init(&loop); 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 */ /* Base FS up, enable standard SysV init signals */
sig_setup(&loop); sig_setup(&loop);
@@ -571,12 +540,13 @@ int main(int argc, char *argv[])
_d("Starting initctl API responder ..."); _d("Starting initctl API responder ...");
api_init(&loop); api_init(&loop);
umask(022);
_d("Starting the big state machine ..."); _d("Starting the big state machine ...");
schedule_work(&crank); schedule_work(&crank);
_d("Starting bootstrap finalize timer ..."); _d("Starting bootstrap finalize timer ...");
schedule_work(&bootstrap); schedule_work(&final);
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
+3 -14
View File
@@ -30,24 +30,14 @@
#include <paths.h> #include <paths.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sysexits.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <lite/lite.h> #include <lite/lite.h>
#include <uev/uev.h> #include <uev/uev.h>
/* just in case */
#ifndef _PATH_STDPATH
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
#endif
#ifndef _PATH_VARRUN #ifndef _PATH_VARRUN
#define _PATH_VARRUN "/var/run/" #define _PATH_VARRUN "/var/run/"
#endif
#ifndef FINIT_CGPATH
#define FINIT_CGPATH "/sys/fs/cgroup"
#endif #endif
#define LOGIT_PATH FINIT_LIBPATH_ "/logit" #define LOGIT_PATH FINIT_LIBPATH_ "/logit"
@@ -72,10 +62,10 @@
/* Finit extensions over std SysV */ /* Finit extensions over std SysV */
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */ #define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */ #define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
#define INIT_CMD_START_SVC 10 /* START service */ #define INIT_CMD_START_SVC 10
#define INIT_CMD_STOP_SVC 11 #define INIT_CMD_STOP_SVC 11
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */ #define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */ #define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */ #define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */ #define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_GET_RUNLEVEL 16
@@ -106,7 +96,6 @@ extern int prevlevel;
extern int debug; extern int debug;
extern int rescue; extern int rescue;
extern int single; extern int single;
extern int bootstrap;
extern char *rcsd; extern char *rcsd;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
+16 -15
View File
@@ -41,6 +41,9 @@
#define _PATH_LOGIN "/bin/login" #define _PATH_LOGIN "/bin/login"
#endif #endif
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
/* /*
* Read one character from stdin. * Read one character from stdin.
*/ */
@@ -51,7 +54,7 @@ static int readch(char *tty)
st = read(STDIN_FILENO, &ch1, 1); st = read(STDIN_FILENO, &ch1, 1);
if (st == 0) { if (st == 0) {
dprint(STDOUT_FILENO, "\n", 0); print("\n");
_exit(0); _exit(0);
} }
@@ -71,32 +74,31 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
s0 = line; s0 = line;
for (s = line; *s != 0; s++) { for (s = line; *s != 0; s++) {
if (*s == '\\') { if (*s == '\\') {
if ((s - s0) > 0) (void)write(1, s0, s - s0);
dprint(STDOUT_FILENO, s0, s - s0);
s0 = s + 2; s0 = s + 2;
switch (*++s) { switch (*++s) {
case 'l': case 'l':
dprint(STDOUT_FILENO, tty, 0); print(tty);
break; break;
case 'm': case 'm':
dprint(STDOUT_FILENO, uts->machine, 0); print(uts->machine);
break; break;
case 'n': case 'n':
dprint(STDOUT_FILENO, uts->nodename, 0); print(uts->nodename);
break; break;
#ifdef _GNU_SOURCE #ifdef _GNU_SOURCE
case 'o': case 'o':
dprint(STDOUT_FILENO, uts->domainname, 0); print(uts->domainname);
break; break;
#endif #endif
case 'r': case 'r':
dprint(STDOUT_FILENO, uts->release, 0); print(uts->release);
break; break;
case 's': case 's':
dprint(STDOUT_FILENO, uts->sysname, 0); print(uts->sysname);
break; break;
case 'v': case 'v':
dprint(STDOUT_FILENO, uts->version, 0); print(uts->version);
break; break;
case 0: case 0:
goto leave; goto leave;
@@ -107,8 +109,7 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
} }
leave: leave:
if ((s - s0) > 0) (void)write(1, s0, s - s0);
dprint(STDOUT_FILENO, s0, s - s0);
} }
/* /*
@@ -158,7 +159,7 @@ static void do_getty(char *tty, char *name, size_t len)
while ((ch = readch(tty)) != '\n') { while ((ch = readch(tty)) != '\n') {
if (ch == CTRL('U')) { if (ch == CTRL('U')) {
while (np > name) { while (np > name) {
dprint(STDOUT_FILENO, "\b \b", 3); (void)write(1, "\b \b", 3);
np--; np--;
} }
continue; continue;
@@ -185,7 +186,7 @@ static int do_login(char *name)
{ {
struct stat st; struct stat st;
cgroup_user(name, 0); cgroup_user(name);
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/* /*
@@ -214,7 +215,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
/* Set up TTY, re-enabling ISIG et al. */ /* Set up TTY, re-enabling ISIG et al. */
stty(STDIN_FILENO, speed); stty(STDIN_FILENO, speed);
dprint(STDERR_FILENO, cln, strlen(cln)); (void)write(STDERR_FILENO, cln, strlen(cln));
/* The getty process is responsible for the UTMP login record */ /* The getty process is responsible for the UTMP login record */
utmp_set_login(tty, NULL); utmp_set_login(tty, NULL);
+16 -88
View File
@@ -103,9 +103,6 @@ static void add_console(char *cons)
if (ptr) if (ptr)
*ptr = 0; *ptr = 0;
if (!strncmp(cons, "/dev/", 5))
cons += 5;
snprintf(path, sizeof(path), "/dev/%s", cons); snprintf(path, sizeof(path), "/dev/%s", cons);
/* check for and skip duplicates */ /* check for and skip duplicates */
@@ -124,7 +121,7 @@ static void add_console(char *cons)
num_cons++; num_cons++;
} }
static void find_active_consoles(void) void console_init(void)
{ {
char *cons, *ptr, *tok; char *cons, *ptr, *tok;
char buf[512]; char buf[512];
@@ -134,7 +131,7 @@ static void find_active_consoles(void)
if (!fp) { if (!fp) {
fallback: fallback:
_d("Using fallback /dev/console"); _d("Using fallback /dev/console");
add_console("console"); add_console("/dev/console");
return; return;
} }
@@ -151,12 +148,6 @@ static void find_active_consoles(void)
} }
} }
void console_init(void)
{
find_active_consoles();
ttinit();
}
ssize_t cprintf(const char *fmt, ...) ssize_t cprintf(const char *fmt, ...)
{ {
const size_t len = strlen(fmt) * 2; const size_t len = strlen(fmt) * 2;
@@ -170,7 +161,7 @@ ssize_t cprintf(const char *fmt, ...)
va_end(ap); va_end(ap);
for (i = 0; i < num_cons; i++) for (i = 0; i < num_cons; i++)
dprint(fds[i], buf, size); (void)write(fds[i], buf, size);
return size; return size;
} }
@@ -255,20 +246,20 @@ static char *pad(char *buf, size_t len, char *ch, size_t width)
void print_banner(const char *heading) void print_banner(const char *heading)
{ {
char buf[4 * ttcols]; char buf[4 * SCREEN_WIDTH];
if (progress_style == PROGRESS_SILENT) if (progress_style == PROGRESS_SILENT)
return; return;
buf[0] = 0; memset(buf, 0, sizeof(buf));
strlcat(buf, "\r\e[2K", sizeof(buf)); strlcat(buf, "\r\e[2K", sizeof(buf));
if (progress_style == PROGRESS_CLASSIC) { if (progress_style == PROGRESS_CLASSIC) {
strlcat(buf, "\e[1m", sizeof(buf)); strlcat(buf, "\e[1m", sizeof(buf));
strlcat(buf, heading, sizeof(buf)); strlcat(buf, heading, sizeof(buf));
pad(buf, sizeof(buf), "=", ttcols + 10); pad(buf, sizeof(buf), "=", SCREEN_WIDTH + 10);
} else { } else {
size_t wmax = 80 <= ttcols ? 80 : ttcols; size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
/* • • • Foo System ═══════════════════════════════════════════════ */ /* • • • Foo System ═══════════════════════════════════════════════ */
/* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */ /* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */
@@ -331,7 +322,7 @@ static char *status(int rc)
void printv(const char *fmt, va_list ap) void printv(const char *fmt, va_list ap)
{ {
char buf[ttcols]; char buf[SCREEN_WIDTH];
size_t len; size_t len;
if (!fmt || progress_style == PROGRESS_SILENT) if (!fmt || progress_style == PROGRESS_SILENT)
@@ -339,7 +330,7 @@ void printv(const char *fmt, va_list ap)
delline(); delline();
buf[0] = 0; memset(buf, 0, sizeof(buf));
len = print_timestamp(buf, sizeof(buf)); len = print_timestamp(buf, sizeof(buf));
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap); vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
@@ -413,64 +404,6 @@ int getgroup(char *group)
#endif #endif
} }
/* get current user */
int getcuser(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct passwd *pw = getpwuid(getuid());
if (pw)
ptr = pw->pw_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
/* get current group */
int getcgroup(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct group *gr = getgrgid(getgid());
if (gr)
ptr = gr->gr_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
int uid, gid;
int rc = 0;
omask = umask(0);
uid = getuser(user, NULL);
if (uid >= 0) {
gid = getgroup(group);
if (gid < 0)
gid = 0;
rc = makedir(dir, mode);
if (chown(dir, uid, gid))
_pe("Failed chown(%s, %d, %d)", dir, uid, gid);
}
umask(omask);
return rc;
}
void set_hostname(char **hostname) void set_hostname(char **hostname)
{ {
FILE *fp; FILE *fp;
@@ -493,16 +426,14 @@ void set_hostname(char **hostname)
return; return;
} }
if (fgets(*hostname, st.st_size, fp)) fgets(*hostname, st.st_size, fp);
chomp(*hostname); chomp(*hostname);
fclose(fp); fclose(fp);
} }
done: done:
if (*hostname) { if (*hostname)
if (sethostname(*hostname, strlen(*hostname))) sethostname(*hostname, strlen(*hostname));
_pe("Failed sethostnaem(%s)", *hostname);
}
} }
/* /*
@@ -540,7 +471,6 @@ void networking(int updown)
run("ifdown -a"); run("ifdown -a");
_exit(0); _exit(0);
} }
cgroup_service("network", pid, NULL);
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping"); print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
} }
@@ -627,12 +557,10 @@ int ismnt(char *file, char *dir, char *mode)
while ((mnt = getmntent(fp))) { while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, dir)) { if (!strcmp(mnt->mnt_dir, dir)) {
if (mode) { if (mode && hasopt(mnt->mnt_opts, mode))
if (hasopt(mnt->mnt_opts, mode)) found = 1;
found = 1; else
} else
found = 1; found = 1;
break; break;
} }
} }
+1 -26
View File
@@ -26,7 +26,6 @@
#define FINIT_HELPERS_H_ #define FINIT_HELPERS_H_
#include <mntent.h> #include <mntent.h>
#include <sched.h> /* sched_yield() */
#include <stdarg.h> #include <stdarg.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/types.h> #include <sys/types.h>
@@ -71,7 +70,7 @@ void show_progress (pstyle_t style);
int getty (char *tty, speed_t speed, char *term, char *user); int getty (char *tty, speed_t speed, char *term, char *user);
int sh (char *tty); int sh (char *tty);
void stty (int fd, speed_t speed); int stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud); speed_t stty_parse_speed(char *baud);
void print_banner (const char *heading); void print_banner (const char *heading);
@@ -82,12 +81,6 @@ int print_result (int fail);
int getuser (char *username, char **home); int getuser (char *username, char **home);
int getgroup (char *group); int getgroup (char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname); void set_hostname (char **hostname);
void networking (int updown); void networking (int updown);
@@ -110,24 +103,6 @@ static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
return 0; return 0;
} }
static inline int dprint(int fd, const char *s, size_t len)
{
size_t loop = 3;
int rc = -1;
if (!len)
len = strlen(s);
while (loop--) {
rc = write(fd, s, len);
if (rc == -1 && errno == EINTR)
continue;
break;
}
return rc;
}
int ismnt (char *file, char *dir, char *mode); int ismnt (char *file, char *dir, char *mode);
int fismnt (char *dir); int fismnt (char *dir);
+257 -458
View File
File diff suppressed because it is too large. Load diff
-46
View File
@@ -1,46 +0,0 @@
/* 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:
*/
+3 -4
View File
@@ -81,10 +81,10 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
return -1; return -1;
if (!fexist(file)) { if (!fexist(file)) {
_d("skipping %s: no such file or directory", file); _d("No such file or directory, skipping %s", file);
return 0; return 0;
} }
_d("adding new watcher for path %s", file); _d("Adding new watcher for path %s", file);
path = strdup(file); path = strdup(file);
if (!path) { if (!path) {
@@ -94,8 +94,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask); wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
if (wd < 0) { if (wd < 0) {
if (errno != EEXIST) _pe("Failed adding watcher for %s", path);
_pe("Failed adding watcher for %s", path);
free(path); free(path);
return -1; return -1;
} }
+9 -6
View File
@@ -22,6 +22,7 @@
*/ */
#include "config.h" #include "config.h"
#include <sched.h> /* sched_yield() */
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <lite/lite.h> #include <lite/lite.h>
@@ -49,13 +50,15 @@ void log_exit(void)
* Unless in debug mode at shutdown, Reinitialize screen, * Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime * terminal may have been resized at runtime
*/ */
if (!debug) if (debug)
ttinit(); screen_exit();
else
screen_init();
enable_progress(1); enable_progress(1);
} }
static void log_open(void) void log_open(void)
{ {
int opts = LOG_PID; int opts = LOG_PID;
@@ -76,9 +79,10 @@ void log_debug(void)
if (debug) { if (debug) {
loglevel = LOG_DEBUG; loglevel = LOG_DEBUG;
screen_exit();
} else { } else {
loglevel = LOG_NOTICE; loglevel = LOG_NOTICE;
ttinit(); screen_init();
} }
log_open(); log_open();
@@ -99,12 +103,11 @@ void logit(int prio, const char *fmt, ...)
if (up || fexist("/dev/log")) { if (up || fexist("/dev/log")) {
if (!up) if (!up)
log_open(); log_open();
vsyslog(prio, fmt, ap); vsyslog(prio, fmt, ap);
goto done; goto done;
} }
if (prio > loglevel) if (!debug && prio > loglevel)
goto done; goto done;
fp = fopen("/dev/kmsg", "w"); fp = fopen("/dev/kmsg", "w");
+65 -2
View File
@@ -30,12 +30,75 @@
#include <syslog.h> #include <syslog.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <lite/lite.h>
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION; 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) static int checksz(FILE *fp, off_t sz)
{ {
struct stat st; struct stat st;
-102
View File
@@ -1,102 +0,0 @@
/*
* 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;
}
+12 -10
View File
@@ -95,15 +95,18 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
char *pid_file(svc_t *svc) char *pid_file(svc_t *svc)
{ {
static char path[256];
char fn[MAX_ARG_LEN];
if (svc->pidfile[0]) { if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!') if (svc->pidfile[0] == '!')
return &svc->pidfile[1]; return &svc->pidfile[1];
return svc->pidfile; return svc->pidfile;
} }
errno = ENOENT; snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
return NULL; return pid_runpath(fn, path, sizeof(path));
} }
pid_t pid_file_read(const char *fn) pid_t pid_file_read(const char *fn)
@@ -140,13 +143,12 @@ int pid_file_create(svc_t *svc)
return fclose(fp); return fclose(fp);
} }
int pid_file_set(svc_t *svc, char *file, int not) static int pid_realpath(svc_t *svc, char *file)
{ {
if (!file) { int not = 0;
if (!file)
file = pid_file(svc); file = pid_file(svc);
if (!file)
return -1;
}
if (file[0] == '!') { if (file[0] == '!') {
not = 1; not = 1;
@@ -200,7 +202,7 @@ int pid_file_parse(svc_t *svc, char *arg)
arg += 4; arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/') if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_file_set(svc, arg, 0); return pid_realpath(svc, arg);
if (arg[0] == '!') { if (arg[0] == '!') {
arg++; arg++;
@@ -211,12 +213,12 @@ int pid_file_parse(svc_t *svc, char *arg)
if (len > 4 && strcmp(&path[len - 4], ".pid")) if (len > 4 && strcmp(&path[len - 4], ".pid"))
strlcat(path, ".pid", sizeof(path)); strlcat(path, ".pid", sizeof(path));
return pid_file_set(svc, path, 0); return pid_realpath(svc, path);
} }
/* 'pid' arg, no argument following */ /* 'pid' arg, no argument following */
if (!strcmp(arg, "pid")) if (!strcmp(arg, "pid"))
return pid_file_set(svc, NULL, 0); return pid_realpath(svc, NULL);
return 1; return 1;
} }
-1
View File
@@ -32,7 +32,6 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len); char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc); char *pid_file (svc_t *svc);
int pid_file_set (svc_t *svc, char *file, int not);
pid_t pid_file_read (const char *fn); pid_t pid_file_read (const char *fn);
int pid_file_create (svc_t *svc); int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg); int pid_file_parse (svc_t *svc, char *arg);
+1 -1
View File
@@ -196,7 +196,7 @@ void plugin_run_hook(hook_point_t no, void *arg)
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) { if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL"); _d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
p->hook[no].cb(arg ? arg : p->hook[no].arg); p->hook[no].cb(arg ? arg : p->hook[no].arg);
} }
} }
+3 -21
View File
@@ -1,12 +1,4 @@
# sample.conf: template cgroup/run/task/service stanza for finit # sample.conf: template run/task/service stanza for finit
#
# The top-level cgroups can be defined anywhere, but share the same
# namespace. It is up to the user to ensure groups are defined only
# once, otherwise the last read .conf wins. See below for assigning
# run/task/services to cgroups. The below example creates the cgroup
# NAME with the following cpu and memory settings:
#
# cgroup NAME cpu.weight:1000 mem.max:65
# #
# The one-shot commands 'run' and 'task' are called only once per # The one-shot commands 'run' and 'task' are called only once per
# runlevel. Finit does not restart them when they exit. # runlevel. Finit does not restart them when they exit.
@@ -33,16 +25,6 @@
# | | `--------------------------------------------------------------- Optional conditions # | | `--------------------------------------------------------------- Optional conditions
# | `---------------------------------------------------------------------- Optional Runlevels # | `---------------------------------------------------------------------- Optional Runlevels
# `------------------------------------------------------------------------------ Monitored application # `------------------------------------------------------------------------------ Monitored application
#
# Each stanza can also hold a 'cgroup' argument, or be prefixed with
# cgroup.NAME to place all following stanzas in the same group. In
# the following example, foo and bar share the cgroup foo but baz
# runs in its own cgroup baz:
#
# cgroup.foo
# service cgroup:cpu.weight:250,mem.max:655350 foo args -- foo desc
# service cgroup:cpu.weight:150,mem.max:655350 bar args -- bar desc
# service cgroup.baz:cpu.weight:300 baz args -- baz desc
# Debian GNU/Linux: start SSH daemon as soon as basic networking comes up # Debian GNU/Linux: start SSH daemon as soon as basic networking comes up
#service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon #service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
@@ -51,11 +33,11 @@
#task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap #task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
# Alpine Linux: start SSH daemon with $DROPBEAR_OPTS from /etc/conf.d # Alpine Linux: start SSH daemon with $DROPBEAR_OPTS from /etc/conf.d
#service [2345] cgroup.user env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon #service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
# Depends on syslogd having started. Redirect any output to log. # Depends on syslogd having started. Redirect any output to log.
#service [2345] <pid/syslogd> cgroup:mem.max:32000,cpu.max:1000 log acpid -f -- ACPI daemon #service [2345] <pid/syslogd> log acpid -f -- ACPI daemon
# Start rsyslogd as soon as possible, should always run # Start rsyslogd as soon as possible, should always run
# Provides pid/syslogd condition # Provides pid/syslogd condition
+20 -87
View File
@@ -30,7 +30,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "initctl.h" #include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
static int calc_width(char *arr[], size_t len) static int calc_width(char *arr[], size_t len)
@@ -50,66 +53,27 @@ static int calc_width(char *arr[], size_t len)
static void do_list(const char *path) static void do_list(const char *path)
{ {
static int once = 0; char buf[screen_cols];
int width, num, prev; int width, num, prev;
glob_t gl; glob_t gl;
size_t i; size_t i;
if (chdir(path)) { 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)) if (glob("*.conf", 0, NULL, &gl))
return; return;
if (gl.gl_pathc <= 0) if (gl.gl_pathc <= 0)
goto done; goto done;
if (plain) { snprintf(buf, sizeof(buf), "%s ", path);
if (heading) printheader(NULL, buf, 0);
print_header("%s%s ", once ? "\n" : "", path);
once++;
for (i = 0; i < gl.gl_pathc; i++) {
if (!heading) {
char buf[512];
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
puts(buf);
} else
puts(gl.gl_pathv[i]);
}
goto done;
}
if (heading)
print_header("%s ", path);
width = calc_width(gl.gl_pathv, gl.gl_pathc); width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0) if (width <= 0)
goto done; goto done;
num = (ttcols - 2) / width; num = (screen_cols - 2) / width;
if ((num - 1) * 2 + num * width > ttcols) if ((num - 1) * 2 + num * width > screen_cols)
num--; num--;
prev = 0; prev = 0;
@@ -154,19 +118,15 @@ int serv_list(char *arg)
if (fisdir(FINIT_RCSD)) if (fisdir(FINIT_RCSD))
do_list(FINIT_RCSD); do_list(FINIT_RCSD);
if (fexist(FINIT_CONF))
do_list(FINIT_CONF);
return 0; return 0;
} }
/* /*
* Return path to configuration file for 'name'. This may be any of the * Return path to configuration file for 'name', relative to FINIT_RCSD.
* following, provided sysconfdir is /etc: * This may be any of the following, provided sysconfdir is /etc:
* *
* - /etc/finit.d/available/$name.conf
* - /etc/finit.d/$name.conf * - /etc/finit.d/$name.conf
* - /etc/finit.conf * - /etc/finit.d/available/$name.conf
* *
* The system *may* have a /etc/finit.d/available/ directory, or it may * The system *may* have a /etc/finit.d/available/ directory, or it may
* just use a plain /etc/finit.d/ -- we do not set policy. * just use a plain /etc/finit.d/ -- we do not set policy.
@@ -178,11 +138,6 @@ static char *conf(char *path, size_t len, char *name, int creat)
{ {
char corr[40]; 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")) { if (!strstr(name, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", name); snprintf(corr, sizeof(corr), "%s.conf", name);
name = corr; name = corr;
@@ -200,10 +155,6 @@ static char *conf(char *path, size_t len, char *name, int creat)
} else } else
strlcat(path, name, len); strlcat(path, name, len);
/* fall back to static service unless edit/create */
if (!creat && !fexist(path))
paste(path, len, FINIT_RCSD, name);
return path; return path;
} }
@@ -257,8 +208,7 @@ int do_disable(char *arg, int check)
if (chdir(FINIT_RCSD)) if (chdir(FINIT_RCSD))
err(1, "failed cd %s", FINIT_RCSD); err(1, "failed cd %s", FINIT_RCSD);
if (chdir("enabled")) /* System *may* have enabled/ dir. */ chdir("enabled"); /* System *may* have enabled/ dir. */
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
if (check && stat(arg, &st)) if (check && stat(arg, &st))
errx(1, "%s not (an) enabled (service).", arg); errx(1, "%s not (an) enabled (service).", arg);
@@ -300,20 +250,6 @@ int serv_touch(char *arg)
return 0; 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 * Try to open an editor for the given file, if creat is given we
* create a new file based on /lib/finit/sample.conf * create a new file based on /lib/finit/sample.conf
@@ -343,7 +279,7 @@ static int do_edit(char *arg, int creat)
if (!fexist(fn)) { if (!fexist(fn)) {
if (!creat) { if (!creat) {
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg); warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
return serv_list(NULL); return serv_list("available");
} }
#ifdef SAMPLE_CONF #ifdef SAMPLE_CONF
@@ -365,8 +301,8 @@ static int do_edit(char *arg, int creat)
int serv_edit(char *arg) int serv_edit(char *arg)
{ {
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF)) warnx("missing argument to edit, may be one of:");
return 0; return serv_list("available");
} }
return do_edit(arg, icreate); return do_edit(arg, icreate);
@@ -410,10 +346,8 @@ int serv_delete(char *arg)
char buf[256]; char buf[256];
char *fn; char *fn;
if (!arg || !arg[0]) { if (!arg || !arg[0])
warnx("missing argument to delete, may be one of:"); errx(1, "missing argument to delete");
return serv_list("available");
}
fn = conf(buf, sizeof(buf), arg, 0); fn = conf(buf, sizeof(buf), arg, 0);
if (!fn) if (!fn)
@@ -424,8 +358,7 @@ int serv_delete(char *arg)
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) { if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
do_disable(arg, 0); do_disable(arg, 0);
if (remove(fn)) remove(fn);
err(1, "Failed removing %s", fn);
} }
return 0; return 0;
-1
View File
@@ -28,7 +28,6 @@ int serv_list (char *arg);
int serv_enable (char *arg); int serv_enable (char *arg);
int serv_disable (char *arg); int serv_disable (char *arg);
int serv_touch (char *arg); int serv_touch (char *arg);
int serv_show (char *arg);
int serv_edit (char *arg); int serv_edit (char *arg);
int serv_creat (char *arg); int serv_creat (char *arg);
int serv_delete (char *arg); int serv_delete (char *arg);
+136 -471
View File
@@ -25,6 +25,7 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */ #include <ctype.h> /* isblank() */
#include <sched.h> /* sched_yield() */
#include <string.h> #include <string.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/resource.h> #include <sys/resource.h>
@@ -48,6 +49,8 @@
#include "utmp-api.h" #include "utmp-api.h"
#include "schedule.h" #include "schedule.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static struct wq work = { static struct wq work = {
.cb = service_worker, .cb = service_worker,
}; };
@@ -195,8 +198,6 @@ static int lredirect(svc_t *svc)
char *tag = basename(svc->cmd); char *tag = basename(svc->cmd);
char *prio = "daemon.info"; char *prio = "daemon.info";
sched_yield();
fds = open(ptsname(fd), O_RDONLY); fds = open(ptsname(fd), O_RDONLY);
close(fd); close(fd);
if (fds == -1) if (fds == -1)
@@ -261,6 +262,37 @@ static int redirect(svc_t *svc)
return 0; return 0;
} }
/*
* non-zero env, no checking if file exists or not
*/
static char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static int check_env(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env)
return 1;
return fexist(env);
}
/* /*
* Source environment file, if it exists * Source environment file, if it exists
* Note: must be called from privsepped child * Note: must be called from privsepped child
@@ -277,7 +309,7 @@ static void source_env(svc_t *svc)
if (!fn) if (!fn)
return; return;
/* Warning in service_start() after svc_checkenv() */ /* Warning in service_start() after check_env() */
fp = fopen(fn, "r"); fp = fopen(fn, "r");
if (!fp) if (!fp)
return; return;
@@ -352,84 +384,6 @@ static int is_norespawn(void)
fexist("/tmp/norespawn"); fexist("/tmp/norespawn");
} }
/* used for process group name, derived from originating filename,
* so to group multiple services, place them in the same .conf
*/
static char *group_name(svc_t *svc, char *buf, size_t len)
{
char *ptr;
if (!svc->file[0])
return svc_ident(svc, buf, len);
ptr = strrchr(svc->file, '/');
if (ptr)
ptr++;
else
ptr = svc->file;
strlcpy(buf, ptr, len);
ptr = strstr(buf, ".conf");
if (ptr)
*ptr = 0;
return buf;
}
static pid_t service_fork(svc_t *svc)
{
pid_t pid;
pid = fork();
if (pid == 0) {
char *home = NULL;
#ifdef ENABLE_STATIC
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
int gid = 0;
#else
int uid = getuser(svc->username, &home);
int gid = getgroup(svc->group);
#endif
sched_yield();
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &svc->rlimit[i]) == -1)
logit(LOG_WARNING,
"%s: rlimit: Failed setting %s",
svc->cmd, rlim2str(i));
}
/* Set desired user+group */
if (gid >= 0) {
if (setgid(gid))
_pe("%s: failed setgid(%d)", svc->cmd, gid);
}
if (uid >= 0) {
if (setuid(uid))
_pe("%s: failed setuid(%d)", svc->cmd, uid);
/* Set default path for regular users */
if (uid > 0)
setenv("PATH", _PATH_DEFPATH, 1);
if (home) {
setenv("HOME", home, 1);
if (chdir(home)) {
if (chdir("/"))
_pe("%s: failed chdir(%s) and chdir(/)", svc->cmd, home);
}
}
}
/* Source any environment from env:/path/to/file */
source_env(svc);
}
return pid;
}
/** /**
* service_start - Start service * service_start - Start service
* @svc: Service to start * @svc: Service to start
@@ -441,7 +395,6 @@ static int service_start(svc_t *svc)
{ {
int result = 0, do_progress = 1; int result = 0, do_progress = 1;
sigset_t nmask, omask; sigset_t nmask, omask;
char grnam[80];
pid_t pid; pid_t pid;
size_t i; size_t i;
@@ -454,28 +407,17 @@ static int service_start(svc_t *svc)
/* Don't try and start service if it doesn't exist. */ /* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd)) { if (!whichp(svc->cmd)) {
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd); print(1, "%s: does not exist", svc->cmd);
svc_missing(svc); svc_missing(svc);
return 1; return 1;
} }
/* Unlike systemd we do not allow starting service if env is missing, unless - */ /* Unlike systemd we do not allow starting service if env is missing, unless - */
if (!svc_checkenv(svc)) { if (!check_env(svc)) {
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env); print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
svc_missing(svc);
return 1; 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)) if (svc_is_sysv(svc))
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd); logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
@@ -497,10 +439,47 @@ static int service_start(svc_t *svc)
sigaddset(&nmask, SIGCHLD); sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, &omask); sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = service_fork(svc); pid = fork();
cgroup_service(svc->name, svc->id, pid);
if (pid == 0) { if (pid == 0) {
char *args[MAX_NUM_SVC_ARGS + 1];
int status; 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);
if (!svc_is_sysv(svc)) { if (!svc_is_sysv(svc)) {
wordexp_t we = { 0 }; wordexp_t we = { 0 };
@@ -564,8 +543,7 @@ static int service_start(svc_t *svc)
} }
args[i] = NULL; args[i] = NULL;
if (!svc_is_tty(svc)) redirect(svc);
redirect(svc);
sig_unblock(); sig_unblock();
/* /*
@@ -583,8 +561,6 @@ static int service_start(svc_t *svc)
if (svc_is_runtask(svc)) if (svc_is_runtask(svc))
status = exec_runtask(args[0], &args[1]); status = exec_runtask(args[0], &args[1]);
else if (svc_is_tty(svc))
status = tty_exec(svc);
else else
status = execvp(args[0], &args[1]); status = execvp(args[0], &args[1]);
@@ -602,11 +578,6 @@ static int service_start(svc_t *svc)
_d("Starting %s %s", svc->cmd, buf); _d("Starting %s %s", svc->cmd, buf);
} }
if (svc_is_tty(svc))
cgroup_user("getty", pid);
else
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid, &svc->cgroup);
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid); logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
svc->pid = pid; svc->pid = pid;
@@ -675,19 +646,12 @@ static void service_cleanup(svc_t *svc)
{ {
char *fn; char *fn;
/* PID collected, cancel any pending SIGKILL */
service_timeout_cancel(svc);
fn = pid_file(svc); fn = pid_file(svc);
if (fn && remove(fn) && errno != ENOENT) if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", logit(LOG_CRIT, "Failed removing service %s pidfile %s",
basename(svc->cmd), fn); basename(svc->cmd), fn);
/* No longer running, update books. */ /* No longer running, update books. */
if (svc_is_tty(svc) && svc->pid > 1)
utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */
svc->oldpid = svc->pid;
svc->start_time = svc->pid = 0; svc->start_time = svc->pid = 0;
} }
@@ -731,7 +695,7 @@ static int service_stop(svc_t *svc)
if (svc->pid > 1) { if (svc->pid > 1) {
/* Kill all children in the same proess group, e.g. logit */ /* Kill all children in the same proess group, e.g. logit */
rc = kill(-svc->pid, svc->sighalt); rc = kill(-svc->pid, svc->sighalt);
_d("kill(-%d, %d) => rc %d", svc->pid, svc->sighalt, rc);
/* PID lost or forking process never really started */ /* PID lost or forking process never really started */
if (rc == -1 && ESRCH == errno) if (rc == -1 && ESRCH == errno)
service_cleanup(svc); service_cleanup(svc);
@@ -898,30 +862,6 @@ static void parse_env(svc_t *svc, char *env)
strlcpy(svc->env, env, sizeof(svc->env)); strlcpy(svc->env, env, sizeof(svc->env));
} }
static void parse_cgroup(svc_t *svc, char *cgroup)
{
char *ptr = cgroup;
if (!cgroup)
return;
if (cgroup[0] == '.') {
ptr = strchr(cgroup, ':');
if (ptr)
*ptr++ = 0;
strlcpy(svc->cgroup.name, &cgroup[1], sizeof(svc->cgroup.name));
if (!ptr)
return;
}
if (strlen(ptr) >= sizeof(svc->cgroup)) {
_e("%s: cgroup settings too long (>%d chars)", svc->cmd, sizeof(svc->cgroup));
return;
}
strlcpy(svc->cgroup.cfg, ptr, sizeof(svc->cgroup.cfg));
}
static void parse_sighalt(svc_t *svc, char *arg) static void parse_sighalt(svc_t *svc, char *arg)
{ {
int signo; int signo;
@@ -948,14 +888,6 @@ static void parse_killdelay(svc_t *svc, char *delay)
svc->killdelay = (int)(sec * 1000); svc->killdelay = (int)(sec * 1000);
} }
static void parse_script(char *type, char *script, char *buf, size_t len)
{
if (access(script, X_OK))
logit(LOG_WARNING, "%s: %s:%s is missing or not executable, skipping.", type, script);
else
strlcpy(buf, script, len);
}
/* /*
* name:<name> * name:<name>
*/ */
@@ -971,10 +903,13 @@ static void parse_name(svc_t *svc, char *arg)
} }
strlcpy(svc->name, name, sizeof(svc->name)); strlcpy(svc->name, name, sizeof(svc->name));
/* Warn if svc generates same condition as an existing service */
svc_validate(svc);
} }
/* /**
* Update the command line args in the svc struct * parse_cmdline_args - Update the command line args in the svc struct
* *
* strtok internal pointer must be positioned at first command line arg * strtok internal pointer must be positioned at first command line arg
* when this function is called. * when this function is called.
@@ -983,29 +918,23 @@ static void parse_name(svc_t *svc, char *arg)
*/ */
static void parse_cmdline_args(svc_t *svc, char *cmd) static void parse_cmdline_args(svc_t *svc, char *cmd)
{ {
int diff = 0;
char sep = 0; char sep = 0;
char *arg; char *arg;
int i = 0; int i = 0;
if (strcmp(svc->args[i], cmd))
diff++;
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while (i < MAX_NUM_SVC_ARGS)
svc->args[i++][0] = 0;
/* /*
* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args * Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
* array to be zero-terminated. * array to be zero-terminated.
*/ */
while ((arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1)) { for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1);) {
char prev[sizeof(svc->args[0])] = { 0 };
char ch = arg[0]; char ch = arg[0];
size_t len; size_t len;
if (!sep) {
strlcpy(prev, svc->args[i], sizeof(prev));
svc->args[i][0] = 0;
}
/* XXX: ugly string arg re-concatenation, fixme */ /* XXX: ugly string arg re-concatenation, fixme */
if (ch == '"' || ch == '\'') if (ch == '"' || ch == '\'')
sep = ch; sep = ch;
@@ -1022,13 +951,6 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
continue; continue;
} }
/* replace any @console arg with the expanded device name */
if (svc_is_tty(svc) && tty_isatcon(svc->args[i]))
strlcpy(svc->args[i], svc->dev, sizeof(svc->args[i]));
if (strcmp(svc->args[i], prev))
diff++;
sep = 0; sep = 0;
i++; i++;
} }
@@ -1037,16 +959,15 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* Clear remaining args in case they were set earlier. * Clear remaining args in case they were set earlier.
* This also zero-terminates the args array. * This also zero-terminates the args array.
*/ */
while (i < MAX_NUM_SVC_ARGS) { while (i < MAX_NUM_SVC_ARGS)
if (svc->args[i++][0]) { svc->args[i++][0] = 0;
svc->args[i-1][0] = 0; #if 0
diff++; for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
} if (!svc->args[i][0])
break;
_d("%s ", svc->args[i]);
} }
#endif
if (diff)
_d("Modified args for %s detected", cmd);
svc->args_dirty = (diff > 0);
} }
@@ -1110,10 +1031,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *cmd, *desc, *runlevels = NULL, *cond = NULL; char *cmd, *desc, *runlevels = NULL, *cond = NULL;
char *username = NULL, *log = NULL, *pid = NULL; char *username = NULL, *log = NULL, *pid = NULL;
char *name = NULL, *halt = NULL, *delay = NULL; char *name = NULL, *halt = NULL, *delay = NULL;
char *id = NULL, *env = NULL, *cgroup = NULL; char *id = NULL, *env = NULL;
char *pre_script = NULL, *post_script = NULL;
struct tty tty = { 0 };
char *dev = NULL;
int levels = 0; int levels = 0;
int manual = 0; int manual = 0;
char *line; char *line;
@@ -1124,7 +1042,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
return errno = EINVAL; return errno = EINVAL;
} }
line = strdupa(cfg); line = strdup(cfg);
if (!line) if (!line)
return 1; return 1;
@@ -1146,17 +1064,15 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
} }
} }
cmd = strtok(line, " \t"); cmd = strtok(line, " ");
if (!cmd) { if (!cmd) {
incomplete: incomplete:
_e("Incomplete service '%s', cannot register", cfg); _e("Incomplete service '%s', cannot register", cfg);
free(line);
return errno = ENOENT; return errno = ENOENT;
} }
while (cmd) { while (cmd) {
if (type == SVC_TYPE_TTY && cmd[0] == '@')
break; /* @console */
if (cmd[0] == '@') /* @username[:group] */ if (cmd[0] == '@') /* @username[:group] */
username = &cmd[1]; username = &cmd[1];
else if (cmd[0] == '[') /* [runlevels] */ else if (cmd[0] == '[') /* [runlevels] */
@@ -1177,16 +1093,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
halt = &cmd[5]; halt = &cmd[5];
else if (!strncasecmp(cmd, "kill:", 5)) else if (!strncasecmp(cmd, "kill:", 5))
delay = &cmd[5]; delay = &cmd[5];
else if (!strncasecmp(cmd, "pre:", 4))
pre_script = &cmd[4];
else if (!strncasecmp(cmd, "post:", 5))
post_script = &cmd[5];
else if (!strncasecmp(cmd, "env:", 4)) else if (!strncasecmp(cmd, "env:", 4))
env = &cmd[4]; env = &cmd[4];
else if (!strncasecmp(cmd, "cgroup:", 7))
cgroup = &cmd[7]; /* only settings */
else if (!strncasecmp(cmd, "cgroup.", 7))
cgroup = &cmd[6]; /* with group */
else else
break; break;
@@ -1199,63 +1107,25 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
levels = conf_parse_runlevels(runlevels); levels = conf_parse_runlevels(runlevels);
if (runlevel > 0 && !ISOTHER(levels, 0)) { if (runlevel > 0 && !ISOTHER(levels, 0)) {
_d("Skipping %s, bootstrap is completed.", cmd); _d("Skipping %s, bootstrap is completed.", cmd);
free(line);
return 0; return 0;
} }
if (!id) if (!id)
id = ""; id = "";
if (type == SVC_TYPE_TTY) { svc = svc_find(cmd, id);
size_t i, len = 0;
if (tty_parse_args(cmd, &tty))
return errno;
if (tty.cmd)
len += strlen(tty.cmd);
else
len += 3;
len += tty.num + 1;
for (i = 0; i < tty.num; i++)
len += strlen(tty.args[i]) + 1;
line = alloca(len);
if (!line)
return errno;
snprintf(line, len, "%s", tty.cmd ?: "tty");
for (i = 0; i < tty.num; i++) {
strlcat(line, " ", len);
strlcat(line, tty.args[i], len);
}
cmd = strtok(line, " \t");
if (!cmd)
return errno;
if (tty_isatcon(tty.dev))
dev = tty_atcon();
else
dev = tty.dev;
next:
svc = svc_find_by_tty(dev);
} else
svc = svc_find(cmd, id);
if (!svc) { if (!svc) {
_d("Creating new svc for %s id #%s type %d", cmd, id, type); _d("Creating new svc for %s id #%s type %d", cmd, id, type);
svc = svc_new(cmd, id, type); svc = svc_new(cmd, id, type);
if (!svc) { if (!svc) {
_e("Out of memory, cannot register service %s", cmd); _e("Out of memory, cannot register service %s", cmd);
free(line);
return errno = ENOMEM; return errno = ENOMEM;
} }
if (type == SVC_TYPE_SERVICE && manual) if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc); svc_stop(svc);
} else {
/* e.g., if missing cmd or env before */
svc_unblock(svc);
} }
/* Always clear svc PID file, for now. See TODO */ /* Always clear svc PID file, for now. See TODO */
@@ -1272,102 +1142,42 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
strlcpy(svc->group, ptr, sizeof(svc->group)); strlcpy(svc->group, ptr, sizeof(svc->group));
} }
strlcpy(svc->username, username, sizeof(svc->username)); strlcpy(svc->username, username, sizeof(svc->username));
} else {
getcuser(svc->username, sizeof(svc->username));
getcgroup(svc->group, sizeof(svc->group));
} }
svc->runlevels = levels;
_d("Service %s runlevel 0x%02x", svc->cmd, svc->runlevels);
conf_parse_cond(svc, cond);
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); parse_cmdline_args(svc, cmd);
/* svc->runlevels = levels;
* Warn if svc generates same condition (based on name:id) _d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
* as an existing service.
*/
svc_validate(svc);
conf_parse_cond(svc, cond);
parse_name(svc, name);
if (halt) if (halt)
parse_sighalt(svc, halt); parse_sighalt(svc, halt);
if (delay) if (delay)
parse_killdelay(svc, delay); parse_killdelay(svc, delay);
if (pre_script)
parse_script("pre", pre_script, svc->pre_script, sizeof(svc->pre_script));
if (post_script)
parse_script("post", post_script, svc->post_script, sizeof(svc->post_script));
if (log) if (log)
parse_log(svc, log); parse_log(svc, log);
if (desc) if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc)); strlcpy(svc->desc, desc, sizeof(svc->desc));
else if (type == SVC_TYPE_TTY)
snprintf(svc->desc, sizeof(svc->desc), "Getty on %s", svc->dev);
if (env) if (env)
parse_env(svc, env); parse_env(svc, env);
if (file)
strlcpy(svc->file, file, sizeof(svc->file));
/* Set configured limits */ /* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit)); memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
/* Seed with currently active group, may be empty */
strlcpy(svc->cgroup.name, cgroup_current, sizeof(svc->cgroup.name));
if (cgroup)
parse_cgroup(svc, cgroup);
/* New, recently modified or unchanged ... used on reload. */ /* New, recently modified or unchanged ... used on reload. */
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc))) if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)))
svc_mark_dirty(svc); svc_mark_dirty(svc);
else else
svc_mark_clean(svc); svc_mark_clean(svc);
svc_enable(svc);
/* for finit native services only, e.g. plugins/hotplug.c */ /* for finit native services only, e.g. plugins/hotplug.c */
if (!file) if (!file)
svc->protect = 1; svc->protect = 1;
/* continue expanding any 'tty @console ...' */ /* Free duped line, from above */
if (tty_isatcon(tty.dev)) { free(line);
dev = tty_atcon();
if (dev)
goto next;
}
return 0; return 0;
} }
@@ -1395,47 +1205,36 @@ void service_monitor(pid_t lost, int status)
if (fexist(SYNC_SHUTDOWN) || lost <= 1) if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return; return;
if (tty_respawn(lost))
return;
svc = svc_find_by_pid(lost); svc = svc_find_by_pid(lost);
if (!svc) { if (!svc) {
_d("collected unknown PID %d", lost); _d("collected unknown PID %d", lost);
return; return;
} }
switch (svc->state) { _d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
case SVC_CLEANUP_STATE: svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
case SVC_SETUP_STATE: svc->status = status;
_d("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script,
lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
kill(-svc->pid, SIGKILL);
goto done;
default:
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
svc->status = status;
break;
}
/* Forking sysv/services declare themselves with pid:!/path/to/pid.file */ /* Forking sysv/services declare themselves with pid:!/path/to/pid.file */
if (svc_is_starting(svc) && svc_is_forking(svc)) if (svc_is_starting(svc) && svc_is_forking(svc))
return; return;
/* Terminate any children in the same proess group, e.g. logit */
kill(-svc->pid, SIGKILL);
/* Try removing PID file (in case service does not clean up after itself) */ /* Try removing PID file (in case service does not clean up after itself) */
if (svc_is_daemon(svc) || svc_is_tty(svc)) { if (svc_is_daemon(svc)) {
service_cleanup(svc); service_cleanup(svc);
} else if (svc_is_runtask(svc)) { } else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */
if (WIFEXITED(status) && !WEXITSTATUS(status)) if (WIFEXITED(status) && !WEXITSTATUS(status))
svc->started = 1; svc->started = 1;
else else
svc->started = 0; svc->started = 0;
} }
done: /* Terminate any children in the same proess group, e.g. logit */
kill(-svc->pid, SIGKILL);
/* No longer running, update books. */ /* No longer running, update books. */
svc->start_time = svc->pid = 0; svc->start_time = svc->pid = 0;
@@ -1448,112 +1247,6 @@ done:
sm_step(&sm); 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) static void service_retry(svc_t *svc)
{ {
int timeout; int timeout;
@@ -1563,13 +1256,12 @@ static void service_retry(svc_t *svc)
if (svc->state != SVC_HALTED_STATE || if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) { svc->block != SVC_BLOCK_RESTARTING) {
if (!svc_is_tty(svc)) _d("%s not crashing anymore", svc->cmd);
_d("%s not crashing anymore", svc->cmd);
*restart_cnt = 0; *restart_cnt = 0;
return; return;
} }
if (*restart_cnt >= SVC_RESPAWN_MAX) { if (*restart_cnt >= RESPAWN_MAX) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.", logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0)); svc_ident(svc, NULL, 0));
svc_crashing(svc); svc_crashing(svc);
@@ -1582,12 +1274,12 @@ static void service_retry(svc_t *svc)
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt); _d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)", logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX); svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
svc_unblock(svc); svc_unblock(svc);
service_step(svc); service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */ /* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000; timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
service_timeout_after(svc, timeout, service_retry); service_timeout_after(svc, timeout, service_retry);
} }
@@ -1598,7 +1290,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
*state = new; *state = new;
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */ /* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if (*state == SVC_STOPPING_STATE) { if ((*state == SVC_STOPPING_STATE)) {
_d("%s is stopping, wait %d sec before sending SIGKILL ...", _d("%s is stopping, wait %d sec before sending SIGKILL ...",
svc->cmd, svc->killdelay / 1000); svc->cmd, svc->killdelay / 1000);
service_timeout_cancel(svc); service_timeout_cancel(svc);
@@ -1630,13 +1322,8 @@ restart:
switch (svc->state) { switch (svc->state) {
case SVC_HALTED_STATE: case SVC_HALTED_STATE:
if (enabled) { if (enabled)
if (svc_has_pre(svc)) { svc_set_state(svc, SVC_READY_STATE);
svc_set_state(svc, SVC_SETUP_STATE);
service_pre_script(svc);
} else
svc_set_state(svc, SVC_READY_STATE);
}
break; break;
case SVC_DONE_STATE: case SVC_DONE_STATE:
@@ -1646,20 +1333,13 @@ restart:
case SVC_STOPPING_STATE: case SVC_STOPPING_STATE:
if (!svc->pid) { if (!svc->pid) {
char cond[MAX_COND_LEN]; /* PID was collected normally, no need to kill it */
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
_d("%s: stopped, cleaning up timers and conditions ...", svc->cmd);
service_timeout_cancel(svc); service_timeout_cancel(svc);
cond_clear(mkcond(svc, cond, sizeof(cond)));
switch (svc->type) { switch (svc->type) {
case SVC_TYPE_SERVICE: case SVC_TYPE_SERVICE:
case SVC_TYPE_TTY: svc_set_state(svc, SVC_HALTED_STATE);
if (svc_has_post(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE);
service_post_script(svc);
} else
svc_set_state(svc, SVC_HALTED_STATE);
break; break;
case SVC_TYPE_TASK: case SVC_TYPE_TASK:
@@ -1675,16 +1355,6 @@ restart:
} }
break; break;
case SVC_CLEANUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_HALTED_STATE);
break;
case SVC_SETUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_READY_STATE);
break;
case SVC_READY_STATE: case SVC_READY_STATE:
if (!enabled) { if (!enabled) {
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
@@ -1695,10 +1365,6 @@ restart:
err = service_start(svc); err = service_start(svc);
if (err) { if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++; (*restart_cnt)++;
break; break;
} }
@@ -1716,7 +1382,7 @@ restart:
} }
if (!svc->pid) { if (!svc->pid) {
if (svc_is_daemon(svc) || svc_is_tty(svc)) { if (svc_is_daemon(svc)) {
svc_restarting(svc); svc_restarting(svc);
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
@@ -1754,9 +1420,7 @@ restart:
case COND_ON: case COND_ON:
if (svc_is_changed(svc)) { if (svc_is_changed(svc)) {
if (svc_nohup(svc)) if (svc->sighup) {
service_stop(svc);
else {
/* /*
* wait until all processes have been * wait until all processes have been
* stopped before continuing... * stopped before continuing...
@@ -1764,7 +1428,8 @@ restart:
if (sm_is_in_teardown(&sm)) if (sm_is_in_teardown(&sm))
break; break;
service_restart(svc); service_restart(svc);
} } else
service_stop(svc);
svc_mark_clean(svc); svc_mark_clean(svc);
} }
@@ -1835,7 +1500,7 @@ void service_step_all(int types)
void service_worker(void *unused) void service_worker(void *unused)
{ {
service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK); service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
} }
/** /**
-1
View File
@@ -33,7 +33,6 @@ void service_unregister (svc_t *svc);
void service_runtask_clean (void); void service_runtask_clean (void);
void service_reload_dynamic (void); void service_reload_dynamic (void);
void service_update_rdeps (void);
int service_step (svc_t *svc); int service_step (svc_t *svc);
void service_step_all (int types); void service_step_all (int types);
+10 -12
View File
@@ -138,9 +138,7 @@ static struct sigmap {
/* Nonstandard signals found in all modern POSIX systems /* Nonstandard signals found in all modern POSIX systems
(including both BSD and Linux). */ (including both BSD and Linux). */
{ SIGWINCH, "SIGWINCH" }, { SIGWINCH, "SIGWINCH" },
#ifdef SIGSTKFLT
{ SIGSTKFLT, "SIGSTKFLT" }, { SIGSTKFLT, "SIGSTKFLT" },
#endif
{ SIGPWR, "SIGPWR" }, { SIGPWR, "SIGPWR" },
/* Archaic names for compatibility. */ /* Archaic names for compatibility. */
@@ -194,10 +192,10 @@ void do_kill(int signo)
if (fgets(file, sizeof(file), fp)) { if (fgets(file, sizeof(file), fp)) {
if (strstr(file, "gdbserver")) if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file); _d("Skipping %s ...", file);
else if (file[0] == '@') else if (file[0] != '@')
_d("Skipping %s ...", &file[1]);
else
kill(pid, signo); kill(pid, signo);
else
_d("Skipping %s ...", &file[1]);
} }
fclose(fp); fclose(fp);
} }
@@ -216,11 +214,11 @@ void do_shutdown(shutop_t op)
utmp_set_halt(); utmp_set_halt();
/* /*
* Tell remaining non-monitored processes to exit, give them * Tell all remaining non-monitored processes to exit, give them
* time to exit gracefully, 2 sec was customary, we go for 1. * some time to exit gracefully, 2 sec is customary.
*/ */
do_kill(SIGTERM); do_kill(SIGTERM);
do_sleep(1); do_sleep(2);
do_kill(SIGKILL); do_kill(SIGKILL);
/* Exit plugins and API gracefully */ /* Exit plugins and API gracefully */
@@ -267,7 +265,7 @@ void do_shutdown(shutop_t op)
/* Reboot via watchdog or kernel, or shutdown? */ /* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) { if (op == SHUT_REBOOT) {
if (wdog && wdog->pid > 1) { if (wdog) {
int timeout = 10; int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */ /* Wait here until the WDT reboots, or timeout with fallback */
@@ -276,15 +274,15 @@ void do_shutdown(shutop_t op)
do_sleep(1); do_sleep(1);
} }
_d("Rebooting ..."); _e("Rebooting ...");
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) { } else if (op == SHUT_OFF) {
_d("Powering down ..."); _e("Powering down ...");
reboot(RB_POWER_OFF); reboot(RB_POWER_OFF);
} }
/* Also fallback if any of the other two fails */ /* Also fallback if any of the other two fails */
_d("Halting ..."); _e("Halting ...");
reboot(RB_HALT_SYSTEM); reboot(RB_HALT_SYSTEM);
} }
+13 -11
View File
@@ -156,11 +156,9 @@ restart:
/* Disable login in single-user mode as well as shutdown/reboot */ /* Disable login in single-user mode as well as shutdown/reboot */
nologin(); nologin();
if (runlevel != 0 && runlevel != 6) { /* Make sure to (re)load all *.conf in /etc/finit.d/ */
/* Make sure to (re)load all *.conf in /etc/finit.d/ */ if (conf_any_change())
if (conf_any_change()) conf_reload();
conf_reload();
}
/* Reset once flag of runtasks */ /* Reset once flag of runtasks */
service_runtask_clean(); service_runtask_clean();
@@ -206,6 +204,10 @@ restart:
break; break;
} }
/* No TTYs run at bootstrap, they have a delayed start. */
if (prevlevel > 0)
tty_runlevel();
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
break; break;
@@ -220,7 +222,8 @@ restart:
_d("Stopping services not allowed after reconf ..."); _d("Stopping services not allowed after reconf ...");
sm->in_teardown = 1; sm->in_teardown = 1;
cond_reload(); cond_reload();
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_SERVICE);
tty_reload(NULL);
sm->state = SM_RELOAD_WAIT_STATE; sm->state = SM_RELOAD_WAIT_STATE;
break; break;
@@ -237,17 +240,16 @@ restart:
} }
sm->in_teardown = 0; sm->in_teardown = 0;
_d("Starting services after reconf ...");
service_step_all(SVC_TYPE_ANY);
/* Cleanup stale services */ /* Cleanup stale services */
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
_d("Starting services after reconf ...");
service_step_all(SVC_TYPE_SERVICE);
_d("Calling reconf hooks ..."); _d("Calling reconf hooks ...");
plugin_run_hooks(HOOK_SVC_RECONF); plugin_run_hooks(HOOK_SVC_RECONF);
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_SERVICE);
_d("Reconfiguration done"); _d("Reconfiguration done");
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
+2 -6
View File
@@ -27,8 +27,6 @@
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
#include <helpers.h>
/* B0 means; keep kernel default */ /* B0 means; keep kernel default */
speed_t stty_parse_speed(char *baud) speed_t stty_parse_speed(char *baud)
{ {
@@ -63,12 +61,10 @@ speed_t stty_parse_speed(char *baud)
{ 1152000, B1152000 }, { 1152000, B1152000 },
{ 1500000, B1500000 }, { 1500000, B1500000 },
{ 2000000, B2000000 }, { 2000000, B2000000 },
#ifdef B4000000
{ 2500000, B2500000 }, { 2500000, B2500000 },
{ 3000000, B3000000 }, { 3000000, B3000000 },
{ 3500000, B3500000 }, { 3500000, B3500000 },
{ 4000000, B4000000 }, { 4000000, B4000000 },
#endif
}; };
if (!baud || !baud[0]) if (!baud || !baud[0])
@@ -125,10 +121,10 @@ void stty(int fd, speed_t speed)
tcsetattr(fd, TCSAFLUSH, &term); tcsetattr(fd, TCSAFLUSH, &term);
/* Show cursor again, if it was hidden previously */ /* Show cursor again, if it was hidden previously */
dprint(fd, "\033[?25h", 6); write(fd, "\033[?25h", 6);
/* Enable line wrap, if disabled previously */ /* Enable line wrap, if disabled previously */
dprint(fd, "\033[7h", 4); write(fd, "\033[7h", 4);
} }
/** /**
+11 -89
View File
@@ -44,36 +44,6 @@ static int jobcounter = 1;
static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list); static TAILQ_HEAD(, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list); static TAILQ_HEAD(, svc) gc_list = TAILQ_HEAD_INITIALIZER(gc_list);
/*
* Before gc removal of svc, make sure we don't clear an active
* condition of a new instance of the svc.
*/
static void maybe_clear_cond(svc_t *svc)
{
char ident[MAX_IDENT_LEN];
char cond[MAX_COND_LEN];
svc_t *iter = NULL;
svc_t *s;
mkcond(svc, cond, sizeof(cond));
svc_ident(svc, ident, sizeof(ident));
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
char c[MAX_COND_LEN];
mkcond(s, c, sizeof(c));
if (!string_compare(cond, c))
continue;
_d("Not clearing cond %s from gc svc %s, provided by new active service %s",
cond, ident, svc_ident(s, NULL, 0));
return;
}
_d("Cleaning out %s, clearing any conditions ...", svc->name);
cond_clear(mkcond(svc, cond, sizeof(cond)));
}
static void svc_gc(void *arg) static void svc_gc(void *arg)
{ {
struct timespec now; struct timespec now;
@@ -82,6 +52,7 @@ static void svc_gc(void *arg)
clock_gettime(CLOCK_MONOTONIC_COARSE, &now); clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) { TAILQ_FOREACH_SAFE(svc, &gc_list, link, next) {
char cond[MAX_COND_LEN];
int msec; int msec;
msec = (now.tv_sec - svc->gc.tv_sec) * 1000 + msec = (now.tv_sec - svc->gc.tv_sec) * 1000 +
@@ -90,7 +61,8 @@ static void svc_gc(void *arg)
continue; continue;
TAILQ_REMOVE(&gc_list, svc, link); TAILQ_REMOVE(&gc_list, svc, link);
maybe_clear_cond(svc); _d("Cleaning out %s, clearing any conditions ...", svc->name);
cond_clear(mkcond(svc, cond, sizeof(cond)));
free(svc); free(svc);
} }
@@ -114,7 +86,7 @@ svc_t *svc_new(char *cmd, char *id, int type)
/* Find first job n:o if registering multiple instances */ /* Find first job n:o if registering multiple instances */
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (cmd && !strcmp(svc->cmd, cmd)) { if (!strcmp(svc->cmd, cmd)) {
job = svc->job; job = svc->job;
break; break;
} }
@@ -130,17 +102,13 @@ svc_t *svc_new(char *cmd, char *id, int type)
svc->job = job; svc->job = job;
if (id && id[0]) if (id && id[0])
strlcpy(svc->id, id, sizeof(svc->id)); strlcpy(svc->id, id, sizeof(svc->id));
if (cmd) strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
/* Default description, if missing */ /* Default description, if missing */
strlcpy(svc->desc, svc->name, sizeof(svc->desc)); strlcpy(svc->desc, svc->name, sizeof(svc->desc));
/* Default HALT signal to send */ /* Default HALT signal to send */
if (svc_is_tty(svc)) svc->sighalt = SIGTERM;
svc->sighalt = SIGHUP;
else
svc->sighalt = SIGTERM;
/* Default delay between SIGTERM and SIGKILL */ /* Default delay between SIGTERM and SIGKILL */
svc->killdelay = SVC_TERM_TIMEOUT; svc->killdelay = SVC_TERM_TIMEOUT;
@@ -192,8 +160,6 @@ void svc_validate(svc_t *svc)
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) { for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
char c[MAX_COND_LEN]; char c[MAX_COND_LEN];
if (s->removed)
continue;
if (s == svc) if (s == svc)
continue; continue;
@@ -333,7 +299,7 @@ svc_t *svc_stop_completed(void)
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->state == SVC_STOPPING_STATE && svc->pid > 1) if (svc->state == SVC_STOPPING_STATE)
return svc; return svc;
} }
@@ -428,26 +394,6 @@ svc_t *svc_find_by_nameid(char *name, char *id)
return NULL; return NULL;
} }
svc_t *svc_find_by_tty(char *dev)
{
svc_t *svc, *iter = NULL;
/* 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 * svc_find_by_plidfile - Find a service by its PID file
* @fn: PID file, can be absolute path or relative to /run * @fn: PID file, can be absolute path or relative to /run
@@ -469,20 +415,8 @@ svc_t *svc_find_by_pidfile(char *fn)
pid_t pid; pid_t pid;
pid = pid_file_read(fn); pid = pid_file_read(fn);
if (pid <= 0) { if (pid == -1)
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
int v = 0;
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue;
return svc;
}
return NULL; return NULL;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid != pid) if (svc->pid != pid)
@@ -512,7 +446,7 @@ void svc_mark_dynamic(void)
if (svc->protect) if (svc->protect)
continue; continue;
*((int *)&svc->removed) = 1; *((int *)&svc->dirty) = -1;
} }
} }
@@ -524,12 +458,6 @@ void svc_mark_dirty(svc_t *svc)
void svc_mark_clean(svc_t *svc) void svc_mark_clean(svc_t *svc)
{ {
*((int *)&svc->dirty) = 0; *((int *)&svc->dirty) = 0;
*((int *)&svc->args_dirty) = 0;
}
void svc_enable(svc_t *svc)
{
*((int *)&svc->removed) = 0;
} }
/** /**
@@ -544,7 +472,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->removed && cb) if (svc->dirty == -1 && cb)
cb(svc); cb(svc);
} }
} }
@@ -604,12 +532,6 @@ int svc_enabled(svc_t *svc)
if (svc_is_removed(svc) || svc_is_blocked(svc)) if (svc_is_removed(svc) || svc_is_blocked(svc))
return 0; return 0;
if (svc_is_missing(svc))
return 0;
if (svc_is_tty(svc) && bootstrap)
return 0;
return 1; return 1;
} }
@@ -680,7 +602,7 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
char *ptr; char *ptr;
_d("Got token:'%s'", input); _d("Got token:'%s'", input);
token = sanitize(input, strlen(input) + 1); token = sanitize(input, len);
if (!token) { if (!token) {
_d("Sanitation of token:'%s' failed", input); _d("Sanitation of token:'%s' failed", input);
goto next; goto next;
+21 -116
View File
@@ -32,7 +32,6 @@
#include <lite/queue.h> /* BSD sys/queue.h API */ #include <lite/queue.h> /* BSD sys/queue.h API */
#include <uev/uev.h> #include <uev/uev.h>
#include "cgroup.h"
#include "helpers.h" #include "helpers.h"
typedef int svc_cmd_t; typedef int svc_cmd_t;
@@ -42,20 +41,16 @@ typedef enum {
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */ SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */ SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */ SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
SVC_TYPE_TTY = 8, /* Like service, but dedicated to TTYs */
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */ SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
} svc_type_t; } svc_type_t;
#define SVC_TYPE_ANY (-1) #define SVC_TYPE_ANY (-1)
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_TTY)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV) #define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV)
typedef enum { typedef enum {
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */ SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
SVC_DONE_STATE, /* Task/Run job has been run */ SVC_DONE_STATE, /* Task/Run job has been run */
SVC_STOPPING_STATE, /* Waiting to collect the child process */ SVC_STOPPING_STATE, /* Waiting to collect the child process */
SVC_CLEANUP_STATE, /* Running post: script */
SVC_SETUP_STATE, /* Running pre: script */
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */ SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
SVC_READY_STATE, /* Enabled but condition not satisfied */ SVC_READY_STATE, /* Enabled but condition not satisfied */
SVC_RUNNING_STATE, /* Process running */ SVC_RUNNING_STATE, /* Process running */
@@ -82,9 +77,6 @@ typedef enum {
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */ /* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
#define SVC_TERM_TIMEOUT 3000 #define SVC_TERM_TIMEOUT 3000
/* Prevent endless respawn of faulty services. */
#define SVC_RESPAWN_MAX 10
/* /*
* Default enable for all services, can be stopped by means * Default enable for all services, can be stopped by means
* of issuing an initctl call. E.g. * of issuing an initctl call. E.g.
@@ -94,17 +86,17 @@ typedef enum {
typedef struct svc { typedef struct svc {
TAILQ_ENTRY(svc) link; TAILQ_ENTRY(svc) link;
/* Origin of service */ /* Instance specifics */
char file[MAX_ARG_LEN]; int job; /* JOB: */
char id[MAX_ID_LEN]; /* :ID */
/* Limits and scoping */ /* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS]; struct rlimit rlimit[RLIMIT_NLIMITS];
struct cgroup cgroup;
/* Service details */ /* Service details */
int sighalt; /* Signal to stop prorcess, default: SIGTERM */ int sighalt; /* Signal to stop prorcess, default: SIGTERM */
int killdelay; /* Delay in msec before sending SIGKILL */ int killdelay; /* Delay in msec before sending SIGKILL */
pid_t oldpid, pid; pid_t pid;
char pidfile[256]; char pidfile[256];
long start_time; /* Start time, as seconds since boot, from sysinfo() */ long start_time; /* Start time, as seconds since boot, from sysinfo() */
int started; /* Set for run/task/sysv to track if started */ int started; /* Set for run/task/sysv to track if started */
@@ -112,45 +104,27 @@ typedef struct svc {
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */ const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, ... */ svc_type_t type; /* Service, run, task, ... */
int protect; /* Services like dbus-daemon & udev by Finit */ int protect; /* Services like dbus-daemon & udev by Finit */
const int dirty; /* 0: unmodified, 1: modified */ const int dirty; /* -1: removal, 0: unmodified, 1: modified */
const int removed;
int starting; /* ... waiting for pidfile to be re-asserted */ int starting; /* ... waiting for pidfile to be re-asserted */
int runlevels; int runlevels;
int sighup; /* This service supports SIGHUP :) */ int sighup; /* This service supports SIGHUP :) */
svc_block_t block; /* Reason that this service is currently stopped */ svc_block_t block; /* Reason that this service is currently stopped */
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
/* Instance specifics */
int job; /* For intenal use only, canonical ref is NAME:ID */
char name[MAX_ARG_LEN]; char name[MAX_ARG_LEN];
char id[MAX_ID_LEN]; /* :ID */
/* Counters */ /* Counters */
char once; /* run/task, (at least) once per runlevel */ char once; /* run/task, (at least) once per runlevel */
const char restart_cnt; /* Incremented for each restart by service monitor. */ const char restart_cnt; /* Incremented for each restart by service monitor. */
union { /* Set for services we need to redirect stdout/stderr to syslog */
/* services we redirect stdout/stderr to syslog (not TTYs!) */ struct {
struct { char enabled;
char enabled; char null;
char null; char console;
char console; char file[64];
char file[64]; char prio[20];
char prio[20]; char ident[20];
char ident[20]; } log;
} 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 */ /* Identity */
char username[MAX_USER_LEN]; char username[MAX_USER_LEN];
@@ -159,11 +133,8 @@ typedef struct svc {
/* Command, arguments and service description */ /* Command, arguments and service description */
char cmd[MAX_ARG_LEN]; char cmd[MAX_ARG_LEN];
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN]; char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
int args_dirty;
char desc[MAX_STR_LEN]; char desc[MAX_STR_LEN];
char env[MAX_ARG_LEN]; char env[MAX_ARG_LEN];
char pre_script[MAX_ARG_LEN];
char post_script[MAX_ARG_LEN];
/* /*
* Used to forcefully kill services that won't shutdown on * Used to forcefully kill services that won't shutdown on
@@ -184,7 +155,6 @@ svc_t *svc_find (char *cmd, char *id);
svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_by_jobid (int job, char *id); svc_t *svc_find_by_jobid (int job, char *id);
svc_t *svc_find_by_nameid (char *name, char *id); svc_t *svc_find_by_nameid (char *name, char *id);
svc_t *svc_find_by_tty (char *dev);
svc_t *svc_find_by_pidfile (char *fn); svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (svc_t **iter, int first); svc_t *svc_iterator (svc_t **iter, int first);
@@ -203,37 +173,30 @@ void svc_clean_dynamic (void (*cb)(svc_t *));
int svc_clean_bootstrap (svc_t *svc); int svc_clean_bootstrap (svc_t *svc);
void svc_prune_bootstrap (void); void svc_prune_bootstrap (void);
void svc_enable (svc_t *svc);
int svc_enabled (svc_t *svc); int svc_enabled (svc_t *svc);
int svc_is_unique (svc_t *svc); int svc_is_unique (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *)); int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; } static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; } static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
static inline int svc_is_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_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);}
static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; } static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); } static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; } static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; } static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; } static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; } static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_running (svc_t *svc) { return svc && svc->state == SVC_RUNNING_STATE; }
static inline int svc_is_removed (svc_t *svc) { return svc && svc->removed; } static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; } static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; } static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; } static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; } static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; } static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
#define svc_start(svc) svc_unblock(svc) #define svc_start(svc) svc_unblock(svc)
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; } static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
@@ -242,27 +205,6 @@ 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_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; } 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) static inline char *svc_status(svc_t *svc)
{ {
if (!svc) if (!svc)
@@ -307,12 +249,6 @@ static inline char *svc_status(svc_t *svc)
case SVC_WAITING_STATE: case SVC_WAITING_STATE:
return "waiting"; return "waiting";
case SVC_CLEANUP_STATE:
return "cleanup";
case SVC_SETUP_STATE:
return "setup";
case SVC_READY_STATE: case SVC_READY_STATE:
return "ready"; return "ready";
@@ -388,37 +324,6 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
return buf; return buf;
} }
/*
* non-zero env, no checking if file exists or not
*/
static inline char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static inline int svc_checkenv(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env || svc->env[0] == '-')
return 1;
return fexist(env);
}
#endif /* FINIT_SVC_H_ */ #endif /* FINIT_SVC_H_ */
/** /**
+392 -124
View File
@@ -35,22 +35,24 @@
#include "finit.h" #include "finit.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
#include "service.h"
#include "tty.h" #include "tty.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
char *tty_canonicalize(char *dev) #ifdef FALLBACK_SHELL
static pid_t fallback = 0;
#endif
static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER();
static char *canonicalize(char *tty)
{ {
static char path[80];
struct stat st; struct stat st;
static char path[80];
if (!dev) if (!tty)
return NULL; return NULL;
if (tty_isatcon(dev))
return dev;
strlcpy(path, dev, sizeof(path)); strlcpy(path, tty, sizeof(path));
if (stat(path, &st)) { if (stat(path, &st)) {
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) { if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
unavailable: unavailable:
@@ -58,7 +60,7 @@ char *tty_canonicalize(char *dev)
return path; return path;
} }
snprintf(path, sizeof(path), "%s%s", _PATH_DEV, dev); snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty);
if (stat(path, &st)) if (stat(path, &st))
goto unavailable; goto unavailable;
} }
@@ -69,65 +71,37 @@ char *tty_canonicalize(char *dev)
return path; return path;
} }
/* void tty_mark(void)
* The @console syntax is a wildcard to match the system console(s) given
* on the kernel cmdline. As such it can match multiple, or even none.
*/
int tty_isatcon(char *dev)
{ {
return dev && !strcmp(dev, "@console"); struct tty *tty;
LIST_FOREACH(tty, &tty_list, link)
tty->dirty = -1;
} }
/* void tty_sweep(void)
* Returns console TTYs known by the kernel, from cmdline
*/
char *tty_atcon(void)
{ {
static char *buf = NULL; struct tty *tty, *tmp;
static char *save;
char *ptr = NULL;
char *dev;
if (!buf) { LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
FILE *fp; if (!tty->dirty)
continue;
fp = fopen("/sys/class/tty/console/active", "r"); _d("TTY %s dirty, stopping ...", tty->name);
if (!fp) { tty_stop(tty);
_e("Cannot find system console, is sysfs not mounted?");
errno = ENOENT; if (tty->dirty == -1) {
return NULL; _d("TTY %s removed, cleaning up.", tty->name);
tty_unregister(tty);
} }
buf = malloc(512);
if (!buf) {
_pe("Failed allocating memory for @console");
fclose(fp);
return NULL;
}
if (!fgets(buf, 512, fp)) {
fclose(fp);
goto done;
}
fclose(fp);
ptr = chomp(buf);
_d("consoles: %s", ptr);
} }
dev = strtok_r(ptr, " \t", &save);
if (!dev) {
done:
free(buf);
return buf = NULL;
}
return dev;
} }
/** /**
* tty_parse_args - Parse cmdline args for a tty * tty_register - Register a getty on a device
* @cmd: command or tty * @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
* *
* A Finit tty line can use the internal getty implementation or an * A Finit tty line can use the internal getty implementation or an
* external one, like the BusyBox getty for instance. This function * external one, like the BusyBox getty for instance. This function
@@ -145,55 +119,66 @@ char *tty_atcon(void)
* Different getty implementations prefer the TTY device argument in * Different getty implementations prefer the TTY device argument in
* different order, so take care to investigate this first. * different order, so take care to investigate this first.
*/ */
int tty_parse_args(char *cmd, struct tty *tty) int tty_register(char *line, struct rlimit rlimit[], char *file)
{ {
char *dev = NULL; struct tty *entry;
size_t i; 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;
do { if (!line) {
if (!strcmp(cmd, "noclear")) _e("Missing argument");
tty->noclear = 1; return errno = EINVAL;
else if (!strcmp(cmd, "nowait")) }
tty->nowait = 1;
else if (!strcmp(cmd, "nologin")) /*
tty->nologin = 1; * Split line in separate arguments. For an external getty
else if (!strcmp(cmd, "notty")) * this is used with execv(), for the built-in it simplifies
tty->notty = 1; /* for fallback shell */ * further translation.
else if (!access(cmd, X_OK)) */
tty->cmd = cmd; 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;
else else
tty->args[tty->num++] = cmd; args[num++] = tok;
cmd = strtok(NULL, " \t"); tok = 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 */ /* Iterate over all args */
for (i = 0; i < tty->num; i++) { for (i = 0; i < num; i++) {
/* /*
* First, figure out if built-in or external getty * First, figure out if built-in or external getty
* tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in * tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in
* tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external * tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external
*/ */
if (!dev) { if ((!cmd && !dev) || (cmd && !dev)) {
if (!strcmp(tty->args[i], "@console")) if (args[i][0] == '[')
dev = tty->args[i]; runlevels = line;
if (!strncmp(tty->args[i], "/dev", 4)) if (!strcmp(args[i], "@console"))
dev = tty->args[i]; dev = args[i];
if (!strncmp(tty->args[i], "tty", 3) || !strcmp(tty->args[i], "console")) if (!strncmp(args[i], "/dev", 4))
dev = tty->args[i]; 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];
/* The first arg must be one of the above */ /* The first arg must be one of the above */
continue; continue;
} }
/* Built-in getty args */ /* Built-in getty args */
if (!tty->cmd && dev) { if (!cmd && dev) {
if (isdigit(tty->args[i][0])) { if (isdigit(args[i][0])) {
tty->baud = tty->args[i]; baud = args[i];
continue; continue;
} }
@@ -201,86 +186,369 @@ int tty_parse_args(char *cmd, struct tty *tty)
* Last arg, if not anything else, is the value * Last arg, if not anything else, is the value
* to be used for the TERM environment variable. * to be used for the TERM environment variable.
*/ */
if (i + 1 == tty->num) if (i + 1 == num)
tty->term = tty->args[i]; term = args[i];
} }
} }
tty->dev = tty_canonicalize(dev); if (!dev) {
if (!tty->dev) { error:
_e("Incomplete or non-existing TTY device given, cannot register."); _e("Incomplete or non-existing TTY device given, cannot register.");
return errno = EINVAL; return errno = EINVAL;
} }
_d("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in", /* Auto-detect serial console, for embedded devices mostly */
tty->dev, tty->baud ?: "0", tty->term ?: "N/A"); 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;
}
}
return 0; return 0;
} }
int tty_exists(char *dev) 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 fd, result; int fd, result;
struct termios c; struct termios c;
fd = open(dev, O_RDWR); fd = open(dev, O_RDWR);
if (-1 == fd) if (-1 == fd)
return 0; return 1;
/* XXX: Add check for errno == EIO? */ /* XXX: Add check for errno == EIO? */
result = tcgetattr(fd, &c); result = tcgetattr(fd, &c);
close(fd); close(fd);
return result == 0; return result;
} }
int tty_exec(svc_t *tty) void tty_start(struct tty *tty)
{ {
char *args[MAX_NUM_SVC_ARGS];
char *dev; char *dev;
int i, j;
if (tty->notty) { if (tty->pid) {
/* _d("%s: TTY already active", tty->name);
* Become session leader and set controlling TTY return;
* 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 = tty_canonicalize(tty->dev); dev = canonicalize(tty->name);
if (!dev) { if (!dev) {
_d("%s: Cannot find TTY device: %s", tty->dev, strerror(errno)); _d("%s: Cannot find TTY device: %s", tty->name, strerror(errno));
return EX_CONFIG; return;
} }
if (!tty_exists(dev)) { if (tty_exist(dev)) {
_d("%s: Not a valid TTY: %s", dev, strerror(errno)); _d("%s: Not a valid TTY: %s", dev, strerror(errno));
return EX_OSFILE; return;
} }
if (tty->nologin) { if (tty->nologin) {
_d("%s: Starting /bin/sh ...", dev); _d("%s: Starting /bin/sh ...", dev);
return run_sh(dev, tty->noclear, tty->nowait, tty->rlimit); tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
return;
} }
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : ""); _d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : "");
if (!strcmp(tty->cmd, "tty")) if (!tty->cmd)
return run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit); tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
else
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
}
for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) { void tty_stop(struct tty *tty)
if (!tty->args[i][0]) {
break; if (!tty->pid)
return;
args[j++] = tty->args[i]; /*
* XXX: TTY handling should be refactored to regular services,
* XXX: that way we could rely on the state machine to properly
* XXX: send SIGTERM, wait for max 2 sec to collect PID before
* XXX: sending SIGKILL.
*/
_d("Stopping TTY %s", tty->name);
kill(tty->pid, SIGKILL);
waitpid(tty->pid, NULL, 0);
tty->pid = 0;
}
int tty_enabled(struct tty *tty)
{
if (!tty)
return 0;
if (ISSET(tty->runlevels, runlevel))
return 1;
return 0;
}
/*
* Fallback shell if no TTYs are active
*/
int tty_fallback(pid_t lost)
{
#ifdef FALLBACK_SHELL
if (lost == 1) {
if (fallback) {
kill(fallback, SIGKILL);
fallback = 0;
}
return 0;
} }
args[j++] = NULL;
return run_getty2(dev, tty->cmd, args, tty->noclear, tty->nowait, tty->rlimit); 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);
} }
/** /**
+39 -19
View File
@@ -25,32 +25,52 @@
#ifndef FINIT_TTY_H_ #ifndef FINIT_TTY_H_
#define FINIT_TTY_H_ #define FINIT_TTY_H_
#include <stdio.h> #include <limits.h>
#include "svc.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))
struct tty { struct tty {
char *cmd; LIST_ENTRY(tty) link;
char *args[MAX_NUM_SVC_ARGS];
size_t num;
char *dev; char name[42];
char *baud; char baud[10];
char *term; char term[10];
char noclear; int noclear;
char nowait; int nowait;
char nologin; int nologin;
char notty; 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 *tty_canonicalize (char *dev); void tty_mark (void);
void tty_sweep (void);
int tty_isatcon (char *dev); int tty_register (char *line, struct rlimit rlimit[], char *file);
char *tty_atcon (void); int tty_unregister (struct tty *tty);
int tty_parse_args (char *cmdline, struct tty *tty); struct tty *tty_find (char *dev);
size_t tty_num (void);
int tty_exists (char *dev); size_t tty_num_active (void);
int tty_exec (svc_t *tty); 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);
#endif /* FINIT_TTY_H_ */ #endif /* FINIT_TTY_H_ */
+32 -284
View File
@@ -26,110 +26,18 @@
#include <errno.h> #include <errno.h>
#include <regex.h> #include <regex.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <utmp.h> #include <utmp.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <sys/sysinfo.h> /* sysinfo() */ #include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */ #include <lite/lite.h> /* strlcat() */
#include "util.h" #include "util.h"
#ifdef HAVE_TERMIOS_H int screen_rows = 24;
/* int screen_cols = 80;
* This flag is used on *BSD when calling tcsetattr() to prevent it char *prognm = NULL;
* from changing speed, duplex, parity. GNU says we should use the
* CIGNORE flag to c_cflag, but that doesn't exist so ... we rely on
* our initial tcgetattr() and prey that nothing changes on the TTY
* before we exit and restore with tcsetattr()
*/
#ifndef TCSASOFT
#define TCSASOFT 0
#endif
static struct termios ttold;
static struct termios ttnew;
#endif
int ttrows = 24;
int ttcols = 80;
char *prognm = NULL;
static char *signames[] = {
"",
"HUP", /* 1 */
"INT",
"QUIT",
"ILL",
"TRAP",
"ABRT",
"BUS",
"FPE", /* 8 */
"KILL",
"USR1",
"SEGV",
"USR2",
"PIPE",
"ALRM",
"TERM",
"STKFLT", /* 16 */
"CHLD",
"CONT",
"STOP",
"TSTP",
"TTIN",
"TTOU",
"URG",
"XCPU", /* 24 */
"XFSZ",
"VTALRM",
"PROF",
"WINCH",
"IO",
"PWR",
"SYS",
};
/* https://freedesktop.org/software/systemd/man/systemd.exec.html#id-1.20.8 */
static char *exitcodes[] = {
"SUCCESS", /* 0: Std C exit OK */
"FAILURE", /* 1: Std C exit FAIL */
/* 2-7: LSB init scripts (usually) */
"INVALIDARGUMENT", /* 2: Invalid or excess args */
"NOTIMPLEMENTED", /* 3: Unimplemented feature, e.g. 'reload' */
"NOPERMISSION", /* 4: Insufficient privilege */
"NOTINSTALLED", /* 5: Program is not installed */
"NOTCONFIGURED", /* 6: Program is not configured */
"NOTRUNNING", /* 7: Program is not running */
"", "", "", "", "", "", "", "", /* 8-63: Not standardized */
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 32-47 */
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 48-63 */
/* 64-78: BSD, from sysexit.h */
"USAGE", /* 64: Command line usage error */
"DATAERR", /* 65: data format error */
"NOINPUT", /* 66: cannot open input */
"NOUSER", /* 67: addressee unknown */
"NOHOST", /* 68: host name unknown */
"UNAVAILABLE", /* 69: service unavailable */
"SOFTWARE", /* 70: internal software error */
"OSERR", /* 71: system error (e.g., can't fork) */
"OSFILE", /* 72: critical OS file missing */
"CANTCREAT", /* 73: can't create (user) output file */
"IOERR", /* 74: input/output error */
"TEMPFAIL", /* 75: temp failure; user is invited to retry */
"PROTOCOL", /* 76: remote error in protocol */
"NOPERM", /* 77: permission denied */
"CONFIG", /* 78: configuration error */
/* 79-199: Not standardized, typically 127 == -1 */
/* >= 200: reserved (LSB), used by systemd */
};
char *progname(char *arg0) char *progname(char *arg0)
{ {
@@ -142,47 +50,30 @@ char *progname(char *arg0)
return prognm; return prognm;
} }
char *str(char *fmt, ...) int echo(char *file, int append, 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; va_list ap;
FILE *fp; FILE *fp;
int rc;
if (!value) { if (!file)
errno = EINVAL; fp = stdout;
return -1; else
} fp = fopen(file, append ? "a" : "w");
va_start(ap, fmt);
vsnprintf(path, sizeof(path), fmt, ap);
va_end(ap);
fp = fopen(path, "w");
if (!fp) if (!fp)
return -1; return -1;
/* echo(1) always adds a newline */ if (fmt) {
if (fputs(value, fp) == EOF || fputs("\n", fp) == EOF) va_start(ap, fmt);
rc = -1; vfprintf(fp, fmt, ap);
if (fclose(fp) == EOF) va_end(ap);
rc = -1; }
else
rc = 0;
return rc; /* echo(1) always adds a newline */
fprintf(fp, "\n");
if (file)
fclose(fp);
return 0;
} }
/* /*
@@ -224,29 +115,6 @@ int strtobytes(char *arg)
return bytes; return bytes;
} }
char *sig2str(int sig)
{
static char signame[8];
if (sig < 1 || sig >= (int)NELEMS(signames))
return "";
snprintf(signame, sizeof(signame), "/%s", signames[sig]);
return signame;
}
char *code2str(int code)
{
static char codename[20];
if (code < 0 || code >= (int)NELEMS(exitcodes))
return "";
snprintf(codename, sizeof(codename), "/%s", exitcodes[code]);
return codename;
}
void do_sleep(unsigned int sec) void do_sleep(unsigned int sec)
{ {
struct timespec requested_sleep = { struct timespec requested_sleep = {
@@ -308,32 +176,6 @@ char *uptime(long secs, char *buf, size_t len)
return buf; return buf;
} }
char *memsz(uint64_t sz, char *buf, size_t len)
{
int gb, mb, kb, b;
if (!sz) {
strlcpy(buf, "--.--", len);
return buf;
}
gb = sz / (1024 * 1024 * 1024);
sz = sz % (1024 * 1024 * 1024);
mb = sz / (1024 * 1024);
sz = sz % (1024 * 1024);
kb = sz / (1024);
b = sz % (1024);
if (gb)
snprintf(buf, len, "%d.%dG", gb, mb / 102);
else if (mb)
snprintf(buf, len, "%d.%dM", mb, kb / 102);
else
snprintf(buf, len, "%d.%dk", kb, b / 102);
return buf;
}
/* /*
* Verify string argument is NUL terminated * Verify string argument is NUL terminated
* Verify string is a JOB[:ID], JOB and ID * Verify string is a JOB[:ID], JOB and ID
@@ -345,7 +187,7 @@ char *sanitize(char *arg, size_t len)
regex_t preg; regex_t preg;
int rc; int rc;
if (!memchr(arg, 0, len)) if (strlen(arg) > len)
return NULL; return NULL;
if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED)) if (regcomp(&preg, regex, REG_ICASE | REG_EXTENDED))
@@ -359,122 +201,28 @@ char *sanitize(char *arg, size_t len)
return arg; return arg;
} }
#ifdef HAVE_TERMIOS_H
/* /*
* Called by initctl, and by finit at boot and shutdown, to * Called at boot, and shutdown, to (re)initialize the
* (re)initialize the screen size for print() et al. * screen size for print() et al.
*/ */
int ttinit(void) void screen_init(void)
{ {
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 }; if (!isatty(STDOUT_FILENO))
struct winsize ws = { 0 }; return;
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;
} }
/* /*
* This function sets the terminal to RAW mode, as defined for the current * Called when debug mode is enabled to revert back
* shell. This is called both by ttopen() above and by spawncli() to * to old-school safe defaults.
* get the current terminal settings and then change them to what
* mg expects. Thus, tty changes done while spawncli() is in effect
* will be reflected in mg.
*/ */
int ttraw(void) void screen_exit(void)
{ {
if (tcgetattr(0, &ttold) == -1) { screen_rows = 24;
fprintf(stderr, "%s: failed querying tty attrs: %s\n", screen_cols = 80;
prognm, strerror(errno));
return 1;
}
(void)memcpy(&ttnew, &ttold, sizeof(ttnew));
/* Set terminal to 'raw' mode and ignore a 'break' */
ttnew.c_cc[VMIN] = 1;
ttnew.c_cc[VTIME] = 0;
ttnew.c_iflag |= IGNBRK;
ttnew.c_iflag &= ~(BRKINT | PARMRK | INLCR | IGNCR | ICRNL | IXON);
ttnew.c_oflag &= ~OPOST;
ttnew.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
if (tcsetattr(0, TCSASOFT | TCSADRAIN, &ttnew) == -1) {
fprintf(stderr, "%s: failed setting tty in raw mode: %s\n",
prognm, strerror(errno));
return 1;
}
return 0;
} }
/*
* This function restores all terminal settings to their default values,
* in anticipation of exiting or suspending the editor.
*/
int ttcooked(void)
{
if (tcsetattr(0, TCSASOFT | TCSADRAIN, &ttold) == -1) {
fprintf(stderr, "%s: failed restoring tty to cooked mode: %s\n",
prognm, strerror(errno));
return 1;
}
return 0;
}
#endif /* HAVE_TERMIOS_H */
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+9 -36
View File
@@ -25,10 +25,9 @@
#define FINIT_UTIL_H_ #define FINIT_UTIL_H_
#include "config.h" #include "config.h"
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H /* for screen_width() */
#include <poll.h> #include <poll.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h>
#include <sys/reboot.h> #include <sys/reboot.h>
#include <termios.h> #include <termios.h>
#endif #endif
@@ -38,53 +37,27 @@
#define RB_SW_SUSPEND 0xd000fce2 #define RB_SW_SUSPEND 0xd000fce2
#endif #endif
extern int debug; /* defined in finit or initctl */ extern int screen_rows;
extern int ttrows; extern int screen_cols;
extern int ttcols;
extern char *prognm; extern char *prognm;
/* Define for printheader() from conio.h, used in initctl */ #define SCREEN_WIDTH screen_cols
#define SCREEN_WIDTH ttcols
#include <lite/conio.h> #include <lite/conio.h>
char *progname (char *arg0); char *progname (char *arg0);
int echo (char *file, int append, char *fmt, ...);
char *str (char *fmt, ...);
int fnwrite (char *value, char *fmt, ...);
int has_utmp (void); int has_utmp (void);
int strtobytes (char *arg); int strtobytes (char *arg);
char *sig2str (int sig);
char *code2str (int code);
void do_sleep (unsigned int sec); void do_sleep (unsigned int sec);
long jiffies (void); long jiffies (void);
char *uptime (long secs, char *buf, size_t len); char *uptime (long secs, char *buf, size_t len);
char *memsz (uint64_t sz, char *buf, size_t len);
char *sanitize (char *arg, size_t len); char *sanitize (char *arg, size_t len);
#ifdef HAVE_TERMIOS_H void screen_init (void);
int ttinit (void); void screen_exit (void);
int ttraw (void); int screen_width (void);
int ttcooked (void);
#else
#define ttinit() ttcols
#define ttraw() 0
#define ttcooked() 0
#endif
static inline void dbg(char *fmt, ...)
{
va_list ap;
if (!debug)
return;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
static inline char *strterm(char *str, size_t len) static inline char *strterm(char *str, size_t len)
{ {
+61 -5
View File
@@ -35,9 +35,6 @@
#define MAX_NO 5 #define MAX_NO 5
#define MAX_SZ 100 * 1024 #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) static void utmp_strncpy(char *dst, const char *src, size_t dlen)
{ {
size_t i; size_t i;
@@ -49,6 +46,65 @@ static void utmp_strncpy(char *dst, const char *src, size_t dlen)
dst[i] = 0; 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 * Rotate /var/log/wtmp (+ btmp?) and /run/utmp
* *
@@ -138,13 +194,13 @@ static int set_getty(int type, char *tty, char *id, char *user)
return 0; return 0;
if (!tty) if (!tty)
line = NULL; line = "";
else if (!strncmp(tty, "/dev/", 5)) else if (!strncmp(tty, "/dev/", 5))
line = tty + 5; line = tty + 5;
else else
line = tty; line = tty;
if (!id && line) { if (!id) {
id = line; id = line;
if (!strncmp(id, "pts/", 4)) if (!strncmp(id, "pts/", 4))
id += 4; id += 4;
+14 -18
View File
@@ -23,8 +23,6 @@
#include <config.h> #include <config.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
@@ -60,7 +58,7 @@ static int init(char *progname, char *devnode)
signal(SIGPWR, sighandler); signal(SIGPWR, sighandler);
openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON); openlog(&progname[1], LOG_CONS | LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Finit v%s watchdog %s starting ...", VERSION, devnode); syslog(LOG_INFO, "Finit v%s basic watchdogd starting ...", VERSION);
fd = open(devnode, O_WRONLY); fd = open(devnode, O_WRONLY);
if (fd == -1) if (fd == -1)
@@ -91,38 +89,36 @@ static int loop(int fd, int timeout)
if (handover) { if (handover) {
syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE); syslog(LOG_INFO, "Handing over %s and exiting ...", WDT_DEVNODE);
ioctl(fd, WDIOC_KEEPALIVE, &dummy); ioctl(fd, WDIOC_KEEPALIVE, &dummy);
if (write(fd, "V", 1) == -1) return !write(fd, "V", 1);
return EX_IOERR; }
} else
syslog(LOG_ALERT, "System going down ...");
return EX_OK; return 0;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int fd, rc = EX_OK; int fd, ret;
fd = init(argv[0], WDT_DEVNODE); fd = init(argv[0], WDT_DEVNODE);
if (fd == -1) { if (fd == -1) {
syslog(LOG_CRIT, "Failed connecting to %s: %s", WDT_DEVNODE, strerror(errno)); if (ENOENT != errno)
rc = EX_OSFILE; syslog(LOG_CRIT, "Failed connecting to watchdog %s", WDT_DEVNODE);
goto done; return 1;
} }
rc = loop(fd, WDT_TIMEOUT); ret = loop(fd, WDT_TIMEOUT);
while (!handover) { while (!handover) {
/* Waiting for SIGTERM or WDT reset ... */ syslog(LOG_ALERT, "System going down ...");
/* Waiting for SIGTERM ... */
sleep(1); sleep(1);
/* Set lowest possible timeout on SIGPWR */ /* Set lowest possible timeout on SIGTERM */
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown); ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
} }
close(fd); close(fd);
done:
closelog();
return rc; return ret;
} }
/** /**
+6 -2
View File
@@ -23,11 +23,15 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#ifndef WDT_DEVNODE
#define WDT_DEVNODE "/dev/watchdog" #define WDT_DEVNODE "/dev/watchdog"
#endif
#define WDT_TIMEOUT 30 #define WDT_TIMEOUT 30
#ifdef BUILTIN_WATCHDOG
int watchdog(char *progname);
#else
#define watchdog(progname) 0
#endif /* BUILTIN_WATCHDOG */
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+4 -3
View File
@@ -3,6 +3,7 @@ EXTRA_DIST += tenv/root.mk tenv/busybox-x86_64.md5
EXTRA_DIST += tenv/chrootsetup.sh EXTRA_DIST += tenv/chrootsetup.sh
EXTRA_DIST += setup-root.sh EXTRA_DIST += setup-root.sh
EXTRA_DIST += common/service.conf common/service.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.sh
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
EXTRA_DIST += start-stop-service.sh EXTRA_DIST += start-stop-service.sh
@@ -15,9 +16,9 @@ AM_TESTS_ENVIRONMENT += top_builddir=$(top_builddir);
AM_TESTS_ENVIRONMENT += export top_builddir; AM_TESTS_ENVIRONMENT += export top_builddir;
checkself.sh: checkself.sh:
@echo '#!/bin/sh' > $@ echo '#!/bin/sh' > $@
@echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(EXTRA_DIST)))) >> $@ echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(EXTRA_DIST)))) >> $@
@chmod +x $@ chmod +x $@
TEST_EXTENSIONS = .sh TEST_EXTENSIONS = .sh
TESTS = checkself.sh TESTS = checkself.sh
+1 -1
View File
@@ -44,6 +44,6 @@ fi
mkdir -p "$FINIT_RCSD" mkdir -p "$FINIT_RCSD"
tty=/dev/$(cat /sys/class/tty/console/active) tty=/dev/$(cat /sys/class/tty/console/active)
mkfifo "$tty" ln -fs /shared/pts "$tty"
exec "$@" exec "$@"
-4
View File
@@ -138,7 +138,3 @@ echo "$finit_ppid" > "$TENV_ROOT"/running_test.pid
log "$color_reset" 'Setup of test environment done' '' log "$color_reset" 'Setup of test environment done' ''
finit_pid=$(retry "pgrep -P $finit_ppid") finit_pid=$(retry "pgrep -P $finit_ppid")
tty=/dev/$(texec cat /sys/class/tty/console/active)
texec cat "$tty" &
sleep 1
+2
View File
@@ -38,6 +38,7 @@ binaries = $(DEST)/bin/cat \
$(DEST)/bin/env \ $(DEST)/bin/env \
$(DEST)/bin/find \ $(DEST)/bin/find \
$(DEST)/bin/kill \ $(DEST)/bin/kill \
$(DEST)/bin/ln \
$(DEST)/bin/ls \ $(DEST)/bin/ls \
$(DEST)/bin/mkdir \ $(DEST)/bin/mkdir \
$(DEST)/bin/mkfifo \ $(DEST)/bin/mkfifo \
@@ -57,6 +58,7 @@ dirs = $(DEST)/bin \
$(DEST)/etc \ $(DEST)/etc \
$(DEST)/proc \ $(DEST)/proc \
$(DEST)/sbin \ $(DEST)/sbin \
$(DEST)/shared \
$(DEST)/sys \ $(DEST)/sys \
$(DEST)/test_assets \ $(DEST)/test_assets \
$(DEST)/tmp $(DEST)/tmp
+4 -1
View File
@@ -45,4 +45,7 @@ exec "$unshare" \
--mount \ --mount \
--mount-proc \ --mount-proc \
--uts --ipc --net \ --uts --ipc --net \
"$chroot" "$TENV_ROOT" /bin/chrootsetup.sh "$@" sh -c "
ln -fs /proc/1/fd/1 $TENV_ROOT/shared/pts;
exec $chroot $TENV_ROOT /bin/chrootsetup.sh $*
"