mirror of
https://github.com/troglobit/finit.git
synced 2026-10-13 10:14:47 +00:00
@@ -34,8 +34,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config tree jq libcap-dev
|
||||
sudo apt-get -y install pkg-config
|
||||
sudo apt-get -y install pkg-config tree jq libcap-dev libconfuse-dev
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
|
||||
tar xf libuev-2.4.1.tar.xz
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config libcap-dev
|
||||
sudo apt-get -y install pkg-config libcap-dev libconfuse-dev
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
|
||||
tar xf libuev-2.4.1.tar.xz
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
- name: Installing dependencies ...
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config jq libcap-dev
|
||||
sudo apt-get -y install pkg-config jq libcap-dev libconfuse-dev
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
|
||||
tar xf libuev-2.4.1.tar.xz
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config jq libcap-dev
|
||||
sudo apt-get -y install pkg-config jq libcap-dev libconfuse-dev
|
||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
||||
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
|
||||
tar xf libuev-2.4.1.tar.xz
|
||||
|
||||
+7
-1
@@ -1,4 +1,4 @@
|
||||
AC_INIT([Finit], [4.17], [https://github.com/finit-project/finit/issues],
|
||||
AC_INIT([Finit], [5.0-dev], [https://github.com/finit-project/finit/issues],
|
||||
[finit], [https://http://finit-project.github.io/])
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
|
||||
@@ -50,6 +50,12 @@ PKG_PROG_PKG_CONFIG
|
||||
# Check for required libraries
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 2.4.1])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 2.6.1])
|
||||
# 3.3 is the floor: CFGF_KEYSTRVAL, which environment {} and the
|
||||
# free-form cgroup keys are built on, does not exist before it. 3.3
|
||||
# parses both correctly. Two workarounds hang off this number, the
|
||||
# spurious KEYSTRVAL warning and cfg_parse_buf() losing the file name
|
||||
# of a template; grep the XXX notes in src/conf.c before raising it.
|
||||
PKG_CHECK_MODULES([confuse], [libconfuse >= 3.3])
|
||||
|
||||
# Check for configured Finit features
|
||||
AC_ARG_ENABLE(auto_reload,
|
||||
|
||||
@@ -3,24 +3,34 @@
|
||||
|
||||
## Runlevel to start after bootstrap, runlevel 'S'
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
#runlevel = 2
|
||||
|
||||
# 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
|
||||
#cgroup init { cpu.weight = 100 }
|
||||
#cgroup user { cpu.weight = 100 }
|
||||
#cgroup system { cpu.weight = 9700 }
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
#task reboot {
|
||||
# description = "Rebooting system"
|
||||
# runlevel = "12345789"
|
||||
# conditions = { "sys/key/ctrlaltdel" }
|
||||
# command = "initctl reboot"
|
||||
#}
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
#task poweroff {
|
||||
# description = "Shutting down system"
|
||||
# runlevel = "12345789"
|
||||
# conditions = { "sys/pwr/fail" }
|
||||
# command = "initctl poweroff"
|
||||
#}
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
||||
# Depends on syslogd having started. Redirect any output to log.
|
||||
service [2345] <pid/syslogd> log acpid -f -- ACPI daemon
|
||||
service acpid {
|
||||
description = "ACPI daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
log { }
|
||||
command = "acpid -f"
|
||||
}
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
service [2345] <pid/syslogd> env:-/etc/conf.d/crond crond -f -S $CRON_OPTS -- Cron daemon
|
||||
service crond {
|
||||
description = "Cron daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
envfile = "-/etc/conf.d/crond"
|
||||
command = "crond -f -S $CRON_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
# Start SSH daemon as soon as basic networking comes up
|
||||
service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
|
||||
service dropbear {
|
||||
description = "Dropbear SSH daemon"
|
||||
runlevel = "2345"
|
||||
envfile = "-/etc/conf.d/dropbear"
|
||||
command = "dropbear -R -F $DROPBEAR_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# Consoles to start getty on when system is up
|
||||
tty [12345] /dev/ttyS0 noclear
|
||||
tty [12345] /dev/tty1 noclear nowait
|
||||
tty [2345] /dev/tty2 noclear nowait
|
||||
tty [2345] /dev/tty3 noclear nowait
|
||||
tty ttyS0 {
|
||||
runlevel = "12345"
|
||||
device = "/dev/ttyS0"
|
||||
noclear = true
|
||||
}
|
||||
tty tty1 {
|
||||
runlevel = "12345"
|
||||
device = "/dev/tty1"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
tty tty2 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty2"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
tty tty3 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty3"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
# Oneshot task to run once at bootstrap
|
||||
task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
|
||||
task keymap {
|
||||
description = "Loading keymap"
|
||||
runlevel = "S"
|
||||
envfile = "/etc/conf.d/loadkmap"
|
||||
command = "zcat $KEYMAP | loadkmap"
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
service [2345] lxdm -- Lightweight Login Manager
|
||||
service lxdm {
|
||||
description = "Lightweight Login Manager"
|
||||
runlevel = "2345"
|
||||
command = "lxdm"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
## Linux kernel modules to load
|
||||
#module fbcon
|
||||
#module button
|
||||
#module evdev
|
||||
#module mousedev
|
||||
#module 8139cp
|
||||
#module softdog
|
||||
#modules = { "fbcon", "button", "evdev", "mousedev", "8139cp", "softdog" }
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
# Busybox ntpd has no option to use syslog when running in foreground
|
||||
service [2345] <pid/syslogd> env:-/etc/conf.d/ntpd log ntpd -n $NTPD_OPTS -- NTP daemon
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
envfile = "-/etc/conf.d/ntpd"
|
||||
log { }
|
||||
command = "ntpd -n $NTPD_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
# 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] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||
service syslogd {
|
||||
description = "Syslog daemon"
|
||||
runlevel = "S12345"
|
||||
envfile = "-/etc/conf.d/syslog"
|
||||
command = "syslogd -n $SYSLOGD_OPTS"
|
||||
}
|
||||
service klogd {
|
||||
description = "Kernel log daemon"
|
||||
runlevel = "S12345"
|
||||
conditions = { "pid/syslogd" }
|
||||
envfile = "-/etc/conf.d/klogd"
|
||||
command = "klogd -n $KLOGD_OPTS"
|
||||
}
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
service [S123456789] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog
|
||||
service watchdog {
|
||||
description = "System watchdog"
|
||||
runlevel = "S123456789"
|
||||
envfile = "-/etc/conf.d/watchdog"
|
||||
command = "watchdog $WATCHDOG_OPTS $WATCHDOG_DEV"
|
||||
}
|
||||
|
||||
@@ -3,24 +3,34 @@
|
||||
|
||||
## Runlevel to start after bootstrap, runlevel 'S'
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
#runlevel = 2
|
||||
|
||||
# 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
|
||||
#cgroup init { cpu.weight = 100 }
|
||||
#cgroup user { cpu.weight = 100 }
|
||||
#cgroup system { cpu.weight = 9700 }
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
#task reboot {
|
||||
# description = "Rebooting system"
|
||||
# runlevel = "12345789"
|
||||
# conditions = { "sys/key/ctrlaltdel" }
|
||||
# command = "initctl reboot"
|
||||
#}
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
#task poweroff {
|
||||
# description = "Shutting down system"
|
||||
# runlevel = "12345789"
|
||||
# conditions = { "sys/pwr/fail" }
|
||||
# command = "initctl poweroff"
|
||||
#}
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
||||
# Depends on a syslog daemon having started. Redirect any output to log.
|
||||
service [2345] <pid/syslogd> env:-/etc/default/acpid log acpid -f $OPTIONS -- ACPI daemon
|
||||
service acpid {
|
||||
description = "ACPI daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
envfile = "-/etc/default/acpid"
|
||||
log { }
|
||||
command = "acpid -f $OPTIONS"
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
|
||||
task anacron {
|
||||
description = "Anacron jobs"
|
||||
runlevel = "2345"
|
||||
conditions = { "sys/pwr/ac", "pid/syslogd" }
|
||||
envfile = "-/etc/default/anacron"
|
||||
stop-signal = "usr1"
|
||||
command = "anacron -d -q $ANACRON_ARGS"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
|
||||
sysv apparmor {
|
||||
description = "AppArmor initialization"
|
||||
runlevel = "S"
|
||||
command = "/etc/init.d/apparmor"
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
|
||||
service avahi-daemon {
|
||||
description = "Avahi mDNS/DNS-SD Stack"
|
||||
runlevel = "2345"
|
||||
envfile = "-/etc/default/avahi-daemon"
|
||||
command = "avahi-daemon -s $AVAHI_DAEMON_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# To ignore errors like "org.freedesktop.hostname1", update the env file with
|
||||
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
|
||||
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
|
||||
service bluetoothd {
|
||||
description = "Bluetooth service"
|
||||
runlevel = "2345"
|
||||
envfile = "-/etc/default/bluetooth"
|
||||
log { file = "/dev/null" }
|
||||
command = "/usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
# Oneshot task to run once at bootstrap
|
||||
task [S] /lib/console-setup/console-setup.sh -- Setting up console font and keymap
|
||||
task console-setup {
|
||||
description = "Setting up console font and keymap"
|
||||
runlevel = "S"
|
||||
command = "/lib/console-setup/console-setup.sh"
|
||||
}
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
|
||||
service cron {
|
||||
description = "Background job processing daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "sys/pwr/ac", "pid/syslogd" }
|
||||
envfile = "-/etc/default/cron"
|
||||
command = "cron -f $EXTRA_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
# On some systems, bootstrap can be really quick and cause elogind
|
||||
# to not start before a user has managed to log in. To prevent
|
||||
# this, add <pid/elogind> as condition to your TTYs in getty.conf
|
||||
service [2345] <pid/syslogd> /usr/libexec/elogind -- Login manager
|
||||
service elogind {
|
||||
description = "Login manager"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
command = "/usr/libexec/elogind"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,38 @@
|
||||
# Consoles to start getty on when system is up
|
||||
tty [12345] /dev/tty1 linux noclear nowait
|
||||
tty [2345] /dev/tty2 linux nowait
|
||||
tty [2345] /dev/tty3 linux nowait
|
||||
tty [2345] /dev/tty4 linux nowait
|
||||
tty [2345] /dev/tty5 linux nowait
|
||||
tty [2345] /dev/tty6 linux nowait
|
||||
tty tty1 {
|
||||
runlevel = "12345"
|
||||
device = "/dev/tty1"
|
||||
term = "linux"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
tty tty2 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty2"
|
||||
term = "linux"
|
||||
nowait = true
|
||||
}
|
||||
tty tty3 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty3"
|
||||
term = "linux"
|
||||
nowait = true
|
||||
}
|
||||
tty tty4 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty4"
|
||||
term = "linux"
|
||||
nowait = true
|
||||
}
|
||||
tty tty5 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty5"
|
||||
term = "linux"
|
||||
nowait = true
|
||||
}
|
||||
tty tty6 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty6"
|
||||
term = "linux"
|
||||
nowait = true
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
# Oneshot task to run once at bootstrap
|
||||
task [S] /lib/console-setup/keyboard-setup.sh -- Setting up console keyboard layout
|
||||
task keyboard-setup {
|
||||
description = "Setting up console keyboard layout"
|
||||
runlevel = "S"
|
||||
command = "/lib/console-setup/keyboard-setup.sh"
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
service [3] lightdm -- Light Display Manager
|
||||
service lightdm {
|
||||
description = "Light Display Manager"
|
||||
runlevel = "3"
|
||||
command = "lightdm"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
## Linux kernel modules to load
|
||||
#module fbcon
|
||||
#module button
|
||||
#module evdev
|
||||
#module loop
|
||||
#module psmouse
|
||||
#module mousedev
|
||||
#module 8139cp
|
||||
#modules = { "fbcon", "button", "evdev", "loop", "psmouse", "mousedev", "8139cp" }
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
# Need to stop plymouth, or it'll block stdin on tty1
|
||||
task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
|
||||
task plymouth {
|
||||
description = "Stopping Plymouth Boot Screen"
|
||||
runlevel = "S"
|
||||
command = "/usr/bin/plymouth quit"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Start rsyslogd as soon as possible, should always run
|
||||
# Provides pid/syslogd condition
|
||||
service [S12345] name:syslogd env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_OPTIONS -- Reliable syslog daemon
|
||||
service syslogd {
|
||||
description = "Reliable syslog daemon"
|
||||
runlevel = "S12345"
|
||||
envfile = "-/etc/default/rsyslog"
|
||||
command = "rsyslogd -n $RSYSLOGD_OPTIONS"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
# Simple NTP client (SNTP)
|
||||
service [2345] <net/route/default> env:-/etc/default/sntpd sntpd -n $SNTPD_OPTIONS -- SNTP client
|
||||
service sntpd {
|
||||
description = "SNTP client"
|
||||
runlevel = "2345"
|
||||
conditions = { "net/route/default" }
|
||||
envfile = "-/etc/default/sntpd"
|
||||
command = "sntpd -n $SNTPD_OPTIONS"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
# 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 sshd {
|
||||
description = "OpenSSH daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "net/lo/up" }
|
||||
envfile = "-/etc/default/ssh"
|
||||
command = "/usr/sbin/sshd -D $SSHD_OPTS"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
# Start sysklogd as soon as possible, should always run if enabled
|
||||
service [S123456789] env:-/etc/default/syslogd syslogd -F $SYSLOGD_OPTIONS -- System logging daemon
|
||||
service syslogd {
|
||||
description = "System logging daemon"
|
||||
runlevel = "S123456789"
|
||||
envfile = "-/etc/default/syslogd"
|
||||
command = "syslogd -F $SYSLOGD_OPTIONS"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# Here we use XDM since it has few dependencies (relatively speaking).
|
||||
# It is also an example of a service without a -- description.
|
||||
# service [3] lxdm -- Lightweight Login Manager
|
||||
service [3] xdm -nodaemon -- X Display Manager
|
||||
service xdm {
|
||||
description = "X Display Manager"
|
||||
runlevel = "3"
|
||||
command = "xdm -nodaemon"
|
||||
}
|
||||
|
||||
+107
-43
@@ -1,85 +1,149 @@
|
||||
# Sample finit.conf to demonstrate the syntax
|
||||
#
|
||||
host testbed
|
||||
hostname = "testbed"
|
||||
|
||||
# Linux kernel modules to load
|
||||
module button
|
||||
module evdev
|
||||
module loop
|
||||
module psmouse
|
||||
modules = { "button", "evdev", "loop", "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
|
||||
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
|
||||
cgroup maint { cpu.weight = 100 }
|
||||
|
||||
# Runlevel to start after bootstrap, runlevel 'S'
|
||||
runlevel 2
|
||||
runlevel = 2
|
||||
|
||||
# Network bringup script
|
||||
network service networking start
|
||||
network = "service networking start"
|
||||
|
||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||
log size:100k count:4
|
||||
log {
|
||||
size = 100k
|
||||
count = 4
|
||||
}
|
||||
|
||||
# Virtual consoles to start getty on
|
||||
tty /dev/tty1
|
||||
tty /dev/tty2
|
||||
tty /dev/tty3
|
||||
tty tty1 { device = "/dev/tty1" }
|
||||
tty tty2 { device = "/dev/tty2" }
|
||||
tty tty3 { device = "/dev/tty3" }
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
task reboot {
|
||||
description = "Rebooting system"
|
||||
runlevel = "12345789"
|
||||
conditions = { "sys/key/ctrlaltdel" }
|
||||
command = "initctl reboot"
|
||||
}
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
task poweroff {
|
||||
description = "Shutting down system"
|
||||
runlevel = "12345789"
|
||||
conditions = { "sys/pwr/fail" }
|
||||
command = "initctl poweroff"
|
||||
}
|
||||
|
||||
# Alternative method instead of runparts
|
||||
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd start -- Preparing console
|
||||
#run [2] /etc/init.d/networking start -- Start networking
|
||||
#task keyboard-setup {
|
||||
# description = "Setting up preliminary keymap"
|
||||
# runlevel = "S"
|
||||
# command = "/etc/init.d/keyboard-setup start"
|
||||
#}
|
||||
#task acpid {
|
||||
# description = "Starting ACPI Daemon"
|
||||
# runlevel = "S"
|
||||
# command = "/etc/init.d/acpid start"
|
||||
#}
|
||||
#task kbd {
|
||||
# description = "Preparing console"
|
||||
# runlevel = "S"
|
||||
# command = "/etc/init.d/kbd start"
|
||||
#}
|
||||
#run networking {
|
||||
# description = "Start networking"
|
||||
# runlevel = "2"
|
||||
# command = "/etc/init.d/networking start"
|
||||
#}
|
||||
|
||||
# Services to be monitored and respawned as needed
|
||||
|
||||
# klgod and syslogd are placed in the maint cgroup
|
||||
cgroup.maint
|
||||
service [2345] <pid/syslogd> klogd -n -- Kernel logging server
|
||||
service [2345] syslogd -n -- Syslog server
|
||||
# klogd and syslogd are placed in the maint cgroup. There is no
|
||||
# "current cgroup" to switch in this format, every block names its own.
|
||||
service klogd {
|
||||
description = "Kernel logging server"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
cgroup maint {}
|
||||
command = "klogd -n"
|
||||
}
|
||||
service syslogd {
|
||||
description = "Syslog server"
|
||||
runlevel = "2345"
|
||||
cgroup maint {}
|
||||
command = "syslogd -n"
|
||||
}
|
||||
|
||||
# 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
|
||||
# gdm gets its own limits inside the system group, which is where
|
||||
# services end up when no cgroup block is given.
|
||||
service gdm {
|
||||
description = "GNOME Display Manager"
|
||||
runlevel = "3"
|
||||
cgroup system {
|
||||
cpu.weight = 250
|
||||
memory.max = 10M
|
||||
}
|
||||
command = "gdm"
|
||||
}
|
||||
|
||||
# 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
|
||||
# to user cgroup with cpu.weight 250. Default weight in the user group
|
||||
# is 100 (above).
|
||||
service sshd {
|
||||
description = "OpenSSH daemon"
|
||||
runlevel = "2345"
|
||||
envfile = "-/etc/default/ssh"
|
||||
cgroup user {
|
||||
cpu.weight = 250
|
||||
memory.max = 10M
|
||||
}
|
||||
command = "/usr/sbin/sshd -D $SSHD_OPTS"
|
||||
}
|
||||
|
||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||
# log file is rotated with the above settings. The condition declares
|
||||
# a dependency on a system default route (gateway) to be set. A single
|
||||
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||
service [2345] <!net/route/default> log:/var/log/ntpd.log ntpd -n -l -I eth0 -- NTP daemon
|
||||
# a dependency on a system default route (gateway) to be set. ntpd
|
||||
# does not respect SIGHUP, so Finit restarts it on reload instead.
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "net/route/default" }
|
||||
reload-signal = "none"
|
||||
log { file = "/var/log/ntpd.log" }
|
||||
command = "ntpd -n -l -I eth0"
|
||||
}
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
#runparts = "/etc/start.d"
|
||||
|
||||
# For multiple instances of the same service, add :ID somewhere between
|
||||
# the service/run/task keyword and the command.
|
||||
service :80 [2345] /sbin/httpd -f -h /http -p 80 -- Web server
|
||||
service :8080 [2345] /sbin/httpd -f -h /http -p 8080 -- Old web server
|
||||
# For multiple instances of the same service, add :ID to the block title.
|
||||
service httpd:80 {
|
||||
description = "Web server"
|
||||
runlevel = "2345"
|
||||
command = "/sbin/httpd -f -h /http -p 80"
|
||||
}
|
||||
service httpd:8080 {
|
||||
description = "Old web server"
|
||||
runlevel = "2345"
|
||||
command = "/sbin/httpd -f -h /http -p 8080"
|
||||
}
|
||||
|
||||
+16
-6
@@ -3,24 +3,34 @@
|
||||
|
||||
## Runlevel to start after bootstrap, runlevel 'S'
|
||||
# Default is 2
|
||||
#runlevel 2
|
||||
#runlevel = 2
|
||||
|
||||
# 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
|
||||
#cgroup init { cpu.weight = 100 }
|
||||
#cgroup user { cpu.weight = 100 }
|
||||
#cgroup system { cpu.weight = 9700 }
|
||||
|
||||
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
|
||||
# Finit upon receiving SIGINT (from the kernel).
|
||||
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
|
||||
#task reboot {
|
||||
# description = "Rebooting system"
|
||||
# runlevel = "12345789"
|
||||
# conditions = { "sys/key/ctrlaltdel" }
|
||||
# command = "initctl reboot"
|
||||
#}
|
||||
|
||||
# Task to run if the kernel gets a power fail condition is pressed.
|
||||
# Asserted by Finit upon receiving SIGPWR (from the kernel).
|
||||
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
|
||||
#task poweroff {
|
||||
# description = "Shutting down system"
|
||||
# runlevel = "12345789"
|
||||
# conditions = { "sys/pwr/fail" }
|
||||
# command = "initctl poweroff"
|
||||
#}
|
||||
|
||||
## Bootstrap services and tasks
|
||||
# Can be listed here or in /etc/finit.d/*.conf
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
||||
# Depends on syslogd having started. Redirect any output to log.
|
||||
service [2345] acpid -f -- ACPI daemon
|
||||
service acpid {
|
||||
description = "ACPI daemon"
|
||||
runlevel = "2345"
|
||||
command = "acpid -f"
|
||||
}
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
service [2345] <pid/syslogd> crond -f -S -c /etc/crontabs -- Cron daemon
|
||||
service crond {
|
||||
description = "Cron daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
command = "crond -f -S -c /etc/crontabs"
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
service [2345] dhcpcd -B -M -- DHCP client
|
||||
service dhcpcd {
|
||||
description = "DHCP client"
|
||||
runlevel = "2345"
|
||||
command = "dhcpcd -B -M"
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
service [S12345] dmeventd -f -- Device mapper event daemon
|
||||
service dmeventd {
|
||||
description = "Device mapper event daemon"
|
||||
runlevel = "S12345"
|
||||
command = "dmeventd -f"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
# Consoles to start Finit built-in getty on when system is up
|
||||
tty [12345] /dev/tty1 noclear nowait
|
||||
tty [2345] /dev/tty2 noclear nowait
|
||||
tty [2345] /dev/tty3 noclear nowait
|
||||
tty tty1 {
|
||||
runlevel = "12345"
|
||||
device = "/dev/tty1"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
tty tty2 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty2"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
tty tty3 {
|
||||
runlevel = "2345"
|
||||
device = "/dev/tty3"
|
||||
noclear = true
|
||||
nowait = true
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
# Oneshot task to run once at bootstrap
|
||||
task [S] zcat /etc/keymap/se.bmap.gz | loadkmap -- Loading keymap
|
||||
task keymap {
|
||||
description = "Loading keymap"
|
||||
runlevel = "S"
|
||||
command = "zcat /etc/keymap/se.bmap.gz | loadkmap"
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
service [2345] lxdm -- Lightweight Login Manager
|
||||
service lxdm {
|
||||
description = "Lightweight Login Manager"
|
||||
runlevel = "2345"
|
||||
command = "lxdm"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
## Linux kernel modules to load
|
||||
#module fbcon
|
||||
#module button
|
||||
#module evdev
|
||||
#module mousedev
|
||||
#module 8139cp
|
||||
module softdog
|
||||
# Other candidates: fbcon, button, evdev, mousedev, 8139cp
|
||||
modules = { "softdog" }
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
# Silly Busybox ntpd has no option to use syslog when running in foreground
|
||||
service [2345] <pid/syslogd> log ntpd -n -N -p pool.ntp.org -- NTP daemon
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
log { }
|
||||
command = "ntpd -n -N -p pool.ntp.org"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
# Start SSH daemon as soon as basic networking comes up
|
||||
service [2345] /usr/sbin/sshd -D -- OpenSSH daemon
|
||||
service sshd {
|
||||
description = "OpenSSH daemon"
|
||||
runlevel = "2345"
|
||||
command = "/usr/sbin/sshd -D"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
# 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
|
||||
service syslogd {
|
||||
description = "Syslog daemon"
|
||||
runlevel = "S12345"
|
||||
command = "syslogd -n"
|
||||
}
|
||||
service klogd {
|
||||
description = "Kernel log daemon"
|
||||
runlevel = "S12345"
|
||||
conditions = { "pid/syslogd" }
|
||||
command = "klogd -n"
|
||||
}
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
service @uuidd:uuidd [2345] uuidd -F -- UUID generation daemon
|
||||
service uuidd {
|
||||
description = "UUID generation daemon"
|
||||
runlevel = "2345"
|
||||
user = "uuidd"
|
||||
group = "uuidd"
|
||||
command = "uuidd -F"
|
||||
}
|
||||
|
||||
+5
-1
@@ -171,7 +171,11 @@ Notice the `--` separator.
|
||||
To debug startup issues, in particular issues with getty/login, add
|
||||
the following to your Finit .conf file:
|
||||
|
||||
tty [12345789] notty noclear
|
||||
tty board {
|
||||
runlevel = "12345789"
|
||||
notty = true
|
||||
noclear = true
|
||||
}
|
||||
|
||||
The `notty` option ensures reusing the stdin/stdout set up by the
|
||||
kernel. Remember, this is only for debugging and would leave your
|
||||
|
||||
+4
-3
@@ -41,7 +41,7 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
|
||||
Useful when starting up in various [rescue mode][rescue], factory, or
|
||||
production test setups. Use the top-level configuration file
|
||||
directive `rcsd /path/to/finit.d` to override the default rcS.d
|
||||
setting `rcsd = "/path/to/finit.d"` to override the default rcS.d
|
||||
directory.
|
||||
|
||||
* `finit.debug[=bool]`: Enable finit debug. This is operated
|
||||
@@ -104,8 +104,9 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
|
||||
* `single`, or `S`: Overrides the configured runlevel (default: 2) to go
|
||||
to after bootstrap by forcing it to runlevel 1, this is also known
|
||||
as single user mode. Useful to debug startup problems. All services
|
||||
and TTYs in `[1]` will be started, so a `tty [1] @console nologin`
|
||||
configuration presents you with a root console without login.
|
||||
and TTYs in runlevel 1 will be started, so a `tty` block with
|
||||
`runlevel = "1"`, `device = "@console"`, and `nologin = true`
|
||||
presents you with a root console without login.
|
||||
|
||||
* `1-9`, except `6`: override the configured `runlevel`. Like the `S`
|
||||
and `rescue`, giving a single number on the kernel command line tells
|
||||
|
||||
+79
-37
@@ -7,19 +7,27 @@ mechanism for common synchronization problems. For example:
|
||||
- *"wait for service A to start before starting service B"*, or
|
||||
- *"wait for basic network access to be available"*
|
||||
|
||||
Conditions are similar in syntax to declaring runlevels per service.
|
||||
They are specified within angle brackets `<>` and can be applied to any
|
||||
of the `service`, `task`, or `run` stanza. Multiple conditions may be
|
||||
specified separated by comma. Multiple conditions are logically AND'ed
|
||||
A condition is named in the `conditions` list of a `service`, `task`, or
|
||||
`run` block. The list may hold several, and they are logically AND'ed
|
||||
during evaluation, i.e. all conditions must be satisfied in order for a
|
||||
service to run.
|
||||
service to run. In running text, and in `initctl` output, a condition
|
||||
is written inside angle brackets, `<pid/syslogd>`.
|
||||
|
||||
Two special prefixes can be used inside the angle brackets:
|
||||
One prefix can be used on a condition:
|
||||
|
||||
- `!` -- service does not support `SIGHUP` (noreload), or run/task
|
||||
should not block runlevel changes (i.e., bootstrap)
|
||||
- `~` -- propagate reload from this dependency, see below
|
||||
|
||||
The line-based format also accepts a leading `!` on the list, which is
|
||||
not a condition and not a negation. It is a flag on the block, and it
|
||||
means two unrelated things depending on where it sits: a service does
|
||||
not support `SIGHUP` (noreload), or a run/task should not block
|
||||
runlevel changes, i.e. bootstrap. Each of those is its own key here:
|
||||
|
||||
| Line-based | Block format |
|
||||
|---|---|
|
||||
| `<!...>` on a service or sysv | `reload-signal = "none"` |
|
||||
| `<!...>` on a run or task | `required = false` |
|
||||
|
||||
Finit guarantees by default that all run/tasks run (at least) once
|
||||
per runlevel. For most tasks this is a good default, for example
|
||||
checking SSH host keys or loading keymap. However, for conditions
|
||||
@@ -27,7 +35,12 @@ that are unlikely to happen it is not. (See example below.)
|
||||
|
||||
### Example
|
||||
|
||||
service [2345] <pid/setupd,pid/zebra> /sbin/netd -- Network monitor
|
||||
service netd {
|
||||
description = "Network monitor"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/setupd", "pid/zebra" }
|
||||
command = "/sbin/netd"
|
||||
}
|
||||
|
||||
In this example the Network monitor daemon `netd` is not started until
|
||||
both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A
|
||||
@@ -42,15 +55,28 @@ being created, i.e., the service's default readiness notification.
|
||||
Another example is `dropbear`, it does not support `SIGHUP`, but we can
|
||||
also see optional sourcing of arguments from an environment file:
|
||||
|
||||
service [2345789] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
|
||||
service dropbear {
|
||||
description = "Dropbear SSH daemon"
|
||||
runlevel = "2345789"
|
||||
reload-signal = "none"
|
||||
envfile = "-/etc/default/dropbear"
|
||||
command = "dropbear -F -R $DROPBEAR_ARGS"
|
||||
}
|
||||
|
||||
Finally, the weird "block runlevel changes" example. Here we see what
|
||||
happens when Finit receives `SIGPWR`, sent from a power daemon like
|
||||
[powstatd(8)][]. A condition is asserted and a user can set up their
|
||||
own task to act on it. We do not want this task to block Finit from
|
||||
moving to the next runlevel after bootstrap, so we set `<!>`:
|
||||
moving to the next runlevel after bootstrap, so we set
|
||||
`required = false`:
|
||||
|
||||
task [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
|
||||
task pwrfail {
|
||||
description = "Power failure, shutting down"
|
||||
runlevel = "S0123456789"
|
||||
conditions = { "sys/pwr/fail" }
|
||||
required = false
|
||||
command = "initctl poweroff"
|
||||
}
|
||||
|
||||
|
||||
Propagating Reload in Dependencies
|
||||
@@ -65,13 +91,23 @@ not care if it reloads its config.
|
||||
For services that need to react when their upstream reloads, the `~`
|
||||
prefix propagates the reload from the dependency:
|
||||
|
||||
service <pid/svc_a> name:svc_b /sbin/svc_b -- Needs A (barrier)
|
||||
service <!~pid/svc_b> name:svc_c /sbin/svc_c -- Propagate reload from B
|
||||
service svc_b {
|
||||
description = "Needs A (barrier)"
|
||||
conditions = { "pid/svc_a" }
|
||||
command = "/sbin/svc_b"
|
||||
}
|
||||
|
||||
service svc_c {
|
||||
description = "Propagate reload from B"
|
||||
conditions = { "~pid/svc_b" }
|
||||
reload-signal = "none"
|
||||
command = "/sbin/svc_c"
|
||||
}
|
||||
|
||||
Here, `<~pid/svc_b>` means: propagate a reload of `svc_b` to `svc_c`.
|
||||
When `svc_b` reloads, `svc_c` will be restarted (because of `!`,
|
||||
noreload) instead of merely resumed. If `svc_c` supported `SIGHUP`
|
||||
(no `!` prefix), it would be sent `SIGHUP` instead.
|
||||
When `svc_b` reloads, `svc_c` will be restarted, because of
|
||||
`reload-signal = "none"`, instead of merely resumed. If `svc_c`
|
||||
supported `SIGHUP`, it would be sent `SIGHUP` instead.
|
||||
|
||||
This is similar to systemd's `PropagatesReloadTo=` directive, but
|
||||
declared on the consumer side rather than the provider side.
|
||||
@@ -103,7 +139,12 @@ services, or run/task jobs, on external site-dependent stimuli.
|
||||
|
||||
**Example:**
|
||||
|
||||
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
|
||||
service alarm {
|
||||
description = "Foo alarm"
|
||||
runlevel = "2345"
|
||||
conditions = { "usr/foo" }
|
||||
command = "alarm --arg foo"
|
||||
}
|
||||
|
||||
For convenience, prefixing with `usr/` is allowed, but any other slashes
|
||||
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
|
||||
@@ -126,8 +167,9 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
|
||||
> "touch" or recreate, their PID file on `SIGHUP`. This can be done by
|
||||
> calling `utimensat()` on the PID file. Provided, of course, that the
|
||||
> service supports reloading on `SIGHUP`, otherwise it will be restarted
|
||||
> by Finit when they instead exit on the signal. For such services, use
|
||||
> `<!>` to tell Finit the service does not support `SIGHUP`.
|
||||
> by Finit when they instead exit on the signal. For such services,
|
||||
> set `reload-signal = "none"` to tell Finit the service does not
|
||||
> support `SIGHUP`.
|
||||
|
||||
|
||||
Built-in Conditions
|
||||
@@ -145,15 +187,15 @@ The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device
|
||||
nodes being created and removed. It is active only when a run, task, or
|
||||
service has declared a `<dev/foo>` or `<dev/dir/bar>` condition.
|
||||
|
||||
The `pidfile` plugin (recursively) watches `/run/` (recursively) for PID
|
||||
files created by the monitored services, and sets a corresponding
|
||||
condition in the `pid/` namespace.
|
||||
The `pidfile` plugin recursively watches `/run/` for PID files created
|
||||
by the monitored services, and sets a corresponding condition in the
|
||||
`pid/` namespace.
|
||||
|
||||
Similarly, the `netlink` plugin provides basic conditions for when an
|
||||
interface is brought up/down and when a default route (gateway) is set,
|
||||
in the `net/` namespace.
|
||||
|
||||
The `sys` and `usr` plugins monitor are passive condition monitors where
|
||||
The `sys` and `usr` plugins are passive condition monitors where
|
||||
the action is provided by `keventd`, signal handlers, and in the case of
|
||||
`usr`, the end-user via the `initctl` tool.
|
||||
|
||||
@@ -199,16 +241,16 @@ Composition
|
||||
-----------
|
||||
|
||||
The `pid/` conditions are generated by the Finit `pidfile.so` plugin and
|
||||
composed from a service's `name:` and `:id`. By default the basename of
|
||||
the daemon and the empty string.
|
||||
composed from a service's block title and its `:id`. By default the
|
||||
basename of the daemon and the empty string.
|
||||
|
||||
| **service** | **condition** |
|
||||
|----------------------------------------------------|------------------|
|
||||
| /sbin/foo | pid/foo |
|
||||
| /sbin/bar -p /run/baz.pid | pid/bar |
|
||||
| name:lxc :foo lxc-start -n foo -p /run/lxc/foo.pid | pid/lxc:foo |
|
||||
| /usr/bin/dbus-daemon | pid/dbus-daemon |
|
||||
| :222 dropbear -p 222 | pid/dropbear:222 |
|
||||
| **service** | **condition** |
|
||||
|-------------------------------------------------------------------|------------------|
|
||||
| `service { command = "/sbin/foo" }` | pid/foo |
|
||||
| `service { command = "/sbin/bar -p /run/baz.pid" }` | pid/bar |
|
||||
| `service lxc:foo { command = "lxc-start -n foo -p /run/lxc/foo.pid" }` | pid/lxc:foo |
|
||||
| `service { command = "/usr/bin/dbus-daemon" }` | pid/dbus-daemon |
|
||||
| `service dropbear:222 { command = "dropbear -p 222" }` | pid/dropbear:222 |
|
||||
|
||||
The condition is asserted when `pidfile.so` receives an inotify event
|
||||
for a file matching `/run/*.pid`, `/run/**/*.pid`, or `/run/**/pid`,
|
||||
@@ -223,8 +265,8 @@ its conditions are cleared and reasserted, ensuring dependent services
|
||||
are properly updated.
|
||||
|
||||
Daemons that don't create PID files, or fail to touch them on reload,
|
||||
can be worked around by using the `pid:/path/to/file.pid` syntax in
|
||||
the service stanza for the daemon. It is far from optimal since any
|
||||
can be worked around by setting `pidfile` and `pidfile-create` in the
|
||||
service block for the daemon. It is far from optimal since any
|
||||
synchronization of depending services may fail due to the daemon not
|
||||
having reinitialized/created their IPC sockets, or similar.
|
||||
|
||||
@@ -307,8 +349,8 @@ restarts that would otherwise occur because a depending service was sent
|
||||
|
||||
Services with the `~` prefix are an exception to this rule: when their
|
||||
conditions return to `on` after being in `flux`, the reload is propagated
|
||||
-- the service is reloaded (SIGHUP) or restarted (noreload `!`) instead
|
||||
of simply being resumed.
|
||||
-- the service is reloaded (SIGHUP), or restarted if it has
|
||||
`reload-signal = "none"`, instead of simply being resumed.
|
||||
|
||||
Therefore, any plugin that supplies Finit with conditions must ensure
|
||||
that their state is updated after each reconfiguration. This can be
|
||||
|
||||
+47
-32
@@ -16,14 +16,17 @@ which is the same approach used by other modern service managers like dinit.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Capabilities are specified using the `caps:` directive in service configuration:
|
||||
Capabilities are specified with the `capabilities` key, alias `caps`:
|
||||
|
||||
```conf
|
||||
service [2345] name:nginx \
|
||||
@www-data:www-data \
|
||||
caps:^cap_net_bind_service \
|
||||
/usr/sbin/nginx -g 'daemon off;' \
|
||||
-- Web server
|
||||
service nginx {
|
||||
description = "Web server"
|
||||
runlevel = "2345"
|
||||
user = "www-data"
|
||||
group = "www-data"
|
||||
capabilities = { "^cap_net_bind_service" }
|
||||
command = "/usr/sbin/nginx -g 'daemon off;'"
|
||||
}
|
||||
```
|
||||
|
||||
This example allows nginx to bind to privileged ports (like 80 and 443) while
|
||||
@@ -52,7 +55,7 @@ with the following prefixes:
|
||||
Multiple capabilities can be specified as a comma-separated list:
|
||||
|
||||
```conf
|
||||
caps:^cap_net_raw,^cap_net_admin,^cap_net_bind_service
|
||||
capabilities = { "^cap_net_raw", "^cap_net_admin", "^cap_net_bind_service" }
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
@@ -62,10 +65,13 @@ caps:^cap_net_raw,^cap_net_admin,^cap_net_bind_service
|
||||
Allow a web server to bind to ports 80 and 443 without running as root:
|
||||
|
||||
```conf
|
||||
service [2345] name:webserver \
|
||||
@www-data:www-data \
|
||||
caps:^cap_net_bind_service \
|
||||
/usr/sbin/nginx -g 'daemon off;'
|
||||
service webserver {
|
||||
runlevel = "2345"
|
||||
user = "www-data"
|
||||
group = "www-data"
|
||||
capabilities = { "^cap_net_bind_service" }
|
||||
command = "/usr/sbin/nginx -g 'daemon off;'"
|
||||
}
|
||||
```
|
||||
|
||||
### Network Monitoring (Raw Sockets)
|
||||
@@ -73,10 +79,12 @@ service [2345] name:webserver \
|
||||
Allow packet capture without root privileges:
|
||||
|
||||
```conf
|
||||
service [2345] name:tcpdump \
|
||||
@tcpdump \
|
||||
caps:^cap_net_raw,^cap_net_admin \
|
||||
/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap
|
||||
service tcpdump {
|
||||
runlevel = "2345"
|
||||
user = "tcpdump"
|
||||
capabilities = { "^cap_net_raw", "^cap_net_admin" }
|
||||
command = "/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap"
|
||||
}
|
||||
```
|
||||
|
||||
### NTP Daemon (System Time)
|
||||
@@ -84,10 +92,12 @@ service [2345] name:tcpdump \
|
||||
Allow time synchronization without full root:
|
||||
|
||||
```conf
|
||||
service [2345] name:ntpd \
|
||||
@ntp \
|
||||
caps:^cap_sys_time,^cap_sys_nice \
|
||||
/usr/sbin/ntpd -n
|
||||
service ntpd {
|
||||
runlevel = "2345"
|
||||
user = "ntp"
|
||||
capabilities = { "^cap_sys_time", "^cap_sys_nice" }
|
||||
command = "/usr/sbin/ntpd -n"
|
||||
}
|
||||
```
|
||||
|
||||
## Available Capabilities
|
||||
@@ -116,9 +126,9 @@ Common capabilities include (see `man 7 capabilities` for the complete list):
|
||||
- Don't grant `cap_sys_admin` unless absolutely necessary
|
||||
|
||||
2. **Specify a user (preferably non-root)**
|
||||
- The `@user` directive is **required** for `caps:` to take effect
|
||||
- For ambient capabilities (`^`), use a non-root user (not `@root`)
|
||||
- Example: `@www-data`, `@nginx`, `@tcpdump`
|
||||
- The `user` setting is **required** for `capabilities` to take effect
|
||||
- For ambient capabilities (`^`), use a non-root user (not `"root"`)
|
||||
- Example: `user = "www-data"`, `user = "nginx"`, `user = "tcpdump"`
|
||||
|
||||
3. **Use ambient capabilities (`^`)**
|
||||
- The `^` prefix ensures capabilities survive exec()
|
||||
@@ -159,21 +169,26 @@ ps -o user,pid,cmd -p $(pidof nginx)
|
||||
|
||||
- Linux kernel 4.3+ (for ambient capabilities support)
|
||||
- libcap library installed
|
||||
- Finit built with `--enable-libcap`
|
||||
- Finit built with `--enable-libcap`, otherwise a `capabilities` list is
|
||||
ignored, with a warning
|
||||
|
||||
## Limitations
|
||||
|
||||
- The `caps:` directive requires `@user` to be specified for it to take effect
|
||||
- Without `@user`, the service runs as root with full capabilities and
|
||||
the `caps:` configuration is silently ignored
|
||||
- You can use `@root` with `caps:`, but see below about ambient capabilities
|
||||
- `capabilities` requires `user` to be set for it to take effect
|
||||
- Without `user`, the service runs as root with full capabilities and
|
||||
the `capabilities` list is silently ignored
|
||||
- You can use `user = "root"`, but see below about ambient capabilities
|
||||
- For ambient capabilities (`^`, recommended), the user **must be non-root**
|
||||
- Using `@root` with `caps:^...` will not work effectively, as ambient
|
||||
- Using `user = "root"` with `^` capabilities will not work effectively, as ambient
|
||||
capabilities are only added to the effective set when euid ≠ 0
|
||||
- Use inheritable (`%`) or bounding (`!`) capabilities with `@root` if needed
|
||||
- Services without `caps:` use standard privilege dropping:
|
||||
- Services with `@user` (non-root) have no special capabilities
|
||||
- Services without `@user` run as root with full capabilities
|
||||
- Use inheritable (`%`) or bounding (`!`) capabilities with `user = "root"` if needed
|
||||
- Finit warns about this when reading the .conf file:
|
||||
|
||||
nginx: ambient capabilities ('^') have no effect as root, use a
|
||||
non-root user, or '%' and '!' entries
|
||||
- Services without `capabilities` use standard privilege dropping:
|
||||
- Services with a non-root `user` have no special capabilities
|
||||
- Services without `user` run as root with full capabilities
|
||||
- Some very old binaries may not work correctly with ambient capabilities
|
||||
- File system capabilities are not managed by Finit (use `setcap` for that)
|
||||
|
||||
|
||||
+126
-99
@@ -1,19 +1,17 @@
|
||||
Finit provides three different cgroup directives for controlling resource allocation:
|
||||
Finit has two `cgroup` blocks for controlling resource allocation:
|
||||
|
||||
1. **Top-level cgroup definition**: `cgroup NAME settings`
|
||||
- Defines a top-level cgroup (e.g., `init`, `system`, `user`) with default settings
|
||||
- Space-separated syntax
|
||||
- Example: `cgroup system cpu.weight:9700`
|
||||
1. **Top-level definition**, at file scope: declares a group such as
|
||||
`init`, `system`, or `user`, and its default settings.
|
||||
|
||||
2. **Global cgroup selector**: `cgroup.NAME[,options]` (standalone directive)
|
||||
- Sets the default cgroup for subsequent services in a `.conf` file
|
||||
- Dot-separated with optional comma-separated options
|
||||
- Example: `cgroup.maint` or `cgroup.system,delegate`
|
||||
cgroup system { cpu.weight = 9700 }
|
||||
|
||||
3. **Per-service cgroup option**: `cgroup.NAME[,options]` or `cgroup:options`
|
||||
- Overrides the cgroup for a specific service
|
||||
- Part of the service directive line
|
||||
- Example: `service [...] cgroup.maint,mem.max:1G /path/to/cmd`
|
||||
2. **Joining a group**, inside a service block: names the group this
|
||||
service runs in, and may override settings for itself alone.
|
||||
|
||||
service foo {
|
||||
cgroup maint { memory.max = 1G }
|
||||
command = "/path/to/cmd"
|
||||
}
|
||||
|
||||
> [!NOTE]
|
||||
> Linux cgroups and details surrounding values are not explained in the
|
||||
@@ -23,83 +21,72 @@ Finit provides three different cgroup directives for controlling resource alloca
|
||||
Top-level Cgroup Definition
|
||||
----------------------------
|
||||
|
||||
**Syntax:** `cgroup NAME settings`
|
||||
**Syntax:** `cgroup NAME { settings }`
|
||||
|
||||
# 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
|
||||
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
|
||||
cgroup system { cpu.weight = 9700 }
|
||||
|
||||
# Example extra cgroup 'maint'
|
||||
cgroup maint cpu.weight:100
|
||||
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.
|
||||
|
||||
Global Cgroup Selector
|
||||
----------------------
|
||||
Joining a Cgroup
|
||||
----------------
|
||||
|
||||
**Syntax:** `cgroup.NAME[,options]` (standalone directive)
|
||||
**Syntax:** `cgroup NAME { settings }` (inside a service block)
|
||||
|
||||
To select a different top-level cgroup, e.g. `maint/`, for a group of
|
||||
run/task/service directives in a `.conf` file, use the `cgroup.NAME`
|
||||
directive as a standalone line:
|
||||
Every block says which group it joins, by name. An empty block joins
|
||||
without changing anything:
|
||||
|
||||
cgroup.maint
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service [...] <...> /path/to/bar args -- description
|
||||
service foo {
|
||||
cgroup maint {}
|
||||
command = "/path/to/foo args"
|
||||
}
|
||||
|
||||
Both services will run in the `maint/` cgroup.
|
||||
service bar {
|
||||
cgroup maint {}
|
||||
command = "/path/to/bar args"
|
||||
}
|
||||
|
||||
You can also include options with the global selector:
|
||||
Both services run in the `maint/` cgroup. Settings inside the block
|
||||
apply to that service alone:
|
||||
|
||||
cgroup.system,delegate
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service foo {
|
||||
cgroup maint {
|
||||
cpu.max = 10000
|
||||
memory.max = 655360
|
||||
}
|
||||
command = "/path/to/foo args"
|
||||
}
|
||||
|
||||
Per-Service Cgroup Option
|
||||
--------------------------
|
||||
> [!NOTE]
|
||||
> The legacy format has a standalone `cgroup.NAME` line that selects a
|
||||
> group for every stanza following it in the file, and a `cgroup:options`
|
||||
> form that applies settings to whichever group is current without
|
||||
> naming it. Neither has an equivalent here, by design: a block that
|
||||
> joins a group says so itself, so the group cannot depend on what came
|
||||
> earlier in the file.
|
||||
|
||||
**Syntax:** `cgroup.NAME[,options]` or `cgroup:options` (service option)
|
||||
|
||||
To override the cgroup for a specific service, use the `cgroup.NAME`
|
||||
option within the service directive:
|
||||
|
||||
service [...] <...> cgroup.maint /path/to/foo args -- description
|
||||
|
||||
This form also allows per-service limits. Two syntaxes are supported:
|
||||
|
||||
**New comma-separated syntax (recommended):**
|
||||
|
||||
service [...] <...> cgroup.maint,cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||
|
||||
**Old colon-separated syntax (legacy):**
|
||||
|
||||
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||
|
||||
You can also apply options to the current default cgroup (without changing it)
|
||||
using the `cgroup:options` syntax:
|
||||
|
||||
service [...] <...> cgroup:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||
|
||||
Both syntaxes work identically. The new comma-separated syntax is recommended
|
||||
as it's more consistent with other option parsing in Finit.
|
||||
|
||||
Note 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 no 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.
|
||||
Every cgroup setting maps directly to cgroup v2 syntax, so `cpu.max`
|
||||
maps to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no
|
||||
filtering, the one exception being the shorthand `mem.`, which expands
|
||||
to `memory.`. If the file is not available, either the controller is
|
||||
missing from your Linux kernel, or the name is misspelled.
|
||||
|
||||
### Overriding Cgroup Leaf Names
|
||||
|
||||
@@ -108,40 +95,66 @@ configuration filename (without the `.conf` extension). For example, a
|
||||
service defined in `system/10-hotplug.conf` would create a cgroup at
|
||||
`/sys/fs/cgroup/system/10-hotplug/` by default.
|
||||
|
||||
To use a more descriptive name (recommended for clarity), you can specify
|
||||
`name:` in the cgroup directive:
|
||||
To use a more descriptive name (recommended for clarity), set `name`
|
||||
inside the cgroup block:
|
||||
|
||||
service [...] <...> cgroup.system,name:udevd /lib/systemd/systemd-udevd -- Device event daemon
|
||||
service udevd {
|
||||
description = "Device event daemon"
|
||||
cgroup system { name = "udevd" }
|
||||
command = "/lib/systemd/systemd-udevd"
|
||||
}
|
||||
|
||||
This creates the cgroup at `/sys/fs/cgroup/system/udevd/` instead.
|
||||
|
||||
The syntax supports combining the name override with other options:
|
||||
`name` combines with any other setting:
|
||||
|
||||
service [...] <...> cgroup.system,name:udevd,cpu.max:10000 /lib/systemd/systemd-udevd -- Device event daemon
|
||||
service udevd {
|
||||
description = "Device event daemon"
|
||||
cgroup system {
|
||||
name = "udevd"
|
||||
cpu.max = 10000
|
||||
}
|
||||
command = "/lib/systemd/systemd-udevd"
|
||||
}
|
||||
|
||||
Or with delegation:
|
||||
|
||||
service [2345] @podman:podman \
|
||||
cgroup.containers,name:podman,delegate,mem.max:4G \
|
||||
/usr/bin/podman system service -- Podman API
|
||||
service podman {
|
||||
description = "Podman API"
|
||||
runlevel = "2345"
|
||||
user = "podman"
|
||||
group = "podman"
|
||||
cgroup containers {
|
||||
name = "podman"
|
||||
delegate = true
|
||||
memory.max = 4G
|
||||
}
|
||||
command = "/usr/bin/podman system service"
|
||||
}
|
||||
|
||||
A daemon using `SCHED_RR` currently needs to run outside the default cgroups.
|
||||
A daemon using `SCHED_RR` currently needs to run outside the default
|
||||
cgroups.
|
||||
|
||||
service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
|
||||
service rt {
|
||||
description = "Real-Time process"
|
||||
cgroup root {}
|
||||
command = "/path/to/daemon arg"
|
||||
}
|
||||
|
||||
Cgroup Delegation
|
||||
-----------------
|
||||
|
||||
For services that need to create their own child cgroups (container runtimes
|
||||
like Docker, Podman, systemd-nspawn, LXC), use the `delegate` option:
|
||||
like Docker, Podman, systemd-nspawn, LXC), set `delegate`:
|
||||
|
||||
service [2345] @dockerd:dockerd \
|
||||
cgroup.system,delegate /usr/bin/dockerd -- Docker daemon
|
||||
|
||||
Or with the old colon syntax:
|
||||
|
||||
service [2345] @dockerd:dockerd \
|
||||
cgroup.system:delegate /usr/bin/dockerd -- Docker daemon
|
||||
service dockerd {
|
||||
description = "Docker daemon"
|
||||
runlevel = "2345"
|
||||
user = "dockerd"
|
||||
group = "dockerd"
|
||||
cgroup system { delegate = true }
|
||||
command = "/usr/bin/dockerd"
|
||||
}
|
||||
|
||||
This allows the container runtime to:
|
||||
|
||||
@@ -159,32 +172,46 @@ When delegation is enabled, Finit:
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- The service should specify `@user:group` for proper ownership
|
||||
- The service should specify `user` and `group` for proper ownership
|
||||
- Controllers are delegated from the parent cgroup
|
||||
|
||||
**Example with additional config (new syntax):**
|
||||
**Example with additional config:**
|
||||
|
||||
service [2345] @podman:podman \
|
||||
cgroup.containers,delegate,mem.max:4G \
|
||||
/usr/bin/podman system service -- Podman API
|
||||
service podman {
|
||||
description = "Podman API"
|
||||
runlevel = "2345"
|
||||
user = "podman"
|
||||
group = "podman"
|
||||
cgroup containers {
|
||||
delegate = true
|
||||
memory.max = 4G
|
||||
}
|
||||
command = "/usr/bin/podman system service"
|
||||
}
|
||||
|
||||
**Or with old syntax:**
|
||||
|
||||
service [2345] @podman:podman \
|
||||
cgroup.containers:delegate,mem.max:4G \
|
||||
/usr/bin/podman system service -- Podman API
|
||||
|
||||
Both examples delegate the cgroup while also setting a 4GB memory limit.
|
||||
This delegates the cgroup while also setting a 4GB memory limit.
|
||||
|
||||
**Container template example:**
|
||||
|
||||
Here's a real-world example from [Infix OS](https://github.com/kernelkit/infix)
|
||||
for running rootful podman container instances using delegation:
|
||||
|
||||
sysv log:prio:local1,tag:%i kill:30 pid:!/run/container:%i.pid \
|
||||
pre:0,/usr/sbin/container cleanup:0,/usr/sbin/container \
|
||||
cgroup.system,delegate \
|
||||
[2345] <!> :%i container -n %i -- container %i
|
||||
sysv container:%i {
|
||||
description = "container %i"
|
||||
runlevel = "2345"
|
||||
reload-signal = "none"
|
||||
pidfile = "/run/container:%i.pid"
|
||||
stop-timeout = 30
|
||||
log { priority = "local1" identity = "%i" }
|
||||
|
||||
exec-start-pre = "/usr/sbin/container"
|
||||
exec-start-pre-timeout = 0
|
||||
exec-cleanup = "/usr/sbin/container"
|
||||
exec-cleanup-timeout = 0
|
||||
|
||||
cgroup system { delegate = true }
|
||||
command = "container -n %i"
|
||||
}
|
||||
|
||||
This template uses `sysv` type with delegation, demonstrating that cgroup
|
||||
delegation works with different service types, not just `service`.
|
||||
@@ -200,7 +227,7 @@ Initially, the service process runs directly in the cgroup root:
|
||||
|
||||
Once the container runtime creates child cgroups (e.g., `libpod-*/`), cgroups v2
|
||||
enforces the "no internal processes" rule. When Finit detects this (`EBUSY` error),
|
||||
it automatically creates an `supervisor/` subdirectory and moves service-related
|
||||
it automatically creates a `supervisor/` subdirectory and moves service-related
|
||||
processes there:
|
||||
|
||||
/sys/fs/cgroup/system/container@web/
|
||||
|
||||
+7
-6
@@ -6,15 +6,16 @@ and any `*.conf`, was added. It is worth noting that these are global
|
||||
and *shared with all* services -- the only way to have a service-local
|
||||
environment is detailed in [Services Environment](service-env.md).
|
||||
|
||||
The syntax for global environment variables is straight forward. In
|
||||
Finit v4.4 the `set` keyword was added for completeness, but the old
|
||||
syntax (without the `set ` prefix) is still honored:
|
||||
Global environment variables go in an `environment` block, `env` for
|
||||
short:
|
||||
|
||||
set foo=bar
|
||||
set baz="qux"
|
||||
environment {
|
||||
foo = "bar"
|
||||
baz = "qux"
|
||||
}
|
||||
|
||||
On reload of .conf files, all tracked environment variables are cleared
|
||||
so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
|
||||
so if `foo` is removed from `finit.conf`, or any `finit.d/*.conf`
|
||||
file, it will no longer be used by Finit or any new (!) started
|
||||
run/tasks or services. The environment of already started processes can
|
||||
not be changed.
|
||||
|
||||
+8
-8
@@ -70,12 +70,12 @@ unique group, where files within each group are sorted alphabetically.
|
||||
/etc/finit.d/enabled/1-aaa.conf
|
||||
/etc/finit.d/enabled/1-abc.conf
|
||||
|
||||
The resulting combined configuration is read line by line, each `run`,
|
||||
The resulting combined configuration is read in order, each `run`,
|
||||
`task`, and `service` added to an ordered list that ensures they are
|
||||
started in the same order. This is important because of the blocking
|
||||
properties of the `run` statement. For an example on the relation of
|
||||
`service` and `run` statements, and dependency handling between them,
|
||||
see [Conditional Loading](services.md#conditional-loading), below.
|
||||
properties of `run`. For an example on the relation of `service` and
|
||||
`run`, and dependency handling between them, see
|
||||
[Conditional Loading](services.md#conditional-loading), below.
|
||||
|
||||
> [!NOTE]
|
||||
> The names `finit.conf` and `finit.d/` are only defaults. They can be
|
||||
@@ -84,7 +84,7 @@ see [Conditional Loading](services.md#conditional-loading), below.
|
||||
>
|
||||
> They can also be overridden from the [kernel command line](../cmdline.md)
|
||||
> using: `-- finit.config=/etc/bar.conf` and in that file use the
|
||||
> top-level configuration directive `rcsd /path/to/finit.d`.
|
||||
> top-level setting `rcsd = "/path/to/finit.d"`.
|
||||
|
||||
Filesystem Layout
|
||||
-----------------
|
||||
@@ -180,7 +180,7 @@ document [Finit Services](../service.md).
|
||||
Alternate finit.d/
|
||||
------------------
|
||||
|
||||
**Syntax:** `rcsd /path/to/finit.d`
|
||||
**Syntax:** `rcsd = "/path/to/finit.d"`
|
||||
|
||||
The Finit rcS.d directory is set at compile time with:
|
||||
|
||||
@@ -194,7 +194,7 @@ configurations, starting with the kernel command line option:
|
||||
This file in turn can use the `rcsd` directive to tell Finit to use
|
||||
another set of .conf files, e.g.:
|
||||
|
||||
rcsd /etc/factory.d
|
||||
rcsd = "/etc/factory.d"
|
||||
|
||||
> [!NOTE]
|
||||
> This directive is only available from the top-level bootstrap .conf
|
||||
@@ -203,6 +203,6 @@ another set of .conf files, e.g.:
|
||||
Including Finit Configs
|
||||
------------------------
|
||||
|
||||
**Syntax:** `include <CONF>`
|
||||
**Syntax:** `include("CONF")`
|
||||
|
||||
Include another configuration file. Absolute path required.
|
||||
|
||||
+98
-13
@@ -6,24 +6,109 @@ the navigation menu.
|
||||
Configuration File Syntax
|
||||
--------------------------
|
||||
|
||||
The file format is line based, empty lines and comments, lines starting
|
||||
with `#`, are ignored. A configuration directive starts with a keyword
|
||||
followed by a space and the rest of the line is treated as the value.
|
||||
|
||||
As of Finit v4.4, configuration directives can be broken up in multiple
|
||||
lines using the continuation character `\`, and trailing comments are
|
||||
also allowed. Example:
|
||||
A `.conf` file is a series of blocks. Every setting is a key inside
|
||||
one, so nothing has to be remembered by position:
|
||||
|
||||
```aconf
|
||||
# Escape \# chars if you want them literal in, e.g., descriptions
|
||||
service name:sysklogd [S123456789] \
|
||||
env:-/etc/default/sysklogd \
|
||||
syslogd -F $SYSLOGD_ARGS \
|
||||
-- System log daemon \# 1 # Comments allowed
|
||||
service sysklogd {
|
||||
description = "System log daemon"
|
||||
runlevel = "S123456789"
|
||||
envfile = "-/etc/default/sysklogd"
|
||||
command = "syslogd -F $SYSLOGD_ARGS"
|
||||
}
|
||||
```
|
||||
|
||||
Values are quoted strings, bare words, or integers. Lists use braces,
|
||||
and a block may carry a title, which becomes the service identity:
|
||||
|
||||
```aconf
|
||||
# shell comment
|
||||
// C++ comment
|
||||
/* C comment */
|
||||
|
||||
key = "value" # string
|
||||
number = 20 # integer
|
||||
flag = true # boolean
|
||||
list = { "one", "two" } # string list
|
||||
block title { key = "value" } # titled section
|
||||
|
||||
path = "${HOME}/thing" # environment expansion
|
||||
include("/etc/finit.d/extra.conf") # include another file
|
||||
```
|
||||
|
||||
Bare words work wherever a string is expected, so `memory.max = 65M`
|
||||
and `restart = always` need no quotes.
|
||||
|
||||
### Two conventions
|
||||
|
||||
Keys are kebab-case, never `snake_case` or `CamelCase`: `restart-sec`,
|
||||
`stop-timeout`, `reboot-watchdog`.
|
||||
|
||||
Keys that take a list are plural: `conditions`, `conflicts`,
|
||||
`capabilities`, `modules`, `extra-groups`. Two imperatives keep their
|
||||
singular form because they are verbs rather than nouns: `mknod` and
|
||||
`include`.
|
||||
|
||||
### Short forms
|
||||
|
||||
Nine keys have an alias. An alias may abbreviate the canonical name,
|
||||
or preserve the spelling the line-based format used; it never renames.
|
||||
|
||||
| Canonical | Alias | Canonical | Alias |
|
||||
|---|---|---|---|
|
||||
| `description` | `desc` | `manual-start` | `manual` |
|
||||
| `conditions` | `cond` | `remain-after-exit` | `remain` |
|
||||
| `capabilities` | `caps` | `stop-signal` | `halt` |
|
||||
| `modules` | `mod` | `stop-timeout` | `kill` |
|
||||
| `envfile` | `env` | | |
|
||||
|
||||
### Optional paths
|
||||
|
||||
A leading `-` on a path means carry on if it is not there:
|
||||
|
||||
```aconf
|
||||
service foo {
|
||||
envfile = "-/etc/default/foo" # skip the file if missing
|
||||
command = "-/usr/sbin/foo" # skip the whole block if missing
|
||||
}
|
||||
```
|
||||
|
||||
That is the only mark meaning "optional". Two others mean something
|
||||
else: `!` on `runlevel` inverts the set, e.g. `runlevel = "!12345"`,
|
||||
and `~` on a condition propagates a reload from the service it names.
|
||||
Both are covered where they apply.
|
||||
|
||||
|
||||
Both Formats Are Read
|
||||
---------------------
|
||||
|
||||
Finit also reads the line-based format it has always used, where a
|
||||
stanza is a keyword followed by values whose meaning comes from their
|
||||
position:
|
||||
|
||||
```aconf
|
||||
service [S123456789] env:-/etc/default/sysklogd syslogd -F $SYSLOGD_ARGS -- System log daemon
|
||||
```
|
||||
|
||||
That format still works and is not going away. It is frozen at the
|
||||
Finit 4.x feature set, so new settings appear only in the block format,
|
||||
and the documentation is written in blocks throughout.
|
||||
|
||||
There is no new file extension and no marker line. Every file is still
|
||||
`*.conf`, and Finit decides which format a file is in by reading it: if
|
||||
it parses as blocks it is a block file, otherwise it goes to the
|
||||
line-based parser. A file is one format or the other, never a mix.
|
||||
|
||||
Because the two are told apart before either parser commits, a typo
|
||||
reports its own file and line instead of being mistaken for the other
|
||||
format:
|
||||
|
||||
```
|
||||
parse error: /etc/finit.d/foo.conf:3: no such option 'commnad'
|
||||
```
|
||||
|
||||
The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support many
|
||||
directives. Some are restricted, e.g., only available at bootstrap,
|
||||
settings. Some are restricted, e.g., only available at bootstrap,
|
||||
runlevel `S`. Read on in [Files & Layout](files.md) for more on how
|
||||
to structure your .conf files.
|
||||
|
||||
|
||||
+36
-17
@@ -1,13 +1,13 @@
|
||||
General Logging
|
||||
===============
|
||||
|
||||
**Syntax:** `log size:200k count:5`
|
||||
**Syntax:** `log { size = 200k count = 5 }`
|
||||
|
||||
Log rotation for run/task/services using the `log` sub-option with
|
||||
redirection to a log file. Global setting, applies to all services.
|
||||
Log rotation for run/task/services that redirect output to a log file
|
||||
with their own `log` block. Global setting, applies to all services.
|
||||
|
||||
The size can be given as bytes, without a specifier, or in `k`, `M`,
|
||||
or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables
|
||||
or `G`, e.g. `size = 10M`, or `size = 3G`. A value of `size = 0` disables
|
||||
log rotation. The default is `200k`.
|
||||
|
||||
The count value is recommended to be between 1-5, with a default 5.
|
||||
@@ -17,33 +17,48 @@ size limit is reached.
|
||||
Redirecting Output
|
||||
------------------
|
||||
|
||||
The `run`, `task`, and `service` stanzas also allow the keyword `log` to
|
||||
redirect `stderr` and `stdout` of the application to a file or syslog
|
||||
The `run`, `task`, and `service` blocks take a `log` block of their own,
|
||||
redirecting `stderr` and `stdout` of the application to a file or syslog
|
||||
using the native `logit` tool. This is useful for programs that do not
|
||||
support syslog on their own, which is sometimes the case when running
|
||||
in the foreground.
|
||||
|
||||
The full syntax is:
|
||||
An empty block means syslog with the defaults, and three keys adjust it:
|
||||
|
||||
log:/path/to/file
|
||||
log:prio:facility.level,tag:ident
|
||||
log:console
|
||||
log:null
|
||||
log
|
||||
| Setting | Description |
|
||||
|---|---|
|
||||
| `file` | Write to this path instead of syslog |
|
||||
| `priority` | Syslog `facility.level`, default `daemon.info` |
|
||||
| `identity` | Syslog tag, default the basename of the command |
|
||||
|
||||
Default `prio` is `daemon.info` and default `tag` is the basename of the
|
||||
service or run/task command.
|
||||
`/dev/console` and `/dev/null` are spelled as the paths they are:
|
||||
|
||||
service foo { log { } command = "foo" } # syslog
|
||||
service foo { log { file = "/var/log/foo" } command = "foo" } # a file
|
||||
service foo { log { file = "/dev/console" } command = "foo" } # console
|
||||
service foo { log { file = "/dev/null" } command = "foo" } # discard
|
||||
|
||||
> [!NOTE]
|
||||
> A `log` block at file scope is a different setting -- that one is the
|
||||
> global rotation above, and it takes only `size` and `count`.
|
||||
|
||||
Log rotation is controlled using the global `log` setting.
|
||||
|
||||
**Example:**
|
||||
|
||||
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
log {
|
||||
priority = "user.warn"
|
||||
identity = "ntpd"
|
||||
}
|
||||
command = "/sbin/ntpd pool.ntp.org"
|
||||
}
|
||||
|
||||
Output Buffering
|
||||
----------------
|
||||
|
||||
When using the `log` directive, Finit redirects the service's stdout and
|
||||
When using the `log` block, Finit redirects the service's stdout and
|
||||
stderr to a pipe connected to a logger process. Programs detect this as
|
||||
non-interactive output (i.e., `isatty()` returns false) and typically
|
||||
switch from line-buffered to fully-buffered mode.
|
||||
@@ -53,7 +68,11 @@ directly, so this is rarely an issue. However, if a service's log
|
||||
messages appear delayed or batched, you can force line-buffered output
|
||||
by wrapping the command with `stdbuf`:
|
||||
|
||||
service log /usr/bin/stdbuf -oL /path/to/command -- My service
|
||||
service myservice {
|
||||
description = "My service"
|
||||
log { }
|
||||
command = "/usr/bin/stdbuf -oL /path/to/command"
|
||||
}
|
||||
|
||||
The `-oL` option forces line-buffered output, and `-o0` forces unbuffered
|
||||
output. See `stdbuf(1)` for details.
|
||||
|
||||
+13
-6
@@ -47,16 +47,23 @@ system administrator.
|
||||
|
||||
The bundled default `rescue.conf` contains nothing more than:
|
||||
|
||||
runlevel 1
|
||||
tty [12345] rescue
|
||||
runlevel = 1
|
||||
tty rescue {
|
||||
runlevel = "12345"
|
||||
rescue = true
|
||||
}
|
||||
|
||||
The `tty` has the `rescue` option set, which works similar to the board
|
||||
bring-up tty option `notty`. The major difference being that `sulogin`
|
||||
The `tty` block has `rescue` set, which works similar to the board
|
||||
bring-up setting `notty`. The major difference being that `sulogin`
|
||||
is started to query for root/admin password. If `sulogin` is not found,
|
||||
`rescue` behaves like `notty` and gives a plain root shell prompt.
|
||||
|
||||
If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
|
||||
If Finit cannot find `/lib/finit/rescue.conf` it falls back to a
|
||||
built-in equivalent, which runs in every runlevel it can:
|
||||
|
||||
tty [12345] rescue
|
||||
tty rescue {
|
||||
runlevel = "12345789"
|
||||
rescue = true
|
||||
}
|
||||
|
||||
There is no way to exit the *fallback* rescue mode.
|
||||
+36
-25
@@ -26,12 +26,20 @@ the lifetime of your system.
|
||||
|
||||
Example:
|
||||
|
||||
task [S] /lib/console-setup/console-setup.sh
|
||||
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
|
||||
task console-setup {
|
||||
runlevel = "S"
|
||||
command = "/lib/console-setup/console-setup.sh"
|
||||
}
|
||||
|
||||
service rsyslogd {
|
||||
runlevel = "S12345"
|
||||
envfile = "-/etc/default/rsyslog"
|
||||
command = "rsyslogd -n $RSYSLOGD_ARGS"
|
||||
}
|
||||
|
||||
When bootstrap has completed, Finit moves to runlevel 2. This can be
|
||||
changed in `/etc/finit.conf` using the `runlevel N` directive, or by a
|
||||
script running in runlevel S that calls, e.g., `initctl runlevel 9`.
|
||||
changed in `/etc/finit.conf` with `runlevel = N`, or by a script running
|
||||
in runlevel S that calls, e.g., `initctl runlevel 9`.
|
||||
The latter is useful if startup scripts detect problems outside of
|
||||
Finit's control, e.g., critical services/devices missing or hardware
|
||||
problems.
|
||||
@@ -44,7 +52,7 @@ complete before proceeding to 2.
|
||||
Finit first stops everything that is not allowed to run in 2, and then
|
||||
brings up networking. Networking is expected to be available in all
|
||||
runlevels except: S, 1 (single user level), 6, and 0. Networking is
|
||||
enabled either by the `network script` directive, or if you have an
|
||||
enabled either by `network = "script"`, or if you have an
|
||||
`/etc/network/interfaces` file, Finit calls `ifup -a` -- at the very
|
||||
least the loopback interface is brought up.
|
||||
|
||||
@@ -58,7 +66,7 @@ least the loopback interface is brought up.
|
||||
Runlevel Configuration
|
||||
----------------------
|
||||
|
||||
**Syntax:** `runlevel <N>`
|
||||
**Syntax:** `runlevel = N`
|
||||
|
||||
The system runlevel to go to after bootstrap (S) has completed. `N` is
|
||||
the runlevel number 0-9, where 6 is reserved for reboot and 0 for halt.
|
||||
@@ -76,11 +84,11 @@ Finit disables networking in this mode.
|
||||
Networking
|
||||
----------
|
||||
|
||||
**Syntax:** `network <PATH>`
|
||||
**Syntax:** `network = "PATH"`
|
||||
|
||||
Script or program to bring up networking, with optional arguments.
|
||||
|
||||
Deprecated. We recommend using dedicated task/run stanzas per runlevel,
|
||||
Deprecated. We recommend using dedicated task/run blocks per runlevel,
|
||||
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
|
||||
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
||||
|
||||
@@ -90,7 +98,7 @@ Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
|
||||
System Hostname
|
||||
---------------
|
||||
|
||||
**Syntax:** `host <NAME>`, or `hostname <NAME>`
|
||||
**Syntax:** `hostname = "NAME"`
|
||||
|
||||
Set system hostname to NAME, unless `/etc/hostname` exists in which case
|
||||
the contents of that file is used.
|
||||
@@ -103,10 +111,16 @@ Deprecated. We recommend using `/etc/hostname` instead.
|
||||
Kernel Modules
|
||||
--------------
|
||||
|
||||
**Syntax:** `module <MODULE> [ARGS]`
|
||||
**Syntax:** `modules = { "MODULE [ARGS]", ... }`, alias `mod`
|
||||
|
||||
Load a kernel module, with optional arguments. Similar to `insmod`
|
||||
command line tool.
|
||||
Load kernel modules, each with optional arguments. Similar to the
|
||||
`insmod` command line tool.
|
||||
|
||||
modules = { "button", "evdev", "softdog" }
|
||||
|
||||
> [!NOTE]
|
||||
> A list cannot hold comments; the lexer reads the entries after a `#`
|
||||
> regardless. Put commented-out candidates above the list.
|
||||
|
||||
Deprecated, there is both a `modules-load.so` and a `modprobe.so` plugin
|
||||
that can handle module loading better. The former supports loading from
|
||||
@@ -122,9 +136,9 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
|
||||
Resource Limits
|
||||
---------------
|
||||
|
||||
**Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
|
||||
**Syntax:** `rlimit { RESOURCE = LIMIT }`, with `soft.` or `hard.` prefix
|
||||
|
||||
Set the hard or soft limit for a resource, or both if that argument is
|
||||
Set the hard or soft limit for a resource, or both if the prefix is
|
||||
omitted. `RESOURCE` is the lower-case `RLIMIT_` string constants from
|
||||
`setrlimit(2)`, without prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||
|
||||
@@ -134,23 +148,20 @@ or the kernel `/proc/PID/limits` file, for details. Finit versions
|
||||
before v3.1 used `infinity` for `unlimited`, which is still supported,
|
||||
albeit deprecated.
|
||||
|
||||
# No process is allowed more than 8MB of address space
|
||||
rlimit hard as 8388608
|
||||
|
||||
# Core dumps may be arbitrarily large
|
||||
rlimit soft core infinity
|
||||
|
||||
# CPU limit for all services, soft & hard = 10 sec
|
||||
rlimit cpu 10
|
||||
rlimit {
|
||||
hard.as = 8388608 # no more than 8MB of address space
|
||||
soft.core = unlimited # core dumps may be arbitrarily large
|
||||
cpu = 10 # soft & hard = 10 sec
|
||||
}
|
||||
|
||||
`rlimit` can be set globally, in `/etc/finit.conf`, or locally per
|
||||
each `/etc/finit.d/*.conf` read. I.e., a set of task/run/service
|
||||
stanzas can share the same rlimits if they are in the same .conf.
|
||||
blocks can share the same rlimits if they are in the same .conf.
|
||||
|
||||
Miscellaneous Settings
|
||||
----------------------
|
||||
|
||||
**Syntax:** `reboot-delay <0-60>`
|
||||
**Syntax:** `reboot-delay = 0-60`
|
||||
|
||||
Optional delay at reboot (or shutdown or halt) to allow kernel
|
||||
filesystem threads to complete after calling `sync(2)` before
|
||||
@@ -168,7 +179,7 @@ sync(2) has been called, twice.
|
||||
> writing; it can actually take a short time before all the blocks are
|
||||
> finally written.
|
||||
|
||||
**Syntax:** `reboot-watchdog <on|off|true|false|1|0>`
|
||||
**Syntax:** `reboot-watchdog = true|false`
|
||||
|
||||
Controls whether the system should reboot via the watchdog timer (WDT)
|
||||
or directly via the SoC/kernel. When enabled, Finit will:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Run-parts Scripts
|
||||
-----------------
|
||||
|
||||
**Syntax:** `runparts [progress] [sysv] <DIR>`
|
||||
**Syntax:** `runparts = "DIR"`
|
||||
|
||||
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
|
||||
files in the directory are called, in alphabetic order. The scripts in
|
||||
@@ -14,9 +14,10 @@ whatever the next runlevel is set to be (default 2). E.g., generate a
|
||||
|
||||
**Options:**
|
||||
|
||||
- `progress`: display the progress of each script being executed
|
||||
- `sysv`: run only SysV style scripts, i.e., `SNNfoo`, or `KNNbar`,
|
||||
where `NN` is a number (0-99).
|
||||
- `runparts-progress = true`: display the progress of each script being
|
||||
executed
|
||||
- `runparts-sysv = true`: run only SysV style scripts, i.e., `SNNfoo`,
|
||||
or `KNNbar`, where `NN` is a number (0-99).
|
||||
|
||||
If global debug mode is enabled, the `runparts` program is also called
|
||||
with the debug flag.
|
||||
@@ -24,7 +25,7 @@ with the debug flag.
|
||||
**Limitations:**
|
||||
|
||||
Scripts called from `runparts`, or hook scripts (see below), are limited
|
||||
in their interaction with Finit. Like the standalone `run` stanza and
|
||||
in their interaction with Finit. Like a standalone `run` block and
|
||||
the `/etc/rc.local` shell script, Finit waits for their completion
|
||||
before continuing. None of them can issue commands to start, stop, or
|
||||
restart other services. Also, ensure all your services and programs
|
||||
@@ -39,13 +40,13 @@ either terminate or start in the background or you will block Finit.
|
||||
|
||||
It can be beneficial to use `01-name`, `02-othername`, etc., to ensure
|
||||
the scripts are started in that order, e.g., if there is a dependency
|
||||
order between scripts. Symlinks to existing daemons can talso be used,
|
||||
order between scripts. Symlinks to existing daemons can also be used,
|
||||
but make sure they daemonize (background) themselves properly, otherwise
|
||||
Finit will lock up.
|
||||
|
||||
If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will
|
||||
be called with an extra argument, `start` and `stop`, respectively.
|
||||
E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo`
|
||||
and `K01foo` may be a symlink to to `another/directory/foo`.
|
||||
and `K01foo` may be a symlink to `another/directory/foo`.
|
||||
|
||||
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
|
||||
|
||||
@@ -13,7 +13,12 @@ file `/etc/default/foo`. Like this:
|
||||
|
||||
* `/etc/finit.conf`:
|
||||
|
||||
service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
|
||||
service foo {
|
||||
description = "Example foo daemon"
|
||||
runlevel = "2345"
|
||||
envfile = "-/etc/default/foo"
|
||||
command = "foo -n $FOO_OPTIONS"
|
||||
}
|
||||
|
||||
Here the service `foo` is started with `-n`, to make sure it runs in the
|
||||
foreground, and the with the options found in the environment file. With
|
||||
@@ -22,6 +27,8 @@ the `ps` command we can see that the process is started with:
|
||||
foo -n --extra-arg=bar -s -x
|
||||
|
||||
> [!NOTE]
|
||||
> The leading `-` in `env:` determines if Finit should treat a missing
|
||||
> environment file as blocking the start of the service or not. When
|
||||
> `-` is used, a missing environment file does *not* block the start.
|
||||
> The leading `-` on `envfile` determines if Finit should treat a
|
||||
> missing environment file as blocking the start of the service or not.
|
||||
> When `-` is used, a missing environment file does *not* block the
|
||||
> start. The same mark works on `command`, where it means carry on if
|
||||
> the binary is missing.
|
||||
|
||||
+186
-96
@@ -1,21 +1,22 @@
|
||||
Service Options
|
||||
===============
|
||||
|
||||
The run/task/tty/service/sysv directives take modifiers, or options, to
|
||||
control their behavior. This section lists them with their limitations.
|
||||
All modifiers must be placed between the directive and its command.
|
||||
The `run`, `task`, `tty`, `service` and `sysv` blocks take settings as
|
||||
keys. This section lists them with their limitations.
|
||||
|
||||
The name of a service, shown by the `initctl` tool, defaults to the
|
||||
basename of the service executable. It can be changed with the `name:`
|
||||
option:
|
||||
The name of a service, shown by the `initctl` tool, is the block title.
|
||||
It defaults to the basename of the executable if the title is omitted:
|
||||
|
||||
name:<service-name>
|
||||
service sshd {
|
||||
command = "/usr/sbin/sshd -D"
|
||||
}
|
||||
|
||||
For multiple instances of a service, with the same `name`, set the
|
||||
identifier `:ID` to prevent Finit from replacing previous instances:
|
||||
For multiple instances of a service, with the same name, add an
|
||||
identifier after a colon to prevent Finit from replacing previous
|
||||
instances:
|
||||
|
||||
service name:ssdp :eth1 ssdpd eth1 -- Windows discovery on eth1
|
||||
service name:ssdp :eth2 ssdpd eth2 -- Windows discovery on eth2
|
||||
service ssdp:eth1 { command = "ssdpd eth1" }
|
||||
service ssdp:eth2 { command = "ssdpd eth2" }
|
||||
|
||||
The [`initctl`](../initctl.md) tool will list these two services as:
|
||||
|
||||
@@ -23,107 +24,194 @@ The [`initctl`](../initctl.md) tool will list these two services as:
|
||||
- ssdp:eth2
|
||||
|
||||
Conflicting services that must be prevented from starting, use the
|
||||
`conflict:` option:
|
||||
`conflicts` key:
|
||||
|
||||
service [S12345789] udevd -- Device event management daemon
|
||||
run [S] conflict:udevd mdev -s -- Populating device tree
|
||||
service udevd { command = "udevd" }
|
||||
run mdev {
|
||||
runlevel = "S"
|
||||
conflicts = { "udevd" }
|
||||
command = "mdev -s"
|
||||
}
|
||||
|
||||
Multiple conflicting services can be separated using `,`:
|
||||
The list may name several, and an instance is named the same way it is
|
||||
declared:
|
||||
|
||||
service :1 abc
|
||||
service :2 abc
|
||||
service conflict:abc:1,abc:2 cde
|
||||
service abc:1 { command = "abc 1" }
|
||||
service abc:2 { command = "abc 2" }
|
||||
service cde { conflicts = { "abc:1", "abc:2" } command = "cde" }
|
||||
|
||||
If a service should not be automatically started, it can be configured
|
||||
as manual with the optional `manual` argument. The service can then be
|
||||
started at any time by running `initctl start <service>`.
|
||||
If a service should not be automatically started, set `manual-start`.
|
||||
It can then be started at any time with `initctl start <service>`:
|
||||
|
||||
manual:yes
|
||||
service lldpd {
|
||||
manual-start = true
|
||||
command = "/usr/sbin/lldpd -d"
|
||||
}
|
||||
|
||||
Other run/task/service options are:
|
||||
Other run/task/service settings are:
|
||||
|
||||
* `caps:...` -- see the [Linux Capabilities](capabilities.md) section
|
||||
* `cgroup.NAME[,opts]` or `cgroup:opts` -- see the [Cgroups](cgroups.md) section
|
||||
* `env:[-]/path/to/env` -- see the [Service Environment](service-env.md) section
|
||||
* `log:...` -- see [Redirecting Output](logging.md#redirecting-output)
|
||||
* `tty:<dev>` -- see [Controlling TTY](tty.md#controlling-tty)
|
||||
* `nowarn` -- see [Conditional Loading](services.md#conditional-loading)
|
||||
* `notify:...` -- see [Service Synchronization](service-sync.md)
|
||||
* `if:...` -- see [Conditional Execution](services.md#conditional-execution)
|
||||
* `type:forking` -- see description of the [service](services.md) directive
|
||||
* `capabilities` -- see the [Linux Capabilities](capabilities.md) section
|
||||
* `cgroup NAME {}` -- see the [Cgroups](cgroups.md) section
|
||||
* `envfile` -- see the [Service Environment](service-env.md) section
|
||||
* `log {}` -- see [Redirecting Output](logging.md#redirecting-output)
|
||||
* `tty` -- see [Controlling TTY](tty.md#controlling-tty)
|
||||
* `notify` -- see [Service Synchronization](service-sync.md)
|
||||
* `if` -- see [Conditional Execution](services.md#conditional-execution)
|
||||
* `type = "forking"` -- see description of the [service](services.md) block
|
||||
* a leading `-` on `command` -- see
|
||||
[Conditional Loading](services.md#conditional-loading)
|
||||
|
||||
As mentioned previously, services are automatically started, restarted,
|
||||
and stopped, depending on the configuration and conditions. Within the
|
||||
confines of that the following options are available:
|
||||
Restarting
|
||||
----------
|
||||
|
||||
* `restart:NUM` -- number of times Finit tries to restart a crashing
|
||||
service, default: 10, max: 255. When this limit is reached the
|
||||
Services are automatically started, restarted, and stopped, depending
|
||||
on the configuration and conditions. Within the confines of that the
|
||||
following settings are available:
|
||||
|
||||
* `restart-max = NUM` -- number of times Finit tries to restart a
|
||||
crashing service, default: 10. When this limit is reached the
|
||||
service is marked *crashed* and must be restarted manually with
|
||||
`initctl restart NAME`
|
||||
* `restart_sec:SEC` -- number of seconds before Finit tries to restart
|
||||
a crashing service, default: 2 seconds for the first five retries,
|
||||
then back-off to 5 seconds. The maximum of this configured value
|
||||
and the above (2 and 5) will be used
|
||||
* `restart:always` -- no upper limit on the number of times Finit
|
||||
tries to restart a crashing service. Same as `restart:-1`
|
||||
* `norestart` -- dont restart on failures, same as `restart:0`
|
||||
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
||||
endless restarts. Useful in many use-cases, but not what `service`
|
||||
was originally designed for so not the default behavior
|
||||
* `remain:yes` -- for `run` and `task` only. Prevents the task from
|
||||
re-running on runlevel re-entry and ensures the `post:` script runs
|
||||
when the task is explicitly stopped or leaves its valid runlevels.
|
||||
Similar to systemd's `RemainAfterExit=yes`. See [Task and Run](task-and-run.md)
|
||||
for more details
|
||||
* `oncrash:reboot` -- when all retries have failed, and the service
|
||||
has *crashed*, if this option is set the system is rebooted
|
||||
* `oncrash:script` -- similarly, but instead of rebooting, call the
|
||||
`post:script` action with exit code `crashed`, see below
|
||||
* `reload:'script [args]'` -- some services do not support `SIGHUP` but
|
||||
may have other ways to update the configuration of a running daemon.
|
||||
When `reload:script` is defined it is preferred over `SIGHUP`. Like
|
||||
systemd, Finit sets `$MAINPID` as a convenience to scripts, which in
|
||||
effect also allow `reload:'kill -HUP $MAINPID'`
|
||||
* `stop:'script [args]'` -- some services may require alternate methods
|
||||
to be stopped. If a `stop:script` is defined it is preferred over
|
||||
`SIGTERM` and `stop`, for `service` and `sysv`, respectively.
|
||||
Similar to `reload:script`, Finit sets `$MAINPID`
|
||||
* `restart-sec = SEC` -- number of seconds before Finit tries to
|
||||
restart a crashing service. The default is 2 seconds for the first
|
||||
half of `restart-max` attempts, then a back-off to 5 seconds -- with
|
||||
the default `restart-max` that is the first five retries. The
|
||||
greater of this configured value and the back-off is used
|
||||
* `restart = "always"` -- no upper limit on the number of times Finit
|
||||
tries to restart a crashing service
|
||||
* `restart = "never"` -- do not restart on failures. `false` is
|
||||
accepted as a synonym, and `true` selects the default policy
|
||||
* `respawn = true` -- bypasses the `restart` mechanism completely,
|
||||
allowing endless restarts. Exiting is treated as normal work
|
||||
rather than failure, so the service is restarted at once instead of
|
||||
being counted and delayed. This is how a `tty` behaves; it is not
|
||||
what `service` was designed for, so it is not the default
|
||||
* `remain-after-exit = true` -- for `run` and `task` only. Prevents
|
||||
the task from re-running on runlevel re-entry and ensures the
|
||||
`exec-stop-post` script runs when the task is explicitly stopped or
|
||||
leaves its valid runlevels. This is systemd's `RemainAfterExit=`.
|
||||
See [Task and Run](task-and-run.md) for more details
|
||||
* `oncrash = "reboot"` -- when all retries have failed, and the
|
||||
service has *crashed*, the system is rebooted
|
||||
* `oncrash = "script"` -- similarly, but instead of rebooting, call
|
||||
the `exec-stop-post` script with exit code `crashed`, see below
|
||||
|
||||
> [!CAUTION]
|
||||
> Both `reload:script` and `stop:script` are called as PID 1, without
|
||||
> any timeout! Meaning, it is up to you to ensure the script is not
|
||||
> blocking for seconds at a time or never terminates.
|
||||
Service directories
|
||||
-------------------
|
||||
|
||||
When stopping a service (run/task/sysv/service), either manually or when
|
||||
moving to another runlevel, Finit starts by sending `SIGTERM`, to allow
|
||||
the process to shut down gracefully (unless a `stop:'script'` is used).
|
||||
However, if the process has not been collected within 3 seconds, Finit
|
||||
will send `SIGKILL`. To stop the process using a different signal than
|
||||
`SIGTERM`, use `halt:SIGNAL`, e.g., `halt:SIGPWR`. To change the delay
|
||||
between the stop signal and KILL, use the option `kill:<1-60>`, e.g.,
|
||||
`kill:10` to wait 10 seconds before sending `SIGKILL`.
|
||||
Five settings ask Finit to create a directory for the service before it
|
||||
starts, owned by its `user` and `group`, mode 0755. The value is a
|
||||
directory name, resolved under a fixed base -- absolute paths and `..`
|
||||
are refused:
|
||||
|
||||
Services, including the `sysv` variant, support pre/post/ready and
|
||||
cleanup scripts:
|
||||
| Setting | Base | Environment variable |
|
||||
|---|---|---|
|
||||
| `runtime-dir` | `/run` | `RUNTIME_DIRECTORY` |
|
||||
| `state-dir` | `/var/lib` | `STATE_DIRECTORY` |
|
||||
| `cache-dir` | `/var/cache` | `CACHE_DIRECTORY` |
|
||||
| `logs-dir` | `/var/log` | `LOGS_DIRECTORY` |
|
||||
| `config-dir` | `/etc` | `CONFIGURATION_DIRECTORY` |
|
||||
|
||||
* `pre:[0-3600,]script` -- called before the sysv/service is stated
|
||||
* `post:[0-3600,]script` -- called after the sysv/service has stopped
|
||||
* `ready:[0-3600,]script` -- called when the sysv/service is ready
|
||||
* `cleanup:[0-3600,]script` -- called when run/task/sysv/service is removed
|
||||
Each resolved path is exported to the process environment under the
|
||||
listed name, the same names systemd uses for `RuntimeDirectory=` and
|
||||
friends. As in systemd, `config-dir` is the odd one out: it is created
|
||||
but never chowned.
|
||||
|
||||
The optional number (0-3600) is the timeout before Finit kills the
|
||||
script, it defaults to the kill delay value and can be disabled by
|
||||
setting it to zero. These scripts run as the same `@USER:GROUP` as the
|
||||
service itself, with any `env:file` sourced. The scripts are executed
|
||||
from the `$HOME` of the given user. The scripts are not called with any
|
||||
argument, but get a set of environment variables:
|
||||
Each takes a matching `-mode`, e.g. `runtime-dir-mode = 0700`, default
|
||||
0755. Modes are octal, with the leading zero.
|
||||
|
||||
The mode of the named directory is locked down again on every start.
|
||||
Its contents are left alone as long as the owner is right; if the owner
|
||||
has drifted, everything under it is chowned back.
|
||||
|
||||
The runtime directory is removed again when the service stops, after
|
||||
any `exec-stop-post` script has run; `/run` is a tmpfs so it would not
|
||||
survive a reboot anyway. The other four persist. A completed `run` or
|
||||
`task` counts as stopped, unless `remain-after-exit` keeps it alive
|
||||
until stopped for real. `runtime-dir-preserve` adjusts this, same
|
||||
values as systemd's `RuntimeDirectoryPreserve=`:
|
||||
|
||||
* `"no"` -- removed when the service stops, the default
|
||||
* `"restart"` -- kept across restarts, removed on a real stop
|
||||
* `"yes"` -- never removed
|
||||
|
||||
This is what lets a service drop privileges and still create, and
|
||||
later touch, its own PID file:
|
||||
|
||||
service ntpd {
|
||||
user = "ntp"
|
||||
group = "ntp"
|
||||
runtime-dir = "ntpd"
|
||||
pidfile = "/run/ntpd/ntpd.pid"
|
||||
command = "/usr/sbin/ntpd -n -p /run/ntpd/ntpd.pid"
|
||||
}
|
||||
|
||||
These settings exist only in the block format.
|
||||
|
||||
Stopping and reloading
|
||||
----------------------
|
||||
|
||||
When stopping a service, either manually or when moving to another
|
||||
runlevel, Finit starts by sending `SIGTERM` to let the process shut
|
||||
down gracefully. If it has not been collected within 3 seconds,
|
||||
`SIGKILL` follows. Both ends of that are configurable:
|
||||
|
||||
* `stop-signal = "SIGPWR"` -- send this instead of `SIGTERM`
|
||||
* `stop-timeout = 10` -- seconds to wait before `SIGKILL`, 1-300
|
||||
|
||||
Some services need more than a signal:
|
||||
|
||||
* `exec-stop = "script [args]"` -- run instead of sending the stop
|
||||
signal, and instead of `stop` for `sysv`
|
||||
* `exec-reload = "script [args]"` -- run instead of sending `SIGHUP`.
|
||||
Like systemd, Finit sets `$MAINPID` as a convenience to scripts,
|
||||
which in effect also allows `exec-reload = "kill -HUP $MAINPID"`
|
||||
|
||||
If a daemon cannot be reloaded with a signal at all, say so and Finit
|
||||
restarts it instead:
|
||||
|
||||
service dropbear {
|
||||
reload-signal = "none"
|
||||
command = "/usr/sbin/dropbear -R -F"
|
||||
}
|
||||
|
||||
`SIGHUP` is the default and the only other accepted value, with or
|
||||
without the `SIG` prefix and in any case.
|
||||
|
||||
Scripts
|
||||
-------
|
||||
|
||||
Services, including the `sysv` variant, support six lifecycle scripts:
|
||||
|
||||
* `exec-start-pre` -- called before the sysv/service is started
|
||||
* `exec-start-ready` -- called when the sysv/service is ready
|
||||
* `exec-stop` -- called instead of the stop signal
|
||||
* `exec-stop-post` -- called after the sysv/service has stopped
|
||||
* `exec-reload` -- called instead of `SIGHUP`
|
||||
* `exec-cleanup` -- called when run/task/sysv/service is removed
|
||||
|
||||
Each takes a matching `-timeout`, in seconds, 0-3600, after which Finit
|
||||
kills the script. It defaults to the `stop-timeout` value and can be
|
||||
disabled by setting it to zero:
|
||||
|
||||
service foo {
|
||||
exec-start-pre = "/etc/foo/pre.sh"
|
||||
exec-start-pre-timeout = 10
|
||||
command = "/usr/sbin/foo"
|
||||
}
|
||||
|
||||
These scripts run as the same user and group as the service itself,
|
||||
with any `envfile` sourced. They are executed from the `$HOME` of the
|
||||
given user. The scripts are not called with any argument, but get a
|
||||
set of environment variables:
|
||||
|
||||
* `SERVICE_IDENT=foo:1`
|
||||
* `SERVICE_NAME=foo`
|
||||
* `SERVICE_ID=1`
|
||||
|
||||
The `post:script` is called with an additional set of environment
|
||||
variables. Yes, the text is correct, the naming was an accident:
|
||||
The `exec-stop-post` script is called with an additional set of
|
||||
environment variables. Yes, the text is correct, the naming was an
|
||||
accident:
|
||||
|
||||
- `EXIT_CODE=[exited,signal,crashed]`: normal exit, signaled, or
|
||||
crashed
|
||||
@@ -131,11 +219,13 @@ variables. Yes, the text is correct, the naming was an accident:
|
||||
the program, if it exited normally, or the signal name (`HUP`,
|
||||
`TERM`, etc.) if it exited due to signal
|
||||
|
||||
When a run/task/sys/service is removed (disable + reload) it is first
|
||||
stopped and then removed from the runlevel. The `post:script` always
|
||||
runs when the process has stopped, and the `cleanup:script` runs when
|
||||
the the stanza has been removed from the runlevel.
|
||||
When a run/task/sysv/service is removed (disable + reload) it is first
|
||||
stopped and then removed from the runlevel. The `exec-stop-post`
|
||||
script always runs when the process has stopped, and `exec-cleanup`
|
||||
runs when the block has been removed from the runlevel.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> These script actions are intended for setup, cleanup, and readiness
|
||||
> notification. It is up to the user to ensure the scripts terminate.
|
||||
> notification. A script that outlives its timeout is killed, so pick
|
||||
> one that suits the work, or set it to zero to opt out and take
|
||||
> responsibility for the script terminating.
|
||||
|
||||
+20
-13
@@ -32,28 +32,35 @@ notification is available, and the native PID file mode of operation is,
|
||||
as of Finit v4.6 optional, by default it is still enabled, but this can
|
||||
be changed in `finit.conf`:
|
||||
|
||||
readiness none
|
||||
readiness = "none"
|
||||
|
||||
This will be made the default in Finit 5.0. In this mode of operation,
|
||||
every service needs to explicitly declare their readiness notification,
|
||||
like this:
|
||||
|
||||
service notify:pid watchdogd
|
||||
service notify:systemd foo
|
||||
service notify:s6 bar
|
||||
service notify:none qux
|
||||
service watchdogd { notify = "pid" command = "watchdogd" }
|
||||
service foo { notify = "systemd" command = "foo" }
|
||||
service bar { notify = "s6" command = "bar" }
|
||||
service qux { notify = "none" command = "qux" }
|
||||
|
||||
The `notify:none` syntax is for completeness in systems which run in
|
||||
`readiness pid` mode (default). Services declared with `notify:none`
|
||||
will transition to ready as soon as Finit has started them, e.g.,
|
||||
`service/qux/ready`.
|
||||
The `notify = "none"` setting is for completeness in systems which run
|
||||
in `readiness = "pid"` mode (default). Services declared with
|
||||
`notify = "none"` will transition to ready as soon as Finit has started
|
||||
them, e.g., `service/qux/ready`.
|
||||
|
||||
To synchronize two services the following condition can be used:
|
||||
|
||||
service notify:pid watchdogd
|
||||
service <service/watchdogd/ready> stress-ng --cpu 8
|
||||
service watchdogd {
|
||||
notify = "pid"
|
||||
command = "watchdogd"
|
||||
}
|
||||
|
||||
For details on the syntax and options, see below.
|
||||
service stress-ng {
|
||||
conditions = { "service/watchdogd/ready" }
|
||||
command = "stress-ng --cpu 8"
|
||||
}
|
||||
|
||||
For the full list of conditions, see [Finit Conditions](../conditions.md).
|
||||
|
||||
> [!NOTE]
|
||||
> On `initctl reload` conditions are set in "flux", while figuring out
|
||||
@@ -66,7 +73,7 @@ For details on the syntax and options, see below.
|
||||
> However, the s6 notify mode does not support this because in s6 you
|
||||
> are expected to close your notify descriptor after having written
|
||||
> `\n`. This means s6 style daemons currently must be stop-started.
|
||||
> (Declare the service with `<!>` in its condition statement.)
|
||||
> (Declare the service with `reload-signal = "none"`.)
|
||||
>
|
||||
> For default, PID file style readiness notification, daemons are
|
||||
> expected to either create their PID files, or touch it using
|
||||
|
||||
@@ -7,14 +7,19 @@ use a wrapper shell script to start your service.
|
||||
|
||||
The Finit service `.conf` file can be put into `/etc/finit.d/available`,
|
||||
so you can control the service using `initctl`. Then use the path to
|
||||
the wrapper script in the Finit `.conf` service stanza. The following
|
||||
the wrapper script in the Finit `.conf` service block. The following
|
||||
example employs a wrapper script in `/etc/start.d`.
|
||||
|
||||
**Example:**
|
||||
|
||||
* `/etc/finit.d/available/program.conf`:
|
||||
|
||||
service [235] <!> /etc/start.d/program -- Example Program
|
||||
service program {
|
||||
description = "Example Program"
|
||||
runlevel = "235"
|
||||
reload-signal = "none"
|
||||
command = "/etc/start.d/program"
|
||||
}
|
||||
|
||||
* `/etc/start.d/program:`
|
||||
|
||||
@@ -26,6 +31,6 @@ example employs a wrapper script in `/etc/start.d`.
|
||||
exec /usr/bin/program $OPTIONS
|
||||
|
||||
> [!NOTE]
|
||||
> The example sets `<!>` to denote that it doesn't support `SIGHUP`.
|
||||
> That way Finit will stop/start the service instead of sending SIGHUP
|
||||
> at restart/reload events.
|
||||
> The example sets `reload-signal = "none"` to say the program does not
|
||||
> support `SIGHUP`. Finit then stop/starts the service instead of
|
||||
> signalling it at restart/reload events.
|
||||
|
||||
+206
-91
@@ -1,16 +1,17 @@
|
||||
Services
|
||||
========
|
||||
|
||||
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
|
||||
**Syntax:** `service NAME { command = "/path/to/daemon ARGS" }`
|
||||
|
||||
Service, or daemon, to be monitored and automatically restarted if it
|
||||
exits prematurely. Finit tries to restart services that die, by default
|
||||
10 times before giving up and marking them as *crashed*. After which
|
||||
they have to be manually restarted with `initctl restart NAME`. The
|
||||
limits controlling this are configurable, see the options below.
|
||||
limits controlling this are configurable, see
|
||||
[Service Options](service-opts.md).
|
||||
|
||||
> [!TIP]
|
||||
> To allow endless restarts, see the [`respawn` option](service-opts.md)
|
||||
> To allow endless restarts, see [`respawn`](service-opts.md#restarting)
|
||||
|
||||
For daemons that support it, we recommend appending `--foreground`,
|
||||
`--no-background`, `-n`, `-F`, or similar command line argument to
|
||||
@@ -19,20 +20,25 @@ the most reliable way to monitor a service.
|
||||
|
||||
However, not all daemons support running in the foreground, or they may
|
||||
start logging to the foreground as well, these are forking daemons and
|
||||
are supported using the same syntax as forking `sysv` services, using
|
||||
the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax
|
||||
that may be more intuitive, where Finit can also guess the PID file
|
||||
based on the daemon's command name:
|
||||
are supported using the same syntax as forking `sysv` services, by
|
||||
naming the file to watch with `pidfile`. There is an alternative that
|
||||
may be more intuitive, where Finit can also guess the PID file based on
|
||||
the daemon's command name:
|
||||
|
||||
service type:forking ntpd -- NTP daemon
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
type = "forking"
|
||||
command = "ntpd"
|
||||
}
|
||||
|
||||
This example lets BusyBox `ntpd` daemonize itself. Finit uses the
|
||||
basename of the binary to guess the PID file to watch for the PID:
|
||||
`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
|
||||
full `pid:!/path/to/file.pid`.
|
||||
`/var/run/ntpd.pid`. If Finit guesses wrong, name the file yourself
|
||||
with `pidfile = "/path/to/file.pid"`.
|
||||
|
||||
With `pid:!/path`, the file belongs to the service: Finit reads it
|
||||
but does not create or remove it. The one exception is *stale*
|
||||
The file belongs to the service: Finit reads it but does not create or
|
||||
remove it. That is the default, and `pidfile-create = true` is what
|
||||
asks Finit to write the file instead. The one exception is *stale*
|
||||
cleanup — if the service dies without removing its own pidfile
|
||||
(SIGKILL, OOM, segfault), and the file still names the just-reaped
|
||||
PID, Finit removes it before the next retry. This prevents daemons
|
||||
@@ -44,56 +50,75 @@ from getting stuck in a crash-restart loop.
|
||||
In the case of `ospfd` (below), we omit the `-d` flag (daemonize) to
|
||||
prevent it from forking to the background:
|
||||
|
||||
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
|
||||
service ospfd {
|
||||
description = "OSPF daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/zebra" }
|
||||
command = "/sbin/ospfd"
|
||||
}
|
||||
|
||||
`[2345]` denote the runlevels `ospfd` is allowed to run in, they are
|
||||
optional and default to level 2-5 if omitted.
|
||||
|
||||
`<...>` is the condition for starting `ospfd`. In this example Finit
|
||||
waits for another service, `zebra`, to have created its PID file in
|
||||
`/var/run/quagga/zebra.pid` before starting `ospfd`. Finit watches
|
||||
*all* files in `/var/run`, for each file named `*.pid`, or `*/pid`,
|
||||
Finit opens it and find the matching `NAME:ID` using the PID.
|
||||
`runlevel` denotes the runlevels `ospfd` is allowed to run in, it is
|
||||
optional and defaults to level 2-4 if omitted.
|
||||
|
||||
The condition can be prefixed with `!` and/or `~`:
|
||||
`conditions` lists what must be asserted before starting `ospfd`. In
|
||||
this example Finit waits for another service, `zebra`, to have created
|
||||
its PID file in `/var/run/quagga/zebra.pid`. Finit watches *all* files
|
||||
in `/var/run`, for each file named `*.pid`, or `*/pid`, Finit opens it
|
||||
and finds the matching `NAME:ID` using the PID.
|
||||
|
||||
- `<!pid/zebra>` -- `ospfd` does not support `SIGHUP` (noreload)
|
||||
- `<~pid/zebra>` -- propagate reload from `zebra` to `ospfd`
|
||||
- `<!~pid/zebra>` -- both: noreload and propagate reload
|
||||
A condition may be prefixed with `~` to propagate a reload of the
|
||||
upstream service to this one, rather than merely pausing and resuming
|
||||
it:
|
||||
|
||||
For details, see the [Finit Conditions](../conditions.md) document.
|
||||
conditions = { "~pid/zebra" }
|
||||
|
||||
If `ospfd` cannot be reloaded with `SIGHUP` at all, that is a property
|
||||
of `ospfd` and not of the condition, so it is said directly:
|
||||
|
||||
reload-signal = "none"
|
||||
|
||||
The legacy format spells that second one as a `!` leading the condition
|
||||
list, which is not accepted here. For details, see the
|
||||
[Finit Conditions](../conditions.md) document.
|
||||
|
||||
Some services do not maintain a PID file and rather than patching each
|
||||
application Finit provides a workaround. A `pid` keyword can be set
|
||||
to have Finit automatically create (when starting) and later remove
|
||||
(when stopping) the PID file. The file is created in the `/var/run`
|
||||
directory using the `basename(1)` of the service. The default can be
|
||||
modified with an optional `pid:`-argument:
|
||||
application Finit provides a workaround. With `pidfile-create` Finit
|
||||
creates the file when starting and removes it when stopping. The path
|
||||
comes from `pidfile`, which takes three forms:
|
||||
|
||||
pid[:[/path/to/]filename[.pid]]
|
||||
pidfile = true # /var/run/<command basename>.pid
|
||||
pidfile = "bar" # a bare name, /var/run/bar.pid
|
||||
pidfile = "/run/bar.pid" # an explicit path
|
||||
|
||||
For example, by adding `pid:/run/bar.pid` to the service `/sbin/bar`,
|
||||
that PID file will, not only be created and removed automatically, but
|
||||
also be used by the Finit condition subsystem. So a service/run/task
|
||||
can depend on `<pid/bar>`, like this foo will not be started until bar
|
||||
has started:
|
||||
Such a file is also used by the Finit condition subsystem, so another
|
||||
service, run or task can depend on `pid/bar`. Here foo is not started
|
||||
until bar has:
|
||||
|
||||
service pid:/run/bar.pid bar -- Bar Service
|
||||
service <pid/bar> foo -- Foo Service
|
||||
service bar {
|
||||
description = "Bar Service"
|
||||
pidfile = "/run/bar.pid"
|
||||
pidfile-create = true
|
||||
command = "bar"
|
||||
}
|
||||
|
||||
service foo {
|
||||
description = "Foo Service"
|
||||
conditions = { "pid/bar" }
|
||||
command = "foo"
|
||||
}
|
||||
|
||||
Needless to say, it is better if `bar` creates its own PID file when it
|
||||
has completed starting up and is ready for service.
|
||||
|
||||
As an alternative "readiness" notification, Finit supports both systemd
|
||||
and s6 style notification. This can be enabled by using the `notify`
|
||||
option:
|
||||
and s6 style notification. This is enabled with the `notify` key:
|
||||
|
||||
* `notify:systemd` -- tells Finit the service uses the `sd_notify()`
|
||||
* `notify = "systemd"` -- tells Finit the service uses the `sd_notify()`
|
||||
API to signal PID 1 when it has completed its startup and is ready
|
||||
to service events. The [sd_notify()][] API expects `NOTIFY_SOCKET`
|
||||
to be set to the socket where the application can send `"READY=1\n"`
|
||||
when it is starting up or has processed a `SIGHUP`.
|
||||
* `notify:s6` -- puts Finit in s6 compatibility mode. Compared to the
|
||||
* `notify = "s6"` -- puts Finit in s6 compatibility mode. Compared to the
|
||||
systemd notification, [s6 expect][] compliant daemons to send `"\n"`
|
||||
and then close their socket. Finit takes care of "hard-wiring" the
|
||||
READY state as long as the application is running, events across any
|
||||
@@ -101,7 +126,11 @@ option:
|
||||
(must be >3) on then command line, Finit provides the following
|
||||
syntax (`%n` is replaced by Finit with then descriptor number):
|
||||
|
||||
service [S12345789] notify:s6 mdevd -O 4 -D %n
|
||||
service mdevd {
|
||||
runlevel = "S12345789"
|
||||
notify = "s6"
|
||||
command = "mdevd -O 4 -D %n"
|
||||
}
|
||||
|
||||
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
[s6 expect]: https://skarnet.org/software/s6/notifywhenup.html
|
||||
@@ -115,7 +144,13 @@ then service's ready condition which other services can depend on:
|
||||
|
||||
This can be used to synchronize the start of another run/task/service:
|
||||
|
||||
task [S] <service/mdevd/ready> @root:root mdevd-coldplug
|
||||
task mdevd-coldplug {
|
||||
runlevel = "S"
|
||||
conditions = { "service/mdevd/ready" }
|
||||
user = "root"
|
||||
group = "root"
|
||||
command = "mdevd-coldplug"
|
||||
}
|
||||
|
||||
Finit waits for `mdevd` to notify it, before starting `mdevd-coldplug`.
|
||||
Notice how both start in runlevel S, and the coldplug task only runs in
|
||||
@@ -131,20 +166,29 @@ Non-privileged Services
|
||||
-----------------------
|
||||
|
||||
Every `run`, `task`, or `service` can also list the privileges the
|
||||
`/path/to/cmd` should be executed with. Prefix the command with
|
||||
`@USR[:GRP[,SUPP,...]]`, where group and supplementary groups are
|
||||
optional, like this:
|
||||
command should be executed with, using `user`, `group` and
|
||||
`extra-groups`, all optional:
|
||||
|
||||
run [2345] @joe:users logger "Hello world"
|
||||
run hello {
|
||||
runlevel = "2345"
|
||||
user = "joe"
|
||||
group = "users"
|
||||
command = "logger \"Hello world\""
|
||||
}
|
||||
|
||||
Finit reads the user's supplementary group membership from `/etc/group`
|
||||
automatically. Any groups the user belongs to will be inherited by
|
||||
the service.
|
||||
|
||||
To specify additional supplementary groups beyond those in `/etc/group`,
|
||||
append them after the primary group, separated by commas:
|
||||
To specify additional supplementary groups beyond those in
|
||||
`/etc/group`, list them in `extra-groups`:
|
||||
|
||||
service @caddy:caddy,ssl-cert /usr/bin/caddy run
|
||||
service caddy {
|
||||
user = "caddy"
|
||||
group = "caddy"
|
||||
extra-groups = { "ssl-cert" }
|
||||
command = "/usr/bin/caddy run"
|
||||
}
|
||||
|
||||
This runs the `caddy` service as user `caddy`, with primary group
|
||||
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
|
||||
@@ -152,81 +196,152 @@ plus the additional `ssl-cert` group. This is useful when a service
|
||||
needs access to resources owned by groups not listed in `/etc/group`.
|
||||
|
||||
For multiple instances of the same command, e.g. a DHCP client or
|
||||
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
||||
`service` keyword and the command, like this:
|
||||
multiple web servers, add `:ID` to the block title, like this:
|
||||
|
||||
service :80 [2345] httpd -f -h /http -p 80 -- Web server
|
||||
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
|
||||
service httpd:80 {
|
||||
description = "Web server"
|
||||
runlevel = "2345"
|
||||
command = "httpd -f -h /http -p 80"
|
||||
}
|
||||
|
||||
Without the `:ID` to the service the latter will overwrite the former
|
||||
and only the old web server would be started and supervised.
|
||||
service httpd:8080 {
|
||||
description = "Old web server"
|
||||
runlevel = "2345"
|
||||
command = "httpd -f -h /http -p 8080"
|
||||
}
|
||||
|
||||
Without the `:ID` the latter will overwrite the former and only the old
|
||||
web server would be started and supervised.
|
||||
|
||||
> [!NOTE]
|
||||
> The line-based format also accepts a bare ID, `service :80 ...`,
|
||||
> deriving the name from the command. There is no block equivalent:
|
||||
> the title carries both name and ID.
|
||||
|
||||
|
||||
Conditional Loading
|
||||
-------------------
|
||||
|
||||
Finit support conditional loading of stanzas. The following example is
|
||||
take from the `system/hotplug.conf` file in the Finit distribution.
|
||||
Finit supports conditional loading of blocks. The following example is
|
||||
taken from the `system/10-hotplug.conf` file in the Finit distribution.
|
||||
Here we only show a simplified subset.
|
||||
|
||||
Starting with the `nowarn` option.
|
||||
Starting with the leading `-` on `command`.
|
||||
|
||||
service nowarn name:udevd pid:udevd /lib/systemd/systemd-udevd
|
||||
service nowarn name:udevd pid:udevd udevd
|
||||
service udevd {
|
||||
pidfile = "udevd"
|
||||
command = "-/lib/systemd/systemd-udevd"
|
||||
}
|
||||
|
||||
When loading the .conf file Finit looks for `/lib/systemd/systemd-udevd`
|
||||
if that is not found Finit automatically logs a warning. The `nowarn`
|
||||
option disables this warning so that the second line can be evaluated,
|
||||
service udevd {
|
||||
pidfile = "udevd"
|
||||
command = "-udevd"
|
||||
}
|
||||
|
||||
When loading the .conf file Finit looks for
|
||||
`/lib/systemd/systemd-udevd`, and if that is not found it logs a
|
||||
warning. The leading `-` says a missing binary is expected here, so
|
||||
the block is skipped quietly and the second one can be evaluated,
|
||||
which also provides a service named `udevd`.
|
||||
|
||||
run nowarn if:udevd <pid/udevd> :1 udevadm settle -t 0
|
||||
run udevadm:1 {
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "pid/udevd" }
|
||||
command = "-udevadm settle -t 0"
|
||||
}
|
||||
|
||||
This line is only loaded if we know of a service named `udevd`. Again,
|
||||
This block is only loaded if we know of a service named `udevd`. Again,
|
||||
we do not warn if `udevadm` is not found, execution will also stop here
|
||||
until the PID condition is asserted, i.e., Finit detecting udevd has
|
||||
started.
|
||||
|
||||
run nowarn conflict:udevd [S] mdev -s -- Populating device tree
|
||||
run mdev {
|
||||
description = "Populating device tree"
|
||||
runlevel = "S"
|
||||
conflicts = { "udevd" }
|
||||
command = "-mdev -s"
|
||||
}
|
||||
|
||||
If `udevd` is not available, we try to run `mdev`, but if that is not
|
||||
found, again we do not warn.
|
||||
|
||||
Conditional loading statements can also be negated, so the previous stanza
|
||||
can also be written as:
|
||||
Conditional loading can also be negated, so the previous block can be
|
||||
written as:
|
||||
|
||||
run nowarn if:!udevd [S] mdev -s -- Populating device tree
|
||||
run mdev {
|
||||
description = "Populating device tree"
|
||||
runlevel = "S"
|
||||
if = "!udevd"
|
||||
command = "-mdev -s"
|
||||
}
|
||||
|
||||
The reason for using `conflict` in this example is that a conflict can be
|
||||
resolved. Stanzas marked with `conflict:foo` are rechecked at runtime.
|
||||
The reason for using `conflicts` in this example is that a conflict can
|
||||
be resolved. Blocks naming a conflict are rechecked at runtime.
|
||||
|
||||
|
||||
Conditional Execution
|
||||
---------------------
|
||||
|
||||
Similar to conditional loading of stanzas there is conditional runtime
|
||||
Similar to conditional loading of blocks there is conditional runtime
|
||||
execution. This can be confusing at first, since Finit already has a
|
||||
condition subsystem, but this is more akin to the qualification to a
|
||||
runlevel. E.g., a `task [123]` is qualified to run only in runlevel 1,
|
||||
2, and 3. It is not considered for other runlevels.
|
||||
runlevel. E.g., a task with `runlevel = "123"` is qualified to run
|
||||
only in runlevel 1, 2, and 3. It is not considered for other
|
||||
runlevels.
|
||||
|
||||
Conditional execution qualify a run/task/service based on a condition.
|
||||
Consider this (simplified) example from the Infix operating system:
|
||||
|
||||
run [S] name:startup <pid/sysrepo> confd -b --load startup-config
|
||||
run [S] if:<usr/fail-startup> name:failure <pid/sysrepo> confd --load failure-config
|
||||
run startup {
|
||||
runlevel = "S"
|
||||
conditions = { "pid/sysrepo" }
|
||||
command = "confd -b --load startup-config"
|
||||
}
|
||||
|
||||
The two run statements reside in the same .conf file so Finit runs them
|
||||
in true sequence. If loading the file `startup-config` fails confd sets
|
||||
the condition `usr/fail-startup`, thus allowing the next run statement
|
||||
to load `failure-config`.
|
||||
run failure {
|
||||
runlevel = "S"
|
||||
if = "usr/fail-startup"
|
||||
conditions = { "pid/sysrepo" }
|
||||
command = "confd --load failure-config"
|
||||
}
|
||||
|
||||
Notice the critical difference between the `<pid/sysrepo>` condition and
|
||||
`if:<usr/fail-startup>`. The former is a condition for starting and the
|
||||
latter is a condition to check if a run/task/service is qualified to
|
||||
even be considered.
|
||||
The two run blocks reside in the same .conf file so Finit runs them in
|
||||
true sequence. If loading the file `startup-config` fails confd sets
|
||||
the condition `usr/fail-startup`, thus allowing the next one to load
|
||||
`failure-config`.
|
||||
|
||||
Conditional execution statements can also be negated, so provided the
|
||||
file loaded did the opposite, i.e., set a condition on success, the
|
||||
previous stanza can also be written as:
|
||||
Notice the critical difference between the `conditions` list and `if`.
|
||||
The former is a condition for starting; the latter is a condition to
|
||||
check whether a run/task/service is qualified to even be considered.
|
||||
`if` has a negation of its own, `!`, which is unrelated to anything in
|
||||
the `conditions` list.
|
||||
|
||||
run [S] if:<!usr/startup-ok> name:failure <pid/sysrepo> confd ...
|
||||
What `if` compares against depends on the value. A namespace
|
||||
separator makes it a condition, anything else is a service name:
|
||||
|
||||
| `if` | Asks |
|
||||
|---|---|
|
||||
| `"udevd"` | is a service by this name known? |
|
||||
| `"usr/foo"` | was this condition set? |
|
||||
|
||||
Both are questions about whether the block belongs in the running
|
||||
configuration at all, usually answered from what bootstrap established.
|
||||
A statement is all of one kind or the other, so the block is rejected
|
||||
if you mix them.
|
||||
|
||||
> [!NOTE]
|
||||
> `if` qualifies, it does not track. A condition asserted or cleared
|
||||
> later does not start or stop the service by itself -- that is what
|
||||
> the `conditions` list is for.
|
||||
|
||||
Conditional execution can also be negated, so provided the file loaded
|
||||
did the opposite, i.e., set a condition on success, the previous block
|
||||
can be written as:
|
||||
|
||||
run failure {
|
||||
runlevel = "S"
|
||||
if = "!usr/startup-ok"
|
||||
conditions = { "pid/sysrepo" }
|
||||
command = "confd ..."
|
||||
}
|
||||
|
||||
+26
-17
@@ -9,42 +9,45 @@ from a serialized boot process.
|
||||
SysV Init Scripts
|
||||
-----------------
|
||||
|
||||
**Syntax:** `sysv [LVLS] <COND> /path/to/init-script -- Optional description`
|
||||
**Syntax:** `sysv NAME { command = "/path/to/init-script" }`
|
||||
|
||||
> `<COND>` is described in the [Services](services.md) section.
|
||||
> [!NOTE]
|
||||
> Conditions, runlevels, and the other settings a `sysv` block takes
|
||||
> are described in [Service Options](service-opts.md).
|
||||
|
||||
Similar to `task` is the `sysv` stanza, which can be used to call SysV
|
||||
style scripts. The primary intention for this command is to be able to
|
||||
reuse much of existing setup and init scripts in Linux distributions.
|
||||
A `sysv` block is a supervised daemon, like `service`, but started and
|
||||
stopped through a SysV style init script instead of a command line. The
|
||||
intention is to reuse existing setup and init scripts from Linux
|
||||
distributions.
|
||||
|
||||
When entering an allowed runlevel, Finit calls `init-script start`, when
|
||||
entering a disallowed runlevel, Finit calls `init-script stop`, and if
|
||||
the Finit .conf, where `sysv` stanza is declared, is modified, Finit
|
||||
the Finit .conf, where the `sysv` block is declared, is modified, Finit
|
||||
calls `init-script restart` on `initctl reload`. Similar to how
|
||||
`service` stanzas work.
|
||||
`service` blocks work.
|
||||
|
||||
Forking services started with `sysv` scripts can be monitored by Finit
|
||||
by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
|
||||
Notice the leading `!`, it signifies Finit should not try to create the
|
||||
file, but rather watch that file for the resulting forked-off PID. This
|
||||
syntax also works for forking daemons that do not have a command line
|
||||
option to run it in the foreground, more on this below in `service`.
|
||||
by declaring the PID file to look for: `pidfile = "/path/to/file.pid"`.
|
||||
Finit does not create that file, it watches it for the resulting
|
||||
forked-off PID. That is the default; `pidfile-create = true` asks Finit
|
||||
to write it instead. The same applies to forking daemons with no way to
|
||||
run in the foreground, see [Services](services.md).
|
||||
|
||||
> [!TIP]
|
||||
> See also [SysV Init Compatibility](#sysv-init-compatibility).
|
||||
|
||||
`runparts DIRECTORY`
|
||||
--------------------
|
||||
Run-parts
|
||||
---------
|
||||
|
||||
For a directory with traditional start/stop scripts that should run, in
|
||||
order, at bootstrap, Finit provides the `runparts` directive. It runs
|
||||
order, at bootstrap, Finit provides the `runparts` setting. It runs
|
||||
in runlevel S, at the very end of it (before calling `/etc/rc.local`)
|
||||
making it perfect for most scenarios.
|
||||
|
||||
For syntax details, see the [Run-parts Scripts](runparts.md) section.
|
||||
Here is an example take from a Debian installation:
|
||||
|
||||
runparts /etc/rc2.d
|
||||
runparts = "/etc/rc2.d"
|
||||
|
||||
Files in these directories are usually named `SNNfoo` and `KNNfoo`,
|
||||
which Finit knows about and automatically appends the correct argument:
|
||||
@@ -64,7 +67,12 @@ Start/Stop Scripts
|
||||
For syntax details, see [SysV Init Scripts](#sysv-init-scripts), above.
|
||||
Here follows an example taken from a Debian installation:
|
||||
|
||||
sysv [2345] <pid/syslogd> /etc/init.d/openbsd-inetd -- OpenBSD inet daemon
|
||||
sysv inetd {
|
||||
description = "OpenBSD inet daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
command = "/etc/init.d/openbsd-inetd"
|
||||
}
|
||||
|
||||
The init script header could be parsed to extract `Default-Start:` and
|
||||
other parameters for the `sysv` command to Finit. There is currently no
|
||||
@@ -83,6 +91,7 @@ it exists, and is executable. It is called very late in the boot process
|
||||
when the system has left runlevel S, stopped all old and started all new
|
||||
services in the target runlevel (default 2).
|
||||
|
||||
> [!NOTE]
|
||||
> In Finit releases before v4.5 this script blocked Finit execution and
|
||||
> made it as good as impossible to call `initctl` during that time.
|
||||
|
||||
|
||||
+34
-20
@@ -1,9 +1,10 @@
|
||||
run (sequence)
|
||||
--------------
|
||||
|
||||
**Syntax:** `run [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||
**Syntax:** `run NAME { command = "/path/to/cmd ARGS" }`
|
||||
|
||||
> `<COND>` is described in the [Services](services.md) section.
|
||||
> Conditions, runlevels, and the other settings a `run` block takes are
|
||||
> described in [Service Options](service-opts.md).
|
||||
|
||||
One-shot command to run in sequence when entering a runlevel, with
|
||||
optional arguments and description. `run` commands are guaranteed to be
|
||||
@@ -31,36 +32,45 @@ also the `--quiet` and `--batch` options.
|
||||
task (parallel)
|
||||
---------------
|
||||
|
||||
**Syntax:** `task [LVLS] <COND> /path/to/cmd ARGS -- Optional description`
|
||||
**Syntax:** `task NAME { command = "/path/to/cmd ARGS" }`
|
||||
|
||||
> `<COND>` is described in the [Services](services.md) section.
|
||||
> A `task` block takes the same settings as `run`, see
|
||||
> [Service Options](service-opts.md).
|
||||
|
||||
One-shot like 'run', but starts in parallel with the next command.
|
||||
|
||||
Both `run` and `task` commands are run in a shell, so basic pipes and
|
||||
redirects can be used:
|
||||
|
||||
task [s] echo "foo" | cat >/tmp/bar
|
||||
task bar {
|
||||
runlevel = "S"
|
||||
command = "echo \"foo\" | cat >/tmp/bar"
|
||||
}
|
||||
|
||||
Please note, `;`, `&&`, `||`, and similar are *not supported*. Any
|
||||
non-trivial constructs are better placed in a separate shell script.
|
||||
|
||||
|
||||
remain:yes
|
||||
----------
|
||||
remain-after-exit
|
||||
-----------------
|
||||
|
||||
By default, a `run` or `task` will re-run each time its runlevel is
|
||||
entered, and its `post:` script does not run on completion.
|
||||
entered, and its `exec-stop-post` script does not run on completion.
|
||||
|
||||
With `remain:yes`, the task runs once and does not re-run on runlevel
|
||||
re-entry:
|
||||
With `remain-after-exit`, the task runs once and does not re-run on
|
||||
runlevel re-entry:
|
||||
|
||||
task [2345] remain:yes /usr/sbin/setup-firewall -- Firewall setup
|
||||
task firewall {
|
||||
description = "Firewall setup"
|
||||
runlevel = "2345"
|
||||
remain-after-exit = true
|
||||
command = "/usr/sbin/setup-firewall"
|
||||
}
|
||||
|
||||
This has the following effects:
|
||||
|
||||
* The task does not re-run on runlevel re-entry
|
||||
* The `post:` script runs when:
|
||||
* The `exec-stop-post` script runs when:
|
||||
- The task is explicitly stopped (`initctl stop NAME`)
|
||||
- The task leaves its valid runlevels (e.g., runlevel change)
|
||||
|
||||
@@ -72,16 +82,20 @@ This is useful for tasks that set up persistent state where:
|
||||
**Example:** Setting up firewall rules with cleanup on shutdown:
|
||||
|
||||
```
|
||||
task [2345] remain:yes \
|
||||
post:/usr/sbin/teardown-firewall \
|
||||
/usr/sbin/setup-firewall -- Firewall setup
|
||||
task firewall {
|
||||
description = "Firewall setup"
|
||||
runlevel = "2345"
|
||||
remain-after-exit = true
|
||||
exec-stop-post = "/usr/sbin/teardown-firewall"
|
||||
command = "/usr/sbin/setup-firewall"
|
||||
}
|
||||
```
|
||||
|
||||
The firewall rules are created once. The `post:` script runs when
|
||||
The firewall rules are created once. The `exec-stop-post` script runs when
|
||||
entering runlevel 0 (halt) or 6 (reboot), or on explicit stop.
|
||||
|
||||
> [!NOTE]
|
||||
> The `remain:yes` option is not supported for bootstrap-only tasks
|
||||
> (tasks with only runlevel S). Bootstrap tasks are deleted immediately
|
||||
> after completion, and their `post:` scripts never run. A warning is
|
||||
> logged if `remain:yes` is used on such tasks.
|
||||
> The `remain-after-exit` setting is not supported for bootstrap-only
|
||||
> tasks (tasks with only runlevel S). Bootstrap tasks are deleted
|
||||
> immediately after completion, and their `exec-stop-post` scripts never
|
||||
> run. A warning is logged if `remain-after-exit` is used on such tasks.
|
||||
|
||||
@@ -5,16 +5,20 @@ Finit comes with rudimentary support for templating, similar to that of
|
||||
systemd. Best illustrated with an example:
|
||||
|
||||
$ initctl show avahi-autoipd@
|
||||
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
|
||||
service avahi-autoipd:%i {
|
||||
description = "ZeroConf for %i"
|
||||
command = "avahi-autoipd --syslog %i"
|
||||
}
|
||||
|
||||
To enable ZeroConf for, e.g., `eth0`, use
|
||||
|
||||
$ initctl enable avahi-autoipd@eth0.conf
|
||||
|
||||
The enabled symlink will be set up to `avahi-autoipd@.conf` and every
|
||||
instance of `%i` will in the instantiated directive be replaced with
|
||||
`eth0`. Inspect the resulting instantiated template with `initctl show
|
||||
avahi-autoipd:eth0` and check the status of a running instance with:
|
||||
instance of `%i` will be replaced with `eth0` before the file is
|
||||
parsed, so it works in the block title, in any value, and in the
|
||||
command line alike. Inspect the result with `initctl show
|
||||
avahi-autoipd:eth0`, and check a running instance with:
|
||||
|
||||
```
|
||||
$ initctl status avahi-autoipd:eth0
|
||||
|
||||
+94
-45
@@ -1,67 +1,94 @@
|
||||
TTYs and Consoles
|
||||
=================
|
||||
|
||||
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
|
||||
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
|
||||
`tty [LVLS] <COND> [notty] [rescue]`
|
||||
**Syntax:** `tty NAME { device = DEV }` -- built-in getty
|
||||
`tty NAME { command = "CMD ARGS" }` -- external getty
|
||||
`tty NAME { notty = true }`, or `{ rescue = true }` -- bare shell, no device
|
||||
|
||||
The first variant of this option uses the built-in getty on the given
|
||||
TTY device DEV, in the given runlevels. DEV may be the special keyword
|
||||
`@console`, which is expanded from `/sys/class/tty/console/active`,
|
||||
useful on embedded systems.
|
||||
The block title NAME is what `initctl` shows. The three variants differ
|
||||
in what they open: `device` runs the built-in getty on that TTY, and DEV
|
||||
may be the special keyword `@console`, expanded from
|
||||
`/sys/class/tty/console/active`, useful on embedded systems. `command`
|
||||
hands the TTY to an external getty. `notty` opens nothing at all.
|
||||
|
||||
The default baud rate is 0, i.e., keep kernel default.
|
||||
Settings common to all three:
|
||||
|
||||
> The `tty` stanza inherits runlevel, condition (and other feature)
|
||||
> parsing from the `service` stanza. So TTYs can run in one or many
|
||||
| Setting | Alias | Description |
|
||||
|---|---|---|
|
||||
| `runlevel` | | Runlevels to run in, e.g. `"12345"` |
|
||||
| `conditions` | `cond` | Conditions to wait for |
|
||||
| `noclear` | | Do not clear the TTY after each session |
|
||||
| `nowait` | | Do not wait for Enter before the login prompt |
|
||||
| `nologin` | | Skip login, give a shell straight away |
|
||||
|
||||
The `device` variant takes two more:
|
||||
|
||||
| Setting | Description |
|
||||
|---|---|
|
||||
| `baud` | Baud rate, default 0, i.e., keep kernel default |
|
||||
| `term` | `$TERM` value, e.g. `"vt220"` |
|
||||
|
||||
> A `tty` block inherits runlevel, condition (and other feature)
|
||||
> parsing from the `service` block. So TTYs can run in one or many
|
||||
> runlevels and depend on any condition supported by Finit. This is
|
||||
> useful e.g. to depend on `<pid/elogind>` before starting a TTY.
|
||||
|
||||
**Example:**
|
||||
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||
tty ttyAMA0 {
|
||||
runlevel = "12345"
|
||||
device = "/dev/ttyAMA0"
|
||||
baud = 115200
|
||||
term = "vt220"
|
||||
noclear = true
|
||||
}
|
||||
|
||||
The second `tty` syntax variant is for using an external getty, like
|
||||
agetty or the BusyBox getty.
|
||||
|
||||
The third variant is for board bringup and the `rescue` boot mode. No
|
||||
device node is required in this variant, the same output that the kernel
|
||||
uses is reused for stdio. If the `rescue` option is omitted, a shell is
|
||||
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
|
||||
option is set the bundled `/libexec/finit/sulogin` is started to present
|
||||
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
|
||||
not have a password set, no rescue is possible. For more information,
|
||||
see the [Rescue Mode](rescue.md) section.
|
||||
device node is required, the same output the kernel uses is reused for
|
||||
stdio. With `notty` a shell is started (`nologin`, `noclear`, and
|
||||
`nowait` are implied); with `rescue` the bundled
|
||||
`/libexec/finit/sulogin` presents a bare-bones root login prompt. If
|
||||
the root (uid:0, gid:0) user does not have a password set, no rescue is
|
||||
possible. For more information, see the [Rescue Mode](rescue.md)
|
||||
section.
|
||||
|
||||
By default, the first two syntax variants *clear* the TTY and *wait* for
|
||||
the user to press enter before starting getty.
|
||||
|
||||
**Example:**
|
||||
|
||||
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
||||
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
|
||||
tty getty {
|
||||
runlevel = "12345"
|
||||
command = "/sbin/getty -L 115200 /dev/ttyAMA0 vt100"
|
||||
}
|
||||
|
||||
The `noclear` option disables clearing the TTY after each session.
|
||||
tty agetty {
|
||||
runlevel = "12345"
|
||||
command = "/sbin/agetty -L ttyAMA0 115200 vt100"
|
||||
nowait = true
|
||||
}
|
||||
|
||||
The `noclear` setting disables clearing the TTY after each session.
|
||||
Clearing the TTY when a user logs out is usually preferable.
|
||||
|
||||
The `nowait` option disables the `press Enter to activate console`
|
||||
The `nowait` setting disables the `press Enter to activate console`
|
||||
message before actually starting the getty program. On small and
|
||||
embedded systems running multiple unused getty wastes both memory
|
||||
and CPU cycles, so `wait` is the preferred default.
|
||||
|
||||
The `nologin` option disables getty and `/bin/login`, and gives the
|
||||
user a root (login) shell on the given TTY `<DEV>` immediately.
|
||||
The `nologin` setting disables getty and `/bin/login`, and gives the
|
||||
user a root (login) shell on the given TTY immediately.
|
||||
Needless to say, this is a rather insecure option, but can be very
|
||||
useful for developer builds, during board bringup, or similar.
|
||||
|
||||
Notice the ordering, the `TERM` option to the built-in getty must be
|
||||
the last argument.
|
||||
|
||||
Embedded systems may want to enable automatic `DEV` by supplying the
|
||||
special `@console` device. This works regardless weather the system
|
||||
special `@console` device. This works regardless whether the system
|
||||
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
|
||||
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
|
||||
can be omitted to keep the kernel default.
|
||||
it out by querying sysfs: `/sys/class/tty/console/active`. Leave
|
||||
`baud` out 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,
|
||||
@@ -69,45 +96,67 @@ can be omitted to keep the kernel default.
|
||||
|
||||
**Example:**
|
||||
|
||||
tty [12345] @console noclear vt220
|
||||
tty console {
|
||||
runlevel = "12345"
|
||||
device = "@console"
|
||||
term = "vt220"
|
||||
noclear = true
|
||||
}
|
||||
|
||||
On really bare bones systems, or for board bringup, Finit can give you a
|
||||
shell prompt as soon as bootstrap is done, without opening any device
|
||||
node:
|
||||
|
||||
tty [12345789] notty
|
||||
tty board {
|
||||
runlevel = "12345789"
|
||||
notty = true
|
||||
}
|
||||
|
||||
This should of course not be enabled on production systems. Because it
|
||||
may give a user root access without having to log in. However, for
|
||||
board bringup and system debugging it can come in handy.
|
||||
|
||||
One can also use the `service` stanza to start a stand-alone shell:
|
||||
One can also use a `service` block to start a stand-alone shell:
|
||||
|
||||
service [12345] /bin/sh -l
|
||||
service shell {
|
||||
runlevel = "12345"
|
||||
command = "/bin/sh -l"
|
||||
}
|
||||
|
||||
Controlling TTY for Services
|
||||
----------------------------
|
||||
|
||||
The `tty:<dev>` option gives a `run`, `task`, or `service` a controlling
|
||||
The `tty` setting gives a `run`, `task`, or `service` a controlling
|
||||
terminal on the given device. The device is opened, set as the
|
||||
controlling terminal for the session (after `setsid()`), and connected to
|
||||
the process's stdin, stdout, and stderr. A default `TERM` environment
|
||||
variable is set based on the device type: `vt102` for serial lines and
|
||||
`linux` for virtual terminals.
|
||||
|
||||
`<dev>` may be a device node like `/dev/ttyS0`, or the special keyword
|
||||
`@console` (see above). Note that `@console` expands only to the
|
||||
first console, not all.
|
||||
The value may be a device node like `/dev/ttyS0`, or the special
|
||||
keyword `@console` (see above). Note that `@console` expands only to
|
||||
the first console, not all.
|
||||
|
||||
When `tty:` is combined with `log:`, stdout and stderr are redirected
|
||||
to the log sink instead of the TTY, but stdin remains connected to the
|
||||
TTY device.
|
||||
When `tty` is combined with a `log` block, stdout and stderr are
|
||||
redirected to the log sink instead of the TTY, but stdin remains
|
||||
connected to the TTY device.
|
||||
|
||||
> The `tty:<dev>` option is for `run`, `task`, and `service` stanzas only.
|
||||
> The `tty` directive itself (for getty/login) has its own syntax, see
|
||||
> The `tty` setting is for `run`, `task`, and `service` blocks only.
|
||||
> A `tty` block (for getty/login) is a different thing entirely, see
|
||||
> above.
|
||||
|
||||
**Example:**
|
||||
|
||||
service [2345] tty:/dev/ttyS0 /usr/sbin/foo -- Foo on serial console
|
||||
task [S] tty:@console my-setup-script -- Board bringup on console
|
||||
service foo {
|
||||
description = "Foo on serial console"
|
||||
runlevel = "2345"
|
||||
tty = "/dev/ttyS0"
|
||||
command = "/usr/sbin/foo"
|
||||
}
|
||||
|
||||
task setup {
|
||||
description = "Board bringup on console"
|
||||
runlevel = "S"
|
||||
tty = "@console"
|
||||
command = "my-setup-script"
|
||||
}
|
||||
+135
-58
@@ -10,117 +10,194 @@ See the [contrib/][contrib] directory on GitHub for examples, or take a
|
||||
peek at systems using Finit, like [Infix OS][infix] and [myLinux][].
|
||||
|
||||
> [!TIP]
|
||||
> As of Finit v4.4, `.conf` lines can be broken up using the standard UNIX
|
||||
> continuation character (`\`), trailing comments are also supported. The
|
||||
> latter means you must escape any hashes used in directives and descriptions
|
||||
> (`\#`). For more on this and examples, see the [finit.conf(5)][] manual or
|
||||
> the [Configuration](config/index.md) section.
|
||||
> A block spans as many lines as it needs, so no continuation character is
|
||||
> called for. For the full syntax, see the [finit.conf(5)][] manual or the
|
||||
> [Configuration](config/index.md) section.
|
||||
|
||||
```ApacheConf
|
||||
# Fallback if /etc/hostname is missing
|
||||
host default
|
||||
hostname = "default"
|
||||
|
||||
# Runlevel to start after bootstrap, 'S', default: 2
|
||||
#runlevel 2
|
||||
#runlevel = 2
|
||||
|
||||
# Support for setting global environment variables, using foo=bar syntax
|
||||
# be careful though with variables like PATH, SHELL, LOGNAME, etc.
|
||||
#PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
# Global environment variables, be careful though with variables like
|
||||
# PATH, SHELL, LOGNAME, etc.
|
||||
#environment {
|
||||
# PATH = "/usr/bin:/bin:/usr/sbin:/sbin"
|
||||
#}
|
||||
|
||||
# Max file size for each log file: 100 kiB, rotate max 4 copies:
|
||||
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
|
||||
log size=100k count=4
|
||||
log {
|
||||
size = 100k
|
||||
count = 4
|
||||
}
|
||||
|
||||
# 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] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
|
||||
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
||||
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab)
|
||||
service watchdog {
|
||||
description = "System watchdog daemon"
|
||||
runlevel = "S12345"
|
||||
envfile = "-/etc/conf.d/watchdog"
|
||||
command = "watchdog $WATCHDOG_OPTS $WATCHDOG_DEV"
|
||||
}
|
||||
service syslogd {
|
||||
description = "System log daemon"
|
||||
runlevel = "S12345"
|
||||
envfile = "-/etc/conf.d/syslog"
|
||||
command = "syslogd -n $SYSLOGD_OPTS"
|
||||
}
|
||||
service klogd {
|
||||
description = "Kernel log daemon"
|
||||
runlevel = "S12345"
|
||||
conditions = { "pid/syslogd" }
|
||||
envfile = "-/etc/conf.d/klogd"
|
||||
command = "klogd -n $KLOGD_OPTS"
|
||||
}
|
||||
service lldpd {
|
||||
description = "LLDP daemon (IEEE 802.1ab)"
|
||||
runlevel = "2345"
|
||||
envfile = "-/etc/conf.d/lldpd"
|
||||
command = "lldpd -d $LLDPD_OPTS"
|
||||
}
|
||||
|
||||
# The BusyBox ntpd does not use syslog when running in the foreground
|
||||
# So we use this trick to redirect stdout/stderr to a log file. The
|
||||
# log file is rotated with the above settings. The condition declares
|
||||
# a dependency on a system default route (gateway) to be set. A single
|
||||
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
|
||||
service [2345] log:/var/log/ntpd.log <!net/route/default> ntpd -n -l -I eth0 -- NTP daemon
|
||||
# a dependency on a system default route (gateway) to be set. ntpd
|
||||
# does not respect SIGHUP, so Finit restarts it on reload instead.
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "net/route/default" }
|
||||
reload-signal = "none"
|
||||
log { file = "/var/log/ntpd.log" }
|
||||
command = "ntpd -n -l -I eth0"
|
||||
}
|
||||
|
||||
# For multiple instances of the same service, add :ID somewhere between
|
||||
# the service/run/task keyword and the command.
|
||||
service :80 [2345] merecat -n -p 80 /var/www -- Web server
|
||||
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
|
||||
# For multiple instances of the same service, add :ID to the block title.
|
||||
service merecat:80 {
|
||||
description = "Web server"
|
||||
runlevel = "2345"
|
||||
command = "merecat -n -p 80 /var/www"
|
||||
}
|
||||
service merecat:8080 {
|
||||
description = "Old web server"
|
||||
runlevel = "2345"
|
||||
command = "merecat -n -p 8080 /var/www"
|
||||
}
|
||||
|
||||
# Alternative method instead of below runparts, can also use /etc/rc.local
|
||||
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
|
||||
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
|
||||
#task [S] /etc/init.d/kbd -- Preparing console
|
||||
#sysv keyboard-setup {
|
||||
# description = "Setting up preliminary keymap"
|
||||
# runlevel = "S"
|
||||
# command = "/etc/init.d/keyboard-setup"
|
||||
#}
|
||||
|
||||
# Hidden from boot progress, using empty `--` description
|
||||
#sysv [S] /etc/init.d/keyboard-setup --
|
||||
#sysv [S] /etc/init.d/acpid --
|
||||
#task [S] /etc/init.d/kbd --
|
||||
# Hidden from boot progress, using an empty description
|
||||
#sysv acpid {
|
||||
# description = ""
|
||||
# runlevel = "S"
|
||||
# command = "/etc/init.d/acpid"
|
||||
#}
|
||||
|
||||
# Run start scripts from this directory
|
||||
# runparts /etc/start.d
|
||||
#runparts = "/etc/start.d"
|
||||
|
||||
# Virtual consoles run BusyBox getty, keep kernel default speed
|
||||
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
|
||||
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
|
||||
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
|
||||
tty tty1 {
|
||||
runlevel = "12345"
|
||||
command = "/sbin/getty -L 0 /dev/tty1 linux"
|
||||
nowait = true
|
||||
noclear = true
|
||||
}
|
||||
tty tty2 {
|
||||
runlevel = "2345"
|
||||
command = "/sbin/getty -L 0 /dev/tty2 linux"
|
||||
nowait = true
|
||||
noclear = true
|
||||
}
|
||||
tty tty3 {
|
||||
runlevel = "2345"
|
||||
command = "/sbin/getty -L 0 /dev/tty3 linux"
|
||||
nowait = true
|
||||
noclear = true
|
||||
}
|
||||
|
||||
# Use built-in getty for serial port and USB serial
|
||||
#tty [12345] /dev/ttyAMA0 noclear nowait
|
||||
#tty [12345] /dev/ttyUSB0 noclear
|
||||
#tty ttyAMA0 { runlevel = "12345" device = "/dev/ttyAMA0" noclear = true nowait = true }
|
||||
#tty ttyUSB0 { runlevel = "12345" device = "/dev/ttyUSB0" noclear = true }
|
||||
|
||||
# Just give me a shell, I need to debug this embedded system!
|
||||
#tty [12345] console noclear nologin
|
||||
#tty console { runlevel = "12345" device = "@console" noclear = true nologin = true }
|
||||
```
|
||||
|
||||
The `service` stanza, as well as `task`, `run` and others are described in
|
||||
The `service` block, as well as `task`, `run` and others are described in
|
||||
full in the [Services Syntax](config/services.md) section.
|
||||
|
||||
Here's a quick overview of some of the most common components needed to start
|
||||
a UNIX daemon:
|
||||
|
||||
```
|
||||
service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Example daemon
|
||||
^ ^ ^ ^ ^ ^ ^ ^
|
||||
| | | | | | | `---------- Optional description
|
||||
| | | | | | `------------------ Daemon arguments
|
||||
| | | | | `------------------------- Path to daemon
|
||||
| | | | `---------------------------------------------------- Optional env. file
|
||||
| | | `-------------------------------------------------------- Redirect output to log
|
||||
| | `--------------------------------------------------------------- Optional conditions
|
||||
| `---------------------------------------------------------------------- Optional Runlevels
|
||||
`------------------------------------------------------------------------------ Supervised program (daemon)
|
||||
service NAME { <-- Supervised program (daemon)
|
||||
description = "Example daemon" <-- Optional description
|
||||
runlevel = "2345" <-- Optional runlevels
|
||||
conditions = { "net/route/default" } <-- Optional conditions
|
||||
envfile = "-/etc/default/daemon" <-- Optional env. file
|
||||
log { } <-- Redirect output to log
|
||||
command = "daemon ARGS" <-- Path to daemon, and its arguments
|
||||
}
|
||||
```
|
||||
|
||||
Some components are optional: runlevel(s), condition(s) and description,
|
||||
making it easy to create simple start scripts and still possible for more
|
||||
advanced uses as well:
|
||||
Only `command` is required, which makes simple cases short while leaving
|
||||
room for more advanced uses:
|
||||
|
||||
service /usr/sbin/sshd -D
|
||||
service sshd {
|
||||
command = "/usr/sbin/sshd -D"
|
||||
}
|
||||
|
||||
Dependencies are handled using [conditions](conditions.md). One of
|
||||
the most common conditions is to wait for basic networking to become
|
||||
available:
|
||||
|
||||
service <net/route/default> nginx -- High performance HTTP server
|
||||
service nginx {
|
||||
description = "High performance HTTP server"
|
||||
conditions = { "net/route/default" }
|
||||
command = "nginx"
|
||||
}
|
||||
|
||||
Here is another example where we instruct Finit to not start BusyBox
|
||||
`ntpd` until `syslogd` has started properly. Finit waits for `syslogd`
|
||||
to create its PID file, by default `/var/run/syslogd.pid`.
|
||||
|
||||
service [2345] log <!pid/syslogd> ntpd -n -N -p pool.ntp.org
|
||||
service [S12345] syslogd -n -- Syslog daemon
|
||||
service ntpd {
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd" }
|
||||
reload-signal = "none"
|
||||
log { }
|
||||
command = "ntpd -n -N -p pool.ntp.org"
|
||||
}
|
||||
|
||||
Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
|
||||
run in the foreground. With `log` Finit redirects `stdout` + `stderr`
|
||||
to the system log daemon using the command line `logger(1)` tool.
|
||||
service syslogd {
|
||||
description = "Syslog daemon"
|
||||
runlevel = "S12345"
|
||||
command = "syslogd -n"
|
||||
}
|
||||
|
||||
Notice the empty `log` block, BusyBox `ntpd` uses `stderr` for logging
|
||||
when run in the foreground. With it Finit redirects `stdout` +
|
||||
`stderr` to the system log daemon using the command line `logger(1)`
|
||||
tool.
|
||||
|
||||
A service, or task, can have multiple dependencies listed. Here we wait
|
||||
for *both* `syslogd` to have started and basic networking to be up:
|
||||
|
||||
service [2345] log <pid/syslogd,net/route/default> ntpd -n -N -p pool.ntp.org
|
||||
service ntpd {
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/syslogd", "net/route/default" }
|
||||
log { }
|
||||
command = "ntpd -n -N -p pool.ntp.org"
|
||||
}
|
||||
|
||||
If either condition fails, e.g. loss of networking, `ntpd` is stopped
|
||||
and as soon as it comes back up again `ntpd` is restarted automatically.
|
||||
|
||||
+41
-25
@@ -18,9 +18,9 @@ waits for user input before handing over to `/bin/login`, which is
|
||||
responsible for handling the actual authentication.
|
||||
|
||||
```conf
|
||||
tty [12345] /dev/tty1 nowait linux
|
||||
tty [12345] /dev/ttyAMA0 noclear vt100
|
||||
tty [12345] /sbin/getty -L /dev/ttyAMA0 vt100
|
||||
tty tty1 { runlevel = "12345" device = "/dev/tty1" term = "linux" nowait = true }
|
||||
tty ttyAMA0 { runlevel = "12345" device = "/dev/ttyAMA0" term = "vt100" noclear = true }
|
||||
tty getty { runlevel = "12345" command = "/sbin/getty -L /dev/ttyAMA0 vt100" }
|
||||
```
|
||||
|
||||
Users of embedded systems may want to enable automatic serial console
|
||||
@@ -29,7 +29,7 @@ system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
|
||||
figures it out by querying sysfs: `/sys/class/tty/console/active`.
|
||||
|
||||
```conf
|
||||
tty [12345] @console linux noclear
|
||||
tty console { runlevel = "12345" device = "@console" term = "linux" noclear = true }
|
||||
```
|
||||
|
||||
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
|
||||
@@ -40,21 +40,21 @@ see the [TTY and Consoles](config/tty.md) section.
|
||||
**Runlevels**
|
||||
|
||||
Support for SysV init-style [runlevels][5] is available, in the same
|
||||
minimal style as everything else in Finit. The `[2345]` syntax can be
|
||||
applied to service, task, run, and TTY stanzas.
|
||||
minimal style as everything else in Finit. The `runlevel` setting
|
||||
applies to service, task, run, and tty blocks alike.
|
||||
|
||||
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
|
||||
SysV init. Runlevel 1 can be configured freely, but is recommended to
|
||||
be kept as the system single-user runlevel since Finit will not start
|
||||
networking here. The configured `runlevel NUM` from `/etc/finit.conf`
|
||||
networking here. The configured `runlevel` from `/etc/finit.conf`
|
||||
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
|
||||
given on the kernel cmdline, in which case runlevel 1 is started.
|
||||
|
||||
All services in runlevel S) are started first, followed by the desired
|
||||
All services in runlevel S are started first, followed by the desired
|
||||
run-time runlevel. Run tasks in runlevel S can be started in sequence
|
||||
by using `run [S] cmd`. Changing runlevels at runtime is done like any
|
||||
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
|
||||
[`initctl`](initctl.md) tool.
|
||||
by using a `run` block with `runlevel = "S"`. Changing runlevels at
|
||||
runtime is done like any other init, e.g. <kbd>init 4</kbd>, but also
|
||||
using the more advanced [`initctl`](initctl.md) tool.
|
||||
|
||||
|
||||
**Conditions**
|
||||
@@ -86,8 +86,17 @@ the condition `<hw/model/foo>` when starting other scripts. Here is an
|
||||
example:
|
||||
|
||||
```
|
||||
run [S] /path/to/ident --
|
||||
task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
|
||||
run ident {
|
||||
description = ""
|
||||
runlevel = "S"
|
||||
command = "/path/to/ident"
|
||||
}
|
||||
task foo-init {
|
||||
description = "Initializing Foo board"
|
||||
runlevel = "2"
|
||||
conditions = { "hw/model/foo" }
|
||||
command = "/path/to/foo-init"
|
||||
}
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
@@ -141,17 +150,20 @@ required privileges instead of running as root. This improves security by
|
||||
following the principle of least privilege.
|
||||
|
||||
```conf
|
||||
service [2345] name:nginx \
|
||||
www-data:www-data \
|
||||
caps:^cap_net_bind_service \
|
||||
/usr/sbin/nginx -g 'daemon off;'
|
||||
service nginx {
|
||||
runlevel = "2345"
|
||||
user = "www-data"
|
||||
group = "www-data"
|
||||
capabilities = { "^cap_net_bind_service" }
|
||||
command = "/usr/sbin/nginx -g 'daemon off;'"
|
||||
}
|
||||
```
|
||||
|
||||
In this example, nginx runs as the unprivileged `www-data` user but retains
|
||||
the ability to bind to privileged ports (80, 443) through the
|
||||
`cap_net_bind_service` capability.
|
||||
|
||||
The `caps:` directive uses the IAB (Inheritable, Ambient, Bounding) format:
|
||||
The `capabilities` list uses the IAB (Inheritable, Ambient, Bounding) format:
|
||||
- `^` = Ambient (recommended) - capabilities survive exec()
|
||||
- `%` = Inheritable only - requires file capabilities
|
||||
- `!` = Bounding - block from acquiring capability
|
||||
@@ -159,7 +171,7 @@ The `caps:` directive uses the IAB (Inheritable, Ambient, Bounding) format:
|
||||
Multiple capabilities can be specified as comma-separated:
|
||||
|
||||
```conf
|
||||
caps:^cap_net_raw,^cap_net_admin,!cap_sys_admin
|
||||
capabilities = { "^cap_net_raw", "^cap_net_admin", "!cap_sys_admin" }
|
||||
```
|
||||
|
||||
See the [Linux Capabilities](config/capabilities.md) section for detailed
|
||||
@@ -210,15 +222,19 @@ 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:
|
||||
declared as such in their service block like this:
|
||||
|
||||
service [...] <...> cgroup.root /path/to/foo args -- description
|
||||
service foo {
|
||||
cgroup root {}
|
||||
command = "/path/to/foo args"
|
||||
}
|
||||
|
||||
or
|
||||
Every block names the group it joins, so a second RT task says so too:
|
||||
|
||||
cgroup.root
|
||||
service [...] <...> /path/to/foo args -- description
|
||||
service [...] <...> /path/to/bar args -- description
|
||||
service bar {
|
||||
cgroup root {}
|
||||
command = "/path/to/bar args"
|
||||
}
|
||||
|
||||
See the [Cgroups](config/cgroups.md) section for more information, e.g.,
|
||||
how to configure per-group limits.
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ For your convenience a set of *optional* plugins are available:
|
||||
name of the module to load. Any line starting with the standard UNIX
|
||||
comment character, `#`, or `;`, is skipped.
|
||||
|
||||
Modules are by default loaded in runlevel `S` using the `task` stanza.
|
||||
Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
|
||||
Modules are by default loaded in runlevel `S` using a `task` block.
|
||||
Each module is named `modprobe.foo`, with an optional `:ID`. The
|
||||
runlevel can be changed per file using:
|
||||
|
||||
set runlevel 2345
|
||||
|
||||
+5
-1
@@ -15,7 +15,11 @@ done slightly differently and on systems with udev you might want to add
|
||||
the following one-shot task early in your `/etc/finit.conf`:
|
||||
|
||||
```conf
|
||||
run [S] udevadm settle --timeout=120 -- Waiting for udev
|
||||
run udevadm {
|
||||
description = "Waiting for udev"
|
||||
runlevel = "S"
|
||||
command = "udevadm settle --timeout=120"
|
||||
}
|
||||
```
|
||||
|
||||
Finit has a built-in Getty for TTYs, but requires a working `/bin/login`
|
||||
|
||||
+36
-13
@@ -13,20 +13,37 @@ more of a policy for the user to define. Normally only runlevels 1-6
|
||||
are used, and even more commonly, only the default runlevel is used.
|
||||
|
||||
To specify an allowed set of runlevels for a `service`, `run` command,
|
||||
`task`, or `tty`, add `[NNN]` to your `/etc/finit.conf`, like this:
|
||||
`task`, or `tty`, set `runlevel` in your `/etc/finit.conf`, like this:
|
||||
|
||||
```
|
||||
service [S12345] syslogd -n -x -- System log daemon
|
||||
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||
task [S] /etc/init.d/kbd start -- Preparing console
|
||||
service [S12345] <pid/syslogd> klogd -n -x -- Kernel log daemon
|
||||
service syslogd {
|
||||
description = "System log daemon"
|
||||
runlevel = "S12345"
|
||||
command = "syslogd -n -x"
|
||||
}
|
||||
run acpid {
|
||||
description = "Starting ACPI Daemon"
|
||||
runlevel = "S"
|
||||
command = "/etc/init.d/acpid start"
|
||||
}
|
||||
task kbd {
|
||||
description = "Preparing console"
|
||||
runlevel = "S"
|
||||
command = "/etc/init.d/kbd start"
|
||||
}
|
||||
service klogd {
|
||||
description = "Kernel log daemon"
|
||||
runlevel = "S12345"
|
||||
conditions = { "pid/syslogd" }
|
||||
command = "klogd -n -x"
|
||||
}
|
||||
|
||||
tty [12345] /dev/tty1
|
||||
tty [2] /dev/tty2
|
||||
tty [2] /dev/tty3
|
||||
tty [2] /dev/tty4
|
||||
tty [2] /dev/tty5
|
||||
tty [2] /dev/tty6
|
||||
tty tty1 { runlevel = "12345" device = "/dev/tty1" }
|
||||
tty tty2 { runlevel = "2" device = "/dev/tty2" }
|
||||
tty tty3 { runlevel = "2" device = "/dev/tty3" }
|
||||
tty tty4 { runlevel = "2" device = "/dev/tty4" }
|
||||
tty tty5 { runlevel = "2" device = "/dev/tty5" }
|
||||
tty tty6 { runlevel = "2" device = "/dev/tty6" }
|
||||
```
|
||||
|
||||
In this example syslogd is first started, in parallel, and then acpid is
|
||||
@@ -46,8 +63,14 @@ are also removed when they have completed, `initctl show` will not list
|
||||
them.
|
||||
|
||||
```
|
||||
task [S] echo "foo" | cat >/tmp/bar
|
||||
run [S] echo "$HOME" >/tmp/secret
|
||||
task foo {
|
||||
runlevel = "S"
|
||||
command = "echo \"foo\" | cat >/tmp/bar"
|
||||
}
|
||||
run secret {
|
||||
runlevel = "S"
|
||||
command = "echo \"$HOME\" >/tmp/secret"
|
||||
}
|
||||
```
|
||||
|
||||
Switching between runlevels can be done by calling init with a single
|
||||
|
||||
+4
-4
@@ -3,18 +3,18 @@ Runparts & `/etc/rc.local`
|
||||
|
||||
At the end of the boot, when all bootstrap (`S`) tasks and services have
|
||||
started, but not networking, Finit calls its built-in [run-parts(8)][]
|
||||
command on any configured `runparts <DIR>` directory. This happens just
|
||||
command on any configured `runparts = "DIR"` directory. This happens just
|
||||
before changing to the configured runlevel (default 2). (Networking is
|
||||
enabled just prior to changing from single user mode.)
|
||||
|
||||
```shell
|
||||
runparts /etc/rc.d/
|
||||
```aconf
|
||||
runparts = "/etc/rc.d/"
|
||||
```
|
||||
|
||||
Right after the runlevel change when all services have started properly,
|
||||
`/etc/rc.local` is called.
|
||||
|
||||
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
|
||||
No setting in `/etc/finit.conf` is required for `rc.local`.
|
||||
If it exists and is an executable shell script Finit calls it at the very
|
||||
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more in the
|
||||
[Hook Scripts](plugins.md#hooks) section.
|
||||
|
||||
+30
-9
@@ -27,13 +27,22 @@ Finit can *not* start and monitor a daemon that:
|
||||
|
||||
### Forks to bg w/ PID file
|
||||
|
||||
There are two syntax variants, type 1 and type 2. The former is the
|
||||
traditional one used also for `sysv` start/stop scripts, and the latter
|
||||
is inspired by systemd, with a twist -- it lets Finit guess the pifdile
|
||||
to look for based on the standard path and the basename of the command.
|
||||
There are two variants. The former names the pidfile to watch, as for
|
||||
`sysv` start/stop scripts, and the latter is inspired by systemd, with a
|
||||
twist -- it lets Finit guess the pidfile based on the standard path and
|
||||
the basename of the command.
|
||||
|
||||
service pid:!/run/serv.pid serv -- Forking service, type 1
|
||||
service type:forking serv -- Forking service, type 2
|
||||
service serv {
|
||||
description = "Forking service, type 1"
|
||||
pidfile = "/run/serv.pid"
|
||||
command = "serv"
|
||||
}
|
||||
|
||||
service serv {
|
||||
description = "Forking service, type 2"
|
||||
type = "forking"
|
||||
command = "serv"
|
||||
}
|
||||
|
||||
In this example the resulting files to watch for are `/run/serv.pid` and
|
||||
`/var/run/serv.pid`, respectively. On most modern Linux systems this is
|
||||
@@ -41,17 +50,29 @@ the same directory (`/var/run` is a symlink to `../run`).
|
||||
|
||||
### Runs in fg w/ PID file
|
||||
|
||||
service serv -n -p -- Foreground service w/ PID file
|
||||
service serv {
|
||||
description = "Foreground service w/ PID file"
|
||||
command = "serv -n -p"
|
||||
}
|
||||
|
||||
### Runs in fg w/o PID file
|
||||
|
||||
Same as previous, but we tell Finit to create the PID file, because we
|
||||
need it to synchronize start/stop of a dependent service.
|
||||
|
||||
service pid:/run/serv.pid serv -n -- Foreground service w/o PID file
|
||||
service serv {
|
||||
description = "Foreground service w/o PID file"
|
||||
pidfile = "/run/serv.pid"
|
||||
pidfile-create = true
|
||||
command = "serv -n"
|
||||
}
|
||||
|
||||
### Runs in fg w/ custom PID file
|
||||
|
||||
service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
|
||||
service serv {
|
||||
description = "Foreground service w/ custom PID file"
|
||||
pidfile = "/run/servy.pid"
|
||||
command = "serv -n -p -P /run/servy.pid"
|
||||
}
|
||||
|
||||
[1]: config/service-sync.md
|
||||
|
||||
+55
-11
@@ -51,27 +51,52 @@ Configuration file `/etc/finit.conf` in the initramfs:
|
||||
# /etc/finit.conf in initramfs
|
||||
|
||||
# Mount the real root filesystem
|
||||
run [S] name:mount-root /bin/mount /dev/sda1 /mnt/root -- Mounting root filesystem
|
||||
run mount-root {
|
||||
description = "Mounting root filesystem"
|
||||
runlevel = "S"
|
||||
command = "/bin/mount /dev/sda1 /mnt/root"
|
||||
}
|
||||
|
||||
# Switch to real root after mount completes
|
||||
run [S] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root
|
||||
run switch-root {
|
||||
description = "Switching to real root"
|
||||
runlevel = "S"
|
||||
command = "/sbin/initctl switch-root /mnt/root"
|
||||
}
|
||||
```
|
||||
|
||||
For more complex setups (LUKS, LVM, etc.):
|
||||
|
||||
```
|
||||
# Unlock LUKS volume
|
||||
# The tty:@console stanza is required so cryptsetup can prompt for a passphrase
|
||||
run [S] name:cryptsetup tty:@console /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root
|
||||
# The tty setting is required so cryptsetup can prompt for a passphrase
|
||||
run cryptsetup {
|
||||
description = "Unlocking encrypted root"
|
||||
runlevel = "S"
|
||||
tty = "@console"
|
||||
command = "/sbin/cryptsetup open /dev/sda2 cryptroot"
|
||||
}
|
||||
|
||||
# Activate LVM
|
||||
run [S] name:lvm /sbin/lvm vgchange -ay -- Activating LVM volumes
|
||||
run lvm {
|
||||
description = "Activating LVM volumes"
|
||||
runlevel = "S"
|
||||
command = "/sbin/lvm vgchange -ay"
|
||||
}
|
||||
|
||||
# Mount root
|
||||
run [S] name:mount-root /bin/mount /dev/vg0/root /mnt/root -- Mounting root
|
||||
run mount-root {
|
||||
description = "Mounting root"
|
||||
runlevel = "S"
|
||||
command = "/bin/mount /dev/vg0/root /mnt/root"
|
||||
}
|
||||
|
||||
# Switch root
|
||||
run [S] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root
|
||||
run switch-root {
|
||||
description = "Switching to real root"
|
||||
runlevel = "S"
|
||||
command = "/sbin/initctl switch-root /mnt/root"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -85,15 +110,34 @@ difficult in runlevel S, you can perform the switch-root in runlevel 1:
|
||||
# /etc/finit.conf in initramfs
|
||||
|
||||
# Start mdevd for device handling
|
||||
service [S] name:mdevd notify:s6 /sbin/mdevd -D %n -- Device event daemon
|
||||
run [S] name:coldplug <service/mdevd/ready> /sbin/mdevd-coldplug -- Coldplug devices
|
||||
service mdevd {
|
||||
description = "Device event daemon"
|
||||
runlevel = "S"
|
||||
notify = "s6"
|
||||
command = "/sbin/mdevd -D %n"
|
||||
}
|
||||
run coldplug {
|
||||
description = "Coldplug devices"
|
||||
runlevel = "S"
|
||||
conditions = { "service/mdevd/ready" }
|
||||
command = "/sbin/mdevd-coldplug"
|
||||
}
|
||||
|
||||
# Mount the real root filesystem (after devices are ready)
|
||||
run [S] name:mount-root <run/coldplug/success> /bin/mount /dev/sda1 /mnt/root -- Mounting root
|
||||
run mount-root {
|
||||
description = "Mounting root"
|
||||
runlevel = "S"
|
||||
conditions = { "run/coldplug/success" }
|
||||
command = "/bin/mount /dev/sda1 /mnt/root"
|
||||
}
|
||||
|
||||
# Transition to runlevel 1 after all S tasks complete
|
||||
# The switch-root runs cleanly in runlevel 1
|
||||
run [1] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root
|
||||
run switch-root {
|
||||
description = "Switching to real root"
|
||||
runlevel = "1"
|
||||
command = "/sbin/initctl switch-root /mnt/root"
|
||||
}
|
||||
```
|
||||
|
||||
This approach separates the initramfs setup (runlevel S) from the
|
||||
|
||||
+3
-2
@@ -59,6 +59,7 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||
clone3.c clone3.h \
|
||||
cond.c cond-w.c cond.h \
|
||||
conf.c conf.h \
|
||||
legacy.c legacy.h \
|
||||
devmon.c devmon.h \
|
||||
exec.c finit.c finit.h \
|
||||
stty.c \
|
||||
@@ -86,8 +87,8 @@ pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \
|
||||
|
||||
finit_CPPFLAGS = $(AM_CPPFLAGS) -D__FINIT__
|
||||
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) $(confuse_CFLAGS)
|
||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS) $(confuse_LIBS)
|
||||
if STATIC
|
||||
finit_LDADD += ../plugins/libplug.la
|
||||
else
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
|
||||
#include <uev/uev.h>
|
||||
|
||||
#define CGROUP_NAME_SIZE 16
|
||||
#define CGROUP_SETTINGS_SIZE 128
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct svc svc_t;
|
||||
|
||||
|
||||
+15
-54
@@ -34,6 +34,7 @@
|
||||
#include "cgutil.h"
|
||||
#include "initctl.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
#define CDIM plain ? "" : "\e[2m"
|
||||
#define CRST plain ? "" : "\e[0m"
|
||||
@@ -54,42 +55,18 @@ int cgroup_avail(void)
|
||||
return fismnt(FINIT_CGPATH);
|
||||
}
|
||||
|
||||
static size_t flen(FILE *fp)
|
||||
{
|
||||
size_t total = 0, sz;
|
||||
char buf[512];
|
||||
|
||||
while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
|
||||
total += sz;
|
||||
rewind(fp);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
char *pid_cmdline(int pid)
|
||||
{
|
||||
size_t i, len;
|
||||
char *buf;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/cmdline", pid);
|
||||
if (!fp)
|
||||
buf = fslurp(&len, "/proc/%d/cmdline", pid);
|
||||
if (!buf)
|
||||
return strdup(""); /* regular process */
|
||||
|
||||
len = flen(fp);
|
||||
if (len == 0) {
|
||||
fail:
|
||||
fclose(fp);
|
||||
return NULL; /* kernel thread */
|
||||
}
|
||||
|
||||
buf = calloc(len + 1, 1);
|
||||
if (!buf)
|
||||
goto fail;
|
||||
|
||||
if (fread(buf, 1, len, fp) < len) {
|
||||
free(buf);
|
||||
goto fail;
|
||||
return NULL; /* kernel thread */
|
||||
}
|
||||
|
||||
/* replace all NUL chars with space */
|
||||
@@ -98,46 +75,30 @@ char *pid_cmdline(int pid)
|
||||
buf[i] = ' ';
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *pid_cgroup(int pid)
|
||||
{
|
||||
char *buf, *ptr = NULL;
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
char *buf, *ptr;
|
||||
|
||||
fp = fopenf("r", "/proc/%d/cgroup", pid);
|
||||
if (!fp)
|
||||
buf = fslurp(NULL, "/proc/%d/cgroup", pid);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
len = flen(fp);
|
||||
if (len == 0) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
len++;
|
||||
|
||||
buf = calloc(1, len);
|
||||
if (!buf) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fgets(buf, len, fp))
|
||||
ptr = chomp(buf);
|
||||
fclose(fp);
|
||||
ptr = strchr(buf, '\n'); /* first line only */
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
chomp(buf);
|
||||
|
||||
ptr = strchr(buf, '/');
|
||||
if (ptr) {
|
||||
ptr = strchr(ptr, '/');
|
||||
if (ptr) {
|
||||
memmove(buf, ptr, strlen(ptr) + 1);
|
||||
return buf;
|
||||
}
|
||||
memmove(buf, ptr, strlen(ptr) + 1);
|
||||
return buf;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
+1367
-559
File diff suppressed because it is too large
Load Diff
+33
-21
@@ -27,6 +27,10 @@
|
||||
#include "cgroup.h"
|
||||
#include "svc.h"
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#define BOOTSTRAP (runlevel == INIT_LEVEL)
|
||||
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int cmdlevel;
|
||||
@@ -46,31 +50,39 @@ extern char *hostname;
|
||||
extern char *runparts;
|
||||
extern char *osheading;
|
||||
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
extern int runparts_progress;
|
||||
extern int runparts_sysv;
|
||||
|
||||
extern struct rlimit global_rlimit[];
|
||||
extern char cgroup_current[];
|
||||
extern char cgroup_settings_current[];
|
||||
extern int cgroup_delegate_current;
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
|
||||
int str2rlim(char *str);
|
||||
char *rlim2str(int rlim);
|
||||
extern char cgroup_current[CGROUP_NAME_SIZE];
|
||||
extern char cgroup_settings_current[CGROUP_SETTINGS_SIZE];
|
||||
extern int cgroup_delegate_current;
|
||||
|
||||
int conf_init (uev_ctx_t *ctx);
|
||||
void conf_reload (void);
|
||||
int conf_any_change (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (void);
|
||||
extern struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||
extern struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
int conf_init (uev_ctx_t *ctx);
|
||||
int conf_reload (void);
|
||||
int conf_any_change (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (void);
|
||||
|
||||
void conf_saverc (void);
|
||||
void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
|
||||
void conf_save_service (int type, char *cfg, char *file);
|
||||
void conf_parse_cmdline (int argc, char *argv[]);
|
||||
|
||||
void conf_reset_env (void);
|
||||
void conf_set_env (char *line);
|
||||
char *conf_parse_env (char *line, char **val);
|
||||
|
||||
void conf_parse_rlimit (char *line, struct rlimit arr[]);
|
||||
int conf_parse_runlevels (const char *runlevels);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
int conf_parse_file (char *file, int is_rcsd);
|
||||
|
||||
void conf_reset_env (void);
|
||||
void conf_saverc (void);
|
||||
void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
|
||||
void conf_save_service (int type, char *cfg, char *file);
|
||||
void conf_parse_cmdline (int argc, char *argv[]);
|
||||
char *conf_parse_env (char *line, char **val);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
|
||||
#endif /* FINIT_CONF_H_ */
|
||||
|
||||
|
||||
+19
@@ -65,6 +65,25 @@ uev_ctx_t *ctx; /* Main loop context */
|
||||
svc_t *wdog; /* No watchdog by default */
|
||||
char *arg0; /* Saved for setprocnm() */
|
||||
|
||||
/*
|
||||
* --enable-fastboot => fsck_mode: NULL => no fsck by default
|
||||
* --enable-fsckfix => fsck_mode: "-f" + fsck_repair: "y"
|
||||
*/
|
||||
#ifdef FSCK_FIX
|
||||
# ifdef FAST_BOOT
|
||||
char *fsck_mode = NULL;
|
||||
# else
|
||||
char *fsck_mode = "-f";
|
||||
# endif
|
||||
char *fsck_repair = "-y";
|
||||
#else
|
||||
# ifdef FAST_BOOT
|
||||
char *fsck_mode = NULL;
|
||||
# else
|
||||
char *fsck_mode = "";
|
||||
# endif
|
||||
char *fsck_repair = "-p";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Show user configured banner before service bootstrap progress
|
||||
|
||||
@@ -470,6 +470,52 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
static int kmod_exists(const char *mod)
|
||||
{
|
||||
char buf[256];
|
||||
int found = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/modules", "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (!found && fgets(buf, sizeof(buf), fp)) {
|
||||
const char *kmod = strtok(buf, " \t");
|
||||
|
||||
if (kmod && !strcmp(kmod, mod))
|
||||
found = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a kernel module during bootstrap
|
||||
*/
|
||||
void kmod_load(char *mod)
|
||||
{
|
||||
char module[64] = { 0 };
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
if (runlevel != INIT_LEVEL)
|
||||
return;
|
||||
|
||||
/* Strip args for progress below and kmod_exists() */
|
||||
strlcpy(module, mod, sizeof(module));
|
||||
if (!strtok(module, " \t"))
|
||||
return;
|
||||
|
||||
if (kmod_exists(module))
|
||||
return;
|
||||
|
||||
strcpy(cmd, "modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
|
||||
run_interactive(cmd, "Loading kernel module %s", module);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bring up networking, but only if not single-user or rescue mode
|
||||
*/
|
||||
|
||||
@@ -71,6 +71,7 @@ int print_result (int fail);
|
||||
void print_exit (void);
|
||||
|
||||
void set_hostname (char **hostname);
|
||||
void kmod_load (char *mod);
|
||||
void networking (int updown);
|
||||
int in_container (void);
|
||||
void setprocnm (const char *name);
|
||||
@@ -113,6 +114,19 @@ static inline int dprint(int fd, const char *s, size_t len)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int istrue(char *arg, int defval)
|
||||
{
|
||||
if (!arg)
|
||||
goto fallback;
|
||||
|
||||
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
|
||||
return 1;
|
||||
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
|
||||
return 0;
|
||||
fallback:
|
||||
return defval;
|
||||
}
|
||||
|
||||
static inline char *fgetval(const char *line, const char *key, char *sep)
|
||||
{
|
||||
char *ptr, *str, *copy;
|
||||
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
/* Legacy one-liner parser for /etc/finit.conf and the finit.d hierarchy
|
||||
*
|
||||
* NOTE: this parser is frozen at the Finit 4.x feature set. It is
|
||||
* kept, indefinitely, for backwards compatibility -- existing
|
||||
* configurations must keep working. All new directives and
|
||||
* options land in the libconfuse-based format, see conf.c
|
||||
*
|
||||
* Copyright (c) 2012-2025 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" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
# include <libite/queue.h> /* BSD sys/queue.h API */
|
||||
#else
|
||||
# include <lite/lite.h>
|
||||
# include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#endif
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "legacy.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/* 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 int parse_static(char *line, int is_rcsd)
|
||||
{
|
||||
char cmd[CMD_SIZE];
|
||||
char *x;
|
||||
|
||||
if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
|
||||
if (hostname) free(hostname);
|
||||
hostname = strdup(strip_line(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) {
|
||||
char *dev = strip_line(x);
|
||||
|
||||
strcpy(cmd, "mknod ");
|
||||
strlcat(cmd, dev, sizeof(cmd));
|
||||
run_interactive(cmd, "Creating device node %s", dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Kernel module to load */
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) {
|
||||
kmod_load(strip_line(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) {
|
||||
if (network) free(network);
|
||||
network = strdup(strip_line(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "rcsd ", x)) {
|
||||
if (finit_rcsd) free(finit_rcsd);
|
||||
finit_rcsd = strdup(strip_line(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
|
||||
if (runparts) free(runparts);
|
||||
runparts_progress = runparts_sysv = 0;
|
||||
while (x) {
|
||||
if (MATCH_CMD(x, "progress", x))
|
||||
runparts_progress = 1;
|
||||
else if (MATCH_CMD(x, "sysv", x))
|
||||
runparts_sysv = 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
runparts = strdup(strip_line(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "set ", x)) {
|
||||
conf_set_env(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "include ", x)) {
|
||||
char *file = strip_line(x);
|
||||
|
||||
strlcpy(cmd, file, sizeof(cmd));
|
||||
if (!fexist(cmd)) {
|
||||
errx(1, "Cannot find include file %s, absolute path required!", x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return conf_parse_file(cmd, is_rcsd);
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "log ", x)) {
|
||||
char *tok;
|
||||
static int size = 200000, count = 5;
|
||||
|
||||
tok = strtok(x, ":= ");
|
||||
while (tok) {
|
||||
if (!strncmp(tok, "size", 4))
|
||||
size = strtobytes(strtok(NULL, ":= "));
|
||||
else if (!strncmp(tok, "count", 5))
|
||||
count = strtobytes(strtok(NULL, ":= "));
|
||||
|
||||
tok = strtok(NULL, ":= ");
|
||||
}
|
||||
|
||||
if (size >= 0)
|
||||
logfile_size_max = size;
|
||||
if (count >= 0)
|
||||
logfile_count_max = count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||
if (sdown) free(sdown);
|
||||
sdown = strdup(strip_line(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The desired runlevel to start when leaving bootstrap (S).
|
||||
* Finit supports 1-9, but most systems only use 1-6, where
|
||||
* 6 is reserved for reboot and 0 for halt/poweroff.
|
||||
*/
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) {
|
||||
char *token = strip_line(x);
|
||||
const char *err = NULL;
|
||||
|
||||
cfglevel = strtonum(token, 1, 9, &err);
|
||||
if (err)
|
||||
cfglevel = RUNLEVEL;
|
||||
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
|
||||
cfglevel = 2; /* Fallback */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before Finit 5 the default readiness notification is PID.
|
||||
* This default can be changed by setting 'readiness none' in
|
||||
* the file /etc/finit.conf, only read once at bootstrap.
|
||||
*/
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "readiness ", x)) {
|
||||
char *token = strip_line(x);
|
||||
|
||||
if (!strcmp(token, "none"))
|
||||
readiness = SVC_NOTIFY_NONE;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "reboot-delay ", x)) {
|
||||
syncsec = strtonum(strip_line(x), 0, 60, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "reboot-watchdog ", x)) {
|
||||
wdtreboot = istrue(strip_line(x), 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Periodic check and instability index leveler, seconds
|
||||
*/
|
||||
if (MATCH_CMD(line, "service-interval ", x)) {
|
||||
char *token = strip_line(x);
|
||||
const char *err = NULL;
|
||||
int val;
|
||||
|
||||
/* 0 min to 1 day, should check at least daily */
|
||||
val = strtonum(token, 0, 1440, &err);
|
||||
if (!err) {
|
||||
int disabled = !service_interval;
|
||||
|
||||
service_interval = val * 1000; /* to milliseconds */
|
||||
if (disabled)
|
||||
service_init(NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
char *x;
|
||||
|
||||
/* Monitored daemon, will be respawned on exit */
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, x, rlimit, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One-shot task, will not be respawned */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, rlimit, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Like task but waits for completion, useful w/ [S] */
|
||||
if (MATCH_CMD(line, "run ", x)) {
|
||||
service_register(SVC_TYPE_RUN, x, rlimit, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Similar to task but is treated like a SysV init script */
|
||||
if (MATCH_CMD(line, "sysv ", x)) {
|
||||
service_register(SVC_TYPE_SYSV, x, rlimit, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read resource limits */
|
||||
if (MATCH_CMD(line, "rlimit ", x)) {
|
||||
conf_parse_rlimit(x, rlimit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read control group limits */
|
||||
if (MATCH_CMD(line, "cgroup ", x)) {
|
||||
conf_parse_cgroup(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set current cgroup for the following services/run/tasks */
|
||||
if (MATCH_CMD(line, "cgroup.", x)) {
|
||||
char *group, *token, *saveptr;
|
||||
char settings[128] = {0};
|
||||
|
||||
/* Reset cgroup state */
|
||||
cgroup_settings_current[0] = '\0';
|
||||
cgroup_delegate_current = 0;
|
||||
|
||||
/* First token is the group name (system/user/init) */
|
||||
group = strtok_r(x, ",", &saveptr);
|
||||
if (group)
|
||||
strlcpy(cgroup_current, group, sizeof(cgroup_current));
|
||||
else
|
||||
strlcpy(cgroup_current, x, sizeof(cgroup_current));
|
||||
|
||||
/* Parse any remaining comma-separated options */
|
||||
while ((token = strtok_r(NULL, ",", &saveptr)) != NULL) {
|
||||
if (strncmp(token, "name:", 5) == 0) {
|
||||
/* Cgroup leaf name override - only valid in per-service directives */
|
||||
warnx("cgroup %s: name: option not valid in global directive!", group);
|
||||
} else if (strcmp(token, "delegate") == 0) {
|
||||
cgroup_delegate_current = 1;
|
||||
} else {
|
||||
/* Other settings (cpu.weight:500, memory.max:1G, etc.) */
|
||||
if (settings[0])
|
||||
strlcat(settings, ",", sizeof(settings));
|
||||
strlcat(settings, token, sizeof(settings));
|
||||
}
|
||||
}
|
||||
|
||||
if (settings[0])
|
||||
strlcpy(cgroup_settings_current, settings, sizeof(cgroup_settings_current));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Regular or serial TTYs to run getty */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse FILE, or BUF when the frontend has already read and
|
||||
* instantiated it, i.e. for a template. Either way the text arriving
|
||||
* here is final, no %i is left to substitute.
|
||||
*/
|
||||
int legacy_parse_conf(char *file, char *buf, int is_rcsd)
|
||||
{
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
FILE *fp;
|
||||
|
||||
if (buf)
|
||||
fp = fmemopen(buf, strlen(buf), "r");
|
||||
else
|
||||
fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
/* Prepare default limits and group for each service in /etc/finit.d/ */
|
||||
if (is_rcsd) {
|
||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||
cgroup_current[0] = 0;
|
||||
}
|
||||
|
||||
dbg("*** Parsing %s", file);
|
||||
while (!feof(fp)) {
|
||||
char *line;
|
||||
|
||||
line = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCCOMM);
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
tabstospaces(line);
|
||||
|
||||
if (!parse_static(line, is_rcsd))
|
||||
;
|
||||
else if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file))
|
||||
;
|
||||
else
|
||||
conf_set_env(line);
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Internal interface between conf.c and the legacy one-liner parser
|
||||
*
|
||||
* Copyright (c) 2012-2026 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_LEGACY_H_
|
||||
#define FINIT_LEGACY_H_
|
||||
|
||||
int legacy_parse_conf (char *file, char *buf, int is_rcsd);
|
||||
void legacy_parse_env (char *line);
|
||||
|
||||
#endif /* FINIT_LEGACY_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+5
-2
@@ -1,2 +1,5 @@
|
||||
runlevel 1
|
||||
tty [12345] rescue
|
||||
runlevel = 1
|
||||
tty rescue {
|
||||
runlevel = "12345"
|
||||
rescue = true
|
||||
}
|
||||
|
||||
+71
-45
@@ -1,62 +1,88 @@
|
||||
# sample.conf: template cgroup/run/task/service stanza for finit
|
||||
# sample.conf: template run/task/service stanza for finit
|
||||
#
|
||||
# A .conf file is a series of blocks. Every setting is a key inside
|
||||
# one, so nothing has to be memorised by position:
|
||||
#
|
||||
# service NAME {
|
||||
# description = "What it is"
|
||||
# runlevel = "2345" # default: 234
|
||||
# conditions = { "net/lo/up" } # wait for these
|
||||
# envfile = "-/etc/default/daemon"
|
||||
# command = "/usr/sbin/daemon ARGS"
|
||||
# }
|
||||
#
|
||||
# A leading '-' on a path means carry on if it is missing, so the
|
||||
# envfile above is optional. Debian and Buildroot keep those files in
|
||||
# /etc/default, Alpine in /etc/conf.d.
|
||||
#
|
||||
# There are four kinds of stanza:
|
||||
#
|
||||
# - run : one-shot, wait for it before continuing with the next
|
||||
# - task : one-shot, started in parallel with the next
|
||||
# - service : supervised daemon, run in foreground, restarted if it crashes
|
||||
# - sysv : /etc/init.d style script, start/stop/restart
|
||||
#
|
||||
# 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:
|
||||
# once, otherwise the last read .conf wins:
|
||||
#
|
||||
# cgroup NAME cpu.weight:1000 mem.max:65
|
||||
# cgroup NAME {
|
||||
# cpu.weight = 1000
|
||||
# memory.max = 65M
|
||||
# }
|
||||
#
|
||||
# The one-shot commands 'run' and 'task' are called only once per
|
||||
# runlevel. Finit does not restart them when they exit.
|
||||
# A service joins one by naming it, and may override settings for
|
||||
# itself alone. Here foo and bar share the group foo, baz has its own:
|
||||
#
|
||||
# - run : wait for completion before continuing with next stanza
|
||||
# - task : like run but started in background, parallel with other stanzas
|
||||
# - service : supervised daemon, run in foreground, restarted if it crashes
|
||||
#
|
||||
# The env: is an optional path to a file with environment variables to
|
||||
# adjust the behavior of daemons. Debian and Buildroot use /etc/default
|
||||
# while Alpine use /etc/conf.d -- ensure your Finit is built correctly.
|
||||
# The leading '-' determines if its OK to start the service even if the
|
||||
# env file is missing.
|
||||
#
|
||||
# run [LVLS] <COND> log command ARGS -- Command
|
||||
# task [LVLS] <COND> log command ARGS -- Command
|
||||
# service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon
|
||||
# ^ ^ ^ ^ ^ ^ ^ ^
|
||||
# | | | | | | | `-- Optional description
|
||||
# | | | | | | `----------- Daemon arguments
|
||||
# | | | | | `-------------------------- Path to daemon
|
||||
# | | | | `---------------------------------------------------- Optional env. file
|
||||
# | | | `-------------------------------------------------------- Redirect output to log
|
||||
# | | `--------------------------------------------------------------- Optional conditions
|
||||
# | `---------------------------------------------------------------------- Optional Runlevels
|
||||
# `------------------------------------------------------------------------------ Monitored application
|
||||
#
|
||||
# Each stanza can also hold a 'cgroup' argument, or be prefixed with
|
||||
# cgroup.NAME to place all following stanzas in the same group. In
|
||||
# the following example, foo and bar share the cgroup foo but baz
|
||||
# runs in its own cgroup baz:
|
||||
#
|
||||
# cgroup.foo
|
||||
# service cgroup:cpu.weight:250,mem.max:655350 foo args -- foo desc
|
||||
# service cgroup:cpu.weight:150,mem.max:655350 bar args -- bar desc
|
||||
# service cgroup.baz:cpu.weight:300 baz args -- baz desc
|
||||
# service foo { cgroup foo {} command = "foo args" }
|
||||
# service bar { cgroup foo { cpu.weight = 150 } command = "bar args" }
|
||||
# service baz { cgroup baz { cpu.weight = 300 } command = "baz args" }
|
||||
|
||||
# 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 sshd {
|
||||
# description = "OpenSSH daemon"
|
||||
# runlevel = "2345"
|
||||
# conditions = { "net/lo/up" }
|
||||
# envfile = "-/etc/default/ssh"
|
||||
# command = "/usr/sbin/sshd -D $SSHD_OPTS"
|
||||
#}
|
||||
|
||||
# Alpine Linux: Oneshot task to run once at bootstrap, yes pipes are possible :)
|
||||
#task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
|
||||
#run kmap {
|
||||
# description = "Loading keymap"
|
||||
# runlevel = "S"
|
||||
# envfile = "/etc/conf.d/loadkmap"
|
||||
# command = "zcat $KEYMAP | loadkmap"
|
||||
#}
|
||||
|
||||
# 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 dropbear {
|
||||
# description = "Dropbear SSH daemon"
|
||||
# runlevel = "2345"
|
||||
# envfile = "-/etc/conf.d/dropbear"
|
||||
# cgroup user {}
|
||||
# command = "dropbear -R -F $DROPBEAR_OPTS"
|
||||
#}
|
||||
|
||||
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
|
||||
# Depends on syslogd having started. Redirect any output to log.
|
||||
#service [2345] <pid/syslogd> cgroup:mem.max:32000,cpu.max:1000 log acpid -f -- ACPI daemon
|
||||
# Depends on syslogd having started. Redirect any output to the log.
|
||||
#service acpid {
|
||||
# description = "ACPI daemon"
|
||||
# runlevel = "2345"
|
||||
# conditions = { "pid/syslogd" }
|
||||
# cgroup system {
|
||||
# memory.max = 32M
|
||||
# cpu.max = 1000
|
||||
# }
|
||||
# log { }
|
||||
# command = "acpid -f"
|
||||
#}
|
||||
|
||||
# Start rsyslogd as soon as possible, should always run
|
||||
# Provides pid/syslogd condition
|
||||
#service [S12345] name:syslogd env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_OPTIONS -- Reliable syslog daemon
|
||||
#service syslogd {
|
||||
# description = "Reliable syslog daemon"
|
||||
# runlevel = "S12345"
|
||||
# envfile = "-/etc/default/rsyslog"
|
||||
# command = "rsyslogd -n $RSYSLOGD_OPTIONS"
|
||||
#}
|
||||
|
||||
+188
-26
@@ -66,6 +66,8 @@
|
||||
|
||||
#define NOTIFY_PATH "@run/finit/notify/%d"
|
||||
|
||||
int logfile_size_max = 200000; /* 200 kB */
|
||||
int logfile_count_max = 5;
|
||||
|
||||
/*
|
||||
* run tasks block other tasks/services from starting, we track the
|
||||
@@ -568,6 +570,118 @@ static void set_uid(uid_t uid, svc_t *svc)
|
||||
err(1, "%s: failed setuid(%d)", svc_ident(svc, NULL, 0), uid);
|
||||
}
|
||||
|
||||
/*
|
||||
* systemd-style per-service directories: created before each start,
|
||||
* owned by the service user, and exported to the environment. The
|
||||
* runtime directory is removed again when the service stops, after
|
||||
* any post: script has run. See issue #492.
|
||||
*/
|
||||
const struct svcdir svcdirs[NUM_SVCDIRS] = {
|
||||
{ "runtime-dir", "/run", "RUNTIME_DIRECTORY", offsetof(svc_t, runtime_dir), 1 },
|
||||
{ "state-dir", "/var/lib", "STATE_DIRECTORY", offsetof(svc_t, state_dir), 1 },
|
||||
{ "cache-dir", "/var/cache", "CACHE_DIRECTORY", offsetof(svc_t, cache_dir), 1 },
|
||||
{ "logs-dir", "/var/log", "LOGS_DIRECTORY", offsetof(svc_t, logs_dir), 1 },
|
||||
{ "config-dir", "/etc", "CONFIGURATION_DIRECTORY", offsetof(svc_t, config_dir), 0 },
|
||||
};
|
||||
|
||||
static char *svcdir_path(svc_t *svc, const struct svcdir *sd, char *path, size_t len)
|
||||
{
|
||||
char *name = (char *)svc + sd->off;
|
||||
|
||||
if (!name[0])
|
||||
return NULL;
|
||||
|
||||
paste(path, len, sd->base, name);
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate and set a per-service directory. The value is a name
|
||||
* resolved under sd->base, so an absolute path or an escape is
|
||||
* refused with -1 and errno set.
|
||||
*/
|
||||
int service_set_dir(svc_t *svc, const struct svcdir *sd, const char *name)
|
||||
{
|
||||
char *dir = (char *)svc + sd->off;
|
||||
|
||||
if (name[0] == '/' || strstr(name, "..")) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlcpy(dir, name, MAX_ARG_LEN) >= MAX_ARG_LEN) {
|
||||
dir[0] = 0;
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void service_mkdirs(svc_t *svc, uid_t uid, gid_t gid)
|
||||
{
|
||||
char path[256];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(svcdirs); i++) {
|
||||
uid_t u = svcdirs[i].chown ? uid : (uid_t)-1;
|
||||
gid_t g = svcdirs[i].chown ? gid : 0;
|
||||
struct stat st;
|
||||
char *ptr;
|
||||
|
||||
ptr = svcdir_path(svc, &svcdirs[i], path, sizeof(path));
|
||||
if (!ptr)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Same rules as systemd: the named directory has its mode
|
||||
* locked down again on every start, but its contents are
|
||||
* only touched when the owner has drifted, then everything
|
||||
* under it is chowned back. Script forks land here too,
|
||||
* so this must be idempotent.
|
||||
*/
|
||||
if (stat(ptr, &st) == 0) {
|
||||
chmod(ptr, svc->dir_mode[i]);
|
||||
if (u != (uid_t)-1 && (st.st_uid != u || st.st_gid != g))
|
||||
chownr(ptr, u, g);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only the named directory is chowned, like systemd */
|
||||
if (mksubsysd(ptr, svc->dir_mode[i], u, g))
|
||||
logit(LOG_WARNING, "%s: failed creating %s", svc_ident(svc, NULL, 0), ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void service_dir_env(svc_t *svc)
|
||||
{
|
||||
char path[256];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(svcdirs); i++) {
|
||||
if (svcdir_path(svc, &svcdirs[i], path, sizeof(path)))
|
||||
setenv(svcdirs[i].env, path, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void service_rmdirs(svc_t *svc)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
/* only the runtime directory, /run is tmpfs, the rest persist */
|
||||
if (!svcdir_path(svc, &svcdirs[0], path, sizeof(path)))
|
||||
return;
|
||||
|
||||
if (svc->dir_preserve == SVC_DIR_PRESERVE_YES)
|
||||
return;
|
||||
|
||||
/* still qualified to run means this is a restart, not a stop */
|
||||
if (svc->dir_preserve == SVC_DIR_PRESERVE_RESTART && svc_enabled(svc))
|
||||
return;
|
||||
|
||||
rmrf(path);
|
||||
}
|
||||
|
||||
static pid_t service_fork(svc_t *svc)
|
||||
{
|
||||
const char *cgnm;
|
||||
@@ -608,6 +722,7 @@ static pid_t service_fork(svc_t *svc)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
service_mkdirs(svc, uid, gid);
|
||||
if (svc_is_tty(svc))
|
||||
setprocnm("getty");
|
||||
|
||||
@@ -662,6 +777,8 @@ static pid_t service_fork(svc_t *svc)
|
||||
err(1, "%s: failed setgid(%d)", svc_ident(svc, NULL, 0), gid);
|
||||
}
|
||||
|
||||
service_dir_env(svc);
|
||||
|
||||
if (uid >= 0) {
|
||||
set_uid(uid, svc);
|
||||
|
||||
@@ -1074,7 +1191,7 @@ static void service_kill(svc_t *svc)
|
||||
* Called by service_stop() and service_reload() when alternate mechanisms
|
||||
* for stopping and reloading have been specified by the user.
|
||||
*/
|
||||
static int service_run_script(svc_t *svc, char *script)
|
||||
static int service_run_script(svc_t *svc, char *script, int tmo)
|
||||
{
|
||||
const char *id = svc_ident(svc, NULL, 0);
|
||||
pid_t pid = service_fork(svc);
|
||||
@@ -1104,7 +1221,7 @@ static int service_run_script(svc_t *svc, char *script)
|
||||
}
|
||||
|
||||
dbg("%s: script '%s' started as PID %d", id, script, pid);
|
||||
return service_script_add(svc, pid, svc->killdelay);
|
||||
return service_script_add(svc, pid, tmo ? tmo : svc->killdelay);
|
||||
}
|
||||
|
||||
/* Ensure we don't have any notify socket lingering */
|
||||
@@ -1266,7 +1383,7 @@ int service_stop(svc_t *svc)
|
||||
print_desc("Stopping ", svc->desc);
|
||||
|
||||
if (svc->stop_script[0]) {
|
||||
rc = service_run_script(svc, svc->stop_script);
|
||||
rc = service_run_script(svc, svc->stop_script, svc->stop_tmo);
|
||||
} else if (!svc_is_sysv(svc)) {
|
||||
if (svc->pid > 1) {
|
||||
/*
|
||||
@@ -1358,7 +1475,7 @@ static int service_reload(svc_t *svc)
|
||||
|
||||
if (svc->reload_script[0]) {
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Reloading %s[%d], calling reload:%s ...", id, svc->pid, svc->reload_script);
|
||||
rc = service_run_script(svc, svc->reload_script);
|
||||
rc = service_run_script(svc, svc->reload_script, svc->reload_tmo);
|
||||
} else if (svc->sighup) {
|
||||
if (svc->pid <= 1) {
|
||||
dbg("%s[%d]: bad PID, cannot reload service", id, svc->pid);
|
||||
@@ -1535,11 +1652,26 @@ static void parse_caps(svc_t *svc, char *caps)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(svc->username, "root")) {
|
||||
cap_value_t cap;
|
||||
|
||||
for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
|
||||
if (!cap_iab_get_vector(cap_iab, CAP_IAB_AMB, cap))
|
||||
continue;
|
||||
|
||||
/* the ambient set only reaches effective when euid != 0 */
|
||||
logit(LOG_WARNING, "%s: ambient capabilities ('^') have no effect"
|
||||
" as root, use a non-root user, or '%%' and '!' entries",
|
||||
svc_ident(svc, NULL, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cap_free(cap_iab);
|
||||
strlcpy(svc->capabilities, caps, sizeof(svc->capabilities));
|
||||
#else
|
||||
(void)svc;
|
||||
(void)caps;
|
||||
logit(LOG_WARNING, "%s: capabilities require Finit built with --enable-libcap,"
|
||||
" ignoring '%s'", svc_ident(svc, NULL, 0), caps);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1654,7 +1786,8 @@ static void parse_script(svc_t *svc, char *type, char *script, int *tmo, char *b
|
||||
script, errstr);
|
||||
goto err;
|
||||
}
|
||||
*tmo = (int)(sec * 1000);
|
||||
if (tmo)
|
||||
*tmo = (int)(sec * 1000);
|
||||
} else {
|
||||
path = script;
|
||||
if (tmo)
|
||||
@@ -1857,9 +1990,11 @@ static void parse_cmdline_args(svc_t *svc, char *cmd, char **args)
|
||||
* defaults to "" (empty string).
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
* The registered svc, or %NULL with @errno set on failure. A block
|
||||
* skipped on purpose -- conditional loading, bootstrap over, nowarn --
|
||||
* also returns %NULL, with @errno zero.
|
||||
*/
|
||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
svc_t *service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
|
||||
char *username = NULL, *log = NULL, *pid = NULL;
|
||||
@@ -1887,12 +2022,15 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
if (!cfg) {
|
||||
errx(1, "Invalid input argument");
|
||||
return errno = EINVAL;
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
line = strdupa(cfg);
|
||||
if (!line)
|
||||
return 1;
|
||||
if (!line) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = strstr(line, "-- ");
|
||||
if (desc) {
|
||||
@@ -1917,7 +2055,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
errx(1, "Incomplete service '%s', cannot register", cfg);
|
||||
return errno = ENOENT;
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (cmd) {
|
||||
@@ -2017,14 +2156,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
strlcat(ident, id, sizeof(ident));
|
||||
}
|
||||
|
||||
if (ifstmt && !svc_ifthen(1, ident, ifstmt, nowarn))
|
||||
return 0;
|
||||
if (ifstmt && !svc_ifthen(1, ident, ifstmt, nowarn)) {
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
levels = conf_parse_runlevels(runlevels);
|
||||
if (runlevel != INIT_LEVEL && !ISOTHER(levels, INIT_LEVEL)) {
|
||||
dbg("Skipping %s%s%s, bootstrap is completed.",
|
||||
name, id[0] ? ":" : "", id[0] ? id : "");
|
||||
return 0;
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type == SVC_TYPE_TTY) {
|
||||
@@ -2032,7 +2174,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
char *ptr;
|
||||
|
||||
if (tty_parse_args(&tty, cmd, &args))
|
||||
return errno;
|
||||
return NULL;
|
||||
|
||||
/* NOTE: this may result in dev == NULL! */
|
||||
if (tty_isatcon(tty.dev))
|
||||
@@ -2051,7 +2193,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
line = alloca(len);
|
||||
if (!line)
|
||||
return errno;
|
||||
return NULL;
|
||||
|
||||
snprintf(line, len, "%s", tty.cmd ? tty.cmd : "tty");
|
||||
for (i = 0; i < tty.num; i++) {
|
||||
@@ -2061,7 +2203,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
cmd = strtok_r(line, " \t", &args);
|
||||
if (!cmd)
|
||||
return errno;
|
||||
return NULL;
|
||||
|
||||
/* tty's always respawn, never incr. restart_cnt */
|
||||
respawn = 1;
|
||||
@@ -2086,11 +2228,13 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
svc = svc_find(name, id);
|
||||
|
||||
if (!whichp(cmd)) {
|
||||
if (nowarn)
|
||||
return 0;
|
||||
if (nowarn) {
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
warn("%s: skipping %s", file ? file : "static", cmd);
|
||||
return errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!svc) {
|
||||
@@ -2098,7 +2242,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
svc = svc_new(cmd, name, id, type);
|
||||
if (!svc) {
|
||||
errx(1, "Out of memory, cannot register service %s", cmd);
|
||||
return errno = ENOMEM;
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (manual)
|
||||
@@ -2208,12 +2353,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
memset(svc->cleanup_script, 0, sizeof(svc->cleanup_script));
|
||||
|
||||
if (reload_script)
|
||||
parse_script(svc, "reload", reload_script, NULL, svc->reload_script, sizeof(svc->reload_script));
|
||||
parse_script(svc, "reload", reload_script, &svc->reload_tmo, svc->reload_script, sizeof(svc->reload_script));
|
||||
else
|
||||
memset(svc->reload_script, 0, sizeof(svc->reload_script));
|
||||
|
||||
if (stop_script)
|
||||
parse_script(svc, "stop", stop_script, NULL, svc->stop_script, sizeof(svc->stop_script));
|
||||
parse_script(svc, "stop", stop_script, &svc->stop_tmo, svc->stop_script, sizeof(svc->stop_script));
|
||||
else
|
||||
memset(svc->stop_script, 0, sizeof(svc->stop_script));
|
||||
|
||||
@@ -2242,6 +2387,13 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
else
|
||||
memset(svc->capabilities, 0, sizeof(svc->capabilities));
|
||||
|
||||
/* block format only, set by conf.c after registration */
|
||||
for (int i = 0; i < NUM_SVCDIRS; i++) {
|
||||
memset((char *)svc + svcdirs[i].off, 0, MAX_ARG_LEN);
|
||||
svc->dir_mode[i] = 0755;
|
||||
}
|
||||
svc->dir_preserve = SVC_DIR_PRESERVE_NO;
|
||||
|
||||
if (!svc_is_tty(svc) && ctty) {
|
||||
char *dev = ctty;
|
||||
|
||||
@@ -2357,7 +2509,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return svc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2864,6 +3016,16 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
|
||||
return;
|
||||
*state = new_state;
|
||||
|
||||
/*
|
||||
* The unit has stopped: HALTED comes after any post:/cleanup:
|
||||
* script, DONE is a completed run/task, where remain-after-exit
|
||||
* keeps it alive until stopped for real. Same removal rules as
|
||||
* systemd RuntimeDirectory with RuntimeDirectoryPreserve=no.
|
||||
*/
|
||||
if (new_state == SVC_HALTED_STATE ||
|
||||
(new_state == SVC_DONE_STATE && !svc_is_remain(svc)))
|
||||
service_rmdirs(svc);
|
||||
|
||||
if (svc_is_runtask(svc)) {
|
||||
char success[MAX_COND_LEN], failure[MAX_COND_LEN];
|
||||
|
||||
|
||||
+17
-1
@@ -27,7 +27,23 @@
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
int service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
||||
/*
|
||||
* systemd-style per-service directories, block format only: the .conf
|
||||
* key, the base the name resolves under, and the environment variable
|
||||
* the path is exported as. Shared by service.c and conf.c so adding
|
||||
* one means touching the table and svc.h alone.
|
||||
*/
|
||||
struct svcdir {
|
||||
const char *key;
|
||||
const char *base;
|
||||
const char *env;
|
||||
size_t off; /* offsetof() in svc_t */
|
||||
int chown; /* all but config-dir, like systemd */
|
||||
};
|
||||
extern const struct svcdir svcdirs[NUM_SVCDIRS];
|
||||
|
||||
int service_set_dir (svc_t *svc, const struct svcdir *sd, const char *name);
|
||||
svc_t *service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
||||
void service_unregister (svc_t *svc);
|
||||
|
||||
void service_runtask_clean (void);
|
||||
|
||||
@@ -199,6 +199,20 @@ typedef struct svc {
|
||||
int num_supgroups;
|
||||
char capabilities[MAX_CMD_LEN];
|
||||
|
||||
/* Directories set up for the service, block format only, the
|
||||
* name is resolved under a fixed base, e.g. /run/NAME */
|
||||
#define NUM_SVCDIRS 5
|
||||
char runtime_dir[MAX_ARG_LEN];
|
||||
char state_dir[MAX_ARG_LEN];
|
||||
char cache_dir[MAX_ARG_LEN];
|
||||
char logs_dir[MAX_ARG_LEN];
|
||||
char config_dir[MAX_ARG_LEN];
|
||||
mode_t dir_mode[NUM_SVCDIRS];
|
||||
#define SVC_DIR_PRESERVE_NO 0 /* remove runtime-dir on stop */
|
||||
#define SVC_DIR_PRESERVE_RESTART 1 /* keep it across restarts */
|
||||
#define SVC_DIR_PRESERVE_YES 2 /* never remove it */
|
||||
char dir_preserve;
|
||||
|
||||
/* Command, arguments and service description */
|
||||
char cmd[MAX_CMD_LEN];
|
||||
char args[MAX_NUM_SVC_ARGS][MAX_CMD_LEN];
|
||||
@@ -221,9 +235,11 @@ typedef struct svc {
|
||||
|
||||
/* When set, used instead of SIGHUP or stop-start */
|
||||
char reload_script[MAX_CMD_LEN];
|
||||
int reload_tmo;
|
||||
|
||||
/* When set, used instead of SIGTERM or sysv 'stop' */
|
||||
char stop_script[MAX_CMD_LEN];
|
||||
int stop_tmo;
|
||||
|
||||
/*
|
||||
* Used to forcefully kill services that won't shutdown on
|
||||
|
||||
+1
-30
@@ -130,33 +130,6 @@ static int parse_mm(char *arg, int *major, int *minor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||
{
|
||||
(void)sb;
|
||||
(void)tflag;
|
||||
|
||||
if (ftw->level == 0)
|
||||
return 1;
|
||||
|
||||
if (remove(fpath) && errno != EBUSY)
|
||||
warn("Failed removing %s", fpath);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int rmrf(const char *path)
|
||||
{
|
||||
if (!fisdir(path))
|
||||
return 0;
|
||||
|
||||
nftw(path, do_delete, 20, FTW_DEPTH | FTW_PHYS);
|
||||
if (remove(path) && errno != ENOENT)
|
||||
warn("Failed removing path %s", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mkparent(char *path, mode_t mode)
|
||||
{
|
||||
mkpath(dirname(strdupa(path)), mode);
|
||||
@@ -465,9 +438,7 @@ static void tmpfiles(char *line)
|
||||
case 'd':
|
||||
break;
|
||||
case 'D':
|
||||
if (fisdir(path)) {
|
||||
nftw(path, do_delete, 20, FTW_DEPTH | FTW_PHYS);
|
||||
}
|
||||
rmcontents(path);
|
||||
break;
|
||||
case 'e':
|
||||
case 'f':
|
||||
|
||||
+256
-32
@@ -33,6 +33,7 @@
|
||||
#include <regex.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
@@ -43,8 +44,10 @@
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sys/resource.h>
|
||||
#include <ftw.h> /* rmrf() */
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <sys/vfs.h> /* statfs */
|
||||
#include <sys/vfs.h> /* statfs */
|
||||
#include <linux/magic.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
@@ -108,6 +111,34 @@ static char *signames[] = {
|
||||
"SYS",
|
||||
};
|
||||
|
||||
struct rlimit_name {
|
||||
char *name;
|
||||
int val;
|
||||
};
|
||||
|
||||
static const struct rlimit_name rlimit_names[] = {
|
||||
{ "as", RLIMIT_AS },
|
||||
{ "core", RLIMIT_CORE },
|
||||
{ "cpu", RLIMIT_CPU },
|
||||
{ "data", RLIMIT_DATA },
|
||||
{ "fsize", RLIMIT_FSIZE },
|
||||
{ "locks", RLIMIT_LOCKS },
|
||||
{ "memlock", RLIMIT_MEMLOCK },
|
||||
{ "msgqueue", RLIMIT_MSGQUEUE },
|
||||
{ "nice", RLIMIT_NICE },
|
||||
{ "nofile", RLIMIT_NOFILE },
|
||||
{ "nproc", RLIMIT_NPROC },
|
||||
{ "rss", RLIMIT_RSS },
|
||||
{ "rtprio", RLIMIT_RTPRIO },
|
||||
#ifdef RLIMIT_RTTIME
|
||||
{ "rttime", RLIMIT_RTTIME },
|
||||
#endif
|
||||
{ "sigpending", RLIMIT_SIGPENDING },
|
||||
{ "stack", RLIMIT_STACK },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/* https://freedesktop.org/software/systemd/man/systemd.exec.html#id-1.20.8 */
|
||||
static char *exitcodes[] = {
|
||||
"SUCCESS", /* 0: Std C exit OK */
|
||||
@@ -280,25 +311,85 @@ int getcgroup(char *buf, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
static int rmrf_cb(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||
{
|
||||
(void)sb;
|
||||
(void)tflag;
|
||||
|
||||
if (ftw->level == 0)
|
||||
return 0;
|
||||
|
||||
if (remove(fpath) && errno != EBUSY)
|
||||
warn("Failed removing %s", fpath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nftw() cannot pass user data, see also tmpfiles.c do_clean() */
|
||||
static uid_t chownr_uid;
|
||||
static gid_t chownr_gid;
|
||||
|
||||
static int chownr_cb(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
|
||||
{
|
||||
(void)tflag;
|
||||
(void)ftw;
|
||||
|
||||
if (sb->st_uid == chownr_uid && sb->st_gid == chownr_gid)
|
||||
return 0;
|
||||
|
||||
if (lchown(fpath, chownr_uid, chownr_gid))
|
||||
warn("Failed chown(%s, %d, %d)", fpath, (int)chownr_uid, (int)chownr_gid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* chown -R */
|
||||
int chownr(const char *path, uid_t uid, gid_t gid)
|
||||
{
|
||||
chownr_uid = uid;
|
||||
chownr_gid = gid;
|
||||
|
||||
return nftw(path, chownr_cb, 20, FTW_PHYS);
|
||||
}
|
||||
|
||||
/* empty a directory but keep it, silently ignores a missing path */
|
||||
int rmcontents(const char *path)
|
||||
{
|
||||
if (!fisdir(path))
|
||||
return 0;
|
||||
|
||||
return nftw(path, rmrf_cb, 20, FTW_DEPTH | FTW_PHYS);
|
||||
}
|
||||
|
||||
/* rm -rf, silently ignores a missing path */
|
||||
int rmrf(const char *path)
|
||||
{
|
||||
if (!fisdir(path))
|
||||
return 0;
|
||||
|
||||
nftw(path, rmrf_cb, 20, FTW_DEPTH | FTW_PHYS);
|
||||
if (remove(path) && errno != ENOENT)
|
||||
warn("Failed removing path %s", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Like mksubsys() but with the ids already resolved, uid -1 skips the
|
||||
* chown. Parents are created 0755, only the leaf gets @mode.
|
||||
*/
|
||||
int mksubsysd(const char *dir, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
mode_t omask;
|
||||
int uid, gid;
|
||||
int rc = 0;
|
||||
int rc;
|
||||
|
||||
omask = umask(0);
|
||||
|
||||
uid = getuser(user, NULL);
|
||||
if (uid >= 0) {
|
||||
gid = getgroup(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(dir, mode);
|
||||
if (rc && errno == EEXIST)
|
||||
rc = chmod(dir, mode);
|
||||
if (chown(dir, uid, gid))
|
||||
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
|
||||
rc = mkpath(dir, 0755);
|
||||
if (!rc) {
|
||||
rc = chmod(dir, mode);
|
||||
if (!rc && uid != (uid_t)-1 && chown(dir, uid, gid))
|
||||
err(1, "Failed chown(%s, %d, %d)", dir, (int)uid, (int)gid);
|
||||
}
|
||||
|
||||
umask(omask);
|
||||
@@ -306,34 +397,123 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int fnread(char *buf, size_t len, char *fmt, ...)
|
||||
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
{
|
||||
char path[256];
|
||||
va_list ap;
|
||||
FILE *fp;
|
||||
int uid, gid;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(path, sizeof(path), fmt, ap);
|
||||
va_end(ap);
|
||||
uid = getuser(user, NULL);
|
||||
gid = getgroup(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
if (uid < 0)
|
||||
warnx("Cannot find user %s, %s is owned by root", user, dir);
|
||||
|
||||
if (!buf || !len) {
|
||||
struct stat st;
|
||||
return mksubsysd(dir, mode, uid < 0 ? (uid_t)-1 : (uid_t)uid, (gid_t)gid);
|
||||
}
|
||||
|
||||
if (stat(path, &st))
|
||||
return -1;
|
||||
/*
|
||||
* Read an open stream to EOF into a malloc()'ed, NUL terminated buffer.
|
||||
*
|
||||
* st_size is only a hint here, and zero on procfs, so the read loop
|
||||
* runs until EOF rather than trusting it. Sizing from the already
|
||||
* open fd also leaves no window for the file to change between the
|
||||
* look and the read.
|
||||
*/
|
||||
static char *slurp(FILE *fp, size_t *lenp)
|
||||
{
|
||||
size_t len = 0, size = BUFSIZ;
|
||||
struct stat st;
|
||||
char *buf;
|
||||
|
||||
return (ssize_t)st.st_size;
|
||||
if (!fstat(fileno(fp), &st) && st.st_size > 0 && (size_t)st.st_size > size)
|
||||
size = (size_t)st.st_size;
|
||||
|
||||
buf = malloc(size + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
while (1) {
|
||||
char *ptr;
|
||||
|
||||
len += fread(&buf[len], 1, size - len, fp);
|
||||
if (len < size)
|
||||
break; /* EOF, or error caught below */
|
||||
|
||||
ptr = realloc(buf, size * 2 + 1);
|
||||
if (!ptr) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
buf = ptr;
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
if (ferror(fp)) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = fread(buf, sizeof(char), len - 1, fp);
|
||||
buf[len] = 0;
|
||||
if (lenp)
|
||||
*lenp = len;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a whole file into a malloc()'ed, NUL terminated buffer, which
|
||||
* the caller frees. @lenp, when given, returns the number of bytes
|
||||
* read; the content may itself contain NUL, e.g. /proc/PID/cmdline.
|
||||
*/
|
||||
char *vfslurp(size_t *lenp, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buf;
|
||||
FILE *fp;
|
||||
|
||||
fp = vfopenf("r", fmt, ap);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
buf = slurp(fp, lenp);
|
||||
fclose(fp);
|
||||
|
||||
return (int)len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *fslurp(size_t *lenp, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *buf;
|
||||
|
||||
va_start(ap, fmt);
|
||||
buf = vfslurp(lenp, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int fnread(char *buf, size_t len, char *fmt, ...)
|
||||
{
|
||||
size_t dlen;
|
||||
va_list ap;
|
||||
char *data;
|
||||
|
||||
va_start(ap, fmt);
|
||||
data = vfslurp(&dlen, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!data)
|
||||
return -1;
|
||||
|
||||
if (buf && len) {
|
||||
if (dlen > len - 1)
|
||||
dlen = len - 1;
|
||||
memcpy(buf, data, dlen);
|
||||
buf[dlen] = 0;
|
||||
}
|
||||
free(data);
|
||||
|
||||
return (int)dlen;
|
||||
}
|
||||
|
||||
int fnwrite(char *value, char *fmt, ...)
|
||||
@@ -483,6 +663,50 @@ char *code2str(int code)
|
||||
return exitcodes[code];
|
||||
}
|
||||
|
||||
int str2rlim(char *str)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (!strcmp(str, rn->name))
|
||||
return rn->val;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *rlim2str(int rlim)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (rn->val == rlim)
|
||||
return rn->name;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
char *lim2str(struct rlimit *rlim)
|
||||
{
|
||||
char tmp[25];
|
||||
static char buf[42];
|
||||
|
||||
buf[0] = 0;
|
||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
if (RLIM_INFINITY == rlim->rlim_max)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
|
||||
+13
@@ -29,8 +29,10 @@
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
#include <poll.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/resource.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
@@ -55,6 +57,7 @@ extern char *prognm;
|
||||
# include <lite/conio.h>
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "log.h"
|
||||
|
||||
char *progname (char *arg0);
|
||||
@@ -69,7 +72,13 @@ 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);
|
||||
int mksubsysd (const char *dir, mode_t mode, uid_t uid, gid_t gid);
|
||||
int chownr (const char *path, uid_t uid, gid_t gid);
|
||||
int rmcontents (const char *path);
|
||||
int rmrf (const char *path);
|
||||
|
||||
char *fslurp (size_t *lenp, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
char *vfslurp (size_t *lenp, const char *fmt, va_list ap);
|
||||
int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
int fnwrite (char *value, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
int fngetint (char *path, int *val);
|
||||
@@ -81,6 +90,10 @@ char *sig2str (int sig);
|
||||
int str2sig (char *sig);
|
||||
char *code2str (int code);
|
||||
|
||||
int str2rlim (char *str);
|
||||
char *rlim2str (int rlim);
|
||||
char *lim2str (struct rlimit *rlim);
|
||||
|
||||
void do_sleep (unsigned int sec);
|
||||
void do_usleep (unsigned int usec);
|
||||
long jiffies (void);
|
||||
|
||||
+124
-24
@@ -5,51 +5,151 @@
|
||||
# often needs to be synchronized carefully, we suggest using your system log
|
||||
# daemon as the barrier for all other services. E.g.:
|
||||
#
|
||||
# service if:udevd <run/udevadm:5/success> [S0123456789] syslogd -F -- System log daemon
|
||||
# service if:mdev <run/coldplug/success> [S0123456789] syslogd -F -- System log daemon
|
||||
# service if:mdevd <run/coldplug/success> [S0123456789] syslogd -F -- System log daemon
|
||||
# service syslogd {
|
||||
# description = "System log daemon"
|
||||
# runlevel = "S0123456789"
|
||||
# if = "udevd"
|
||||
# conditions = { "run/udevadm:5/success" }
|
||||
# command = "syslogd -F"
|
||||
# }
|
||||
#
|
||||
# with `if = "mdev"` or `if = "mdevd"` and the matching coldplug condition
|
||||
# for those variants.
|
||||
#
|
||||
# This provdes a <pid/syslogd> condition that can act as a barrier for all
|
||||
# other services. Notice the `if:` statement and the condition for starting
|
||||
# other services. Notice the `if` setting: the condition for starting
|
||||
# syslogd is only considered if either udevd or mdev (service) is loaded and
|
||||
# is guaranteed to run after each respective run stanza have completed.
|
||||
#
|
||||
# Override this file by copying it to /etc/finit.d/, using the same name, then
|
||||
# change the contents any way you like, it can even be empty.
|
||||
#
|
||||
# The leading '-' on every command says a missing binary is expected here, so
|
||||
# the block is skipped quietly and the next candidate gets its turn.
|
||||
|
||||
# Check for systemd-udevd and eudev, if we find both, we opt for the latter.
|
||||
service nowarn cgroup.system,name:udevd name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
||||
[S12345789] /lib/systemd/systemd-udevd $UDEVD_ARGS -- Device event daemon (udev)
|
||||
service nowarn cgroup.system,name:udevd name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
||||
[S12345789] udevd $UDEVD_ARGS -- Device event daemon (udev)
|
||||
service udevd {
|
||||
description = "Device event daemon (udev)"
|
||||
runlevel = "S12345789"
|
||||
notify = "none"
|
||||
envfile = "-/etc/default/udevd"
|
||||
pidfile = "udevd"
|
||||
pidfile-create = true
|
||||
log { }
|
||||
cgroup system { name = "udevd" }
|
||||
command = "-/lib/systemd/systemd-udevd $UDEVD_ARGS"
|
||||
}
|
||||
service udevd {
|
||||
description = "Device event daemon (udev)"
|
||||
runlevel = "S12345789"
|
||||
notify = "none"
|
||||
envfile = "-/etc/default/udevd"
|
||||
pidfile = "udevd"
|
||||
pidfile-create = true
|
||||
log { }
|
||||
cgroup system { name = "udevd" }
|
||||
command = "-udevd $UDEVD_ARGS"
|
||||
}
|
||||
|
||||
# Wait for udevd to start, then trigger coldplug events and module loading.
|
||||
# The last 'settle' call waits for it to finalize processing all uevents.
|
||||
run nowarn if:udevd cgroup.init,name:udevadm :1 [S] <service/udevd/ready> log udevadm settle -t 0 --
|
||||
run nowarn if:udevd cgroup.init,name:udevadm :2 [S] <service/udevd/ready> log udevadm control --reload --
|
||||
run nowarn if:udevd cgroup.init,name:udevadm :3 [S] <service/udevd/ready> log udevadm trigger -c add -t devices -- Requesting device events
|
||||
run nowarn if:udevd cgroup.init,name:udevadm :4 [S] <service/udevd/ready> log udevadm trigger -c add -t subsystems -- Requesting subsystem events
|
||||
run nowarn if:udevd cgroup.init,name:udevadm :5 [S] <service/udevd/ready> log udevadm settle -t 30 -- Waiting for udev to finish
|
||||
run udevadm:1 {
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "service/udevd/ready" }
|
||||
log { }
|
||||
cgroup init { name = "udevadm" }
|
||||
command = "-udevadm settle -t 0"
|
||||
}
|
||||
run udevadm:2 {
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "service/udevd/ready" }
|
||||
log { }
|
||||
cgroup init { name = "udevadm" }
|
||||
command = "-udevadm control --reload"
|
||||
}
|
||||
run udevadm:3 {
|
||||
description = "Requesting device events"
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "service/udevd/ready" }
|
||||
log { }
|
||||
cgroup init { name = "udevadm" }
|
||||
command = "-udevadm trigger -c add -t devices"
|
||||
}
|
||||
run udevadm:4 {
|
||||
description = "Requesting subsystem events"
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "service/udevd/ready" }
|
||||
log { }
|
||||
cgroup init { name = "udevadm" }
|
||||
command = "-udevadm trigger -c add -t subsystems"
|
||||
}
|
||||
run udevadm:5 {
|
||||
description = "Waiting for udev to finish"
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "service/udevd/ready" }
|
||||
log { }
|
||||
cgroup init { name = "udevadm" }
|
||||
command = "-udevadm settle -t 30"
|
||||
}
|
||||
|
||||
# If udevd is not available, check for mdevd. Unlike plain 'mdev -df', the
|
||||
# mdevd-coldplug process exits when mdevd has completed. So the condition
|
||||
# <run/coldplug/success> (same as for plain mdev below) can be relied on as a
|
||||
# barrier for services that, e.g., need to probe for switchcore ports or PHYs
|
||||
# that require firmware loading before they show up.
|
||||
service nowarn conflict:udevd,mdev cgroup.init,name:mdevd notify:none if:!udevd \
|
||||
[S12345789] notify:s6 mdevd -O 4 -D %n -- Device event daemon (mdevd)
|
||||
run nowarn conflict:udevd,mdev cgroup.init,name:coldplug <service/mdevd/ready> \
|
||||
[S] mdevd-coldplug -- Cold plugging system
|
||||
# <run/mdevd-coldplug/success> can be relied on as a barrier for services
|
||||
# that, e.g., need to probe for switchcore ports or PHYs that require
|
||||
# firmware loading before they show up.
|
||||
service mdevd {
|
||||
description = "Device event daemon (mdevd)"
|
||||
runlevel = "S12345789"
|
||||
if = "!udevd"
|
||||
conflicts = { "udevd", "mdev" }
|
||||
notify = "s6"
|
||||
cgroup init { name = "mdevd" }
|
||||
command = "-mdevd -O 4 -D %n"
|
||||
}
|
||||
|
||||
# NOTE: this block is named for its command, so its barrier condition is
|
||||
# <run/mdevd-coldplug/success>, not the <run/coldplug/success> the
|
||||
# plain mdev stanza below provides. The legacy line spelled the
|
||||
# name inside the cgroup argument, where it names the cgroup leaf
|
||||
# rather than the service, and the two paths have differed ever
|
||||
# since. Left as it was so boot ordering does not change.
|
||||
run mdevd-coldplug {
|
||||
description = "Cold plugging system"
|
||||
runlevel = "S"
|
||||
conflicts = { "udevd", "mdev" }
|
||||
conditions = { "service/mdevd/ready" }
|
||||
cgroup init { name = "coldplug" }
|
||||
command = "-mdevd-coldplug"
|
||||
}
|
||||
|
||||
# If neither udevd or mdevd are available, fall back to mdev. The new daemon
|
||||
# mode calls `mdev -s` internally but does not create a PID file when that is
|
||||
# done, so synchronization with coldplug is fragile.
|
||||
service nowarn conflict:udevd,mdevd cgroup.init,name:mdev notify:none if:!udevd,!mdevd \
|
||||
[S12345789] mdev -df -S -- Device event daemon (mdev)
|
||||
service mdev {
|
||||
description = "Device event daemon (mdev)"
|
||||
runlevel = "S12345789"
|
||||
if = "!udevd,!mdevd"
|
||||
conflicts = { "udevd", "mdevd" }
|
||||
notify = "none"
|
||||
cgroup init { name = "mdev" }
|
||||
command = "-mdev -df -S"
|
||||
}
|
||||
|
||||
# Trigger a replay of all uevents from boot, before userspace was up and the
|
||||
# filesystem mounted. Compared to udevd, we don't know when 'mdev -df' has
|
||||
# finished processing all events, i.e., depending on <run/coldplug/success> is
|
||||
# not an indicator of all devices having been (re)discovered and fully set up.
|
||||
run nowarn conflict:udevd,mdevd cgroup.init name:coldplug if:mdev <service/mdev/running> \
|
||||
[S] @pkglibexecdir@/coldplug -- Cold plugging system
|
||||
run coldplug {
|
||||
description = "Cold plugging system"
|
||||
runlevel = "S"
|
||||
if = "mdev"
|
||||
conflicts = { "udevd", "mdevd" }
|
||||
conditions = { "service/mdev/running" }
|
||||
cgroup init {}
|
||||
command = "-@pkglibexecdir@/coldplug"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# Test service, only used for `make check`, not for public use
|
||||
service [S123456789] name:testserv pid:!/run/testserv.pid \
|
||||
<boot/testserv> cgroup.system notify:none \
|
||||
/sbin/serv -n -p -i testserv -- Test serv daemon
|
||||
service testserv {
|
||||
description = "Test serv daemon"
|
||||
runlevel = "S123456789"
|
||||
conditions = { "boot/testserv" }
|
||||
notify = "none"
|
||||
pidfile = "/run/testserv.pid"
|
||||
cgroup system {}
|
||||
command = "/sbin/serv -n -p -i testserv"
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ EXTRA_DIST += add-remove-dynamic-service.sh
|
||||
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
||||
EXTRA_DIST += bootstrap-crash.sh
|
||||
EXTRA_DIST += cond-start-task.sh
|
||||
EXTRA_DIST += conf-format.sh
|
||||
EXTRA_DIST += conf-dirs.sh
|
||||
EXTRA_DIST += conf-if.sh
|
||||
EXTRA_DIST += conf-template.sh
|
||||
EXTRA_DIST += script-timeout.sh
|
||||
EXTRA_DIST += crashing.sh
|
||||
EXTRA_DIST += dep-chain-reload.sh
|
||||
EXTRA_DIST += depserv.sh
|
||||
@@ -75,6 +80,11 @@ TESTS += add-remove-dynamic-service.sh
|
||||
TESTS += add-remove-dynamic-service-sub-config.sh
|
||||
TESTS += bootstrap-crash.sh
|
||||
TESTS += cond-start-task.sh
|
||||
TESTS += conf-format.sh
|
||||
TESTS += conf-dirs.sh
|
||||
TESTS += conf-if.sh
|
||||
TESTS += conf-template.sh
|
||||
TESTS += script-timeout.sh
|
||||
TESTS += crashing.sh
|
||||
TESTS += dep-chain-reload.sh
|
||||
TESTS += depserv.sh
|
||||
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/bin/sh
|
||||
# Verify the per-service directory settings: runtime-dir, state-dir,
|
||||
# cache-dir, logs-dir, and config-dir. The directory is created before
|
||||
# the service starts, owned by the service user, and exported to the
|
||||
# environment. The runtime directory is removed again when the service
|
||||
# stops, the others persist.
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
BOOTSTRAP="service owned {
|
||||
runlevel = \"S12345\"
|
||||
user = \"daemon\"
|
||||
group = \"daemon\"
|
||||
runtime-dir = \"owned\"
|
||||
state-dir = \"owned\"
|
||||
pidfile = \"/run/owned/serv.pid\"
|
||||
command = \"/sbin/serv -np -P /run/owned/serv.pid -i owned -e STATE_DIRECTORY:/var/lib/owned\"
|
||||
}
|
||||
task probe {
|
||||
runlevel = \"S12345\"
|
||||
runtime-dir = \"probe\"
|
||||
cache-dir = \"probe\"
|
||||
command = \"/sbin/serv -h -e RUNTIME_DIRECTORY:/run/probe -e CACHE_DIRECTORY:/var/cache/probe\"
|
||||
}
|
||||
service escape {
|
||||
runlevel = \"S12345\"
|
||||
runtime-dir = \"../escape\"
|
||||
command = \"serv -np -i escape\"
|
||||
}
|
||||
service modes {
|
||||
runlevel = \"S12345\"
|
||||
user = \"daemon\"
|
||||
runtime-dir = \"modes\"
|
||||
runtime-dir-mode = 0700
|
||||
runtime-dir-preserve = \"restart\"
|
||||
config-dir = \"modes\"
|
||||
command = \"/sbin/serv -np -P /run/modes/serv.pid -i modes\"
|
||||
}"
|
||||
|
||||
# ls output is empty for an empty directory, so test -d instead
|
||||
assert_dir()
|
||||
{
|
||||
assert "Directory $1 exists" "$(texec test -d "$1" && echo yes)" = "yes"
|
||||
}
|
||||
|
||||
assert_nodir()
|
||||
{
|
||||
assert "Directory $1 removed" "$(texec test -d "$1" || echo gone)" = "gone"
|
||||
}
|
||||
|
||||
assert_owner()
|
||||
{
|
||||
assert "$1 owned by $2" "$(texec stat -c %U:%G "$1")" = "$2"
|
||||
}
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
|
||||
say 'Directory created before start, owned by the service user'
|
||||
retry 'assert_status owned running'
|
||||
assert_dir /run/owned
|
||||
assert_owner /run/owned daemon:daemon
|
||||
assert_dir /var/lib/owned
|
||||
assert_owner /var/lib/owned daemon:daemon
|
||||
|
||||
say 'The paths are exported to the process environment; both commands'
|
||||
say 'verify their own with serv -e, and refuse to run on a mismatch'
|
||||
|
||||
say 'A completed task no longer holds its runtime directory'
|
||||
retry 'assert_status probe done'
|
||||
assert_nodir /run/probe
|
||||
assert_dir /var/cache/probe
|
||||
|
||||
say 'Stopping the service removes the runtime directory, state persists'
|
||||
run "initctl stop owned"
|
||||
retry 'assert_status owned stopped'
|
||||
assert_nodir /run/owned
|
||||
assert_dir /var/lib/owned
|
||||
|
||||
say 'Starting again recreates it'
|
||||
run "initctl start owned"
|
||||
retry 'assert_status owned running'
|
||||
assert_dir /run/owned
|
||||
|
||||
say 'A path escaping the base directory is refused, service still runs'
|
||||
retry 'assert_status escape running'
|
||||
assert_nodir /escape
|
||||
|
||||
say 'runtime-dir-mode sets the mode, config-dir is never chowned'
|
||||
retry 'assert_status modes running'
|
||||
assert "mode is 0700" "$(texec stat -c %a /run/modes)" = "700"
|
||||
assert_owner /run/modes daemon:root
|
||||
assert_owner /etc/modes root:root
|
||||
|
||||
say 'runtime-dir-preserve restart keeps the directory across a restart'
|
||||
run "touch /run/modes/keepsake" || texec touch /run/modes/keepsake
|
||||
run "initctl restart modes"
|
||||
retry 'assert_status modes running'
|
||||
assert_file_exists /run/modes/keepsake
|
||||
|
||||
say 'but a real stop still removes it'
|
||||
run "initctl stop modes"
|
||||
retry 'assert_status modes stopped'
|
||||
assert_nodir /run/modes
|
||||
|
||||
say 'An existing directory with the wrong owner is chowned back, and'
|
||||
say 'its mode is locked down again'
|
||||
run "initctl stop owned"
|
||||
retry 'assert_status owned stopped'
|
||||
texec mkdir -p /var/lib/owned/sub
|
||||
texec touch /var/lib/owned/sub/file
|
||||
texec chown -R 0:0 /var/lib/owned
|
||||
texec chmod 0700 /var/lib/owned
|
||||
run "initctl start owned"
|
||||
retry 'assert_status owned running'
|
||||
assert "mode locked down to 0755" "$(texec stat -c %a /var/lib/owned)" = "755"
|
||||
assert_owner /var/lib/owned daemon:daemon
|
||||
assert_owner /var/lib/owned/sub/file daemon:daemon
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user