├── doc ├── README.md ├── config │ ├── README.md │ ├── Makefile.am │ ├── limitations.md │ ├── service-env.md │ ├── service-wrappers.md │ ├── index.md │ ├── env.md │ ├── templating.md │ ├── task-and-run.md │ ├── runparts.md │ ├── logging.md │ └── rescue.md ├── .gitignore ├── img │ ├── logo.png │ ├── finit.jpg │ ├── finit3.png │ ├── svc-machine.png │ ├── alpine-screenshot.png │ ├── finit-screenshot.jpg │ ├── finit3-screenshot.png │ ├── finit4-screenshot.png │ ├── alpine-screenshot2.png │ └── finit.txt ├── extra.css ├── AUTHORS ├── Makefile.am ├── commands.md ├── keventd.md ├── requirements.md ├── state-machine.md ├── signals.md ├── runparts.md ├── watchdog.md ├── TODO.md └── service.md ├── test ├── skel │ ├── cdrom │ │ └── .empty │ ├── home │ │ └── .empty │ ├── proc │ │ └── .empty │ ├── root │ │ └── .empty │ ├── run │ │ └── .empty │ ├── srv │ │ └── .empty │ ├── sys │ │ └── .empty │ ├── tmp │ │ └── .empty │ ├── var │ │ └── .empty │ ├── dev │ │ ├── pts │ │ │ └── .empty │ │ └── shm │ │ │ └── .empty │ ├── etc │ │ ├── finit.d │ │ │ ├── .empty │ │ │ ├── available │ │ │ │ └── .empty │ │ │ └── enabled │ │ │ │ └── .empty │ │ ├── hostname │ │ ├── rc.d │ │ │ ├── S02def.sh │ │ │ ├── foo.sh │ │ │ ├── test.sh │ │ │ └── S01abc.sh │ │ ├── rcS.d │ │ │ ├── S02def.sh │ │ │ ├── foo.sh │ │ │ ├── test.sh │ │ │ └── S01abc.sh │ │ ├── init.d │ │ │ ├── rcK │ │ │ ├── rcS │ │ │ ├── S02-serv.sh │ │ │ └── S01-service.sh │ │ ├── env │ │ ├── fstab │ │ └── inittab │ ├── usr │ │ ├── bin │ │ │ └── .empty │ │ ├── games │ │ │ └── .empty │ │ ├── include │ │ │ └── .empty │ │ ├── lib │ │ │ └── .empty │ │ ├── sbin │ │ │ └── .empty │ │ ├── share │ │ │ ├── .empty │ │ │ └── runparts │ │ │ │ ├── sysv.log │ │ │ │ └── ref.log │ │ ├── src │ │ │ └── .empty │ │ └── local │ │ │ ├── bin │ │ │ └── .empty │ │ │ ├── etc │ │ │ └── .empty │ │ │ ├── games │ │ │ └── .empty │ │ │ ├── lib │ │ │ └── .empty │ │ │ ├── sbin │ │ │ └── .empty │ │ │ ├── share │ │ │ └── .empty │ │ │ └── src │ │ │ └── .empty │ ├── bin │ │ ├── fail.sh │ │ ├── ready.sh │ │ ├── crasher.sh │ │ ├── busybox-x86_64.sha256 │ │ ├── pre.sh │ │ ├── post.sh │ │ └── slay │ └── sbin │ │ ├── fail.sh │ │ ├── task.sh │ │ ├── service.sh │ │ └── chrootsetup.sh ├── lib │ ├── .gitignore │ ├── Makefile.am │ ├── enter.sh │ ├── start.sh │ └── exec.sh ├── src │ ├── .gitignore │ └── Makefile.am ├── .gitignore ├── test.env.in ├── rclocal.sh ├── runlevel.sh ├── testserv.sh ├── bootstrap-crash.sh ├── add-remove-dynamic-service.sh ├── svc-env.sh ├── pre-fail.sh ├── start-stop-service.sh ├── setup-sysroot.sh ├── add-remove-dynamic-service-sub-config.sh ├── start-stop-service-sub-config.sh ├── check.sh ├── restart-self.sh ├── runparts.sh ├── sysvparts.sh ├── signal-service.sh ├── start-kill-service.sh ├── run-restart-forever.sh ├── devmon.sh ├── failing-sysv.sh ├── pidfile.sh ├── initctl-status-subset.sh ├── ready-serv.sh ├── README.md ├── crashing.sh ├── global-envs.sh ├── run-task-tricks.sh ├── start-kill-stop.sh ├── process-depends.sh ├── start-stop-serv.sh ├── start-stop-sysv.sh ├── unexpected-restart.sh ├── depserv.sh └── cond-start-task.sh ├── m4 ├── .gitignore ├── expand.m4 └── plugin.m4 ├── contrib ├── .gitignore ├── void │ ├── finit.d │ │ ├── available │ │ │ ├── dhcpcd.conf │ │ │ ├── lxdm.conf │ │ │ ├── dmeventd.conf │ │ │ ├── uuidd.conf │ │ │ ├── crond.conf │ │ │ ├── keymap.conf │ │ │ ├── sshd.conf │ │ │ ├── modules.conf │ │ │ ├── ntpd.conf │ │ │ ├── acpid.conf │ │ │ ├── getty.conf │ │ │ ├── syslog.conf │ │ │ └── Makefile.am │ │ └── Makefile.am │ ├── Makefile.am │ ├── rc.local │ ├── build.sh │ ├── finit.conf │ ├── install.sh │ └── README.md ├── alpine │ ├── finit.d │ │ ├── available │ │ │ ├── lxdm.conf │ │ │ ├── crond.conf │ │ │ ├── watchdog.conf │ │ │ ├── keymap.conf │ │ │ ├── modules.conf │ │ │ ├── dropbear.conf │ │ │ ├── ntpd.conf │ │ │ ├── acpid.conf │ │ │ ├── getty.conf │ │ │ ├── Makefile.am │ │ │ └── syslogd.conf │ │ └── Makefile.am │ ├── Makefile.am │ ├── rc.local │ ├── uninstall.sh │ ├── build.sh │ ├── finit.conf │ ├── install.sh │ └── README.md ├── debian │ ├── finit.d │ │ ├── available │ │ │ ├── lightdm.conf │ │ │ ├── apparmor.conf │ │ │ ├── avahi-daemon.conf │ │ │ ├── anacron.conf │ │ │ ├── cron.conf │ │ │ ├── plymouth-quit.conf │ │ │ ├── sntpd.conf │ │ │ ├── console-setup.conf │ │ │ ├── keyboard-setup.conf │ │ │ ├── sshd.conf │ │ │ ├── modules.conf │ │ │ ├── syslogd.conf │ │ │ ├── rsyslogd.conf │ │ │ ├── xdm.conf │ │ │ ├── acpid.conf │ │ │ ├── bluetooth.conf │ │ │ ├── elogind.conf │ │ │ ├── getty.conf │ │ │ └── Makefile.am │ │ └── Makefile.am │ ├── Makefile.am │ ├── finit.conf │ ├── build.sh │ └── install.sh ├── Makefile.am ├── README.md ├── service ├── fstab.bb └── procmon.sh ├── man ├── .gitignore └── Makefile.am ├── src ├── rescue.conf ├── .gitignore ├── coldplug ├── devmon.h ├── clone3.h ├── watchdog.h ├── schedule.h ├── sm.h ├── serv.h ├── client.h ├── cond.h ├── tty.h ├── schedule.c ├── utmp-api.h ├── cgroup.h ├── initctl.h ├── mdadm.c ├── private.h ├── service.h └── cgutil.h ├── tmpfiles.d ├── .gitignore ├── lldpd.conf ├── sshd.conf ├── openswan.conf ├── dnsmasq.conf ├── uuidd.conf ├── frr.conf ├── quagga.conf ├── testserv.conf ├── etc.conf ├── x11.conf ├── legacy.conf ├── Makefile.am ├── finit.conf └── var.conf ├── autogen.sh ├── system ├── .gitignore ├── 90-testserv.conf └── Makefile.am ├── plugins ├── .gitignore ├── x11-common.c ├── resolvconf.c ├── alsa-utils.c └── Makefile.am ├── .github ├── FUNDING.yml ├── SECURITY.md ├── workflows │ ├── docs.yml │ └── weekly.yml └── CODE-OF-CONDUCT.md ├── libsystemd ├── .gitignore ├── libsystemd.pc.in ├── Makefile.am └── sd-daemon.h ├── .globalrc ├── .gitignore ├── LICENSE ├── Makefile.am └── README.md /doc/README.md: -------------------------------------------------------------------------------- 1 | index.md -------------------------------------------------------------------------------- /test/skel/cdrom/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/home/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/proc/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/root/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/run/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/srv/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/sys/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/tmp/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/var/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/config/README.md: -------------------------------------------------------------------------------- 1 | index.md -------------------------------------------------------------------------------- /test/skel/dev/pts/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/dev/shm/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/etc/finit.d/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/bin/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/games/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/include/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/lib/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/sbin/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/share/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/src/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | *.m4 2 | !plugin.m4 -------------------------------------------------------------------------------- /test/skel/etc/hostname: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/skel/usr/local/bin/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/local/etc/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/local/games/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/local/lib/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/local/sbin/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/local/share/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/usr/local/src/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/etc/finit.d/available/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/etc/finit.d/enabled/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/skel/etc/rc.d/S02def.sh: -------------------------------------------------------------------------------- 1 | S01abc.sh -------------------------------------------------------------------------------- /test/skel/etc/rcS.d/S02def.sh: -------------------------------------------------------------------------------- 1 | S01abc.sh -------------------------------------------------------------------------------- /contrib/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | -------------------------------------------------------------------------------- /man/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | -------------------------------------------------------------------------------- /src/rescue.conf: -------------------------------------------------------------------------------- 1 | runlevel 1 2 | tty [12345] rescue 3 | -------------------------------------------------------------------------------- /test/lib/.gitignore: -------------------------------------------------------------------------------- 1 | /Makefile 2 | /Makefile.in 3 | -------------------------------------------------------------------------------- /tmpfiles.d/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | -------------------------------------------------------------------------------- /tmpfiles.d/lldpd.conf: -------------------------------------------------------------------------------- 1 | d /var/run/lldpd 0755 - - - 2 | -------------------------------------------------------------------------------- /tmpfiles.d/sshd.conf: -------------------------------------------------------------------------------- 1 | d /var/run/sshd 01755 - - - 2 | -------------------------------------------------------------------------------- /tmpfiles.d/openswan.conf: -------------------------------------------------------------------------------- 1 | d /var/run/pluto 0755 - - - 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -vifm 4 | -------------------------------------------------------------------------------- /test/skel/bin/fail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sleep 2 4 | exit 42 5 | -------------------------------------------------------------------------------- /tmpfiles.d/dnsmasq.conf: -------------------------------------------------------------------------------- 1 | d /var/run/dnsmasq 0755 nobody nobody - 2 | -------------------------------------------------------------------------------- /test/skel/bin/ready.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "READY" > /tmp/ready 4 | -------------------------------------------------------------------------------- /test/lib/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = exec.sh setup.sh start.sh sysroot.mk 2 | -------------------------------------------------------------------------------- /test/skel/etc/rcS.d/foo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0" >> /tmp/sysv.log 4 | -------------------------------------------------------------------------------- /test/skel/etc/rcS.d/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0" >> /tmp/sysv.log 4 | -------------------------------------------------------------------------------- /system/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | # generated 4 | 10-hotplug.conf 5 | -------------------------------------------------------------------------------- /test/skel/etc/init.d/rcK: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Shutting down ..." 4 | exit 0 5 | -------------------------------------------------------------------------------- /test/skel/etc/rc.d/foo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0" >> /tmp/runparts.log 4 | -------------------------------------------------------------------------------- /test/skel/etc/rc.d/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0" >> /tmp/runparts.log 4 | -------------------------------------------------------------------------------- /test/skel/etc/rcS.d/S01abc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0 $*" >> /tmp/sysv.log 4 | -------------------------------------------------------------------------------- /tmpfiles.d/uuidd.conf: -------------------------------------------------------------------------------- 1 | # Void Linux uuidd 2 | d /var/run/uuidd 0755 uuidd uuidd 3 | -------------------------------------------------------------------------------- /doc/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/logo.png -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_man8_MANS = finit.8 initctl.8 2 | dist_man5_MANS = finit.conf.5 3 | -------------------------------------------------------------------------------- /test/skel/etc/rc.d/S01abc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0 $*" >> /tmp/runparts.log 4 | -------------------------------------------------------------------------------- /test/src/.gitignore: -------------------------------------------------------------------------------- 1 | /Makefile 2 | /Makefile.in 3 | /.libs/ 4 | /.deps/ 5 | /serv 6 | -------------------------------------------------------------------------------- /tmpfiles.d/frr.conf: -------------------------------------------------------------------------------- 1 | d /var/run/frr 0755 frr frr - 2 | d /var/log/frr 0755 frr frr - 3 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/dhcpcd.conf: -------------------------------------------------------------------------------- 1 | service [2345] dhcpcd -B -M -- DHCP client 2 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/lxdm.conf: -------------------------------------------------------------------------------- 1 | service [2345] lxdm -- Lightweight Login Manager 2 | -------------------------------------------------------------------------------- /doc/img/finit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/finit.jpg -------------------------------------------------------------------------------- /doc/img/finit3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/finit3.png -------------------------------------------------------------------------------- /plugins/.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | .deps/* 4 | .libs/* 5 | Makefile 6 | Makefile.in 7 | -------------------------------------------------------------------------------- /test/skel/bin/crasher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #sleep 2 4 | kill -11 $$ 5 | 6 | exit 1 7 | 8 | -------------------------------------------------------------------------------- /test/skel/etc/init.d/rcS: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Starting up ..." 4 | poweroff -d 2 5 | 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [troglobit] 4 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/lxdm.conf: -------------------------------------------------------------------------------- 1 | service [2345] lxdm -- Lightweight Login Manager 2 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/lightdm.conf: -------------------------------------------------------------------------------- 1 | service [3] lightdm -- Light Display Manager 2 | -------------------------------------------------------------------------------- /doc/img/svc-machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/svc-machine.png -------------------------------------------------------------------------------- /test/skel/usr/share/runparts/sysv.log: -------------------------------------------------------------------------------- 1 | /etc/rcS.d/S01abc.sh start 2 | /etc/rcS.d/S02def.sh start 3 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/dmeventd.conf: -------------------------------------------------------------------------------- 1 | service [S12345] dmeventd -f -- Device mapper event daemon 2 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/uuidd.conf: -------------------------------------------------------------------------------- 1 | service @uuidd:uuidd [2345] uuidd -F -- UUID generation daemon 2 | -------------------------------------------------------------------------------- /tmpfiles.d/quagga.conf: -------------------------------------------------------------------------------- 1 | d /var/run/quagga 0755 quagga quagga - 2 | d /var/log/quagga 0755 quagga quagga - 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/apparmor.conf: -------------------------------------------------------------------------------- 1 | sysv [S] /etc/init.d/apparmor -- AppArmor initialization 2 | 3 | -------------------------------------------------------------------------------- /doc/img/alpine-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/alpine-screenshot.png -------------------------------------------------------------------------------- /doc/img/finit-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/finit-screenshot.jpg -------------------------------------------------------------------------------- /doc/img/finit3-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/finit3-screenshot.png -------------------------------------------------------------------------------- /doc/img/finit4-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/finit4-screenshot.png -------------------------------------------------------------------------------- /contrib/void/finit.d/available/crond.conf: -------------------------------------------------------------------------------- 1 | service [2345] crond -f -S -c /etc/crontabs -- Cron daemon 2 | -------------------------------------------------------------------------------- /doc/img/alpine-screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finit-project/finit/HEAD/doc/img/alpine-screenshot2.png -------------------------------------------------------------------------------- /libsystemd/.gitignore: -------------------------------------------------------------------------------- 1 | .deps/* 2 | .libs/* 3 | .dirstamp 4 | *.lo 5 | libsystemd.* 6 | Makefile 7 | Makefile.in 8 | -------------------------------------------------------------------------------- /test/skel/bin/busybox-x86_64.sha256: -------------------------------------------------------------------------------- 1 | 297fee002c1a4dad65e98634880afb530637af5ec0659115bec5e487ea2cc384 busybox-x86_64 2 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/crond.conf: -------------------------------------------------------------------------------- 1 | service [2345] env:-/etc/conf.d/crond crond -f -S $CRON_OPTS -- Cron daemon 2 | -------------------------------------------------------------------------------- /test/skel/usr/share/runparts/ref.log: -------------------------------------------------------------------------------- 1 | /etc/rc.d/S01abc.sh start 2 | /etc/rc.d/S02def.sh start 3 | /etc/rc.d/foo.sh 4 | /etc/rc.d/test.sh 5 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/watchdog.conf: -------------------------------------------------------------------------------- 1 | service [S123456789] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog 2 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/keymap.conf: -------------------------------------------------------------------------------- 1 | # Oneshot task to run once at bootstrap 2 | task [S] zcat /etc/keymap/se.bmap.gz | loadkmap -- Loading keymap 3 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/sshd.conf: -------------------------------------------------------------------------------- 1 | # Start SSH daemon as soon as basic networking comes up 2 | service [2345] /usr/sbin/sshd -D -- OpenSSH daemon 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/avahi-daemon.conf: -------------------------------------------------------------------------------- 1 | service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack 2 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | # GNU configure and build system 2 | *.log 3 | *.trs 4 | /Makefile 5 | /Makefile.in 6 | 7 | /test.env 8 | /checkself.sh 9 | /sysroot 10 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/keymap.conf: -------------------------------------------------------------------------------- 1 | # Oneshot task to run once at bootstrap 2 | task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/anacron.conf: -------------------------------------------------------------------------------- 1 | task [2345] halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs 2 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/cron.conf: -------------------------------------------------------------------------------- 1 | service [2345] env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon 2 | -------------------------------------------------------------------------------- /test/skel/sbin/fail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This run/task always fails, it is used by the run-restart-forever test 3 | 4 | echo "Aiiee, I've failed!" >&2 5 | exit 1 6 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/plymouth-quit.conf: -------------------------------------------------------------------------------- 1 | # Need to stop plymouth, or it'll block stdin on tty1 2 | task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/sntpd.conf: -------------------------------------------------------------------------------- 1 | # Simple NTP client (SNTP) 2 | service [2345] env:-/etc/default/sntpd sntpd -n $SNTPD_OPTIONS -- SNTP client 3 | -------------------------------------------------------------------------------- /contrib/debian/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = finit.d 2 | docsdir := @docdir@/contrib/debian 3 | docs_DATA = README.md finit.conf build.sh install.sh 4 | EXTRA_DIST = $(docs_DATA) 5 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/console-setup.conf: -------------------------------------------------------------------------------- 1 | # Oneshot task to run once at bootstrap 2 | task [S] /lib/console-setup/console-setup.sh -- Setting up console font and keymap 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/keyboard-setup.conf: -------------------------------------------------------------------------------- 1 | # Oneshot task to run once at bootstrap 2 | task [S] /lib/console-setup/keyboard-setup.sh -- Setting up console keyboard layout 3 | -------------------------------------------------------------------------------- /contrib/void/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = finit.d 2 | docsdir := @docdir@/contrib/void 3 | docs_DATA = README.md finit.conf build.sh install.sh rc.local 4 | EXTRA_DIST = $(docs_DATA) 5 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | .deps/* 2 | finit 3 | getty 4 | initctl 5 | keventd 6 | logit 7 | reboot 8 | runparts 9 | sulogin 10 | tmpfiles 11 | watchdogd 12 | Makefile 13 | Makefile.in 14 | -------------------------------------------------------------------------------- /test/skel/bin/pre.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | marker="PRE" 4 | # shellcheck disable=SC2154 5 | if [ -n "$foo" ]; then 6 | marker=$foo 7 | fi 8 | 9 | echo "$marker" > /tmp/pre 10 | -------------------------------------------------------------------------------- /test/skel/sbin/task.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | num=$(cat /run/task.state) 4 | if [ -z "$num" ]; then 5 | num=0 6 | fi 7 | 8 | num=$((num + 1)) 9 | echo $num > /run/task.state 10 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/modules.conf: -------------------------------------------------------------------------------- 1 | ## Linux kernel modules to load 2 | #module fbcon 3 | #module button 4 | #module evdev 5 | #module mousedev 6 | #module 8139cp 7 | module softdog 8 | -------------------------------------------------------------------------------- /contrib/alpine/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = finit.d 2 | docsdir := @docdir@/contrib/alpine 3 | docs_DATA = README.md finit.conf build.sh install.sh rc.local 4 | EXTRA_DIST = $(docs_DATA) 5 | 6 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/modules.conf: -------------------------------------------------------------------------------- 1 | ## Linux kernel modules to load 2 | #module fbcon 3 | #module button 4 | #module evdev 5 | #module mousedev 6 | #module 8139cp 7 | #module softdog 8 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/ntpd.conf: -------------------------------------------------------------------------------- 1 | # Silly Busybox ntpd has no option to use syslog when running in foreground 2 | service [2345] log ntpd -n -N -p pool.ntp.org -- NTP daemon 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/sshd.conf: -------------------------------------------------------------------------------- 1 | # Start SSH daemon as soon as basic networking comes up 2 | service [2345] env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon 3 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/dropbear.conf: -------------------------------------------------------------------------------- 1 | # Start SSH daemon as soon as basic networking comes up 2 | service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon 3 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/ntpd.conf: -------------------------------------------------------------------------------- 1 | # Busybox ntpd has no option to use syslog when running in foreground 2 | service [2345] env:-/etc/conf.d/ntpd log ntpd -n $NTPD_OPTS -- NTP daemon 3 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/modules.conf: -------------------------------------------------------------------------------- 1 | ## Linux kernel modules to load 2 | #module fbcon 3 | #module button 4 | #module evdev 5 | #module loop 6 | #module psmouse 7 | #module mousedev 8 | #module 8139cp 9 | -------------------------------------------------------------------------------- /tmpfiles.d/testserv.conf: -------------------------------------------------------------------------------- 1 | # Test service, only used for `make check`, not for public use 2 | 3 | d /var/run/serv 0755 - - - 4 | d /var/lib/serv 0755 - - - 5 | d /tmp/serv 0755 - - - 6 | r /run/serv.pid 7 | -------------------------------------------------------------------------------- /test/skel/etc/env: -------------------------------------------------------------------------------- 1 | # Some environments to be sourced by Finit services 2 | # and their pre:- and post:scripts 3 | 4 | foo=bar 5 | baz=qux 6 | 7 | xyzzy=$foo 8 | 9 | FOO_ARGS="-i $foo -l $xyzzy endarg" 10 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/syslogd.conf: -------------------------------------------------------------------------------- 1 | # Start sysklogd as soon as possible, should always run if enabled 2 | service [S123456789] env:-/etc/default/syslogd syslogd -F $SYSLOGD_OPTIONS -- System logging daemon 3 | -------------------------------------------------------------------------------- /test/test.env.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | 4 | export TESTENV_PATH=/bin:/sbin:/usr/bin:/usr/sbin:"@bindir@":"@sbindir@" 5 | export FINIT_CONF="@FINIT_CONF@" 6 | export FINIT_RCSD="@FINIT_RCSD@" 7 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/acpid.conf: -------------------------------------------------------------------------------- 1 | # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) 2 | # Depends on syslogd having started. Redirect any output to log. 3 | service [2345] acpid -f -- ACPI daemon 4 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/getty.conf: -------------------------------------------------------------------------------- 1 | # Consoles to start Finit built-in getty on when system is up 2 | tty [12345] /dev/tty1 noclear nowait 3 | tty [2345] /dev/tty2 noclear nowait 4 | tty [2345] /dev/tty3 noclear nowait 5 | -------------------------------------------------------------------------------- /contrib/void/rc.local: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script is executed once at boot, at the very end, 3 | # right before starting the TTYs. 4 | # 5 | # Each line is run in sequence. 6 | # 7 | # NOTE: Remember to set the executable bit! 8 | -------------------------------------------------------------------------------- /contrib/alpine/rc.local: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script is executed once at boot, at the very end, 3 | # right before starting the TTYs. 4 | # 5 | # Each line is run in sequence. 6 | # 7 | # NOTE: Remember to set the executable bit! 8 | # 9 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/acpid.conf: -------------------------------------------------------------------------------- 1 | # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) 2 | # Depends on syslogd having started. Redirect any output to log. 3 | service [2345] log acpid -f -- ACPI daemon 4 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/getty.conf: -------------------------------------------------------------------------------- 1 | # Consoles to start getty on when system is up 2 | tty [12345] /dev/ttyS0 noclear 3 | tty [12345] /dev/tty1 noclear nowait 4 | tty [2345] /dev/tty2 noclear nowait 5 | tty [2345] /dev/tty3 noclear nowait 6 | -------------------------------------------------------------------------------- /system/90-testserv.conf: -------------------------------------------------------------------------------- 1 | # Test service, only used for `make check`, not for public use 2 | service [S123456789] name:testserv pid:!/run/testserv.pid \ 3 | cgroup.system notify:none \ 4 | /sbin/serv -n -p -i testserv -- Test serv daemon 5 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/rsyslogd.conf: -------------------------------------------------------------------------------- 1 | # Start rsyslogd as soon as possible, should always run 2 | # Provides pid/syslogd condition 3 | service [S12345] name:syslogd env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_OPTIONS -- Reliable syslog daemon 4 | -------------------------------------------------------------------------------- /doc/extra.css: -------------------------------------------------------------------------------- 1 | .md-header__topic:first-child { 2 | font-weight: 500; 3 | font-style: italic; 4 | } 5 | 6 | .md-header__title { 7 | font-size: 1.1rem; 8 | font-style: italic; 9 | font-weight: 500; 10 | line-height: 2.6rem; 11 | } 12 | -------------------------------------------------------------------------------- /tmpfiles.d/etc.conf: -------------------------------------------------------------------------------- 1 | L /etc/os-release - - - - ../usr/lib/os-release 2 | L+ /etc/mtab - - - - ../proc/self/mounts 3 | C! /etc/locale.conf - - - - 4 | C! /etc/nsswitch.conf - - - - 5 | C! /etc/vconsole.conf - - - - 6 | C! /etc/pam.d - - - - 7 | C! /etc/issue - - - - 8 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/Makefile.am: -------------------------------------------------------------------------------- 1 | docsdir := @docdir@/contrib/alpine/finit.d/available 2 | docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \ 3 | lxdm.conf modules.conf ntpd.conf syslogd.conf watchdog.conf 4 | EXTRA_DIST = $(docs_DATA) 5 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/xdm.conf: -------------------------------------------------------------------------------- 1 | # Here we use XDM since it has few dependencies (relatively speaking). 2 | # It is also an example of a service without a -- description. 3 | # service [3] lxdm -- Lightweight Login Manager 4 | service [3] xdm -nodaemon -- X Display Manager 5 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/syslog.conf: -------------------------------------------------------------------------------- 1 | # Start syslogd as soon as possible, should always run 2 | # Start klogd as soon as possible, but after syslogd 3 | service [S12345] syslogd -n -- Syslog daemon 4 | service [S12345] klogd -n -- Kernel log daemon 5 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/acpid.conf: -------------------------------------------------------------------------------- 1 | # Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager) 2 | # Depends on a syslog daemon having started. Redirect any output to log. 3 | service [2345] env:-/etc/default/acpid log acpid -f $OPTIONS -- ACPI daemon 4 | -------------------------------------------------------------------------------- /contrib/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = alpine debian void 2 | bin_SCRIPT = service 3 | docsdir := @docdir@/contrib 4 | docs_DATA = README.md finit.conf procmon.sh 5 | EXTRA_DIST = $(docs_DATA) 6 | 7 | if BASH 8 | bashdir = $(BASH_DIR) 9 | dist_bash_DATA = initctl 10 | endif 11 | -------------------------------------------------------------------------------- /test/skel/bin/post.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | marker="POST" 4 | # shellcheck disable=SC2154 5 | if [ -n "$baz" ]; then 6 | marker=$baz 7 | fi 8 | 9 | echo "$marker" > /tmp/post 10 | echo "EXIT_CODE=$EXIT_CODE" >> /tmp/post 11 | echo "EXIT_STATUS=$EXIT_STATUS" >> /tmp/post 12 | -------------------------------------------------------------------------------- /tmpfiles.d/x11.conf: -------------------------------------------------------------------------------- 1 | D! /tmp/.X11-unix 1777 root root 2 | D! /tmp/.ICE-unix 1777 root root 3 | D! /tmp/.XIM-unix 1777 root root 4 | D! /tmp/.font-unix 1777 root root 5 | 6 | z /tmp/.X11-unix 7 | z /tmp/.ICE-unix 8 | z /tmp/.XIM-unix 9 | z /tmp/.font-unix 10 | 11 | r! /tmp/.X[0-9]*-lock 12 | -------------------------------------------------------------------------------- /libsystemd/libsystemd.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libsystemd 7 | Description: Finit replacement for systemd notification library 8 | Version: @PACKAGE_VERSION@ 9 | Libs: -L${libdir} -lsystemd 10 | Cflags: -I${includedir} -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/bluetooth.conf: -------------------------------------------------------------------------------- 1 | # To ignore errors like "org.freedesktop.hostname1", update the env file with 2 | # "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS 3 | service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service 4 | -------------------------------------------------------------------------------- /contrib/void/finit.d/available/Makefile.am: -------------------------------------------------------------------------------- 1 | docsdir := @docdir@/contrib/void/finit.d/available 2 | docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \ 3 | keymap.conf lxdm.conf modules.conf ntpd.conf \ 4 | sshd.conf syslog.conf uuidd.conf 5 | EXTRA_DIST = $(docs_DATA) 6 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/elogind.conf: -------------------------------------------------------------------------------- 1 | # On some systems, bootstrap can be really quick and cause elogind 2 | # to not start before a user has managed to log in. To prevent 3 | # this, add as condition to your TTYs in getty.conf 4 | service [2345] /lib/elogind/elogind -- Login manager 5 | -------------------------------------------------------------------------------- /system/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = 10-hotplug.conf.in 2 | systemdir = $(system_path) 3 | system_DATA = 4 | dist_system_DATA = 5 | 6 | if BUILD_HOTPLUG_PLUGIN 7 | system_DATA += 10-hotplug.conf 8 | endif 9 | 10 | if BUILD_TESTSERV_PLUGIN 11 | dist_system_DATA += 90-testserv.conf 12 | endif 13 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/getty.conf: -------------------------------------------------------------------------------- 1 | # Consoles to start getty on when system is up 2 | tty [12345] /dev/tty1 linux noclear nowait 3 | tty [2345] /dev/tty2 linux nowait 4 | tty [2345] /dev/tty3 linux nowait 5 | tty [2345] /dev/tty4 linux nowait 6 | tty [2345] /dev/tty5 linux nowait 7 | tty [2345] /dev/tty6 linux nowait 8 | -------------------------------------------------------------------------------- /tmpfiles.d/legacy.conf: -------------------------------------------------------------------------------- 1 | d /run/lock 0755 root root - 2 | L /var/lock - - - - ../run/lock 3 | 4 | d /run/lock/subsys 0755 root root - 5 | 6 | L /run/shm - - - - ../dev/shm 7 | 8 | # These are not supported by Finit, see command line options 9 | r! /forcefsck 10 | r! /fastboot 11 | r! /forcequotacheck 12 | -------------------------------------------------------------------------------- /doc/config/Makefile.am: -------------------------------------------------------------------------------- 1 | docsdir := @docdir@/doc/config 2 | dist_docs_DATA = cgroups.md env.md files.md index.md limitations.md \ 3 | logging.md rescue.md runlevels.md runparts.md \ 4 | service-env.md service-opts.md service-sync.md \ 5 | service-wrappers.md services.md sysv.md \ 6 | task-and-run.md templating.md tty.md 7 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/available/syslogd.conf: -------------------------------------------------------------------------------- 1 | # Start syslogd as soon as possible, should always run 2 | # Start klogd as soon as possible, but after syslogd 3 | service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon 4 | service [S12345] env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon 5 | -------------------------------------------------------------------------------- /test/src/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_PROGRAMS = serv 2 | 3 | serv_SOURCES = serv.c 4 | serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE -I$(top_builddir) 5 | if LIBSYSTEMD 6 | serv_CPPFLAGS += -I$(top_srcdir)/libsystemd $(lite_CFLAGS) 7 | serv_SOURCES += $(top_srcdir)/libsystemd/sd-daemon.c 8 | serv_LDADD = $(lite_LIBS) 9 | endif 10 | -------------------------------------------------------------------------------- /test/rclocal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Regression test for basic /etc/rc.local functionality 3 | # shellcheck disable=2317,2034 4 | set -eu 5 | 6 | RCLOCAL="echo 'KILROY WAS HERE' > /tmp/rclocal" 7 | TEST_DIR=$(dirname "$0") 8 | #DEBUG=1 9 | 10 | # shellcheck source=/dev/null 11 | . "$TEST_DIR/lib/setup.sh" 12 | 13 | sleep 1 14 | assert_file_contains /tmp/rclocal "KILROY WAS HERE" 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/available/Makefile.am: -------------------------------------------------------------------------------- 1 | docsdir := @docdir@/contrib/debian/finit.d/available 2 | docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \ 3 | console-setup.conf elogind.conf getty.conf \ 4 | keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \ 5 | plymouth-quit.conf rsyslogd.conf sshd.conf syslogd.conf \ 6 | xdm.conf 7 | EXTRA_DIST = $(docs_DATA) 8 | 9 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | finit is a small project, as such we have no possibility to support older versions. 6 | The only supported version is the latest released on GitHub: 7 | 8 | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | Contact the project's main author and owner to report and discuss vulnerabilities. 13 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | Finit Examples 2 | ============== 3 | 4 | This section provides configuration examples and helpful tools to 5 | install and try out Finit on various Linux distributions. 6 | 7 | * [Alpine](alpine/) 8 | * [Debian](debian/) 9 | * [Void](void/) 10 | 11 | If you have ideas on how to simplify, extend, or even add new example 12 | configurations for other distributions you are most welcome! :) 13 | 14 | -------------------------------------------------------------------------------- /test/runlevel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify selecting alternate runlevel from cmdline 3 | # shellcheck disable=SC2034 4 | set -eu 5 | 6 | TEST_DIR=$(dirname "$0") 7 | FINIT_RUNLEVEL=9 8 | 9 | # shellcheck source=/dev/null 10 | . "$TEST_DIR/lib/setup.sh" 11 | 12 | say 'Check runlevel' 13 | lvl=$(run "initctl runlevel | awk '{print \$2;}'") 14 | if [ "$lvl" -eq "9" ]; then 15 | return 0 16 | fi 17 | 18 | return 1 19 | -------------------------------------------------------------------------------- /src/coldplug: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # With the daemon mode ('mdev -df') we no longer need the hotplug helper 4 | echo "" > /proc/sys/kernel/hotplug 5 | 6 | # Wait for 'mdev -df' to set up its event queue. 7 | sleep 1 8 | 9 | # Trigger 'add' event for all devices and subsystems to load all modules 10 | # and any firmware needed by the hardware. 11 | find /sys/devices -name uevent -print | while IFS='' read X; do echo add > $X; done 12 | -------------------------------------------------------------------------------- /doc/AUTHORS: -------------------------------------------------------------------------------- 1 | Current Maintainer(s): 2 | * Joachim Wiberg 3 | 4 | Major Contributors: 5 | * Tobias Waldekranz 6 | * Mattias Walström 7 | * Jonas Johansson 8 | * Jacques de Laval 9 | * Magnus Malm 10 | 11 | Original Author(s): 12 | * Claudio Matsuoka 13 | 14 | -------------------------------------------------------------------------------- /test/skel/etc/fstab: -------------------------------------------------------------------------------- 1 | # 2 | tmpfs /tmp tmpfs mode=1777 0 0 3 | mkdir#-p#-m0755 /tmp/etc.u helper none 0 0 4 | mkdir#-p#-m0755 /tmp/etc.w helper none 0 0 5 | tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0 6 | tmpfs /var tmpfs mode=0777 0 0 7 | 8 | # Each test needs a dedicated /etc for finit.conf etc. 9 | etc /etc overlay lowerdir=/etc,upperdir=/tmp/etc.u,workdir=/tmp/etc.w 0 0 10 | -------------------------------------------------------------------------------- /test/testserv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify service readiness notification 3 | # shellcheck disable=SC2034 4 | 5 | set -eu 6 | #set -x 7 | 8 | #DEBUG=true 9 | FINIT_ARGS="finit.cond=testserv" 10 | TEST_DIR=$(dirname "$0") 11 | 12 | # shellcheck source=/dev/null 13 | . "$TEST_DIR/lib/setup.sh" 14 | 15 | run "initctl" 16 | run "initctl status testserv" 17 | run "initctl cond dump" 18 | sleep 1 19 | retry 'assert_norestart testserv' 1 1 20 | 21 | return 0 22 | -------------------------------------------------------------------------------- /contrib/service: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | JOB=$1 4 | CMD=$2 5 | 6 | usage() { 7 | echo "Usage: service [job# | name] " 8 | exit 1 9 | } 10 | 11 | if [ $# -eq 0 ]; then 12 | initctl show 13 | exit 0 14 | fi 15 | 16 | case $CMD in 17 | stop) 18 | ;; 19 | start) 20 | ;; 21 | restart) 22 | ;; 23 | status) 24 | ;; 25 | show) 26 | ;; 27 | *) 28 | usage 29 | ;; 30 | esac 31 | 32 | initctl $2 $1 33 | 34 | -------------------------------------------------------------------------------- /tmpfiles.d/Makefile.am: -------------------------------------------------------------------------------- 1 | finitdir = $(finit_tmpfiles) 2 | dist_finit_DATA = finit.conf 3 | 4 | tmpfilesdir = $(tmpfiles_path) 5 | dist_tmpfiles_DATA = etc.conf legacy.conf var.conf 6 | dist_tmpfiles_DATA += dnsmasq.conf frr.conf lldpd.conf openswan.conf \ 7 | quagga.conf sshd.conf uuidd.conf 8 | 9 | if BUILD_TESTSERV_PLUGIN 10 | dist_tmpfiles_DATA += testserv.conf 11 | endif 12 | 13 | if BUILD_X11_COMMON_PLUGIN 14 | dist_tmpfiles_DATA += x11.conf 15 | endif 16 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = config 2 | EXTRA_DIST = AUTHORS ChangeLog.md 3 | 4 | docsdir := @docdir@/doc 5 | dist_docs_DATA = build.md cmdline.md commands.md conditions.md \ 6 | distro.md example.md features.md index.md initctl.md \ 7 | keventd.md plugins.md requirements.md runlevels.md \ 8 | runparts.md service.md signals.md state-machine.md \ 9 | watchdog.md 10 | dist_docs_DATA += img/logo.png img/svc-machine.png img/svc-machine.svg \ 11 | img/alpine-screenshot2.png 12 | -------------------------------------------------------------------------------- /test/bootstrap-crash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Regression test for issue #351. A run/task that never completes 3 | # successfully in runlevel S should not respawn forever. 4 | #set -eu 5 | 6 | TEST_DIR=$(dirname "$0") 7 | #DEBUG=1 8 | # shellcheck disable=SC2034 9 | BOOTSTRAP="run [S] crasher.sh -- Party crasher" 10 | 11 | # shellcheck source=/dev/null 12 | . "$TEST_DIR/lib/setup.sh" 13 | 14 | say "Hello SMTP Spoken here" 15 | run initctl 16 | run initctl status crasher.sh 17 | 18 | sleep 10 19 | say "We are done" 20 | -------------------------------------------------------------------------------- /libsystemd/Makefile.am: -------------------------------------------------------------------------------- 1 | # libsystemd replacement library 2 | lib_LTLIBRARIES = libsystemd.la 3 | libsystemd_la_SOURCES = sd-daemon.c sd-daemon.h 4 | libsystemd_la_LDFLAGS = -version-info 0:0:0 5 | libsystemd_la_CFLAGS = $(lite_CFLAGS) 6 | libsystemd_la_LIBADD = $(lite_LIBS) 7 | 8 | # pkg-config support 9 | pkgconfigdir = $(libdir)/pkgconfig 10 | pkgconfig_DATA = libsystemd.pc 11 | 12 | # Install header to systemd/ subdirectory 13 | systemddir = $(includedir)/systemd 14 | systemd_HEADERS = sd-daemon.h 15 | -------------------------------------------------------------------------------- /doc/img/finit.txt: -------------------------------------------------------------------------------- 1 | _______ __ _____ ___ __ ___________ _______ 2 | /" "||" \ (\" \|" \ |" \(" _ ")/" __ ) 3 | (: ______)|| | |.\\ \ | || |)__/ \\__/(__/ _) ./ 4 | \/ | |: | |: \. \\ | |: | \\_ / / // 5 | // ___) |. | |. \ \. | |. | |. | __ \_ \\ 6 | (: ( /\ |\| \ \ | /\ |\ \: | (: \__) :\ 7 | \__/ (__\_|_)\___|\____\)(__\_|_) \__| \_______) 8 | -------------------------------------------------------------------------------- /tmpfiles.d/finit.conf: -------------------------------------------------------------------------------- 1 | # Finit expects to run on a Linux FHS compatible system. 2 | # Preferably one where /run is used and is a RAM disk. 3 | 4 | # This should be set up but OS/dist but is sometimes missing 5 | # Note: l+ == only if ../run exists, if so, rm -rf /var/run 6 | l+ /var/run - - - - ../run 7 | 8 | # Finit directories 9 | d /run/finit 0755 - - - 10 | d /run/finit/cond 0755 - - - 11 | d /run/finit/cond/pid 0755 - - - 12 | d /run/finit/cond/sys 0755 - - - 13 | d /run/finit/cond/usr 0755 - - - 14 | d /run/finit/system 0755 - - - 15 | -------------------------------------------------------------------------------- /.globalrc: -------------------------------------------------------------------------------- 1 | # GNU Global .conf file, see /etc/gtags/gtags.conf for details. 2 | # This configuration is optimized for the Finit project structure. 3 | 4 | default:\ 5 | :tc=default@/etc/gtags/gtags.conf:\ 6 | :tc=common: 7 | 8 | common:\ 9 | :skip=tags,TAGS,ID,y.tab.c,y.tab.h,gtags.files,cscope.files,cscope.out,cscope.po.out,cscope.in.out,\ 10 | {arch}/,autom4te.cache/,\ 11 | *.orig,*.rej,*.bak,*~,#*#,*.swp,*.tmp,*_flymake.*,*_flymake,\ 12 | *.o,*.a,*.so,*.lo,*.la,\ 13 | *.zip,*.gz,*.bz2,*.xz,*.lzh,*.Z,*.tgz,\ 14 | *.log,*.trs,test-suite.log,stamp-*,\ 15 | sysroot/: 16 | -------------------------------------------------------------------------------- /test/lib/enter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | cmd="" 6 | if [ "$#" -lt 1 ]; then 7 | cmd="/bin/sh" 8 | else 9 | cmd=$1 10 | shift 11 | fi 12 | 13 | TEST_DIR=$(dirname "$0")/.. 14 | SYSROOT="${SYSROOT:-$(pwd)/${TEST_DIR}/sysroot}" 15 | PID_FILE="$SYSROOT/running_test.pid" 16 | 17 | if [ -f "$PID_FILE" ] ; then 18 | target=$(cat "$PID_FILE") 19 | target=$(pgrep -P "$target") 20 | else 21 | echo "No test running!" 22 | exit 1 23 | fi 24 | 25 | # shellcheck disable=SC1091 26 | . "$TEST_DIR/test.env" 27 | 28 | "$(dirname "$0")"/exec.sh "$target" "$cmd" "$@" 29 | -------------------------------------------------------------------------------- /doc/commands.md: -------------------------------------------------------------------------------- 1 | Rebooting & Halting 2 | =================== 3 | 4 | Traditionally, rebooting and halting a UNIX system is done by switching 5 | to the corresponding runlevel (0 or 6). Over time this has changed and 6 | Finit comes with its own tooling providing: `shutdown`, `reboot`, 7 | `poweroff`, and `suspend`. These commands are also available from the 8 | [`initctl`](initctl.md) tool. 9 | 10 | For compatibility reasons Finit listens to the same set of signals as 11 | BusyBox init. This is not 100% compatible with SysV init, but clearly 12 | the more common combination for Finit. For more details, see the 13 | [Signal](signals.md) section. 14 | -------------------------------------------------------------------------------- /test/skel/sbin/service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cleanup() 4 | { 5 | echo "Got signal, stopping ..." 6 | rm -f /run/service.pid 7 | exit 0 8 | } 9 | 10 | reload() 11 | { 12 | echo 13 | echo "Ready." 14 | echo 15 | sleep 1 16 | echo $$ > /run/service.pid 17 | } 18 | 19 | # Hook SIGUSR1 and dump trace to file system 20 | # shellcheck disable=SC2172 21 | trap 'echo USR1 > /tmp/usr1.log' USR1 22 | trap reload HUP 23 | trap cleanup INT 24 | trap cleanup TERM 25 | trap cleanup QUIT 26 | trap cleanup EXIT 27 | 28 | reload 29 | 30 | # sleep may exit on known signal, so 31 | # we cannot use 'set -e' 32 | while true; do 33 | sleep 1 34 | done 35 | -------------------------------------------------------------------------------- /contrib/void/finit.d/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = available 2 | docsdir := @docdir@/contrib/void/finit.d 3 | d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf 4 | 5 | install-exec-hook: 6 | @$(MKDIR_P) $(DESTDIR)$(docsdir)/available 7 | @$(MKDIR_P) $(DESTDIR)$(docsdir)/enabled 8 | @for file in $(d_LINKS); do \ 9 | lnk=$(DESTDIR)$(docsdir)/enabled/$$file; \ 10 | src=available/$$file; \ 11 | rm -f $$lnk; \ 12 | $(LN_S) ../$$src $$lnk; \ 13 | done 14 | 15 | 16 | uninstall-hook: 17 | @for file in $(d_LINKS); do \ 18 | lnk=$(DESTDIR)$(docsdir)/enabled/$$file; \ 19 | src=available/$$file; \ 20 | $(RM) -f $$lnk; \ 21 | done 22 | -------------------------------------------------------------------------------- /m4/expand.m4: -------------------------------------------------------------------------------- 1 | # From https://github.com/kakaroto/e17/blob/master/BINDINGS/javascript/elixir/m4/ac_expand_dir.m4 2 | # By Alexandre Oliva 3 | 4 | # AC_EXPAND_DIR(VARNAME, DIR) 5 | # ------------------------------------------------------------ 6 | # Expands occurrences of ${prefix} and ${exec_prefix} in the 7 | # given DIR, and assigns the resulting string to VARNAME 8 | # 9 | # Example: AC_DEFINE_DIR(DATADIR, "$datadir") 10 | AC_DEFUN([AC_EXPAND_DIR], [ 11 | $1=$2 12 | $1=`( 13 | test "x$prefix" = xNONE && prefix="$ac_default_prefix" 14 | test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" 15 | eval echo \""[$]$1"\" 16 | )` 17 | ]) 18 | -------------------------------------------------------------------------------- /doc/keventd.md: -------------------------------------------------------------------------------- 1 | keventd 2 | ======= 3 | 4 | The kernel event daemon bundled with Finit is a simple uevent monitor 5 | for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition, 6 | which can be useful to prevent power hungry services like anacron to run 7 | when a laptop is only running on battery, for instance. 8 | 9 | Since keventd is not an integral part of Finit yet it is not enabled by 10 | default. Enable it using `./configure --with-keventd`. The bundled 11 | `contrib/` build scripts for Debian, Alpine, and Void have this enabled. 12 | 13 | This daemon is planned to be extended with monitoring of other uevents, 14 | patches and ideas are welcome in the issue tracker. 15 | -------------------------------------------------------------------------------- /test/add-remove-dynamic-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_teardown() 8 | { 9 | say "Running test teardown." 10 | run "rm -f $FINIT_CONF" 11 | } 12 | 13 | # shellcheck source=/dev/null 14 | . "$TEST_DIR/lib/setup.sh" 15 | 16 | say "Add a dynamic service in $FINIT_CONF" 17 | run "echo 'service [2345] kill:20 log service.sh -- Dyn service' > $FINIT_CONF" 18 | 19 | say 'Reload Finit' 20 | run "initctl reload" 21 | 22 | retry 'assert_num_children 1 service.sh' 23 | 24 | say 'Remove the dynamic service from /etc/finit.conf' 25 | run "echo > $FINIT_CONF" 26 | 27 | say 'Reload Finit' 28 | run "initctl reload" 29 | 30 | retry 'assert_num_children 0 service.sh' 31 | -------------------------------------------------------------------------------- /test/svc-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Ensure service environment variables are sourced and expanded properly. 3 | set -e 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_teardown() 8 | { 9 | say "Running test teardown." 10 | run "rm -f $FINIT_CONF" 11 | } 12 | 13 | # shellcheck source=/dev/null 14 | . "$TEST_DIR/lib/setup.sh" 15 | 16 | sep "/etc/env:" 17 | run "cat /etc/env" 18 | run "echo 'service env:/etc/env serv -np -e xyzzy:bar -e \"FOO_ARGS:-i bar -l bar endarg\" -- serv checks xyzzy=bar' > $FINIT_CONF" 19 | 20 | say 'Reload Finit' 21 | run "initctl reload" 22 | 23 | retry 'assert_num_children 1 serv' 24 | 25 | say "Done, drop service from $FINIT_CONF ..." 26 | run "rm $FINIT_CONF" 27 | run "initctl reload" 28 | -------------------------------------------------------------------------------- /test/pre-fail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify Finit: 3 | # - does not start main process when pre:script fails 4 | # - times out and kills long running pre:script 5 | set -eu 6 | 7 | #export DEBUG=true 8 | TEST_DIR=$(dirname "$0") 9 | # shellcheck source=/dev/null 10 | . "$TEST_DIR/lib/setup.sh" 11 | 12 | vrfy() 13 | { 14 | nm=serv 15 | tmo=$1 16 | msg=$2 17 | err=$3 18 | 19 | run "echo 'service [2345] pre:$tmo,fail.sh $nm -n -- $msg' > $FINIT_CONF" 20 | run "initctl reload" 21 | 22 | retry "assert_status_full $nm 'crashed $err'" 50 23 | } 24 | 25 | #run "initctl debug" 26 | 27 | vrfy 5 "Failing pre: script" "(code=exited, status=42)" 28 | vrfy 1 "Timeout pre: script" "(code=signal, status=9/KILL)" 29 | -------------------------------------------------------------------------------- /contrib/alpine/finit.d/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = available 2 | docsdir := @docdir@/contrib/alpine/finit.d 3 | d_LINKS = acpid.conf crond.conf dropbear.conf getty.conf \ 4 | keymap.conf modules.conf ntpd.conf syslogd.conf 5 | 6 | install-exec-hook: 7 | @$(MKDIR_P) $(DESTDIR)$(docsdir)/available 8 | @$(MKDIR_P) $(DESTDIR)$(docsdir)/enabled 9 | @for file in $(d_LINKS); do \ 10 | lnk=$(DESTDIR)$(docsdir)/enabled/$$file; \ 11 | src=available/$$file; \ 12 | rm -f $$lnk; \ 13 | $(LN_S) ../$$src $$lnk; \ 14 | done 15 | 16 | 17 | uninstall-hook: 18 | @for file in $(d_LINKS); do \ 19 | lnk=$(DESTDIR)$(docsdir)/enabled/$$file; \ 20 | src=available/$$file; \ 21 | $(RM) -f $$lnk; \ 22 | done 23 | -------------------------------------------------------------------------------- /test/start-stop-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_teardown() 8 | { 9 | say "Running test teardown." 10 | run "rm -f $FINIT_CONF" 11 | } 12 | 13 | # shellcheck source=/dev/null 14 | . "$TEST_DIR/lib/setup.sh" 15 | 16 | say "Add service stanza in $FINIT_CONF" 17 | run "echo 'service [2345] kill:20 log service.sh -- Test service' > $FINIT_CONF" 18 | 19 | say 'Reload Finit' 20 | run "initctl reload" 21 | 22 | retry 'assert_num_children 1 service.sh' 23 | 24 | say 'Stop the service' 25 | run "initctl stop service.sh" 26 | 27 | retry 'assert_num_children 0 service.sh' 28 | 29 | say 'Start the service again' 30 | run "initctl start service.sh" 31 | 32 | retry 'assert_num_children 1 service.sh' 33 | -------------------------------------------------------------------------------- /test/setup-sysroot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | # shellcheck disable=SC2154 6 | make -C "$top_builddir" DESTDIR="$SYSROOT" install 7 | 8 | mkdir -p "$SYSROOT/sbin/" 9 | cp "$top_builddir/test/src/serv" "$SYSROOT/sbin/" 10 | 11 | # shellcheck disable=SC2154 12 | FINITBIN="$(pwd)/$top_builddir/src/finit" DEST="$SYSROOT" make -f "$srcdir/lib/sysroot.mk" 13 | 14 | # Drop plugins we don't need in test, only causes confusing FAIL in logs. 15 | for plugin in tty.so urandom.so rtc.so modprobe.so; do 16 | find "$SYSROOT" -name $plugin -delete 17 | done 18 | 19 | # Drop system .conf files we don't need in test, same as above 20 | # shellcheck disable=SC2043 21 | for conf in 10-hotplug.conf; do 22 | find "$SYSROOT" -name $conf -delete 23 | done 24 | -------------------------------------------------------------------------------- /contrib/fstab.bb: -------------------------------------------------------------------------------- 1 | # This is a basic /etc/fstab that mounts up all of the virtual filesystems 2 | # that older versions of Finit used to mount internally. It can serve as a 3 | # starting point for systems running Busybox's mount implementation. Make 4 | # sure that CONFIG_FEATURE_MOUNT_HELPERS enabled; as this is required 5 | # create the /dev/{pts,shm} directories. 6 | 7 | devtmpfs /dev devtmpfs defaults 0 0 8 | mkdir#-p /dev/pts helper none 0 0 9 | devpts /dev/pts devpts mode=620,ptmxmode=0666 0 0 10 | mkdir#-p /dev/shm helper none 0 0 11 | tmpfs /dev/shm tmpfs mode=0777 0 0 12 | proc /proc proc defaults 0 0 13 | tmpfs /tmp tmpfs mode=1777,nosuid,nodev 0 0 14 | tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0 15 | sysfs /sys sysfs defaults 0 0 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.so 4 | *.gz 5 | ID 6 | GPATH 7 | GRTAGS 8 | GSYMS 9 | GTAGS 10 | 11 | # GNU configure and build system 12 | /aux 13 | /.deps 14 | /autom4te.cache 15 | /aclocal.m4 16 | /ar-lib 17 | /backup/ 18 | /compile 19 | /config.cache 20 | /config.h 21 | /config.mk 22 | /config.guess 23 | /config.h.in 24 | /config.log 25 | /config.status 26 | /config.sub 27 | /configure 28 | /depcomp 29 | /install-sh 30 | /libtool 31 | /ltmain.sh 32 | /missing 33 | /Makefile 34 | /Makefile.in 35 | /stamp-h1 36 | 37 | /test-driver 38 | /test-suite.log 39 | 40 | # Misc 41 | /.gdb_history 42 | 43 | # TroglOS build system 44 | /.config 45 | /.stamp 46 | /.unpacked 47 | 48 | # WeOS build system 49 | /.autoconf 50 | /.automake 51 | /.built 52 | 53 | # VS Code user settings 54 | .vscode 55 | -------------------------------------------------------------------------------- /test/add-remove-dynamic-service-sub-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_teardown() 8 | { 9 | say "Running test teardown." 10 | run "rm -f $FINIT_RCSD/service.conf" 11 | } 12 | 13 | # shellcheck source=/dev/null 14 | . "$TEST_DIR/lib/setup.sh" 15 | 16 | say "Add a dynamic service in $FINIT_RCSD/service.conf" 17 | run "echo 'service [2345] kill:20 log service.sh -- Dyn serv' > $FINIT_RCSD/service.conf" 18 | 19 | say 'Reload Finit' 20 | run "initctl reload" 21 | 22 | retry 'assert_num_children 1 service.sh' 23 | 24 | say 'Remove the dynamic service from /etc/finit.d/service.conf' 25 | run "echo > $FINIT_RCSD/service.conf" 26 | 27 | say 'Reload Finit' 28 | run "initctl reload" 29 | 30 | retry 'assert_num_children 0 service.sh' 31 | -------------------------------------------------------------------------------- /test/start-stop-service-sub-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_teardown() 8 | { 9 | say "Running test teardown." 10 | run "rm -f $FINIT_RCSD/service.conf" 11 | } 12 | 13 | # shellcheck source=/dev/null 14 | . "$TEST_DIR/lib/setup.sh" 15 | 16 | say "Add service stanza in $FINIT_RCSD/service.conf" 17 | run "echo 'service [2345] kill:20 log service.sh -- Subserv' > $FINIT_RCSD/service.conf" 18 | 19 | say 'Reload Finit' 20 | run "initctl reload" 21 | 22 | sleep 2 23 | run "ps" 24 | 25 | retry 'assert_num_children 1 service.sh' 26 | 27 | say 'Stop the service' 28 | run "initctl stop service.sh" 29 | 30 | retry 'assert_num_children 0 service.sh' 31 | 32 | say 'Start the service again' 33 | run "initctl start service.sh" 34 | 35 | retry 'assert_num_children 1 service.sh' 36 | -------------------------------------------------------------------------------- /contrib/debian/finit.d/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = available 2 | docsdir := @docdir@/contrib/debian/finit.d 3 | d_LINKS = acpid.conf apparmor.conf avahi-daemon.conf \ 4 | bluetooth.conf console-setup.conf elogind.conf \ 5 | getty.conf keyboard-setup.conf modules.conf \ 6 | plymouth-quit.conf rsyslogd.conf sshd.conf 7 | 8 | install-exec-hook: 9 | @$(MKDIR_P) $(DESTDIR)$(docsdir)/available 10 | @$(MKDIR_P) $(DESTDIR)$(docsdir)/enabled 11 | @for file in $(d_LINKS); do \ 12 | lnk=$(DESTDIR)$(docsdir)/enabled/$$file; \ 13 | src=available/$$file; \ 14 | rm -f $$lnk; \ 15 | $(LN_S) ../$$src $$lnk; \ 16 | done 17 | 18 | 19 | uninstall-hook: 20 | @for file in $(d_LINKS); do \ 21 | lnk=$(DESTDIR)$(docsdir)/enabled/$$file; \ 22 | src=available/$$file; \ 23 | $(RM) -f $$lnk; \ 24 | done 25 | -------------------------------------------------------------------------------- /test/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run test suite with the correct configure params 3 | run_make=1 4 | jobs=17 5 | 6 | while [ $# -gt 0 ]; do 7 | case "$1" in 8 | -n|--no-make) 9 | run_make=0 10 | ;; 11 | -j|--jobs) 12 | shift 13 | jobs="$1" 14 | ;; 15 | *) 16 | echo "Usage: $0 [--no-make] [-j N]" 17 | exit 1 18 | ;; 19 | esac 20 | shift 21 | done 22 | 23 | if [ "$run_make" -eq 1 ]; then 24 | make distclean 25 | fi 26 | 27 | ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ 28 | --enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \ 29 | --with-keventd --with-libsystemd \ 30 | CFLAGS='-fsanitize=address -ggdb' 31 | 32 | if [ "$run_make" -eq 1 ]; then 33 | make -j"$jobs" clean check 34 | fi 35 | -------------------------------------------------------------------------------- /test/restart-self.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify that a service can restart itself, issue #280 3 | set -eu 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_setup() 8 | { 9 | run "mkdir -p /etc/default" 10 | } 11 | 12 | test_teardown() 13 | { 14 | say "Running test teardown." 15 | run "rm -f $FINIT_CONF" 16 | } 17 | 18 | check_restarts() 19 | { 20 | assert "serv restarts" "$(texec cat "$1" | awk '{print $1;}')" -ge "$2" 21 | } 22 | 23 | 24 | # shellcheck source=/dev/null 25 | . "$TEST_DIR/lib/setup.sh" 26 | 27 | say "Add stanza to $FINIT_CONF" 28 | run "echo 'service serv -np -r serv -- Restart self' > $FINIT_CONF" 29 | 30 | say 'Reload Finit' 31 | run "initctl reload" 32 | 33 | sleep 2 34 | run "initctl status serv" 35 | run "ps" 36 | 37 | sleep 2 38 | run "initctl status serv" 39 | run "ps" 40 | 41 | say 'Pending restarts by itself ...' 42 | retry 'check_restarts /tmp/serv-restart.cnt 3' 20 1 43 | 44 | return 0 45 | -------------------------------------------------------------------------------- /test/runparts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Verifies basic runparts behavior: to run all scripts in a directory at 4 | # the end of system bootstrap, right before calling /etc/rc.local: 5 | # 6 | # - only executable scripts should be called 7 | # - always in alphabetical order 8 | # - SNNfoo should be called with a 'start' argument 9 | # - KNNfoo should be called with a 'stop' argument 10 | # 11 | # shellcheck disable=SC2034 12 | 13 | BOOTSTRAP="runparts /etc/rc.d" 14 | TEST_DIR=$(dirname "$0") 15 | #DEBUG=1 16 | 17 | # shellcheck source=/dev/null 18 | . "$TEST_DIR/lib/setup.sh" 19 | 20 | while true; do 21 | lvl=$(texec initctl runlevel) 22 | say "Current runlevel $lvl" 23 | if [ "$lvl" = "N 2" ]; then 24 | break; 25 | fi 26 | sleep 1 27 | done 28 | 29 | sleep 1 30 | texec diff -u /usr/share/runparts/ref.log /tmp/runparts.log 31 | texec cmp /usr/share/runparts/ref.log /tmp/runparts.log || fail "runparts in wrong order" 32 | -------------------------------------------------------------------------------- /test/sysvparts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Verifies SysV runparts behavior: to run all scripts in a directory at 4 | # the end of system bootstrap, right before calling /etc/rc.local: 5 | # 6 | # - only SNNfoo or KNNfoo executable scripts should be called 7 | # - always in alphabetical order 8 | # - SNNfoo should be called with a 'start' argument 9 | # - KNNfoo should be called with a 'stop' argument 10 | # 11 | # shellcheck disable=2034 12 | 13 | BOOTSTRAP="runparts sysv /etc/rcS.d" 14 | TEST_DIR=$(dirname "$0") 15 | DEBUG=1 16 | 17 | # shellcheck source=/dev/null 18 | . "$TEST_DIR/lib/setup.sh" 19 | 20 | while true; do 21 | lvl=$(texec initctl runlevel) 22 | say "Current runlevel $lvl" 23 | if [ "$lvl" = "N 2" ]; then 24 | break; 25 | fi 26 | sleep 1 27 | done 28 | 29 | sleep 1 30 | texec cat /tmp/sysv.log 31 | texec diff -u /usr/share/runparts/sysv.log /tmp/sysv.log 32 | texec cmp /usr/share/runparts/sysv.log /tmp/sysv.log || fail "runparts sysv in wrong order" 33 | -------------------------------------------------------------------------------- /test/signal-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verifies initctl can send arbitrary signals (USR1 in this case) to finit managed services 3 | 4 | set -eu 5 | 6 | TEST_DIR=$(dirname "$0") 7 | 8 | test_teardown() 9 | { 10 | say "Running test teardown." 11 | run "rm -f $FINIT_CONF" 12 | } 13 | 14 | # shellcheck source=/dev/null 15 | . "$TEST_DIR/lib/setup.sh" 16 | 17 | say 'Ensure file system is cleared' 18 | run "rm -f /tmp/usr1.log" 19 | 20 | # Verifies line continuation as well 21 | say "Add service stanza in $FINIT_CONF" 22 | run "echo 'service [2345] kill:20 \\ 23 | log service.sh \\ 24 | -- Test service' > $FINIT_CONF" 25 | run "cat $FINIT_CONF" 26 | #run "initctl debug" 27 | 28 | say 'Reload Finit' 29 | run "initctl reload" 30 | 31 | #run "initctl status service.sh" 32 | 33 | say 'Send SIGUSR1 to service...' 34 | run "initctl signal service.sh SIGUSR1" 35 | 36 | # shellcheck disable=SC2016 37 | retry 'assert "service.sh received SIGUSR" "$(texec cat /tmp/usr1.log)" = "USR1"' 10 1 38 | -------------------------------------------------------------------------------- /doc/config/limitations.md: -------------------------------------------------------------------------------- 1 | Limitations 2 | =========== 3 | 4 | As of Finit v4 there are no limitations to where `.conf` settings can be 5 | placed. Except for the system/global `rlimit` and `cgroup` top-level 6 | group declarations, which can only be set from `/etc/finit.conf`, since 7 | it is the first `.conf` file Finit reads. 8 | 9 | Originally, `/etc/finit.conf` was the only way to set up a Finit system. 10 | Today it is mainly used for bootstrap settings like system hostname, 11 | early module loading for watchdogd, network bringup and system shutdown. 12 | These can now also be set in any `.conf` file in `/etc/finit.d`. 13 | 14 | There is, however, nothing preventing you from having all configuration 15 | settings in `/etc/finit.conf`. 16 | 17 | > [!IMPORTANT] 18 | > The default `rcsd`, i.e., `/etc/finit.d`, was previously the Finit 19 | > [runparts](runparts.md) directory. Finit >=v4.0 no longer has a 20 | > default `runparts` directory, make sure to update your setup, or the 21 | > finit configuration, accordingly. 22 | -------------------------------------------------------------------------------- /contrib/alpine/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "x`id -u`" != "x0" ]; then 4 | echo 5 | echo "*** This script must run as root" 6 | echo 7 | exit 1 8 | fi 9 | 10 | read -p "Do you really want to uninstall Finit (y/N)? " yorn 11 | 12 | if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then 13 | echo 14 | echo "*** Removing all Finit files ..." 15 | rm -rf /usr/share/doc/finit 16 | rm -rf /etc/finit.conf 17 | rm -rf /etc/finit.d 18 | rm -rf /lib/finit 19 | if [ -e /etc/rc.local ]; then 20 | echo "*** Skipping /etc/rc.local, not sure if we installed it ..." 21 | fi 22 | 23 | echo "*** Restoring BusyBox as /sbin/init ..." 24 | cd /sbin 25 | rm init 26 | ln -s /bin/busybox init 27 | 28 | rm halt shutdown reboot suspend 29 | ln -s /bin/busybox halt 30 | ln -s /bin/busybox shutdown 31 | ln -s /bin/busybox reboot 32 | ln -s /bin/busybox suspend 33 | echo "*** Done" 34 | echo 35 | else 36 | echo 37 | echo "*** Aborting uninstall." 38 | echo 39 | fi 40 | -------------------------------------------------------------------------------- /test/skel/etc/init.d/S02-serv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SysV style start script for serv 3 | 4 | DAEMON="/sbin/serv" 5 | PIDFILE="/var/run/serv.pid" 6 | 7 | # shellcheck source=/dev/null 8 | [ -r "/etc/default/serv" ] && . "/etc/default/serv" 9 | 10 | start() 11 | { 12 | printf 'Starting %s: ' "$DAEMON $SERV_ARGS" 13 | # shellcheck disable=SC2086 14 | start-stop-daemon -S -b -q -p "$PIDFILE" -x "$DAEMON" -- $SERV_ARGS 15 | 16 | status=$? 17 | if [ "$status" -eq 0 ]; then 18 | echo "OK" 19 | else 20 | echo "FAIL" 21 | fi 22 | 23 | return "$status" 24 | } 25 | 26 | stop() 27 | { 28 | printf 'Stopping %s: ' "$DAEMON" 29 | start-stop-daemon -K -q -p "$PIDFILE" 30 | 31 | status=$? 32 | if [ "$status" -eq 0 ]; then 33 | echo "OK" 34 | else 35 | echo "FAIL" 36 | fi 37 | 38 | return "$status" 39 | } 40 | 41 | restart() 42 | { 43 | stop 44 | sleep 1 45 | start 46 | } 47 | 48 | case "$1" in 49 | start|stop|restart) 50 | "$1" 51 | ;; 52 | *) 53 | echo "Usage: $0 {start|stop|restart}, got $0 $1" 54 | exit 1 55 | esac 56 | -------------------------------------------------------------------------------- /test/skel/etc/init.d/S01-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SysV style start script for service.sh 3 | 4 | DAEMON="/sbin/service.sh" 5 | PIDFILE="/var/run/service.pid" 6 | 7 | start() 8 | { 9 | printf 'Starting %s: ' "$DAEMON" 10 | start-stop-daemon -S -b -q -p "$PIDFILE" -x "$DAEMON" 11 | 12 | status=$? 13 | if [ "$status" -eq 0 ]; then 14 | echo "OK" 15 | else 16 | echo "FAIL" 17 | fi 18 | 19 | return "$status" 20 | } 21 | 22 | stop() 23 | { 24 | printf 'Stopping %s: ' "$DAEMON" 25 | start-stop-daemon -K -q -p "$PIDFILE" 26 | 27 | status=$? 28 | if [ "$status" -eq 0 ]; then 29 | echo "OK" 30 | else 31 | echo "FAIL" 32 | fi 33 | 34 | return "$status" 35 | } 36 | 37 | reload() 38 | { 39 | if [ -f "$PIDFILE" ]; then 40 | PID=$(cat "$PIDFILE") 41 | kill -HUP "$PID" 42 | fi 43 | } 44 | 45 | restart() 46 | { 47 | stop 48 | sleep 1 49 | start 50 | } 51 | 52 | case "$1" in 53 | start|stop|reload|restart) 54 | "$1" 55 | ;; 56 | *) 57 | echo "Usage: $0 {start|stop|reload|restart}, got $0 $1" 58 | exit 1 59 | esac 60 | -------------------------------------------------------------------------------- /test/start-kill-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verifies Finit restarts crashing services and registers their new PID 3 | # from their (default) PID file. 4 | 5 | set -eu 6 | 7 | TEST_DIR=$(dirname "$0") 8 | 9 | test_teardown() 10 | { 11 | say "Running test teardown." 12 | run "rm -f $FINIT_CONF" 13 | } 14 | 15 | # shellcheck source=/dev/null 16 | . "$TEST_DIR/lib/setup.sh" 17 | 18 | rm -f "$SYSROOT"/oldpid 19 | 20 | say "Add service stanza in $FINIT_CONF" 21 | run "echo 'service [2345] respawn log:stderr service.sh -- Test service' > $FINIT_CONF" 22 | 23 | say 'Reload Finit' 24 | run "initctl reload" 25 | #run "initctl status" 26 | #run "initctl debug" 27 | 28 | retry 'assert_num_children 1 service.sh' 29 | run "initctl status service.sh" 30 | 31 | say 'Simulate service crash (kill -9 ..)' 32 | i=0 33 | laps=1000 34 | while [ $i -lt $laps ]; do 35 | i=$((i + 1)) 36 | say "Lap $i/$laps, killing service ..." # we have this, no sleep needed 37 | run "slay service.sh" 38 | done 39 | 40 | retry 'assert_new_pid service.sh /run/service.pid' 41 | retry 'assert_num_children 1 service.sh' 42 | -------------------------------------------------------------------------------- /doc/config/service-env.md: -------------------------------------------------------------------------------- 1 | Service Environment 2 | ------------------- 3 | 4 | Finit supports sourcing environment variables from `/etc/default/*`, or 5 | similar `--with-sysconfig=DIR`. This is a common pattern from SysV init 6 | scripts, where the start-stop script is a generic script for the given 7 | service, `foo`, and the options for the service are sourced from the 8 | file `/etc/default/foo`. Like this: 9 | 10 | * `/etc/default/foo`: 11 | 12 | FOO_OPTIONS=--extra-arg="bar" -s -x 13 | 14 | * `/etc/finit.conf`: 15 | 16 | service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon 17 | 18 | Here the service `foo` is started with `-n`, to make sure it runs in the 19 | foreground, and the with the options found in the environment file. With 20 | the `ps` command we can see that the process is started with: 21 | 22 | foo -n --extra-arg=bar -s -x 23 | 24 | > [!NOTE] 25 | > The leading `-` in `env:` determines if Finit should treat a missing 26 | > environment file as blocking the start of the service or not. When 27 | > `-` is used, a missing environment file does *not* block the start. 28 | -------------------------------------------------------------------------------- /contrib/alpine/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | echo 4 | echo "*** Configuring Finit for Alpine Linux" 5 | 6 | if [ ! -e configure ]; then 7 | echo " The configure script is missing, maybe you're using a version from GIT?" 8 | echo " Attempting to run the autogen.sh script, you will need these tools:" 9 | echo " autoconf, automake, libtool, pkg-config ..." 10 | echo 11 | ./autogen.sh 12 | fi 13 | 14 | # The plugins are optional, but you may need D-Bus and X11 if you want 15 | # to run X-Window, the other configure flags are however required. 16 | PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \ 17 | --prefix=/usr --exec-prefix= \ 18 | --sysconfdir=/etc --localstatedir=/var \ 19 | --enable-dbus-plugin --enable-x11-common-plugin \ 20 | --enable-alsa-utils-plugin --without-watchdog \ 21 | --with-keventd --with-sulogin 22 | 23 | echo 24 | echo "*** Building ..." 25 | echo 26 | make 27 | 28 | if [ $? -ne 0 ]; then 29 | echo 30 | echo "*** The build failed for some reason" 31 | echo 32 | exit 1 33 | fi 34 | 35 | echo 36 | echo "*** Done" 37 | echo 38 | -------------------------------------------------------------------------------- /doc/config/service-wrappers.md: -------------------------------------------------------------------------------- 1 | Service Wrapper Scripts 2 | ======================= 3 | 4 | If your service requires to run additional commands, executed before the 5 | service is actually started, like the systemd `ExecStartPre`, you can 6 | use a wrapper shell script to start your service. 7 | 8 | The Finit service `.conf` file can be put into `/etc/finit.d/available`, 9 | so you can control the service using `initctl`. Then use the path to 10 | the wrapper script in the Finit `.conf` service stanza. The following 11 | example employs a wrapper script in `/etc/start.d`. 12 | 13 | **Example:** 14 | 15 | * `/etc/finit.d/available/program.conf`: 16 | 17 | service [235] /etc/start.d/program -- Example Program 18 | 19 | * `/etc/start.d/program:` 20 | 21 | #!/bin/sh 22 | # Prepare the command line options 23 | OPTIONS="-u $(cat /etc/username)" 24 | 25 | # Execute the program 26 | exec /usr/bin/program $OPTIONS 27 | 28 | > [!NOTE] 29 | > The example sets `` to denote that it doesn't support `SIGHUP`. 30 | > That way Finit will stop/start the service instead of sending SIGHUP 31 | > at restart/reload events. 32 | -------------------------------------------------------------------------------- /test/skel/bin/slay: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | nm=$1 4 | 5 | getpid() 6 | { 7 | initctl status "$1" |awk '/PID :/{ print $3; }' 8 | } 9 | 10 | pid=$(getpid "$nm") 11 | if [ -f oldpid ]; then 12 | oldpid=$(cat oldpid) 13 | if [ "$oldpid" = "$pid" ]; then 14 | echo "Looks bad, wait for it ..." 15 | sleep 3 16 | pid=$(getpid "$nm") 17 | if [ "$oldpid" = "$pid" ]; then 18 | echo "Finit did not deregister old PID $oldpid vs $pid" 19 | initctl status "$nm" 20 | ps 21 | echo "Reloading finit ..." 22 | initctl reload 23 | sleep 1 24 | initctl status "$nm" 25 | exit 1 26 | fi 27 | fi 28 | fi 29 | 30 | timeout=50 31 | while [ "$pid" -le 1 ]; do 32 | sleep 0.1 33 | pid=$(getpid "$nm") 34 | if [ "$pid" -le 1 ]; then 35 | timeout=$((timeout - 1)) 36 | if [ "$timeout" -gt 0 ]; then 37 | continue 38 | fi 39 | 40 | if [ $pid -ne 0 ]; then 41 | echo "Got a bad PID: $pid, aborting ..." 42 | ps 43 | sleep 1 44 | initctl status "$nm" 45 | ps 46 | fi 47 | exit 1 48 | fi 49 | break 50 | done 51 | 52 | echo "$pid" > /tmp/oldpid 53 | 54 | #echo "PID $pid, kill -9 ..." 55 | kill -9 "$pid" 56 | -------------------------------------------------------------------------------- /contrib/void/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | echo 4 | echo "*** Configuring Finit for Void Linux" 5 | 6 | if [ ! -e configure ]; then 7 | echo " The configure script is missing, maybe you're using a version from GIT?" 8 | echo " Attempting to run the autogen.sh script, you will need these tools:" 9 | echo " autoconf, automake, libtool, pkg-config ..." 10 | echo 11 | ./autogen.sh 12 | fi 13 | 14 | # The plugins are optional, but you may need D-Bus and X11 if you want 15 | # to run X-Window, the other configure flags are however required. 16 | PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \ 17 | --prefix=/usr --exec-prefix= \ 18 | --sysconfdir=/etc --localstatedir=/var \ 19 | --enable-dbus-plugin --enable-x11-common-plugin \ 20 | --enable-alsa-utils-plugin --with-watchdog \ 21 | --with-keventd --with-sulogin 22 | 23 | echo 24 | echo "*** Building ..." 25 | echo 26 | make 27 | 28 | if [ $? -ne 0 ]; then 29 | echo 30 | echo "*** The build failed for some reason" 31 | echo 32 | exit 1 33 | fi 34 | 35 | echo 36 | echo "*** Done" 37 | echo 38 | -------------------------------------------------------------------------------- /contrib/void/finit.conf: -------------------------------------------------------------------------------- 1 | # /etc/finit.conf: Example for Void Linux 2 | # See /etc/finit.d/*.conf and `initctl list` for other services 3 | 4 | ## Runlevel to start after bootstrap, runlevel 'S' 5 | # Default is 2 6 | #runlevel 2 7 | 8 | # Top-level cgroups and their default settings. All groups mandatory 9 | # but more can be added, max 8 groups in total currently. The cgroup 10 | # 'root' is also available, reserved for RT processes. Settings are 11 | # as-is, only one shorthand 'mem.' exists, other than that it's the 12 | # cgroup v2 controller default names. 13 | #cgroup init cpu.weight:100 14 | #cgroup user cpu.weight:100 15 | #cgroup system cpu.weight:9700 16 | 17 | # Task to run if ctrl-alt-del is pressed. This condition is asserted by 18 | # Finit upon receiving SIGINT (from the kernel). 19 | #task [12345789] initctl reboot -- Rebooting system 20 | 21 | # Task to run if the kernel gets a power fail condition is pressed. 22 | # Asserted by Finit upon receiving SIGPWR (from the kernel). 23 | #task [12345789] initctl poweroff -- Shutting down system 24 | 25 | ## Bootstrap services and tasks 26 | # Can be listed here or in /etc/finit.d/*.conf 27 | -------------------------------------------------------------------------------- /doc/requirements.md: -------------------------------------------------------------------------------- 1 | Requirements 2 | ============ 3 | 4 | Finit is capable of running on both desktop/server systems with udev and 5 | embedded systems that usually come with BusyBox mdev. Some systems have 6 | systemd-udev or eudev today instead of the original udev, Finit probes 7 | for all of them at runtime and expects `/dev/` to be a writable file 8 | system using `devtmpfs`. It is also possible to run on a statically set 9 | up `/dev` if needed. It is however not a good idea to have both udev 10 | and mdev installed at the same time, this will lead to unpredictable 11 | results. 12 | 13 | At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is 14 | done slightly differently and on systems with udev you might want to add 15 | the following one-shot task early in your `/etc/finit.conf`: 16 | 17 | ```conf 18 | run [S] udevadm settle --timeout=120 -- Waiting for udev 19 | ``` 20 | 21 | Finit has a built-in Getty for TTYs, but requires a working `/bin/login` 22 | or `/bin/sh`, if no TTYs are configured in `/etc/finit.conf`. 23 | 24 | For a fully operational system `/var`, `/run` and `/tmp` must be set up 25 | properly in `/etc/fstab` -- which is iterated over at boot. 26 | -------------------------------------------------------------------------------- /contrib/alpine/finit.conf: -------------------------------------------------------------------------------- 1 | # /etc/finit.conf: Example for Alpine Linux 2 | # See /etc/finit.d/*.conf and `initctl list` for other services 3 | 4 | ## Runlevel to start after bootstrap, runlevel 'S' 5 | # Default is 2 6 | #runlevel 2 7 | 8 | # Top-level cgroups and their default settings. All groups mandatory 9 | # but more can be added, max 8 groups in total currently. The cgroup 10 | # 'root' is also available, reserved for RT processes. Settings are 11 | # as-is, only one shorthand 'mem.' exists, other than that it's the 12 | # cgroup v2 controller default names. 13 | #cgroup init cpu.weight:100 14 | #cgroup user cpu.weight:100 15 | #cgroup system cpu.weight:9700 16 | 17 | # Task to run if ctrl-alt-del is pressed. This condition is asserted by 18 | # Finit upon receiving SIGINT (from the kernel). 19 | #task [12345789] initctl reboot -- Rebooting system 20 | 21 | # Task to run if the kernel gets a power fail condition is pressed. 22 | # Asserted by Finit upon receiving SIGPWR (from the kernel). 23 | #task [12345789] initctl poweroff -- Shutting down system 24 | 25 | ## Bootstrap services and tasks 26 | # Can be listed here or in /etc/finit.d/*.conf 27 | -------------------------------------------------------------------------------- /contrib/debian/finit.conf: -------------------------------------------------------------------------------- 1 | # /etc/finit.conf: Example for Debian GNU/Linux 2 | # See /etc/finit.d/*.conf and `initctl list` for other services 3 | 4 | ## Runlevel to start after bootstrap, runlevel 'S' 5 | # Default is 2 6 | #runlevel 2 7 | 8 | # Top-level cgroups and their default settings. All groups mandatory 9 | # but more can be added, max 8 groups in total currently. The cgroup 10 | # 'root' is also available, reserved for RT processes. Settings are 11 | # as-is, only one shorthand 'mem.' exists, other than that it's the 12 | # cgroup v2 controller default names. 13 | #cgroup init cpu.weight:100 14 | #cgroup user cpu.weight:100 15 | #cgroup system cpu.weight:9700 16 | 17 | # Task to run if ctrl-alt-del is pressed. This condition is asserted by 18 | # Finit upon receiving SIGINT (from the kernel). 19 | #task [12345789] initctl reboot -- Rebooting system 20 | 21 | # Task to run if the kernel gets a power fail condition is pressed. 22 | # Asserted by Finit upon receiving SIGPWR (from the kernel). 23 | #task [12345789] initctl poweroff -- Shutting down system 24 | 25 | ## Bootstrap services and tasks 26 | # Can be listed here or in /etc/finit.d/*.conf 27 | -------------------------------------------------------------------------------- /m4/plugin.m4: -------------------------------------------------------------------------------- 1 | # From https://github.com/collectd/collectd/blob/master/configure.ac 2 | # Dependency handling currently unused 3 | 4 | define([enadis], [ifelse([$1], [yes], [disable], [enable])]) 5 | 6 | # AC_PLUGIN(name, default, info) 7 | # ------------------------------------------------------------ 8 | AC_DEFUN([AC_PLUGIN],[ 9 | m4_divert_once([HELP_ENABLE], [ 10 | Optional Plugins:]) 11 | define(PLUGGY, m4_translit([$1],[a-z-][A-Z-]))dnl 12 | AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [ 13 | if test "x$enableval" = "xyes"; then 14 | enable_plugin="yes" 15 | else 16 | enable_plugin="no" 17 | fi], [ 18 | if test "x$enable_all_plugins" = "xauto"; then 19 | if test "x$2" = "xyes"; then 20 | enable_plugin="yes" 21 | else 22 | enable_plugin="no" 23 | fi 24 | else 25 | enable_plugin="$enable_all_plugins" 26 | fi]) 27 | if test "x$enable_plugin" = "xyes"; then 28 | AC_DEFINE(HAVE_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, 1, [Define to 1 if the $1 plugin is enabled.]) 29 | plugins="$1 $plugins" 30 | fi 31 | 32 | AM_CONDITIONAL(BUILD_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, test "x$enable_plugin" = "xyes")]) 33 | -------------------------------------------------------------------------------- /test/run-restart-forever.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Regression test for issue #351 - verify that a dirty run/task in 3 | # runlevel S is not restarted forever just because it exits with a 4 | # non-zero error code. 5 | set -eu 6 | 7 | #export DEBUG=true 8 | TEST_DIR=$(dirname "$0") 9 | 10 | test_setup() 11 | { 12 | run "mkdir -p /etc/default" 13 | export TEST_TIMEOUT=5 14 | } 15 | 16 | test_teardown() 17 | { 18 | say "Running test teardown." 19 | run "rm -f $FINIT_CONF" 20 | } 21 | 22 | # shellcheck source=/dev/null 23 | . "$TEST_DIR/lib/setup.sh" 24 | 25 | test_one() 26 | { 27 | num=$1 28 | 29 | say "Reloading Finit ..." 30 | run "initctl reload" 31 | # run "initctl status fail.sh" 32 | 33 | say "Reloading Finit ..." 34 | run "initctl reload" 35 | # run "initctl status fail.sh" 36 | 37 | assert_restart_cnt "$num" "0/10" fail.sh 38 | } 39 | 40 | sep 41 | say "Add stanza to $FINIT_CONF" 42 | run "echo 'run /sbin/fail.sh -- Failure' > $FINIT_RCSD/fail.conf" 43 | run "echo 'run initctl touch fail' > $FINIT_RCSD/touch.conf" 44 | test_one 2 45 | 46 | sep 47 | say "Changing runlevel ..." 48 | run "initctl runlevel 3" 49 | test_one 4 50 | 51 | return 0 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2010 Claudio Matsuoka 2 | Copyright (c) 2008-2025 Joachim Wiberg 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/devmon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Verify device monitor (built-in plugin) 4 | # 5 | 6 | set -eu 7 | 8 | TEST_DIR=$(dirname "$0") 9 | 10 | test_teardown() 11 | { 12 | say "Running test teardown." 13 | 14 | run "rm -f $FINIT_CONF /tmp/post" 15 | } 16 | 17 | # Cannot run mknod as non-root even in a chroot 18 | # touch is close enough for our needs 19 | mkdev() 20 | { 21 | dir=$(dirname "$1") 22 | run "mkdir -p $dir" 23 | run "touch $1" 24 | } 25 | 26 | rmdev() 27 | { 28 | run "rm $1" 29 | } 30 | 31 | test_one() 32 | { 33 | cond="$1" 34 | node="/$cond" 35 | 36 | say "Checking cond $cond and device $node ..." 37 | run "echo service log:null \<$cond\> serv -np >> $FINIT_CONF" 38 | run "cat $FINIT_CONF" 39 | run "initctl reload" 40 | 41 | assert_status "serv" "waiting" 42 | 43 | mkdev "$node" 44 | assert_status "serv" "running" 45 | 46 | rmdev "$node" 47 | assert_status "serv" "waiting" 48 | 49 | say "Cleaning up ..." 50 | run "rm $FINIT_CONF" 51 | run "initctl reload" 52 | } 53 | 54 | # shellcheck source=/dev/null 55 | . "$TEST_DIR/lib/setup.sh" 56 | 57 | sep 58 | test_one "dev/fbsplash" 59 | sep 60 | test_one "dev/subdir/42" 61 | sep 62 | -------------------------------------------------------------------------------- /test/failing-sysv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify what happens when sysv scripts start services that keep 3 | # crashing -- hint: crashing sysv services should be detected by 4 | # Finit just like regular services. 5 | set -eu 6 | 7 | TEST_DIR=$(dirname "$0") 8 | 9 | test_setup() 10 | { 11 | run "mkdir -p /etc/default" 12 | } 13 | 14 | test_teardown() 15 | { 16 | say "Running test teardown." 17 | run "rm -f $FINIT_CONF" 18 | } 19 | 20 | # shellcheck source=/dev/null 21 | . "$TEST_DIR/lib/setup.sh" 22 | 23 | # This instructs serv to check the environment, and exit 24 | # if it cannot find "xyzzy", thus triggering a premature 25 | # exit which Finit should act on to retstart it. 26 | say "Setting up bogus /etc/default/serv" 27 | run "echo 'SERV_ARGS=\"-e xyzzy:lives\"' > /etc/default/serv" 28 | 29 | say "Add sysv stanza to $FINIT_CONF" 30 | run "echo 'sysv restart:5 [2345] pid:!/run/serv.pid name:serv /etc/init.d/S02-serv.sh -- Crashing SysV service' > $FINIT_CONF" 31 | 32 | #run "initctl debug" 33 | 34 | say 'Reload Finit' 35 | run "initctl reload" 36 | #run "initctl status serv" 37 | #run "ps" 38 | 39 | say 'Pending sysv restarts by Finit ...' 40 | #run "initctl status serv" 41 | #run "ps" 42 | retry 'assert_restarts 5 serv' 20 1 43 | 44 | return 0 45 | -------------------------------------------------------------------------------- /test/pidfile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify parsing of pid: argument 3 | 4 | set -eu 5 | 6 | TEST_DIR=$(dirname "$0") 7 | 8 | test_teardown() 9 | { 10 | say "Running test teardown." 11 | 12 | run "rm -f $FINIT_CONF" "/tmp/post" 13 | } 14 | 15 | test_one() 16 | { 17 | pidfn=$1 18 | service=$2 19 | 20 | sep 21 | say "Add service stanza '$service' to $FINIT_CONF ..." 22 | run "echo '$service' > $FINIT_CONF" 23 | run "initctl reload" 24 | 25 | assert_is_pidfile "serv" "$pidfn" 26 | 27 | say "Done, drop service from $FINIT_CONF ..." 28 | run "rm $FINIT_CONF" 29 | run "initctl reload" 30 | } 31 | 32 | # shellcheck source=/dev/null 33 | . "$TEST_DIR/lib/setup.sh" 34 | 35 | #run "initctl debug" 36 | #run "ls -l /run/finit/cond/pid/ /lib/finit/plugins/" 37 | 38 | test_one "/run/serv.pid" "service serv -np" 39 | test_one "/run/serv.pid" "service pid:!/run/serv.pid serv" 40 | test_one "/run/serv.pid" "service type:forking serv" 41 | test_one "/run/serv.pid" "service pid:!//run/serv.pid serv -np" 42 | test_one "/run/serv.pid" "service pid://run/serv.pid serv -n" 43 | test_one "/run/serv.pid" "service pid://../run/serv.pid serv -n" 44 | test_one "/run/serv.pid" "service pid://run/../run//serv.pid serv -n" 45 | 46 | sep 47 | -------------------------------------------------------------------------------- /doc/config/index.md: -------------------------------------------------------------------------------- 1 | This section provides an overview of Finit's configuration system. For 2 | detailed information on specific topics, see the individual sections in 3 | the navigation menu. 4 | 5 | 6 | Configuration File Syntax 7 | -------------------------- 8 | 9 | The file format is line based, empty lines and comments, lines starting 10 | with `#`, are ignored. A configuration directive starts with a keyword 11 | followed by a space and the rest of the line is treated as the value. 12 | 13 | As of Finit v4.4, configuration directives can be broken up in multiple 14 | lines using the continuation character `\`, and trailing comments are 15 | also allowed. Example: 16 | 17 | ```aconf 18 | # Escape \# chars if you want them literal in, e.g., descriptions 19 | service name:sysklogd [S123456789] \ 20 | env:-/etc/default/sysklogd \ 21 | syslogd -F $SYSLOGD_ARGS \ 22 | -- System log daemon \# 1 # Comments allowed 23 | ``` 24 | 25 | The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support many 26 | directives. Some are restricted, e.g., only available at bootstrap, 27 | runlevel `S`. Read on in [Files & Layout](files.md) for more on how 28 | to structure your .conf files. 29 | 30 | For details on restrictions, see [Limitations](limitations.md). 31 | -------------------------------------------------------------------------------- /doc/config/env.md: -------------------------------------------------------------------------------- 1 | Environment Variables 2 | --------------------- 3 | 4 | In Finit v4.3 support for setting environment variables in `finit.conf`, 5 | and any `*.conf`, was added. It is worth noting that these are global 6 | and *shared with all* services -- the only way to have a service-local 7 | environment is detailed in [Services Environment](service-env.md). 8 | 9 | The syntax for global environment variables is straight forward. In 10 | Finit v4.4 the `set` keyword was added for completeness, but the old 11 | syntax (without the `set ` prefix) is still honored: 12 | 13 | set foo=bar 14 | set baz="qux" 15 | 16 | On reload of .conf files, all tracked environment variables are cleared 17 | so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf` 18 | file, it will no longer be used by Finit or any new (!) started 19 | run/tasks or services. The environment of already started processes can 20 | not be changed. 21 | 22 | The only variables reset to sane defaults on .conf reload are: 23 | 24 | PATH=_PATH_STDPATH 25 | SHELL=_PATH_BSHELL 26 | LOGNAME=root 27 | USER=root 28 | 29 | It is entirely possible to override these as well from the .conf files, 30 | but be careful. Changing SHELL changes the behavior of `system()` and a 31 | lot of other commands as well. 32 | -------------------------------------------------------------------------------- /doc/state-machine.md: -------------------------------------------------------------------------------- 1 | State Machine 2 | ============= 3 | 4 | A service is bound to a state machine that is in one of ten states. For 5 | run/tasks there is an additional end state called `DONE`. All processes 6 | managed by Finit start in the `HALTED` state. The image shows how both 7 | conditions and commands drive the machine. 8 | 9 | ![The service state machine](img/svc-machine.png "The service state machine") 10 | 11 | The current state depends on the two following conditions: 12 | 13 | * `E`: Service enabled. In order for `E` to be satisfied, the service 14 | must be allowed to run in the current runlevel and not be stopped. 15 | 16 | A service may be stopped, or blocked, for several reasons: 17 | 18 | - The user has manually stopped the service using `initctl stop NAME` 19 | - The program exits immediately. I.e. keeps crashing (make sure to use 20 | the 'run this service in the foreground' command line option) 21 | - The binary is missing in the filesystem 22 | 23 | * `C`: Service conditions are satisfied: 24 | 25 | - `on` (+): The condition is asserted. 26 | - `off` (-): The condition is deasserted. 27 | - `flux` (~): The condition state is unknown. 28 | 29 | For a detailed description of conditions, and how to debug them, 30 | see the [Finit Conditions](conditions.md) document. 31 | -------------------------------------------------------------------------------- /test/initctl-status-subset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify that the status command 'initctl status foo' matches three 3 | # instances of tasks called foo:1, foo:2, and foo:3. It should not 4 | # match the fourth task called foobar. This is a regression test to 5 | # ensure issue #275 doesn't happen again. 6 | set -eu 7 | 8 | TEST_DIR=$(dirname "$0") 9 | 10 | test_teardown() 11 | { 12 | say "Running test teardown." 13 | run "rm -f $FINIT_CONF" 14 | } 15 | 16 | test_init() 17 | { 18 | run "echo '# Three foo and one bar enter Finit' > $FINIT_CONF" 19 | } 20 | 21 | test_add_one() 22 | { 23 | service=$1 24 | 25 | say "Add service stanza '$service' to $FINIT_CONF ..." 26 | run "echo '$service' >> $FINIT_CONF" 27 | } 28 | 29 | # shellcheck source=/dev/null 30 | . "$TEST_DIR/lib/setup.sh" 31 | 32 | test_init 33 | 34 | test_add_one "task manual:yes name:foo :1 serv -- Foo \#1" 35 | test_add_one "task manual:yes name:foo :2 serv -- Foo \#2" 36 | test_add_one "task manual:yes name:foo :3 serv -- Foo \#3" 37 | test_add_one "task manual:yes name:foobar serv -- Foobar" 38 | 39 | say 'Reload Finit' 40 | #run "initctl debug" 41 | run "initctl reload" 42 | run "initctl status" 43 | 44 | #run "initctl status" 45 | #run "ps" 46 | #run "initctl status serv" 47 | 48 | assert_num_services 3 foo 49 | assert_desc "Foobar" foobar 50 | assert_desc "Foo #1" foo:1 51 | 52 | -------------------------------------------------------------------------------- /test/ready-serv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify ready:script for pid/systemd/s6 style services 3 | set -eu 4 | 5 | TEST_DIR=$(dirname "$0") 6 | 7 | test_teardown() 8 | { 9 | say "Running test teardown." 10 | run "rm -f $FINIT_CONF" 11 | } 12 | 13 | test_one() 14 | { 15 | service=$(echo "$1" | tr -s " ") 16 | file=/tmp/ready 17 | 18 | sep 19 | say "Add service stanza '$service' to $FINIT_CONF ..." 20 | run "echo '$service' > $FINIT_CONF" 21 | 22 | say 'Reload Finit' 23 | run "initctl reload" 24 | 25 | retry 'assert_num_children 1 serv' 26 | retry 'assert_ready "serv"' 5 1 27 | # run "initctl status" 28 | # run "initctl cond dump" 29 | assert_file_contains "$file" "READY" 30 | run "rm -f $file" 31 | 32 | say 'Stop the service' 33 | run "initctl stop serv" 34 | retry 'assert_num_children 0 serv' 35 | 36 | say "Done, drop service from $FINIT_CONF ..." 37 | run "rm $FINIT_CONF" 38 | run "initctl reload" 39 | } 40 | 41 | # shellcheck source=/dev/null 42 | . "$TEST_DIR/lib/setup.sh" 43 | 44 | #run "initctl debug" 45 | 46 | test_one "service ready:/bin/ready.sh serv -np -- Native PID style notification" 47 | test_one "service notify:s6 ready:/bin/ready.sh serv -n -N %n -- s6 style notification" 48 | test_one "service notify:systemd ready:/bin/ready.sh serv -n -- systemd style notification" 49 | -------------------------------------------------------------------------------- /libsystemd/sd-daemon.h: -------------------------------------------------------------------------------- 1 | #ifndef SD_DAEMON_H_ 2 | #define SD_DAEMON_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define SD_LISTEN_FDS_START 3 8 | 9 | /* systemd logging defines for stderr parsing by Finit */ 10 | #define SD_EMERG "<0>" /* system is unusable */ 11 | #define SD_ALERT "<1>" /* action must be taken immediately */ 12 | #define SD_CRIT "<2>" /* critical conditions */ 13 | #define SD_ERR "<3>" /* error conditions */ 14 | #define SD_WARNING "<4>" /* warning conditions */ 15 | #define SD_NOTICE "<5>" /* normal but significant condition */ 16 | #define SD_INFO "<6>" /* informational */ 17 | #define SD_DEBUG "<7>" /* debug-level messages */ 18 | 19 | /* Basic notification function */ 20 | int sd_notify(int unset_environment, const char *state); 21 | 22 | /* Printf-style notification functions */ 23 | int sd_notifyf(int unset_environment, const char *format, ...); 24 | 25 | /* PID-specific notification functions */ 26 | int sd_pid_notify(pid_t pid, int unset_environment, const char *state); 27 | int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...); 28 | 29 | /* Socket activation stubs */ 30 | int sd_listen_fds(int unset_environment); 31 | 32 | /* Watchdog stub */ 33 | int sd_watchdog_enabled(int unset_environment, uint64_t *usec); 34 | 35 | /* System detection */ 36 | int sd_booted(void); 37 | 38 | #endif /* SD_DAEMON_H_ */ 39 | -------------------------------------------------------------------------------- /tmpfiles.d/var.conf: -------------------------------------------------------------------------------- 1 | # Standard FHS 2.3 structure in /var and /var/run, err /run 2 | 3 | d /var/cache 0755 - - - 4 | d /var/db 0755 - - - 5 | d /var/games 0755 - - - 6 | d /var/lib 0755 - - - 7 | d /var/lib/alarm 0755 - - - 8 | d /var/lib/misc 0755 - - - 9 | d /var/lib/urandom 0755 - - - 10 | d /var/spool 0755 - - - 11 | d /var/tmp 0777 - - - 12 | 13 | # Used by pam_console(8) 14 | d /run/console 1777 - - - 15 | f /run/console/nobody 0644 - - - 16 | f /run/console/console.lock 0644 - - - nobody 17 | 18 | # /run/lock/subsys is used for serializing SysV service execution 19 | d /run/lock 0777 - - - 20 | d /run/lock/subsys 0755 - - - 21 | 22 | # Compat symlink to new /run ramdisk 23 | L /var/run - - - - ../run 24 | L /var/lock - - - - ../run/lock 25 | 26 | # Needed by Debian/Ubuntu ifupdown 27 | d /var/run/network 0755 - - - 28 | 29 | # Debian has /run/sudo, ensure correct perms and SELinux label 30 | d /run/sudo 0711 root root 31 | d /run/sudo/ts 0700 root root 32 | Z /run/sudo - - - - 33 | 34 | d /var/empty 0755 - - - 35 | d /var/log 0755 - - - 36 | d /var/mail 0755 - - - 37 | d /var/opt 0755 - - - 38 | d /var/spool 0755 - - - 39 | d /var/spool/cron 0755 - - - 40 | 41 | # UTMP actually needs multiple db files 42 | f /var/run/utmp 0664 root utmp - 43 | f /var/log/wtmp 0664 root utmp - 44 | f /var/log/btmp 0660 root utmp - 45 | f /var/log/lastlog 0664 root utmp - 46 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | Finit Test Suite 2 | ================ 3 | 4 | Finit comes with a set of tests and a small framework for running them. 5 | Contributors are encouraged to write new tests when implementing features, or 6 | fixing bugs. 7 | 8 | Each test is run in isolation, in it's own namespace. Finit will therefore 9 | be able to be launched as PID 1. Since it's also running with it's own root 10 | directory it will be able to function properly without having any super user 11 | privileges in the host environment. 12 | 13 | 14 | Running tests 15 | ------------- 16 | 17 | To run the test suite, first [build](../doc/build.md) Finit, e.g: 18 | 19 | ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var --enable-testserv-plugin 20 | make -j9 clean all 21 | 22 | Then run (parallel does not work atm): 23 | 24 | make check 25 | 26 | `make check` will set up the required assets for the test environment, and then 27 | run the full set of tests. The environment is not removed afterwards so at 28 | this point individual tests can be executed without having to run the entire 29 | test suite, which is handy when developing new tests or debugging existing 30 | test. To execute an individual test, simply invoke the script containing it: 31 | 32 | ./test/name-of-the-test.sh 33 | 34 | Another way to run a single (or more) test(s) is to define the `TESTS` 35 | environment variable: 36 | 37 | TESTS="start-kill-service" make check 38 | -------------------------------------------------------------------------------- /test/crashing.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verifies that Finit calls post:script for crashing services 3 | 4 | set -eu 5 | 6 | TEST_DIR=$(dirname "$0") 7 | 8 | test_teardown() 9 | { 10 | say "Running test teardown." 11 | run "rm -f $FINIT_CONF" "/tmp/post" 12 | } 13 | 14 | crashit() 15 | { 16 | nm=$1 17 | 18 | say 'Simulate service crash (kill -9 ..)' 19 | i=0 20 | laps=100 21 | while [ $i -lt $laps ]; do 22 | i=$((i + 1)) 23 | say "Lap $i/$laps, killing service $nm ..." # we have this, no sleep needed 24 | if ! run "slay $nm"; then 25 | break; 26 | fi 27 | done 28 | } 29 | 30 | test_one() 31 | { 32 | type=$1 33 | shift 34 | nm=$1 35 | shift 36 | args=$* 37 | 38 | say "Add service stanza in $FINIT_CONF" 39 | run "echo service log:stderr oncrash:script post:/bin/post.sh $nm $args > $FINIT_CONF" 40 | 41 | say 'Reload Finit' 42 | run "initctl reload" 43 | 44 | if [ "$type" = "sig" ]; then 45 | retry "assert_num_children 1 $nm" 46 | run "initctl status $nm" 47 | crashit "$nm" 48 | fi 49 | 50 | retry "assert_status $nm crashed" 500 51 | run "cat /tmp/post" 52 | assert_file_contains "/tmp/post" "POST" 53 | run "rm -f /tmp/post" 54 | } 55 | 56 | # shellcheck source=/dev/null 57 | . "$TEST_DIR/lib/setup.sh" 58 | 59 | #run "initctl debug" 60 | 61 | test_one sig service.sh " -- Test crashing service.sh" 62 | test_one app serv "-np -c -- Test crashing serv" 63 | -------------------------------------------------------------------------------- /test/global-envs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | TEST_DIR=$(dirname "$0") 5 | # shellcheck disable=SC2034 6 | BOOTSTRAP="foo=bar\n\ 7 | set COLORTERM=yes\n\ 8 | b aq=oroszoe\n\ 9 | baz=qux" 10 | 11 | test_teardown() 12 | { 13 | say "Running test teardown." 14 | run "rm -f $FINIT_CONF" 15 | } 16 | 17 | # shellcheck source=/dev/null 18 | . "$TEST_DIR/lib/setup.sh" 19 | 20 | say "Waiting for runlevel 2" 21 | sleep 2 22 | 23 | run "initctl debug" 24 | 25 | say "Add service stanza to $FINIT_CONF ..." 26 | run "echo 'service serv -np -e foo:bar -- Verify foo=bar' >> $FINIT_CONF" 27 | run "cat $FINIT_CONF" 28 | 29 | say 'Reload Finit' 30 | run "initctl reload" 31 | sleep 1 32 | run "initctl" 33 | retry 'assert_num_children 1 serv' 34 | 35 | say "Modify $FINIT_CONF slightly ..." 36 | run "sed -i 's/foo:bar/baz:qux/; s/foo=bar/baz=qux/' $FINIT_CONF" 37 | 38 | say 'Reload Finit' 39 | run "initctl reload" 40 | sleep 1 41 | retry 'assert_num_children 1 serv' 42 | 43 | say "Swap envs completely in $FINIT_CONF ..." 44 | run "echo 'bar=foo' > $FINIT_CONF" 45 | run "echo 'qux=baz' >> $FINIT_CONF" 46 | 47 | say "Add swapped service stanza to $FINIT_CONF ..." 48 | run "echo 'service serv -np -e bar:foo -E baz -- Verify bar=foo and no baz' >> $FINIT_CONF" 49 | 50 | say 'Reload Finit' 51 | run "initctl reload" 52 | sleep 1 53 | retry 'assert_num_children 1 serv' 54 | 55 | say "Done, drop service from $FINIT_CONF ..." 56 | run "rm $FINIT_CONF" 57 | run "initctl reload" 58 | -------------------------------------------------------------------------------- /test/skel/etc/inittab: -------------------------------------------------------------------------------- 1 | # /etc/inittab 2 | # 3 | # Copyright (C) 2001 Erik Andersen 4 | # 5 | # Note: BusyBox init doesn't support runlevels. The runlevels field is 6 | # completely ignored by BusyBox init. If you want runlevels, use 7 | # sysvinit. 8 | # 9 | # Format for each entry: ::: 10 | # 11 | # id == tty to run on, or empty for /dev/console 12 | # runlevels == ignored 13 | # action == one of sysinit, respawn, askfirst, wait, and once 14 | # process == program to run 15 | 16 | # Startup the system 17 | #::sysinit:/bin/mount -t proc proc /proc 18 | #::sysinit:/bin/mount -o remount,rw / 19 | #::sysinit:/bin/mkdir -p /dev/pts /dev/shm 20 | #::sysinit:/bin/mount -a 21 | #::sysinit:/bin/mkdir -p /run/lock/subsys 22 | #::sysinit:/sbin/swapon -a 23 | null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd 24 | null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin 25 | null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout 26 | null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr 27 | ::sysinit:/bin/hostname -F /etc/hostname 28 | # now run any rc scripts 29 | ::sysinit:/etc/init.d/rcS 30 | 31 | # Put a getty on the serial port 32 | #ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 # GENERIC_SERIAL 33 | 34 | # Stuff to do for the 3-finger salute 35 | #::ctrlaltdel:/sbin/reboot 36 | 37 | # Stuff to do before rebooting 38 | ::shutdown:/etc/init.d/rcK 39 | #::shutdown:/sbin/swapoff -a 40 | #::shutdown:/bin/umount -a -r 41 | -------------------------------------------------------------------------------- /doc/config/templating.md: -------------------------------------------------------------------------------- 1 | Templating 2 | ========== 3 | 4 | Finit comes with rudimentary support for templating, similar to that of 5 | systemd. Best illustrated with an example: 6 | 7 | $ initctl show avahi-autoipd@ 8 | service :%i avahi-autoipd --syslog %i -- ZeroConf for %i 9 | 10 | To enable ZeroConf for, e.g., `eth0`, use 11 | 12 | $ initctl enable avahi-autoipd@eth0.conf 13 | 14 | The enabled symlink will be set up to `avahi-autoipd@.conf` and every 15 | instance of `%i` will in the instantiated directive be replaced with 16 | `eth0`. Inspect the resulting instantiated template with `initctl show 17 | avahi-autoipd:eth0` and check the status of a running instance with: 18 | 19 | ``` 20 | $ initctl status avahi-autoipd:eth0 21 | Status : running 22 | Identity : avahi-autoipd:eth0 23 | Description : ZeroConf for eth0 24 | Origin : /etc/finit.d/enabled/avahi-autoipd@eth0.conf 25 | Environment : -/etc/default/avahi-autoipd-eth0 26 | Command : avahi-autoipd $AVAHI_AUTOIPD_ARGS eth0 27 | PID file : /run/avahi-autoipd.eth0.pid 28 | PID : 4190 29 | User : root 30 | Group : root 31 | Uptime : 24 sec 32 | Restarts : 0 (0/10) 33 | Runlevels : [---2345-789] 34 | Memory : 20.0k 35 | CGroup : /system/avahi-autoipd@eth0 cpu 0 [100, max] mem [0, max] 36 | └─ 4190 avahi-autoipd: [eth0] bound 169.254.1.9 37 | 38 | Jul 8 11:51:42 infix-c0-ff-ee finit[1]: Starting avahi-autoipd:eth0[4190] 39 | ``` 40 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | SUBDIRS = man plugins src system tmpfiles.d 3 | dist_doc_DATA = README.md LICENSE contrib/finit.conf 4 | 5 | if CONTRIB 6 | SUBDIRS += contrib 7 | endif 8 | if DOC 9 | SUBDIRS += doc 10 | endif 11 | if LIBSYSTEMD 12 | SUBDIRS += libsystemd 13 | endif 14 | 15 | if LIBSYSTEMD 16 | SUBDIRS += test 17 | # Explicit dependency to ensure libsystemd is built before test 18 | test: libsystemd 19 | else 20 | SUBDIRS += test 21 | endif 22 | 23 | # Compat hook 24 | install-dev: 25 | @make -C src install-pkgincludeHEADERS 26 | 27 | # Target to run when building a release 28 | release: distcheck 29 | @for file in $(DIST_ARCHIVES); do \ 30 | md5sum $$file > ../$$file.md5; \ 31 | sha256sum $$file > ../$$file.sha256; \ 32 | done 33 | @mv $(DIST_ARCHIVES) ../ 34 | @echo 35 | @echo "Resulting release files:" 36 | @echo "=================================================================" 37 | @for file in $(DIST_ARCHIVES); do \ 38 | printf "$$file \tDistribution tarball\n"; \ 39 | printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \ 40 | printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \ 41 | done 42 | 43 | DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \ 44 | --enable-testserv-plugin --enable-x11-common-plugin \ 45 | --with-watchdog --with-keventd --with-fstab=/etc/fstab \ 46 | --with-libsystemd --with-bash-completion-dir=no 47 | -------------------------------------------------------------------------------- /src/devmon.h: -------------------------------------------------------------------------------- 1 | /* Device node monitor for the Finit condition engine 2 | * 3 | * Copyright (c) 2022-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_DEVMON_H_ 25 | #define FINIT_DEVMON_H_ 26 | 27 | void devmon_add_cond(const char *cond); 28 | void devmon_del_cond(const char *cond); 29 | 30 | void devmon_init(uev_ctx_t *ctx); 31 | 32 | #endif /* FINIT_DEVMON_H_ */ 33 | -------------------------------------------------------------------------------- /src/clone3.h: -------------------------------------------------------------------------------- 1 | /* Portable clone3() wrapper with fallback to fork() 2 | * 3 | * Copyright (c) 2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_CLONE3_H_ 25 | #define FINIT_CLONE3_H_ 26 | 27 | #include 28 | #include 29 | 30 | pid_t call_clone3 (uint64_t flags, int cgroup_fd); 31 | int has_clone3 (void); 32 | 33 | #endif /* FINIT_CLONE3_H_ */ 34 | -------------------------------------------------------------------------------- /test/run-task-tricks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Verifies that a run task can call initctl at bootstrap and runlevel 2, 4 | # without blocking, performing several tasks: 5 | # 6 | # 1. Query running services 7 | # 2. Stop/Start a running service 8 | # 3. Restart a service 9 | # 10 | 11 | TEST_DIR=$(dirname "$0") 12 | #DEBUG=1 13 | 14 | # shellcheck disable=SC2034 15 | BOOTSTRAP="run [S] /bin/ready.sh -- Ready steady go\n\ 16 | run [S] name:one initctl -- Query status\n\ 17 | service [S234] /sbin/service.sh -- Background service\n\ 18 | run [2] name:two initctl stop service.sh -- Stopping service\n\ 19 | run [3] name:three initctl start service.sh -- Starting service again\n\ 20 | run [4] name:four initctl restart service.sh -- Restart service" 21 | # shellcheck disable=SC2034 22 | RCLOCAL="echo \"$0 ==============\" &1>2" 23 | 24 | # shellcheck source=/dev/null 25 | . "$TEST_DIR/lib/setup.sh" 26 | 27 | say "$FINIT_CONF:" 28 | run "cat $FINIT_CONF" 29 | say "/etc/rc.local:" 30 | run "cat /etc/rc.local" 31 | 32 | say "Waiting for runlevel 2" 33 | sleep 2 34 | assert_status "service.sh" "stopped" 35 | 36 | say "Changing to runlevel 3" 37 | run "initctl runlevel 3" 38 | sleep 2 39 | assert_status "service.sh" "running" 40 | run "initctl" 41 | oldpid=$(texec initctl |awk '/service.sh/{print $1}') 42 | 43 | say "Changing to runlevel 4" 44 | run "initctl runlevel 4" 45 | sleep 2 46 | assert_status "service.sh" "running" 47 | assert_pidiff "service.sh" "$oldpid" 48 | 49 | run "initctl" 50 | -------------------------------------------------------------------------------- /src/watchdog.h: -------------------------------------------------------------------------------- 1 | /* Built-in watchdog daemon 2 | * 3 | * Copyright (c) 2016-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include "config.h" /* Generated by configure script */ 25 | 26 | #ifndef WDT_DEVNODE 27 | #define WDT_DEVNODE "/dev/watchdog" 28 | #endif 29 | #define WDT_TIMEOUT 30 30 | 31 | /** 32 | * Local Variables: 33 | * indent-tabs-mode: t 34 | * c-file-style: "linux" 35 | * End: 36 | */ 37 | -------------------------------------------------------------------------------- /contrib/void/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "x`id -u`" != "x0" ]; then 4 | echo 5 | echo "*** This script must run as root" 6 | echo 7 | exit 1 8 | fi 9 | 10 | # Adjust base directory 11 | grep -q void Makefile 2>/dev/null 12 | if [ $? -eq 0 ]; then 13 | cd ../.. 14 | elif [ ! -e configure.ac ]; then 15 | echo "*** Please run this script from the Finit base directory." 16 | exit 1 17 | fi 18 | 19 | echo 20 | echo "*** Install Finit on Void Linux" 21 | echo "========================================================================" 22 | echo "/sbin/finit - PID 1" 23 | echo "/lib/finit/plugins/* - All enabled Finit plugins" 24 | echo "/etc/finit.conf - Finit configuration file" 25 | echo "/etc/finit.d/ - Finit services" 26 | echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader" 27 | echo 28 | read -p "Do you want to continue (y/N)? " yorn 29 | 30 | if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then 31 | echo 32 | echo "*** Installing Finit files ..." 33 | make install 34 | cd /usr/share/doc/finit/contrib/void 35 | for file in finit.conf rc.local; do 36 | install -vbD $file /etc/$file 37 | done 38 | cp -va finit.d /etc/ 39 | 40 | read -p "*** Install Finit as the system default Init (y/N)? " yorn 41 | if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then 42 | echo "*** Updating /sbin/init symlink --> finit ..." 43 | cd /sbin 44 | rm init 45 | ln -s finit init 46 | fi 47 | 48 | echo "*** Done" 49 | echo 50 | else 51 | echo 52 | echo "*** Aborting install." 53 | echo 54 | fi 55 | -------------------------------------------------------------------------------- /src/schedule.h: -------------------------------------------------------------------------------- 1 | /* Work queue helper functions 2 | * 3 | * Copyright (c) 2018-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_SCHEDULE_H_ 25 | #define FINIT_SCHEDULE_H_ 26 | 27 | struct wq { 28 | uev_t watcher; 29 | int init; 30 | int delay; /* msec delay before starting work */ 31 | void (*cb)(void *); 32 | void *arg; 33 | }; 34 | 35 | int schedule_work(struct wq *work); 36 | 37 | #endif /* FINIT_SCHEDULE_H_ */ 38 | -------------------------------------------------------------------------------- /src/sm.h: -------------------------------------------------------------------------------- 1 | /* Finit state machine 2 | * 3 | * Copyright (c) 2016 Jonas Johansson 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_SM_H_ 25 | #define FINIT_SM_H_ 26 | 27 | void sm_init (void); 28 | void sm_step (void); 29 | 30 | int sm_in_reload (void); 31 | void sm_reload (void); 32 | void sm_runlevel (int newlevel); 33 | 34 | #endif /* FINIT_SM_H_ */ 35 | 36 | /** 37 | * Local Variables: 38 | * indent-tabs-mode: t 39 | * c-file-style: "linux" 40 | * End: 41 | */ 42 | -------------------------------------------------------------------------------- /test/start-kill-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Regression test for Finit bug #313: 3 | # - services get the 'forking' flag on initctl reload 4 | # - stopping a crashing task while its restart callback is pending 5 | # causes state 'running' with pid:0 (i.e., not running at all.) 6 | # 7 | 8 | set -eu 9 | 10 | TEST_DIR=$(dirname "$0") 11 | 12 | test_teardown() 13 | { 14 | # run "initctl status -j serv" 15 | say "Running test teardown." 16 | run "rm -f $FINIT_CONF" 17 | } 18 | 19 | # shellcheck source=/dev/null 20 | . "$TEST_DIR/lib/setup.sh" 21 | 22 | say "Check deps ..." 23 | check_dep jq 24 | 25 | rm -f "$SYSROOT"/oldpid 26 | 27 | say "Add service stanza in $FINIT_CONF" 28 | run "echo 'service [2345] log:stderr serv -np -- Test service' > $FINIT_CONF" 29 | 30 | say 'Reload Finit' 31 | run "initctl reload" 32 | 33 | say 'Verify serv is running ...' 34 | retry 'assert_num_children 1 serv' 35 | 36 | say 'Verify reload does not change forking type of service' 37 | #run "initctl status -j serv" 38 | run "initctl reload" 39 | #run "initctl status -j serv" 40 | assert_forking serv false 41 | 42 | say 'Simulate service crash (kill -9 ..)' 43 | run "initctl debug" 44 | i=0 45 | laps=7 46 | while [ $i -lt $laps ]; do 47 | i=$((i + 1)) 48 | say "Lap $i/$laps, killing service ..." # we have this, no sleep needed 49 | run "slay serv" 50 | done 51 | 52 | say 'Verify stopping service actually stops it' 53 | sleep 1 54 | run "initctl stop serv" 55 | sleep 5 56 | #run "initctl status serv" 57 | assert_status serv stopped 58 | 59 | say 'Verify restarting service actually starts it' 60 | run "initctl start serv" 61 | retry 'assert_num_children 1 serv' 62 | -------------------------------------------------------------------------------- /doc/config/task-and-run.md: -------------------------------------------------------------------------------- 1 | run (sequence) 2 | -------------- 3 | 4 | **Syntax:** `run [LVLS] /path/to/cmd ARGS -- Optional description` 5 | 6 | > `` is described in the [Services](services.md) section. 7 | 8 | One-shot command to run in sequence when entering a runlevel, with 9 | optional arguments and description. `run` commands are guaranteed to be 10 | completed before running the next command. Useful when serialization is 11 | required. 12 | 13 | > [!WARNING] 14 | > Try to avoid the `run` command. It blocks much of the functionality 15 | > in Finit, like (re)starting other (perhaps crashing) services while a 16 | > `run` task is executing. Use other synchronization mechanisms 17 | > instead, like conditions. 18 | 19 | Incomplete list of unsupported `initctl` commands in `run` tasks: 20 | 21 | - `initctl runlevel N`, setting runlevel 22 | - `initctl reboot` 23 | - `initctl halt` 24 | - `initctl poweroff` 25 | - `initctl suspend` 26 | 27 | To prevent `initctl` from calling Finit when enabling and disabling 28 | services from inside a `run` task, use the `--force` option. See 29 | also the `--quiet` and `--batch` options. 30 | 31 | task (parallel) 32 | --------------- 33 | 34 | **Syntax:** `task [LVLS] /path/to/cmd ARGS -- Optional description` 35 | 36 | > `` is described in the [Services](services.md) section. 37 | 38 | One-shot like 'run', but starts in parallel with the next command. 39 | 40 | Both `run` and `task` commands are run in a shell, so basic pipes and 41 | redirects can be used: 42 | 43 | task [s] echo "foo" | cat >/tmp/bar 44 | 45 | Please note, `;`, `&&`, `||`, and similar are *not supported*. Any 46 | non-trivial constructs are better placed in a separate shell script. 47 | -------------------------------------------------------------------------------- /test/process-depends.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Regression test for issue #392 3 | 4 | set -eu 5 | #set -x 6 | 7 | TEST_DIR=$(dirname "$0") 8 | 9 | test_setup() 10 | { 11 | run "mkdir -p /etc/default" 12 | } 13 | 14 | test_teardown() 15 | { 16 | say "Running test teardown." 17 | 18 | run "rm -f $FINIT_CONF" 19 | } 20 | 21 | srv() 22 | { 23 | say "Adding service $(echo "$1" | sed -n -e 's/^.*-- //p')" 24 | run "echo '$1' >> $FINIT_CONF" 25 | } 26 | 27 | # shellcheck source=/dev/null 28 | . "$TEST_DIR/lib/setup.sh" 29 | 30 | #run "initctl debug" 31 | 32 | # Test systemd notify 33 | srv 'service name:test1_systemd [2345] notify:systemd restart:0 serv -i test1 -n -- Systemd proc' 34 | srv 'service name:test2 [2345] serv -i test2 -np -- Dependency test 1' 35 | 36 | # Test pid notify 37 | srv "service name:test3_pid [2345] notify:pid serv -i test3 -np -- Dependency test 2" 38 | srv "service name:test4 [2345] serv -i test4 -np -- Dependency test 3" 39 | 40 | sep "$FINIT_CONF" 41 | run "cat $FINIT_CONF" 42 | sep 43 | 44 | say 'Reload Finit' 45 | run "initctl reload" 46 | 47 | say "Waiting for all services to start" 48 | retry 'assert_status "test4" "running"' 5 1 49 | 50 | # Load bearing debug. Without this the bug cannot be reproduced! 51 | run "initctl debug" 52 | 53 | sep 54 | say "Stopping root service, all others should also stop" 55 | run "initctl stop test1_systemd" 56 | retry 'assert_status "test3_pid" "waiting"' 5 1 57 | 58 | sep 59 | say "Verify test4 does not restart" 60 | run "initctl status" 61 | run "initctl cond dump" 62 | assert_status "test4" "waiting" 63 | 64 | return 0 65 | -------------------------------------------------------------------------------- /contrib/procmon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | USAGE="Usage: $0 processName" 3 | LOG_FILE="/tmp/memusage.csv" 4 | ELAPSED_TIME=0 5 | PERIOD=1 # seconds 6 | 7 | if [ $# -ne 1 ]; then 8 | echo $USAGE 9 | exit 1 10 | fi 11 | 12 | echo "ElapsedTime,VmSize,VmRSS" > $LOG_FILE 13 | 14 | # Monitor memory usage forever until script is killed 15 | while : 16 | do 17 | SUM_VM_SIZE=0 18 | SUM_RSS_SIZE=0 19 | # In case the monitored process has not yet started 20 | # keep searching until its PID is found 21 | PROCESS_PIDS="" 22 | while : 23 | do 24 | PROCESS_PIDS=`/bin/pidof $1` 25 | if [ "$PROCESS_PIDS.X" != ".X" ]; then 26 | break 27 | fi 28 | done 29 | 30 | for PID in ${PROCESS_PIDS} ; do 31 | VM_SIZE=`awk '/VmSize/ {print $2}' < /proc/$PID/status` 32 | if [ "$VM_SIZE.X" = ".X" ]; then 33 | continue 34 | fi 35 | #echo exprVM_ $SUM_VM_SIZE + $VM_SIZE 36 | SUM_VM_SIZE=`expr $SUM_VM_SIZE + $VM_SIZE` 37 | 38 | VM_RSS=`awk '/VmRSS/ {print $2}' < /proc/$PID/status` 39 | if [ "$VM_RSS.X" = ".X" ]; then 40 | continue 41 | fi 42 | SUM_RSS_SIZE=`expr $SUM_RSS_SIZE + $VM_RSS` 43 | done 44 | echo "$ELAPSED_TIME sec, $SUM_VM_SIZE KB, $SUM_RSS_SIZE KB" 45 | echo "$ELAPSED_TIME,$SUM_VM_SIZE,$SUM_RSS_SIZE" >> $LOG_FILE 46 | sleep $PERIOD 47 | VM_SIZE="" 48 | VM_RSS="" 49 | # Needs to get actual elapsed time instead of doing this 50 | ELAPSED_TIME=`expr $ELAPSED_TIME + $PERIOD` 51 | done 52 | -------------------------------------------------------------------------------- /src/serv.h: -------------------------------------------------------------------------------- 1 | /* List and enable/disable service configurations 2 | * 3 | * Copyright (c) 2017-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_SERV_H_ 25 | #define FINIT_SERV_H_ 26 | 27 | int serv_list (char *arg); 28 | int serv_enable (char *arg); 29 | int serv_disable (char *arg); 30 | int serv_touch (char *arg); 31 | int serv_show (char *arg); 32 | int serv_edit (char *arg); 33 | int serv_creat (char *arg); 34 | int serv_delete (char *arg); 35 | 36 | #endif /* FINIT_SERV_H_ */ 37 | 38 | /** 39 | * Local Variables: 40 | * indent-tabs-mode: t 41 | * c-file-style: "linux" 42 | * End: 43 | */ 44 | -------------------------------------------------------------------------------- /contrib/debian/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This script configures Finit for Debian, which needs a few more plugins 4 | # and tweaks than an embedded system usually does. 5 | # 6 | # Usage: 7 | # user@jessie:~/finit$ contrib/debian/build.sh 8 | # 9 | 10 | echo 11 | echo "*** Configuring Finit for Debian" 12 | 13 | if [ ! -e configure ]; then 14 | echo " The configure script is missing, maybe you're using a version from GIT?" 15 | echo " Attempting to run the autogen.sh script, you will need these tools:" 16 | echo " autoconf, automake, libtool, pkg-config ..." 17 | echo 18 | ./autogen.sh 19 | fi 20 | echo 21 | 22 | # The plugins are optional, but you may need D-Bus and X11 if you want 23 | # to run X-Window, the other configure flags are however required. 24 | ./configure \ 25 | --prefix=/usr --exec-prefix= \ 26 | --sysconfdir=/etc --localstatedir=/var \ 27 | --enable-dbus-plugin --enable-x11-common-plugin \ 28 | --enable-alsa-utils-plugin --with-keventd \ 29 | --with-random-seed=/var/lib/urandom/random-seed 30 | 31 | if [ $? -ne 0 ]; then 32 | echo 33 | echo "*** Configure script failed, have you installed libuEv and libite?" 34 | echo 35 | exit 1 36 | fi 37 | 38 | echo 39 | echo "*** Building ..." 40 | echo 41 | make 42 | 43 | if [ $? -ne 0 ]; then 44 | echo 45 | echo "*** The build failed for some reason" 46 | echo 47 | exit 1 48 | fi 49 | 50 | echo 51 | echo "*** Done" 52 | echo 53 | 54 | read -p "*** Run (sudo) install script (y/N)? " yorn 55 | if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then 56 | sudo contrib/debian/install.sh 57 | else 58 | echo 59 | echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit" 60 | echo 61 | fi 62 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Dotty the Documenteer 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - 'doc/**' 9 | - 'README.md' 10 | - 'mkdocs.yml' 11 | - '.github/workflows/docs.yml' 12 | pull_request: 13 | types: [opened, synchronize, reopened, labeled] 14 | paths: 15 | - 'doc/**' 16 | - 'README.md' 17 | - 'mkdocs.yml' 18 | - '.github/workflows/docs.yml' 19 | 20 | permissions: 21 | contents: read 22 | pages: write 23 | id-token: write 24 | 25 | concurrency: 26 | group: "pages" 27 | cancel-in-progress: false 28 | 29 | jobs: 30 | build: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 # Needed for git-revision-date-localized plugin 37 | 38 | - name: Setup Python 39 | uses: actions/setup-python@v4 40 | with: 41 | python-version: '3.x' 42 | 43 | - name: Install dependencies 44 | run: | 45 | pipx install mkdocs 46 | pipx inject mkdocs mkdocs-material 47 | pipx inject mkdocs pymdown-extensions 48 | pipx inject mkdocs mkdocs-callouts 49 | 50 | - name: Build documentation 51 | run: mkdocs build 52 | 53 | - name: Upload Pages artifact 54 | uses: actions/upload-pages-artifact@v3 55 | with: 56 | path: site/ 57 | 58 | deploy: 59 | if: github.ref == 'refs/heads/master' && github.event_name == 'push' 60 | needs: build 61 | runs-on: ubuntu-latest 62 | environment: 63 | name: github-pages 64 | url: ${{ steps.deployment.outputs.page_url }} 65 | steps: 66 | - name: Deploy to GitHub Pages 67 | id: deployment 68 | uses: actions/deploy-pages@v4 69 | -------------------------------------------------------------------------------- /contrib/alpine/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | printf "\n\e[1m*** This script must run as root\e[0m\n\n" 5 | exit 1 6 | fi 7 | 8 | # shellcheck disable=SC3037,SC3045,SC2154 9 | yorn() 10 | { 11 | printf "\e[1m> %s (y/N)?\e[0m " "$*" 12 | read -rn1 yorn 13 | echo 14 | 15 | if [ "$yorn" = "y" ] || [ "$yorn" = "Y" ]; then 16 | return 0 17 | fi 18 | 19 | return 1 20 | } 21 | 22 | # Adjust base directory 23 | if grep -q contrib/alpine Makefile 2>/dev/null; then 24 | cd ../.. 25 | fi 26 | 27 | if [ ! -e configure.ac ]; then 28 | printf "\n\e[1m*** Please run this script from the Finit base directory.\e[0m\n" 29 | exit 1 30 | fi 31 | 32 | printf "\n\e[1mInstall Finit on Alpine Linux\n" 33 | printf "========================================================================\e[0m\n" 34 | printf "/sbin/finit - PID 1\n" 35 | printf "/lib/finit/plugins/* - All enabled Finit plugins\n" 36 | printf "/etc/finit.conf - Finit configuration file\n" 37 | printf "/etc/finit.d/ - Finit services\n" 38 | 39 | if yorn "Do you want to continue"; then 40 | printf "\n\e[1m*** Installing Finit files ...\e[0m\n" 41 | make install 42 | cd /usr/share/doc/finit/contrib/alpine || exit 1 43 | for file in finit.conf rc.local; do 44 | install -vbD $file /etc/$file 45 | done 46 | cp -va finit.d /etc/ 47 | 48 | if yorn "Install Finit as the system default Init"; then 49 | printf "\e[1m*** Updating /sbin/init symlink --> finit ...\e[0m\n" 50 | cd /sbin || exit 1 51 | rm init 52 | ln -s finit init 53 | 54 | rm halt shutdown suspend 55 | ln -s reboot halt 56 | ln -s reboot shutdown 57 | ln -s reboot suspend 58 | fi 59 | printf "\n\e[1m*** Done\e[0m\n" 60 | echo 61 | else 62 | printf "\n\e[1m*** Aborting install.\e[0m\n\n" 63 | fi 64 | -------------------------------------------------------------------------------- /contrib/debian/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "x`id -u`" != "x0" ]; then 4 | echo 5 | echo "=> Unfortunately this script must run as root (sudo) <=" 6 | echo 7 | exit 1 8 | fi 9 | 10 | # Adjust base directory 11 | grep -q debian Makefile 2>/dev/null 12 | if [ $? -eq 0 ]; then 13 | cd ../.. 14 | elif [ ! -e configure.ac ]; then 15 | echo "*** Please run this script from the Finit base directory." 16 | exit 1 17 | fi 18 | 19 | echo 20 | echo "*** Install Finit on Debian GNU/Linux" 21 | echo "========================================================================" 22 | echo "/sbin/finit - PID 1" 23 | echo "/lib/finit/plugins/* - All enabled Finit plugins" 24 | echo "/etc/finit.conf - Finit configuration file" 25 | echo "/etc/finit.d/ - Finit services" 26 | echo "/etc/grub.d/10_linux - Add Finit to Grub's SUPPORTED_INITS" 27 | echo 28 | read -p "Do you want to continue (y/N)? " yorn 29 | echo 30 | 31 | if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then 32 | echo "Installing Finit files ..." 33 | make install 34 | cd /usr/share/doc/finit/contrib/debian 35 | for file in finit.conf; do 36 | install -vbD $file /etc/$file 37 | done 38 | cp -va finit.d /etc/ 39 | 40 | echo "*** Setting up a GRUB boot entry ..." 41 | fn=/etc/grub.d/10_linux 42 | if [ -e $fn ]; then 43 | if `grep SUPPORTED_INITS $fn |head -1 |grep -q finit`; then 44 | echo "Already installed, done." 45 | else 46 | echo "Adding Finit to list of SUPPORTED_INITS ..." 47 | sed -i 's/SUPPORTED_INITS="[^"]*/& finit:\/sbin\/finit/' $fn 48 | update-grub 49 | fi 50 | else 51 | echo "Cannot find $fn, you'll have to set up your bootloader on your own." 52 | fi 53 | 54 | echo 55 | echo "*** Done" 56 | echo 57 | else 58 | echo 59 | echo "*** Aborting install." 60 | echo 61 | fi 62 | -------------------------------------------------------------------------------- /src/client.h: -------------------------------------------------------------------------------- 1 | /* External client API, using UNIX domain socket. 2 | * 3 | * Copyright (c) 2015-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_CLIENT_H_ 25 | #define FINIT_CLIENT_H_ 26 | 27 | #include "finit.h" 28 | #include "svc.h" 29 | 30 | int client_connect (void); 31 | int client_disconnect (void); 32 | int client_socket (void); 33 | 34 | int client_request (struct init_request *rq, ssize_t len); 35 | int client_send (struct init_request *rq, ssize_t len); 36 | int client_command (int cmd); 37 | 38 | svc_t *client_svc_iterator (int first); 39 | svc_t *client_svc_find (const char *arg); 40 | svc_t *client_svc_find_by_cond (const char *arg); 41 | 42 | #endif /* FINIT_CLIENT_H_ */ 43 | -------------------------------------------------------------------------------- /test/lib/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start a new test environment. 3 | # 4 | # Copyright (c) 2021 Jacques de Laval 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | set -eu 25 | 26 | TEST_DIR=$(dirname "$0"../) 27 | SYSROOT="${SYSROOT:-$(pwd)/${TEST_DIR}/sysroot}" 28 | 29 | unshare=$(command -v unshare) 30 | chroot=$(command -v chroot) 31 | 32 | export container="unshare" 33 | export PS1='\w \$ ' 34 | export PS2='> ' 35 | export PS3='#? ' 36 | export PS4='+ ' 37 | 38 | PATH="$TESTENV_PATH" 39 | export PATH 40 | 41 | # Not supported by Busybox unshare: 42 | # --cgroup --time 43 | exec "$unshare" \ 44 | --user --map-root-user --map-auto \ 45 | --fork --pid --mount-proc \ 46 | --mount \ 47 | --mount-proc \ 48 | --uts --ipc --net \ 49 | "$chroot" "$SYSROOT" /sbin/chrootsetup.sh "$@" 50 | -------------------------------------------------------------------------------- /test/start-stop-serv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | TEST_DIR=$(dirname "$0") 5 | 6 | test_teardown() 7 | { 8 | say "Running test teardown." 9 | run "rm -f $FINIT_CONF" 10 | } 11 | 12 | test_one() 13 | { 14 | pidfn=$1 15 | service=$2 16 | 17 | say "Add service stanza '$service' to $FINIT_CONF ..." 18 | run "echo '$service' > $FINIT_CONF" 19 | 20 | say 'Reload Finit' 21 | #run "initctl debug" 22 | run "initctl reload" 23 | #run "initctl status" 24 | #run "ps" 25 | 26 | #run "initctl status" 27 | #run "ps" 28 | #run "initctl status serv" 29 | 30 | retry 'assert_num_children 1 serv' 31 | retry "assert_has_pidfile $pidfn" 1 32 | 33 | say 'Stop the service' 34 | run "initctl stop serv" 35 | 36 | retry 'assert_num_children 0 serv' 37 | 38 | say 'Start the service again' 39 | run "initctl start serv" 40 | 41 | retry 'assert_num_children 1 serv' 42 | 43 | say "Done, drop service from $FINIT_CONF ..." 44 | run "rm $FINIT_CONF" 45 | run "initctl reload" 46 | } 47 | 48 | # shellcheck source=/dev/null 49 | . "$TEST_DIR/lib/setup.sh" 50 | 51 | test_one "/run/serv.pid" "service pid:!/run/serv.pid serv -- Forking service, type 1" 52 | test_one "/run/serv.pid" "service type:forking serv -- Forking service, type 2" 53 | # This one could never be started by and monitored by Finit: it forks to 54 | # background and does not create a PID file. Essentially it's lost to 55 | # Finit, and any other sane process monitor. 56 | #test_one "/run/serv.pid" "service pid:/run/serv.pid serv -p -- Forking service w/o PID file" 57 | test_one "/run/serv.pid" "service pid:/run/serv.pid serv -n -- Foreground service w/o PID file" 58 | test_one "/run/serv.pid" "service serv -n -p -- Foreground service w/ PID file" 59 | test_one "/run/servy.pid" "service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file" 60 | -------------------------------------------------------------------------------- /test/lib/exec.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Execute a given command from within the test environment. 3 | # 4 | # Copyright (c) 2021 Jacques de Laval 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | set -eu 25 | 26 | if [ "$#" -lt 2 ]; then 27 | echo "Usage:" 28 | echo " $0 [target-pid] [command [arguments]]" 29 | exit 1 30 | fi 31 | target="$1" 32 | shift 33 | 34 | TEST_DIR=$(dirname "$0"../) 35 | SYSROOT="${SYSROOT:-$(pwd)/${TEST_DIR}/sysroot}" 36 | 37 | nsenter=$(command -v nsenter) 38 | chroot=$(command -v chroot) 39 | 40 | export PS1='\w \$ ' 41 | export PS2='> ' 42 | export PS3='#? ' 43 | export PS4='+ ' 44 | 45 | PATH="$TESTENV_PATH" 46 | export PATH 47 | 48 | "$nsenter" \ 49 | --preserve-credentials \ 50 | --user \ 51 | --mount \ 52 | --uts \ 53 | --ipc \ 54 | --net \ 55 | --pid \ 56 | -w -t "$target" \ 57 | "$chroot" "$SYSROOT" "$@" 58 | -------------------------------------------------------------------------------- /src/cond.h: -------------------------------------------------------------------------------- 1 | #ifndef FINIT_COND_H_ 2 | #define FINIT_COND_H_ 3 | 4 | #include 5 | 6 | #include "svc.h" 7 | 8 | #define COND_BASE "finit/cond" 9 | #define COND_DEV "dev/" 10 | #define COND_PID "pid/" 11 | #define COND_SYS "sys/" 12 | #define COND_USR "usr/" 13 | 14 | #define _PATH_COND _PATH_VARRUN COND_BASE "/" 15 | #define _PATH_CONDDEV _PATH_COND COND_DEV 16 | #define _PATH_CONDPID _PATH_COND COND_PID 17 | #define _PATH_CONDSYS _PATH_COND COND_SYS 18 | #define _PATH_CONDUSR _PATH_COND COND_USR 19 | #define _PATH_RECONF _PATH_COND "reconf" 20 | 21 | typedef enum cond_state { 22 | COND_OFF = 0, 23 | COND_FLUX, 24 | COND_ON 25 | } cond_state_t; 26 | 27 | char *mkcond (svc_t *svc, char *buf, size_t len); 28 | const char *condstr (enum cond_state s); 29 | const char *cond_path (const char *name); 30 | unsigned int cond_get_gen (const char *path); 31 | enum cond_state cond_get_path(const char *path); 32 | enum cond_state cond_get (const char *name); 33 | enum cond_state cond_get_agg (const char *names); 34 | int cond_affects (const char *name, const char *names); 35 | 36 | void cond_boot_parse (char *arg); 37 | int cond_update (const char *name); 38 | int cond_set_path (const char *path, enum cond_state new); 39 | void cond_set (const char *name); 40 | void cond_set_oneshot (const char *name); 41 | void cond_clear (const char *name); 42 | void cond_reload (void); 43 | 44 | int cond_set_noupdate(const char *name); 45 | int cond_set_oneshot_noupdate(const char *name); 46 | int cond_clear_noupdate(const char *name); 47 | 48 | void cond_reassert (const char *pat); 49 | void cond_deassert (const char *pat); 50 | 51 | int cond_is_available(void); 52 | 53 | void cond_init (void); 54 | void cond_exit (void); 55 | 56 | #endif /* FINIT_COND_H_ */ 57 | 58 | /** 59 | * Local Variables: 60 | * indent-tabs-mode: t 61 | * c-file-style: "linux" 62 | * End: 63 | */ 64 | -------------------------------------------------------------------------------- /test/start-stop-sysv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=2034 source=/dev/null 3 | 4 | set -eu 5 | 6 | STANZA1="sysv [2345] pid:!/run/service.pid name:service.sh log:stdout \ 7 | /etc/init.d/S01-service.sh -- SysV test service" 8 | STANZA2="sysv [2345] pid:!/run/service.pid name:service.sh log:stdout \ 9 | reload:'/etc/init.d/S01-service.sh reload' \ 10 | stop:'/etc/init.d/S01-service.sh stop' \ 11 | /etc/init.d/S01-service.sh -- SysV test service" 12 | STANZA3="sysv [2345] pid:!/run/service.pid name:service.sh log:stdout \ 13 | reload:'kill -HUP \\\$MAINPID' \ 14 | stop:'kill -TERM \\\$MAINPID' \ 15 | /etc/init.d/S01-service.sh -- SysV test service" 16 | TEST_DIR=$(dirname "$0") 17 | #DEBUG=1 18 | 19 | test_teardown() 20 | { 21 | assert "Running test teardown." "run rm -f $FINIT_RCSD/service.conf" 22 | } 23 | 24 | test_one() 25 | { 26 | say "Add sysv stanza #1 in $FINIT_CONF" 27 | run "echo \"$1\" > $FINIT_RCSD/service.conf" 28 | 29 | say "Reload Finit" 30 | run "initctl reload" 31 | 32 | retry 'assert_num_children 1 service.sh' 33 | retry 'assert_cond service/service.sh/ready' 34 | 35 | sep 'Stop the sysv service' 36 | run "initctl stop service.sh" 37 | 38 | retry 'assert_num_children 0 service.sh' 39 | 40 | sep 'Start the sysv service again' 41 | run "initctl start service.sh" 42 | 43 | retry 'assert_num_children 1 service.sh' 44 | retry 'assert_cond service/service.sh/ready' 45 | 46 | sep 'Touch the sysv service' 47 | run "initctl touch service.conf" 48 | run "initctl reload" 49 | #run "initctl cond dump" 50 | 51 | retry 'assert_num_children 1 service.sh' 52 | retry 'assert_cond service/service.sh/ready' 53 | } 54 | 55 | . "$TEST_DIR/lib/setup.sh" 56 | 57 | sep "―― 1) Basic stop/start/HUP sysv daemon" 58 | test_one "$STANZA1" 59 | 60 | sep "―― 2) Custom stop/reload script" 61 | test_one "$STANZA2" 62 | 63 | sep "―― 3) Custom stop/start/reload with \$MAINPID" 64 | test_one "$STANZA3" 65 | 66 | return 0 67 | -------------------------------------------------------------------------------- /doc/signals.md: -------------------------------------------------------------------------------- 1 | Finit Signals 2 | ============= 3 | 4 | Finit is often used on embedded and small Linux systems with BusyBox. 5 | Though Finit comes with its own tools for (poweroff, halt, reboot), for 6 | compatibility with the existing BusyBox toolset the following signals 7 | have been adopted: 8 | 9 | `SIGHUP` 10 | -------- 11 | 12 | Same effect as `finit q`, `init q`, or `initctl reload`, reloads all 13 | *.conf files in `/etc/finit.d/` 14 | 15 | This also restarts the API (initctl) socket, like SysV init and systemd 16 | does on USR1 with their FIFO/D-Bus. 17 | 18 | 19 | `SIGUSR1` 20 | --------- 21 | 22 | Since Finit 4.1 this signal causes Finit to restart its API (initctl) 23 | socket, like SysV init and systemd does on USR1 with their FIFO/D-Bus. 24 | 25 | Finit <= 4.0 performed a system halt (like USR2 without power-off), but 26 | this caused compatibility problems with systemd and sysvinit on desktop 27 | systems. Hence, since Finit 4.1 it is no longer possible to halt a 28 | system with a signal. 29 | 30 | 31 | `SIGUSR2` 32 | --------- 33 | 34 | Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running 35 | processes, and unmounts all file systems. Then tells kernel to power 36 | off the system, if ACPI or similar exists to actually do this. If the 37 | kernel fails power-off, Finit falls back to halt. 38 | 39 | SysV init N/A, systemd dumps its internal state to log. 40 | 41 | `SIGTERM` 42 | --------- 43 | 44 | Like `SIGUSR2`, but tell kernel to reboot the system when done. 45 | 46 | SysV init N/A, systemd rexecutes itself. 47 | 48 | `SIGINT` 49 | -------- 50 | 51 | Sent from kernel when the CTRL-ALT-DEL key combo is pressed. SysV init 52 | and systemd default to reboot with `shutdown -r`. 53 | 54 | Finit currently forwards this to `SIGTERM`. 55 | 56 | `SIGPWR` 57 | -------- 58 | 59 | Sent from a power daemon, like `powstatd(8)`, on changes to the 60 | UPS status. Traditionally SysV init read /etc/powerstatus and 61 | acted on "OK", "FAIL", or "LOW" and then removed the file. 62 | Finit currently forwards this to `SIGUSR2`. 63 | 64 | -------------------------------------------------------------------------------- /test/skel/sbin/chrootsetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Setup the things (mount /proc, /tmp, etc.) that needs to be set up from 3 | # within the namespace/chroot and then start PID 1. 4 | # 5 | # Copyright (c) 2021 Jacques de Laval 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | set -eu 26 | export PATH="$PATH":/usr/bin:/usr/sbin 27 | 28 | # It would have been nice to mount /dev as devtmpfs, but unfortunately 29 | # that's not possible if you're not privileged. For now tmpfs will 30 | # have to do. 31 | mount -n -t tmpfs none /dev 32 | # Silence BusyBox init 33 | touch /dev/null 34 | 35 | mount -t proc none /proc 36 | mount -t sysfs none /sys 37 | 38 | if [ "$(ls -A /tmp)" ]; then 39 | mkdir -p /tmp.shadow 40 | mount --bind /tmp /tmp.shadow 41 | mount -t tmpfs none /tmp 42 | cp -a /tmp.shadow/* /tmp/ 43 | else 44 | mount -t tmpfs none /tmp 45 | fi 46 | 47 | mkdir -p "$FINIT_RCSD" 48 | 49 | tty=/dev/$(cat /sys/class/tty/console/active) 50 | mkfifo "$tty" 51 | 52 | exec "$@" 53 | -------------------------------------------------------------------------------- /test/unexpected-restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # A service (B) going into flux, 'initctl reload' with no changes to 3 | # activate, should do not cause ripple effects removing any service 4 | # conditions, e.g., . During reload all services 5 | # are paused while Finit checks for any service3 changes to activate, 6 | # which may mean stopping or restarting existing services. 7 | # 8 | # Issue #382 9 | set -e 10 | 11 | #DEBUG=true 12 | TEST_DIR=$(dirname "$0") 13 | 14 | test_setup() 15 | { 16 | run "cat >> $FINIT_CONF" < name:B serv -np -i B -- B needs A (systemd) 19 | service log:stdout notify:s6 name:C serv -np -i C -N %n -- C needs B (s6) 20 | service log:stdout notify:none name:D type:forking serv -i D -- D needs A (forking) 21 | task name:allup initctl cond set allup -- Everything is up 22 | EOF 23 | } 24 | 25 | pidof() 26 | { 27 | texec initctl -j status "$1" | jq .pid 28 | } 29 | 30 | # shellcheck source=/dev/null 31 | . "$TEST_DIR/lib/setup.sh" 32 | 33 | sep "$FINIT_CONF" 34 | run "cat $FINIT_CONF" 35 | sep 36 | run "initctl reload" 37 | run "initctl status" 38 | run "initctl cond dump" 39 | sep 40 | 41 | run "initctl debug" 42 | say "waiting for primary startup to complete" 43 | retry 'assert_status allup "done"' 10 1 44 | assert_status C "running" 45 | oldpid=$(pidof C) 46 | 47 | sep "pre-reload status" 48 | run "initctl status" 49 | run "initctl cond dump" 50 | sep 51 | 52 | say "Reload Finit, who gets restarted?" 53 | #run "initctl debug" 54 | run "initctl reload" 55 | sleep 2 56 | 57 | sep "post-reload status" 58 | run "initctl status" 59 | run "initctl cond dump" 60 | sep 61 | 62 | assert_status A "running" 63 | assert_status B "running" 64 | assert_status C "running" 65 | 66 | newpid=$(pidof C) 67 | # shellcheck disable=SC2086 68 | assert "C was not restarted" $oldpid -eq $newpid 69 | -------------------------------------------------------------------------------- /src/tty.h: -------------------------------------------------------------------------------- 1 | /* Finit TTY handling 2 | * 3 | * Copyright (c) 2013 Mattias Walström 4 | * Copyright (c) 2013-2025 Joachim Wiberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifndef FINIT_TTY_H_ 26 | #define FINIT_TTY_H_ 27 | 28 | #include 29 | #include "svc.h" 30 | 31 | struct tty { 32 | char *cmd; 33 | char *args[MAX_NUM_SVC_ARGS]; 34 | size_t num; 35 | 36 | char *dev; 37 | char *baud; 38 | char *term; 39 | char noclear; 40 | char nowait; 41 | char nologin; 42 | char notty; 43 | char rescue; 44 | }; 45 | 46 | char *tty_canonicalize (char *dev); 47 | 48 | int tty_isatcon (char *dev); 49 | char *tty_atcon (void); 50 | 51 | int tty_parse_args (struct tty *tty, char *cmd, char **args); 52 | 53 | int tty_exists (char *dev); 54 | int tty_exec (svc_t *tty); 55 | 56 | #endif /* FINIT_TTY_H_ */ 57 | 58 | /** 59 | * Local Variables: 60 | * indent-tabs-mode: t 61 | * c-file-style: "linux" 62 | * End: 63 | */ 64 | -------------------------------------------------------------------------------- /.github/CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Code of Conduct 2 | =========================== 3 | 4 | As contributors and maintainers of this project, and in the interest of 5 | fostering an open and welcoming community, we pledge to respect all 6 | people who contribute through reporting issues, posting feature 7 | requests, updating documentation, submitting pull requests or patches, 8 | and other activities. 9 | 10 | We are committed to making participation in this project a 11 | harassment-free experience for everyone, regardless of level of 12 | experience, gender, gender identity and expression, sexual orientation, 13 | disability, personal appearance, body size, race, ethnicity, age, 14 | religion, or nationality. 15 | 16 | Examples of unacceptable behavior by participants include: 17 | 18 | * The use of sexualized language or imagery 19 | * Personal attacks 20 | * Trolling or insulting/derogatory comments 21 | * Public or private harassment 22 | * Publishing other's private information, such as physical or electronic 23 | addresses, without explicit permission 24 | * Other unethical or unprofessional conduct. 25 | 26 | Project maintainers have the right and responsibility to remove, edit, 27 | or reject comments, commits, code, wiki edits, issues, and other 28 | contributions that are not aligned to this Code of Conduct. By adopting 29 | this Code of Conduct, project maintainers commit themselves to fairly 30 | and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code 32 | of Conduct may be permanently removed from the project team. 33 | 34 | This code of conduct applies both within project spaces and in public 35 | spaces when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may 38 | be reported by opening an issue or contacting one or more of the project 39 | maintainers. 40 | 41 | This Code of Conduct is adapted from the [Contributor Covenant][1], 42 | [version 1.2.0][2]. 43 | 44 | [1]: http://contributor-covenant.org 45 | [2]: http://contributor-covenant.org/version/1/2/0/ 46 | -------------------------------------------------------------------------------- /test/depserv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify handling of dependent services, bug #314 3 | # Two services: foo and bar, where bar depends on foo: 4 | # 5 | # - bar must not start until foo is ready 6 | # - bar must be stopped when foo goes down 7 | # - bar must be restarted when foo is restarted 8 | # 9 | # Regression test bug #314, that bar is not in a restart loop when foo 10 | # is stopped. Also, verify that changing between runlevels, where foo 11 | # is not allowed to run, but bar is, that bar is stopped also for that. 12 | 13 | set -eu 14 | 15 | TEST_DIR=$(dirname "$0") 16 | 17 | test_teardown() 18 | { 19 | say "Running test teardown." 20 | 21 | run "rm -f $FINIT_CONF" "/tmp/post" 22 | } 23 | 24 | # shellcheck source=/dev/null 25 | . "$TEST_DIR/lib/setup.sh" 26 | 27 | #run "initctl debug" 28 | #run "ls -l /run/finit/cond/pid/ /lib/finit/plugins/" 29 | 30 | test_one() 31 | { 32 | cond=$1 33 | say "Verifying foo -> <$cond> bar ..." 34 | run "echo service log:stderr name:foo serv -np -i foo -f /tmp/arrakis > $FINIT_CONF" 35 | run "echo service log:stderr name:bar \ serv -np -i bar -F /tmp/arrakis >> $FINIT_CONF" 36 | run "cat $FINIT_CONF" 37 | 38 | say 'Reload Finit' 39 | run "initctl reload" 40 | 41 | run "initctl status" 42 | assert_status "foo" "running" 43 | assert_cond "$cond" 44 | assert_status "bar" "running" 45 | 46 | say "Verify bar is restarted when foo is ..." 47 | pid=$(texec initctl |grep bar | awk '{print $1;}') 48 | run "initctl restart foo" 49 | run "initctl status" 50 | assert "bar is restarted" "$(texec initctl |grep bar | awk '{print $1;}')" != "$pid" 51 | 52 | # Wait for spice to be stolen by the Harkonnen 53 | sleep 3 54 | # bar should now have detected the loss of spice and be in restart 55 | run "initctl status" 56 | assert_status "bar" "restart" 57 | # verify bar is stopped->waiting and no longer in restart after stopping foo 58 | run "initctl stop foo" 59 | run "initctl status" 60 | run "initctl status bar" 61 | assert_status "bar" "waiting" 62 | } 63 | 64 | sep 65 | test_one "pid/foo" 66 | sep 67 | run "initctl debug" 68 | test_one "service/foo/running" 69 | -------------------------------------------------------------------------------- /doc/runparts.md: -------------------------------------------------------------------------------- 1 | Runparts & `/etc/rc.local` 2 | ========================== 3 | 4 | At the end of the boot, when all bootstrap (`S`) tasks and services have 5 | started, but not networking, Finit calls its built-in [run-parts(8)][] 6 | command on any configured `runparts ` directory. This happens just 7 | before changing to the configured runlevel (default 2). (Networking is 8 | enabled just prior to changing from single user mode.) 9 | 10 | ```shell 11 | runparts /etc/rc.d/ 12 | ``` 13 | 14 | Right after the runlevel change when all services have started properly, 15 | `/etc/rc.local` is called. 16 | 17 | No configuration stanza in `/etc/finit.conf` is required for `rc.local`. 18 | If it exists and is an executable shell script Finit calls it at the very 19 | end of the boot, before calling the `HOOK_SYSTEM_UP`. See more in the 20 | [Hook Scripts](plugins.md#hooks) section. 21 | 22 | 23 | ### Limitations 24 | 25 | It is not possible to call Finit via signals or use `initctl` in any 26 | runparts or `/etc/rc.local` script. This because Finit is single 27 | threaded and is calling these scripts in a blocking fashion at the end 28 | of runlevel S, at which point the event loop has not yet been started. 29 | 30 | The event loop is the whole thing which Finit is built around, except 31 | for runlevel S, which remains a slow procession through a lot of set up, 32 | with a few hooks and blocking call outs to external scripts. 33 | 34 | However, not all `initctl` commands are prohibited. Supported commands: 35 | 36 | - `initctl cond`: only operate on files in `/run/finit/cond` 37 | - `initctl enable/disable`: enabled run/task/service is activated on 38 | the runlevel change from S to 2 39 | - `initctl touch/show/create/delete/list`: `create`, provided the 40 | non-interactive mode is used, again changes take effect in the 41 | runlevel change directly after bootstrap 42 | - `initctl -f reboot/poweroff/halt`: provided the `-f` flag is used to 43 | force direct kernel commands 44 | 45 | > [!TIP] 46 | > you can set a `usr/` condition in `/etc/rc.local` and have a 47 | > service/task in runlevel 2 depend on it to execute. 48 | 49 | [run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts 50 | -------------------------------------------------------------------------------- /plugins/x11-common.c: -------------------------------------------------------------------------------- 1 | /* Console setup (for X) 2 | * 3 | * Copyright (c) 2012-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #ifdef _LIBITE_LITE 28 | # include 29 | #else 30 | # include 31 | #endif 32 | 33 | #include "finit.h" 34 | #include "helpers.h" 35 | #include "plugin.h" 36 | #include "conf.h" 37 | #include "log.h" 38 | 39 | static void setup(void *arg) 40 | { 41 | if (rescue) { 42 | dbg("Skipping %s plugin in rescue mode.", __FILE__); 43 | return; 44 | } 45 | 46 | if (fexist("/var/run/console/console.lock") && which("pam_console_apply")) 47 | run("pam_console_apply", "pam-console"); 48 | } 49 | 50 | static plugin_t plugin = { 51 | .name = __FILE__, 52 | .hook[HOOK_SVC_PLUGIN] = { .cb = setup }, 53 | }; 54 | 55 | PLUGIN_INIT(__init) 56 | { 57 | plugin_register(&plugin); 58 | } 59 | 60 | PLUGIN_EXIT(__exit) 61 | { 62 | plugin_unregister(&plugin); 63 | } 64 | 65 | /** 66 | * Local Variables: 67 | * indent-tabs-mode: t 68 | * c-file-style: "linux" 69 | * End: 70 | */ 71 | -------------------------------------------------------------------------------- /contrib/alpine/README.md: -------------------------------------------------------------------------------- 1 | HowTo: Finit on Alpine Linux 3.4...3.19 2 | ======================================= 3 | 4 | > [!TIP] 5 | > https://troglobit.com/post/2021-02-12-alpine-linux-with-finit/ 6 | 7 | HowTo use Finit to boot an [Alpine Linux][] system. It is assumed that 8 | the user has already installed make, a compiler, C library header files, 9 | and other tools needed to build a GNU configure based project. 10 | 11 | To start with you need to first install [libuEv][] and [libite][]. They 12 | default to install to `/usr/local`, but unlike Debian and Ubuntu based 13 | distros, Alpine's `pkg-config` does not look for libraries and header 14 | files there. So the `PKG_CONFIG_LIBDIR` environment variable must be 15 | used, or change the install prefix to `/usr`. 16 | 17 | The bundled [build.sh](build.sh) script can be used to configure and 18 | build finit: 19 | 20 | alpine:~# cd finit 21 | alpine:~/finit# ./contrib/alpine/build.sh 22 | 23 | Then run the [install.sh](install.sh) script to install all necessary 24 | files, including the sample `finit.conf` and `finit.d/*.conf` files. 25 | More on that below. 26 | 27 | alpine:~/finit# ./contrib/alpine/install.sh 28 | 29 | The install script is non-destructive by default, you have to answer 30 | *Yes* twice to set up Finit as the system default init. Pay close 31 | attention to the last question: 32 | 33 | *** Install Finit as the system default Init (y/N)? 34 | 35 | If you answer `No`, simply by pressing enter, you can change the symlink 36 | yourself later on, to point to `finit` instead of `/bin/busybox`: 37 | 38 | alpine:~/finit# cd /sbin 39 | alpine:/sbin# rm init 40 | alpine:/sbin# ln -s finit init 41 | 42 | Before rebooting, make sure to set up a [/etc/finit.conf](finit.conf), 43 | and [/etc/finit.d/](finit.d/) for your services. Samples are included 44 | in this directory. Notice the symlinks in `/etc/finit.d/`, which can be 45 | managed by the operator at runtime using `initctl enable SERVICE`. You 46 | can also use a standard [/etc/rc.local](rc.local) for one-shot tasks and 47 | initialization like keyboard language etc. 48 | 49 | [libuEv]: https://github.com/troglobit/libuev 50 | [libite]: https://github.com/troglobit/libite 51 | [Alpine Linux]: https://www.alpinelinux.org/ 52 | -------------------------------------------------------------------------------- /doc/watchdog.md: -------------------------------------------------------------------------------- 1 | Bundled Watchdog Daemon 2 | ======================= 3 | 4 | When built `--with-watchdog` a separate service is built and installed 5 | in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT 6 | device node exists, Finit will start it and treat it as the elected 7 | watchdog service to delegate its reboot to. This delegation is to 8 | ensure that the system is rebooted by a hardware watchdog timer -- on 9 | many embedded systems this is crucial to ensure all circuits on the 10 | board are properly reset for the next boot, in effect ensuring the 11 | system works the same after both a power-on and reboot event. 12 | 13 | > [!NOTE] 14 | > The watchdog reboot delegation can be enabled with the `reboot-watchdog` 15 | > configuration option in `/etc/finit.conf`. By default this is disabled 16 | > and the system reboots directly via the SoC using the kernel's `reboot(2)` 17 | > syscall. See [Runlevels](config/runlevels.md) for details. 18 | 19 | The delegation is performed at the very last steps of system shutdown, 20 | if reboot has been selected, `reboot-watchdog` is enabled, and an elected 21 | watchdog is known. First a `SIGPWR` is sent to advise watchdogd of the 22 | pending reboot. Then, when the necessary steps of preparing the system 23 | for shutdown (umount etc.) are completed, Finit sends `SIGTERM` to 24 | watchdogd and puts itself in a 10 sec timeout loop waiting for the WDT 25 | to reset the board. If a reset is not done before the timeout, Finit 26 | falls back to `reboot(RB_AUTOBOOT)` which tells the kernel to do the 27 | reboot. 28 | 29 | An external watchdog service can also be used. The more advanced cousin 30 | [watchdogd](https://github.com/troglobit/watchdogd/) is the recommended 31 | option here. It can register itself with Finit using the same IPC as 32 | `initctl`. If the bundled watchdogd is running a hand-over takes place, 33 | so it's safe to have both services installed on a system. For the 34 | hand-over to work it requires that the WDT driver supports the safe exit 35 | functionality where `"V"` is written to the device before closing the 36 | device descriptor. If the kernel driver has been built without this, 37 | the only option is to remove `/libexec/finit/watchdogd` or build without 38 | it at configure time. 39 | -------------------------------------------------------------------------------- /test/cond-start-task.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # A run/task runs once per runlevel, unless its .conf is touched 3 | # then it will run again on initctl reload. This test verifies 4 | # this behavior, with the added twist of starting the task by 5 | # asserting a condition. 6 | set -eu 7 | 8 | TEST_DIR=$(dirname "$0") 9 | 10 | test_teardown() 11 | { 12 | say "Running test teardown." 13 | run "rm -f $TEST_CONF" 14 | } 15 | 16 | test_one() 17 | { 18 | cond=$1 19 | stanza=$2 20 | 21 | sleep 5 22 | say 'Enable finit debug, try to figure out what is going on ...' 23 | run "initctl debug" 24 | 25 | say "Add stanza '$stanza' to $TEST_CONF ..." 26 | run "echo '$stanza' > $TEST_CONF" 27 | 28 | run "ls -l /run/ /run/finit/" 29 | say 'Reload Finit' 30 | run "initctl ls" 31 | run "initctl reload" 32 | run "initctl status" 33 | run "ps" 34 | 35 | date 36 | sleep 2 37 | date 38 | run "ls -l /run/ /run/finit/" 39 | say 'Asserting condition' 40 | run "initctl cond set $cond" 41 | 42 | date 43 | sleep 2 44 | date 45 | run "ls -l /run/ /run/finit/" 46 | run "initctl status" 47 | run "ps" 48 | run "initctl status task.sh" 49 | 50 | sleep 1 51 | run "echo Hej; cat /run/task.state" 52 | 53 | assert_file_contains /run/task.state 1 54 | 55 | say 'Reload Finit' 56 | run "initctl reload" 57 | sleep 1 58 | say 'Ensure task has not run again ...' 59 | assert_file_contains /run/task.state 1 60 | 61 | say 'Switch to another runlevel ...' 62 | run "initctl runlevel 3" 63 | sleep 1 64 | say 'Ensure task has run again ...' 65 | assert_file_contains /run/task.state 2 66 | 67 | say 'Touch task.sh .conf file and reload Finit ...' 68 | run "touch $TEST_CONF" 69 | run "initctl reload" 70 | sleep 1 71 | # run "initctl status task.sh" 72 | assert_file_contains /run/task.state 3 73 | 74 | say "Done, drop stanza from $TEST_CONF ..." 75 | run "rm $TEST_CONF" 76 | run "initctl reload" 77 | 78 | # Done, disable debug 79 | run "initctl debug" 80 | } 81 | 82 | 83 | # shellcheck source=/dev/null 84 | . "$TEST_DIR/lib/setup.sh" 85 | 86 | TEST_CONF=$FINIT_RCSD/cond.conf 87 | 88 | test_one "hello" "task task.sh -- Hello task" 89 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | Goals of Finit 2 | ============== 3 | 4 | Initially Finit aimed to be more than a processes monitor, acting as a 5 | replacement for cron/at and inetd as well. Over time, however, the true 6 | value in the project turned out to be: 7 | 8 | > A simple SysV init and systemd replacement, primarily for embedded 9 | > systems, container, and dedicated server applications. 10 | 11 | 12 | Misc TODOs 13 | ---------- 14 | 15 | Some of these are also registered in the issue tracker on GitHub. 16 | 17 | * Refactor the .conf parser, parse user input in a separate process 18 | * Move process monitor to separate process (same as parser?) 19 | * Add new (complimentary) .svc file format to slowly migrate away 20 | from the very terse one-liner format currently used 21 | * Allow running as non-pid1 => read .conf and RCSD form cmdline 22 | * Add `finit.conf` support for UPS notification (SIGPWR) to start a task 23 | using, e.g. conditions. More info in sig.c 24 | 25 | As of Finit v4.1 SIGPWR generates condition and it 26 | is up to a task to check why and take appropriate action. 27 | * Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest, 28 | i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task, 29 | e.g, and like SIGPWR handling. 30 | 31 | As of Finit v4.1 SIGINT generates , but there is 32 | no check if the signal actually originates from the kernel. To get 33 | this verification, a new libuEv release is needed. As of June 6 -21 34 | that functionality is not yet released. 35 | 36 | 37 | Initctl 38 | ------- 39 | 40 | * Add support for timed shutdown in Finit, including cancelled shutdown: 41 | 42 | shutdown -h 03:35 "foobar was here" 43 | 44 | * Add support for JSON output, or similar, from `initctl show`, e.g. 45 | `initctl show --json` 46 | * Add support for `-s` to redirect all output to syslog, for scripting 47 | 48 | 49 | Init 50 | ---- 51 | 52 | Finit in itself is not SysV Init compatible, it doesn't use `/etc/rc.d` 53 | and `/etc/init.d` scripts to boot the system. However, a plausible way 54 | to achieve compatibility would be to add a plugin to finit which reads 55 | `/etc/inittab` to be able to start standard Linux systems. 56 | 57 | -------------------------------------------------------------------------------- /src/schedule.c: -------------------------------------------------------------------------------- 1 | /* Work queue helper functions 2 | * 3 | * Copyright (c) 2018-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include "config.h" 25 | #include "private.h" 26 | #include "log.h" 27 | #include "schedule.h" 28 | 29 | #define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */ 30 | 31 | /* 32 | * libuEv callback wrapper 33 | */ 34 | static void cb(uev_t *w, void *arg, int events) 35 | { 36 | struct wq *work = (struct wq *)arg; 37 | 38 | if (UEV_ERROR == events) { 39 | dbg("%s(): spurious problem with schedule work timer, restarting.", __func__); 40 | uev_timer_start(w); 41 | return; 42 | } 43 | 44 | work->cb(work); 45 | } 46 | 47 | /* 48 | * Place work on event queue 49 | */ 50 | int schedule_work(struct wq *work) 51 | { 52 | int msec; 53 | 54 | if (!work) 55 | return errno = EINVAL; 56 | 57 | msec = work->delay; 58 | if (work->init != SC_INIT) { 59 | work->init = SC_INIT; 60 | return uev_timer_init(ctx, &work->watcher, cb, work, msec, 0); 61 | } 62 | 63 | return uev_timer_set(&work->watcher, msec, 0); 64 | } 65 | 66 | /** 67 | * Local Variables: 68 | * indent-tabs-mode: t 69 | * c-file-style: "linux" 70 | * End: 71 | */ 72 | -------------------------------------------------------------------------------- /.github/workflows/weekly.yml: -------------------------------------------------------------------------------- 1 | name: Weekly Distcheck 2 | 3 | # Verify that `make distcheck` works as intended: all tests pass, and 4 | # any new files are distributed in the tarballs 5 | on: 6 | schedule: 7 | - cron: '0 0 * * 0' 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | schedule: 15 | runs-on: ubuntu-latest 16 | outputs: 17 | run: ${{ steps.check.outputs.run }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Check for any commits since last week 21 | id: schedule 22 | run: | 23 | commits="$(git log --format=%H --since='1 week ago')" 24 | if [ -n "$commits" ]; then 25 | echo "run=true" >> $GITHUB_OUTPUT 26 | else 27 | echo "run=false" >> $GITHUB_OUTPUT 28 | fi 29 | distcheck: 30 | needs: schedule 31 | if: ${{ needs.schedule.outputs.run == 'true' }} 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Install dependencies 35 | run: | 36 | sudo apt-get -y update 37 | sudo apt-get -y install pkg-config jq libcap-dev 38 | wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz 39 | wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz 40 | tar xf libuev-2.4.1.tar.xz 41 | tar xf libite-2.6.2.tar.gz 42 | (cd libuev-2.4.1 && ./configure --prefix=/usr && make && sudo make install-strip) 43 | (cd libite-2.6.2 && ./configure --prefix=/usr && make && sudo make install-strip) 44 | - uses: actions/checkout@v4 45 | - name: Base run 46 | run: | 47 | ./autogen.sh 48 | ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var 49 | make -j9 V=1 50 | - name: Install to /tmp 51 | run: | 52 | DESTDIR=/tmp make install-strip 53 | - name: Enable unprivileged userns (unshare) 54 | run: | 55 | sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 56 | - name: Check dist tarball 57 | run: | 58 | ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}') 59 | dir=finit-$ver/_build/sub 60 | make -j1 distcheck || (cat $dir/test/test-suite.log; false) 61 | -------------------------------------------------------------------------------- /doc/config/runparts.md: -------------------------------------------------------------------------------- 1 | Run-parts Scripts 2 | ----------------- 3 | 4 | **Syntax:** `runparts [progress] [sysv] ` 5 | 6 | Call [run-parts(8)][] on `DIR` to run start scripts. All executable 7 | files in the directory are called, in alphabetic order. The scripts in 8 | this directory are executed at the very end of runlevel `S`. 9 | 10 | A common use-case for runparts scripts is to create and enable/disable 11 | services, which Finit will then apply when changing runlevel from S to 12 | whatever the next runlevel is set to be (default 2). E.g., generate a 13 | `/etc/chrony.conf` and call `initctl enable chronyd`. 14 | 15 | **Options:** 16 | 17 | - `progress`: display the progress of each script being executed 18 | - `sysv`: run only SysV style scripts, i.e., `SNNfoo`, or `KNNbar`, 19 | where `NN` is a number (0-99). 20 | 21 | If global debug mode is enabled, the `runparts` program is also called 22 | with the debug flag. 23 | 24 | **Limitations:** 25 | 26 | Scripts called from `runparts`, or hook scripts (see below), are limited 27 | in their interaction with Finit. Like the standalone `run` stanza and 28 | the `/etc/rc.local` shell script, Finit waits for their completion 29 | before continuing. None of them can issue commands to start, stop, or 30 | restart other services. Also, ensure all your services and programs 31 | either terminate or start in the background or you will block Finit. 32 | 33 | > [!NOTE] 34 | > `runparts` scripts are only read and executed in runlevel S. See 35 | > [hook scripts](../plugins.md#hooks) for other ways to run scripts at 36 | > certain points during the complete lifetime of the system. 37 | 38 | **Recommendations:** 39 | 40 | It can be beneficial to use `01-name`, `02-othername`, etc., to ensure 41 | the scripts are started in that order, e.g., if there is a dependency 42 | order between scripts. Symlinks to existing daemons can talso be used, 43 | but make sure they daemonize (background) themselves properly, otherwise 44 | Finit will lock up. 45 | 46 | If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will 47 | be called with an extra argument, `start` and `stop`, respectively. 48 | E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo` 49 | and `K01foo` may be a symlink to to `another/directory/foo`. 50 | 51 | [run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts 52 | -------------------------------------------------------------------------------- /src/utmp-api.h: -------------------------------------------------------------------------------- 1 | /* UTMP API 2 | * 3 | * Copyright (c) 2016-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_UTMP_API_H_ 25 | #define FINIT_UTMP_API_H_ 26 | 27 | #include 28 | #include 29 | 30 | #ifndef _PATH_BTMP 31 | #define _PATH_BTMP "/var/log/btmp" 32 | #endif 33 | 34 | int utmp_set (int type, int pid, char *line, char *id, char *user); 35 | int utmp_set_boot (void); 36 | int utmp_set_halt (void); 37 | int utmp_set_init (char *tty, char *id); 38 | int utmp_set_login (char *tty, char *id); 39 | int utmp_set_dead (int pid); 40 | int utmp_set_runlevel(int pre, int now); 41 | int utmp_show (char *file); 42 | 43 | void runlevel_set (int pre, int now); 44 | 45 | /* 46 | * musl libc default to /dev/null/utmp and /dev/null/wtmp, respectively. 47 | * See https://www.openwall.com/lists/musl/2012/03/04/4 for reasoning. 48 | * Also, there's no __MUSL__, so we cannot make a libc-specific #ifdef 49 | */ 50 | static inline int has_utmp(void) 51 | { 52 | return strncmp(_PATH_UTMP, "/dev/null", 9); 53 | } 54 | 55 | #endif /* FINIT_UTMP_API_H_ */ 56 | 57 | /** 58 | * Local Variables: 59 | * indent-tabs-mode: t 60 | * c-file-style: "linux" 61 | * End: 62 | */ 63 | -------------------------------------------------------------------------------- /doc/config/logging.md: -------------------------------------------------------------------------------- 1 | General Logging 2 | =============== 3 | 4 | **Syntax:** `log size:200k count:5` 5 | 6 | Log rotation for run/task/services using the `log` sub-option with 7 | redirection to a log file. Global setting, applies to all services. 8 | 9 | The size can be given as bytes, without a specifier, or in `k`, `M`, 10 | or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables 11 | log rotation. The default is `200k`. 12 | 13 | The count value is recommended to be between 1-5, with a default 5. 14 | Setting count to 0 means the logfile will be truncated when the MAX 15 | size limit is reached. 16 | 17 | Redirecting Output 18 | ------------------ 19 | 20 | The `run`, `task`, and `service` stanzas also allow the keyword `log` to 21 | redirect `stderr` and `stdout` of the application to a file or syslog 22 | using the native `logit` tool. This is useful for programs that do not 23 | support syslog on their own, which is sometimes the case when running 24 | in the foreground. 25 | 26 | The full syntax is: 27 | 28 | log:/path/to/file 29 | log:prio:facility.level,tag:ident 30 | log:console 31 | log:null 32 | log 33 | 34 | Default `prio` is `daemon.info` and default `tag` is the basename of the 35 | service or run/task command. 36 | 37 | Log rotation is controlled using the global `log` setting. 38 | 39 | **Example:** 40 | 41 | service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon 42 | 43 | Output Buffering 44 | ---------------- 45 | 46 | When using the `log` directive, Finit redirects the service's stdout and 47 | stderr to a pipe connected to a logger process. Programs detect this as 48 | non-interactive output (i.e., `isatty()` returns false) and typically 49 | switch from line-buffered to fully-buffered mode. 50 | 51 | Most well-behaved daemons explicitly flush their output or use syslog 52 | directly, so this is rarely an issue. However, if a service's log 53 | messages appear delayed or batched, you can force line-buffered output 54 | by wrapping the command with `stdbuf`: 55 | 56 | service log /usr/bin/stdbuf -oL /path/to/command -- My service 57 | 58 | The `-oL` option forces line-buffered output, and `-o0` forces unbuffered 59 | output. See `stdbuf(1)` for details. 60 | 61 | > [!NOTE] 62 | > Using `stdbuf` is rarely necessary. Only use it if you observe actual 63 | > buffering issues with a specific service. 64 | -------------------------------------------------------------------------------- /doc/service.md: -------------------------------------------------------------------------------- 1 | Starting & Monitoring 2 | ===================== 3 | 4 | Finit can start and monitor the following types of daemons: 5 | 6 | * Forks to background, creates a PID file 7 | * Runs in foreground and signals ready by: 8 | - creating a PID file 9 | - does not create a PID file -- Finit can create it for you (optional) 10 | - [other mechanism][1] (systemd, s6) 11 | 12 | Finit can *not* start and monitor a daemon that: 13 | 14 | * Forks to background and does *not* create a PID file 15 | 16 | | | Forking | Creates PID File | Finit creates PID File | 17 | |---|---------|------------------|------------------------| 18 | | ✔ | Yes | Yes | No | 19 | | ✔ | No | Yes | No | 20 | | ✔ | No | No | Yes, optionally | 21 | | ✘ | Yes | No | No | 22 | 23 | > [!NOTE] 24 | > PID files is one mechanism used to assert conditions to synchronize 25 | > the start and stop of other, dependent, services. Other mechanisms 26 | > are described in the [Service Synchronization][1] section. 27 | 28 | ### Forks to bg w/ PID file 29 | 30 | There are two syntax variants, type 1 and type 2. The former is the 31 | traditional one used also for `sysv` start/stop scripts, and the latter 32 | is inspired by systemd, with a twist -- it lets Finit guess the pifdile 33 | to look for based on the standard path and the basename of the command. 34 | 35 | service pid:!/run/serv.pid serv -- Forking service, type 1 36 | service type:forking serv -- Forking service, type 2 37 | 38 | In this example the resulting files to watch for are `/run/serv.pid` and 39 | `/var/run/serv.pid`, respectively. On most modern Linux systems this is 40 | the same directory (`/var/run` is a symlink to `../run`). 41 | 42 | ### Runs in fg w/ PID file 43 | 44 | service serv -n -p -- Foreground service w/ PID file 45 | 46 | ### Runs in fg w/o PID file 47 | 48 | Same as previous, but we tell Finit to create the PID file, because we 49 | need it to synchronize start/stop of a dependent service. 50 | 51 | service pid:/run/serv.pid serv -n -- Foreground service w/o PID file 52 | 53 | ### Runs in fg w/ custom PID file 54 | 55 | service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file 56 | 57 | [1]: config/service-sync.md 58 | -------------------------------------------------------------------------------- /src/cgroup.h: -------------------------------------------------------------------------------- 1 | /* Finit control group support functions 2 | * 3 | * Copyright (c) 2019-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_CGROUP_H_ 25 | #define FINIT_CGROUP_H_ 26 | 27 | #include 28 | 29 | /* Forward declaration */ 30 | typedef struct svc svc_t; 31 | 32 | struct cgroup { 33 | char name[16]; 34 | char cfg[128]; 35 | char leafname[128]; 36 | char delegate; 37 | }; 38 | 39 | void cgroup_mark_all(void); 40 | void cgroup_cleanup (void); 41 | 42 | int cgroup_add (char *name, char *cfg, int is_protected); 43 | int cgroup_del (char *dir); 44 | int cgroup_del_svc (svc_t *svc, const char *name); 45 | void cgroup_config (void); 46 | 47 | void cgroup_init (uev_ctx_t *ctx); 48 | 49 | int cgroup_user (const char *name, int pid); 50 | int cgroup_service (const char *name, int pid, struct cgroup *cg, char *username, char *group); 51 | 52 | char *cgroup_svc_name(svc_t *svc, char *buf, size_t len); 53 | int cgroup_prepare (svc_t *svc, const char *name); 54 | int cgroup_watch (const char *group, const char *name); 55 | 56 | int cgroup_move_pid(const char *group, const char *name, int pid, int delegate); 57 | int cgroup_move_svc(svc_t *svc); 58 | 59 | void cgroup_prune (void); 60 | 61 | #endif /* FINIT_CGROUP_H_ */ 62 | -------------------------------------------------------------------------------- /doc/config/rescue.md: -------------------------------------------------------------------------------- 1 | Rescue Mode 2 | =========== 3 | 4 | Finit supports a rescue mode which is activated by the `rescue` option 5 | on the kernel command line. See [cmdline docs](../cmdline.md) for how to 6 | activate it. 7 | 8 | This rescue mode can be disabled at configure time using: 9 | 10 | configure --without-rescue 11 | 12 | The rescue mode comes in two flavors; *traditional* and *fallback*. 13 | 14 | > [!NOTE] 15 | > In this mode `initctl` will not work. Use the `-f` flag to force 16 | > `reboot`, `shutdown`, or `poweroff`. 17 | 18 | Traditional 19 | ----------- 20 | 21 | This is what most users expect. A very early maintenance login prompt, 22 | served by the system `sulogin` program from util-linux, or BusyBox. If 23 | that is not found in `$PATH`, the bundled `/libexec/finit/sulogin` 24 | program is used instead. If a successful login is made, or if the user 25 | exits (Ctrl-D), the rescue mode is ended and the system boots up 26 | normally. 27 | 28 | > [!WARNING] 29 | > The bundled sulogin in Finit can at configure time be given another 30 | > user than the default (root). If the sulogin user does not have a 31 | > password, or __the account is locked__, the user is presented with a 32 | > prompt: `"Press enter to enter maintenance mode."`, which will open 33 | > up a root shell *without prompting for password*! 34 | 35 | Fallback 36 | -------- 37 | 38 | If no `sulogin` program is found, Finit tries to bring up as much of its 39 | own functionality as possible, yet limiting many aspects, meaning; no 40 | network, no `fsck` of file systems in `/etc/fstab`, no `/etc/rc.local`, 41 | no `runparts`, and most plugins are skipped (except those that provide 42 | functionality for the condition subsystem). 43 | 44 | Instead of reading `/etc/finit.conf` et al, system configuration is read 45 | from `/lib/finit/rescue.conf`, which can be freely modified by the 46 | system administrator. 47 | 48 | The bundled default `rescue.conf` contains nothing more than: 49 | 50 | runlevel 1 51 | tty [12345] rescue 52 | 53 | The `tty` has the `rescue` option set, which works similar to the board 54 | bring-up tty option `notty`. The major difference being that `sulogin` 55 | is started to query for root/admin password. If `sulogin` is not found, 56 | `rescue` behaves like `notty` and gives a plain root shell prompt. 57 | 58 | If Finit cannot find `/lib/finit/rescue.conf` it defaults to: 59 | 60 | tty [12345] rescue 61 | 62 | There is no way to exit the *fallback* rescue mode. -------------------------------------------------------------------------------- /src/initctl.h: -------------------------------------------------------------------------------- 1 | /* New client tool, replaces old /dev/initctl API and telinit tool 2 | * 3 | * Copyright (c) 2015-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_INITCTL_H_ 25 | #define FINIT_INITCTL_H_ 26 | 27 | #include 28 | 29 | #include "finit.h" 30 | #include "util.h" 31 | 32 | extern char *finit_conf; 33 | extern char *finit_rcsd; 34 | 35 | extern int icreate; /* initctl -c */ 36 | extern int iforce; /* initctl -f */ 37 | extern int ionce; /* initctl -1 */ 38 | extern int heading; /* initctl -t */ 39 | extern int json; /* initctl -j */ 40 | extern int noerr; /* initctl -n */ 41 | extern int verbose; /* initctl -v */ 42 | extern int plain; /* initctl -p */ 43 | extern int quiet; /* initctl -q */ 44 | 45 | #define ERR(rc, fmt, args...) do { if (!quiet) err(rc, fmt, ##args); else exit(rc); } while (0) 46 | #define ERRX(rc, fmt, args...) do { if (!quiet) errx(rc, fmt, ##args); else exit(rc); } while (0) 47 | #define WARN(fmt, args...) do { if (!quiet) warn(fmt, ##args); } while (0) 48 | #define WARNX(fmt, args...) do { if (!quiet) warnx(fmt, ##args); } while (0) 49 | 50 | extern void print_header(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 51 | 52 | #endif /* FINIT_INITCTL_H_ */ 53 | 54 | /** 55 | * Local Variables: 56 | * indent-tabs-mode: t 57 | * c-file-style: "linux" 58 | * End: 59 | */ 60 | -------------------------------------------------------------------------------- /plugins/resolvconf.c: -------------------------------------------------------------------------------- 1 | /* Setup necessary files for resolvconf 2 | * 3 | * Copyright (c) 2012-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifdef _LIBITE_LITE 25 | # include 26 | #else 27 | # include 28 | #endif 29 | 30 | #include "finit.h" 31 | #include "helpers.h" 32 | #include "plugin.h" 33 | #include "log.h" 34 | 35 | /* 36 | * No need to clean up files in /etc/resolvconf/run/interface/ 37 | * this is already taken care of by bootclean 38 | */ 39 | static void setup(void *arg) 40 | { 41 | dbg("Setting up the resolver ..."); 42 | mkpath("/etc/resolvconf", 0755); 43 | mkpath("/etc/resolvconf/run", 0755); 44 | mkpath("/var/run/resolvconf", 0755); 45 | mkpath("/var/run/resolvconf/interface", 0755); 46 | 47 | touch("/etc/resolvconf/run/enable-updates"); 48 | chdir("/etc/resolvconf/run/interface"); 49 | run_parts("/etc/resolvconf/update.d", "-i", NULL, 0, 0); 50 | chdir("/"); 51 | } 52 | 53 | static plugin_t plugin = { 54 | .name = __FILE__, 55 | .hook[HOOK_BASEFS_UP] = { 56 | .cb = setup 57 | }, 58 | .depends = { "bootmisc", }, 59 | }; 60 | 61 | PLUGIN_INIT(__init) 62 | { 63 | plugin_register(&plugin); 64 | } 65 | 66 | PLUGIN_EXIT(__exit) 67 | { 68 | plugin_unregister(&plugin); 69 | } 70 | 71 | /** 72 | * Local Variables: 73 | * indent-tabs-mode: t 74 | * c-file-style: "linux" 75 | * End: 76 | */ 77 | -------------------------------------------------------------------------------- /src/mdadm.c: -------------------------------------------------------------------------------- 1 | /* Handle MD arrays 2 | * 3 | * Copyright (c) 2016-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include "config.h" /* Generated by configure script */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "helpers.h" 31 | #include "util.h" 32 | 33 | 34 | static glob_t *get_arrays(void) 35 | { 36 | static glob_t arrays; 37 | 38 | glob("/sys/block/md*", 0, NULL, &arrays); 39 | if (arrays.gl_pathc > 0) 40 | return &arrays; 41 | 42 | return NULL; 43 | } 44 | 45 | /* 46 | * If system has an MD raid, we must tell it to stop before continuing 47 | * with the shutdown. Some controller cards, in particular the Intel(R) 48 | * Matrix Storage Manager, must be properly notified. 49 | */ 50 | void mdadm_wait(void) 51 | { 52 | glob_t *gl; 53 | 54 | /* Setup kernel specific settings, e.g. allow broadcast ping, etc. */ 55 | gl = get_arrays(); 56 | if (gl) { 57 | size_t i; 58 | 59 | for (i = 0; i < gl->gl_pathc; i++) { 60 | const char *array; 61 | char cmd[160]; 62 | 63 | array = basenm(gl->gl_pathv[i]); 64 | snprintf(cmd, sizeof(cmd), "mdadm --wait-clean /dev/%s >/dev/null", array); 65 | run_interactive(cmd, "Marking MD array %s as clean", array); 66 | } 67 | globfree(gl); 68 | } 69 | } 70 | 71 | /** 72 | * Local Variables: 73 | * indent-tabs-mode: t 74 | * c-file-style: "linux" 75 | * End: 76 | */ 77 | -------------------------------------------------------------------------------- /plugins/alsa-utils.c: -------------------------------------------------------------------------------- 1 | /* Save and restore ALSA sound settings using alsactl 2 | * 3 | * Copyright (c) 2012-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifdef _LIBITE_LITE 25 | # include 26 | #else 27 | # include 28 | #endif 29 | 30 | #include "conf.h" 31 | #include "finit.h" 32 | #include "helpers.h" 33 | #include "plugin.h" 34 | #include "log.h" 35 | 36 | #define ALSACTL "alsactl" 37 | 38 | static void save(void *arg) 39 | { 40 | if (rescue) { 41 | dbg("Skipping %s plugin in rescue mode.", __FILE__); 42 | return; 43 | } 44 | 45 | if (whichp(ALSACTL)) { 46 | dbg("Saving sound settings ..."); 47 | run_interactive(ALSACTL " -g store", "Saving sound settings"); 48 | } 49 | } 50 | 51 | static void restore(void *arg) 52 | { 53 | if (rescue) { 54 | dbg("Skipping %s plugin in rescue mode.", __FILE__); 55 | return; 56 | } 57 | 58 | if (whichp(ALSACTL)) { 59 | dbg("Restoring sound settings ..."); 60 | run_interactive(ALSACTL " -g restore", "Restoring sound settings"); 61 | } 62 | } 63 | 64 | static plugin_t plugin = { 65 | .name = __FILE__, 66 | .hook[HOOK_BASEFS_UP] = { .cb = restore }, 67 | .hook[HOOK_SHUTDOWN] = { .cb = save } 68 | }; 69 | 70 | PLUGIN_INIT(__init) 71 | { 72 | plugin_register(&plugin); 73 | } 74 | 75 | PLUGIN_EXIT(__exit) 76 | { 77 | plugin_unregister(&plugin); 78 | } 79 | 80 | 81 | /** 82 | * Local Variables: 83 | * indent-tabs-mode: t 84 | * c-file-style: "linux" 85 | * End: 86 | */ 87 | -------------------------------------------------------------------------------- /contrib/void/README.md: -------------------------------------------------------------------------------- 1 | HowTo: Finit on Void Linux 2 | ========================== 3 | 4 | HowTo use Finit to boot an [Void Linux][] system. It is assumed that 5 | the user has already installed make, a compiler, C library header files, 6 | and other tools needed to build a GNU configure based project. 7 | 8 | To start with you need to first install [libuEv][] and [libite][]. They 9 | default to install to `/usr/local`, but unlike Debian and Ubuntu based 10 | distros, Void's `pkg-config` does not look for libraries and header 11 | files there. So the `PKG_CONFIG_LIBDIR` environment variable must be 12 | used, or change the install prefix to `/usr`. 13 | 14 | The bundled [build.sh](build.sh) script can be used to configure and 15 | build finit: 16 | 17 | void:~# cd finit 18 | void:~/finit# ./contrib/void/build.sh 19 | 20 | Then run the [install.sh](install.sh) script to install all necessary 21 | files, including the sample `finit.conf` and `finit.d/*.conf` files. 22 | More on that below. 23 | 24 | void:~/finit# ./contrib/void/install.sh 25 | 26 | The install script is (supposed to be) non-destructive by default, you 27 | have to answer *Yes* twice to set up Finit as the system default init. 28 | Pay close attention to the last question: 29 | 30 | *** Install Finit as the system default Init (y/N)? 31 | 32 | If you answer `No`, simply by pressing enter, you can change the symlink 33 | yourself later on, to point to `finit` instead of `runit`: 34 | 35 | void:~/finit# cd /sbin 36 | void:/sbin# rm init 37 | void:/sbin# ln -s finit init 38 | 39 | Another option is to change the Grub defaults, in `/etc/default/grub`: 40 | 41 | #GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 slub_debug=P page_poison=1" 42 | GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 init=/sbin/finit" 43 | 44 | Alternatively, you can simply modify the default Grub entry at boot, or 45 | set up an alternative Grub entry to include: 46 | 47 | linux /boot/vmlinuz-X.YY.Z ... init=/sbin/finit 48 | 49 | If you modify a Grub configuration file, remember to run: 50 | 51 | update-grub 52 | 53 | Before rebooting, make sure to set up a [/etc/finit.conf](finit.conf), 54 | and [/etc/finit.d/](finit.d/) for your services. Samples are included 55 | in this directory. Notice the symlinks in `/etc/finit.d/`, which can be 56 | managed by the operator at runtime using `initctl enable SERVICE`. You 57 | can also use a standard [/etc/rc.local](rc.local) for one-shot tasks and 58 | initialization like keyboard language etc. 59 | 60 | [libuEv]: https://github.com/troglobit/libuev 61 | [libite]: https://github.com/troglobit/libite 62 | [Void Linux]: https://www.voidlinux.eu/ 63 | -------------------------------------------------------------------------------- /src/private.h: -------------------------------------------------------------------------------- 1 | /* Private header file for main finit daemon, not for plugins 2 | * 3 | * Copyright (c) 2008-2010 Claudio Matsuoka 4 | * Copyright (c) 2008-2025 Joachim Wiberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifndef FINIT_PRIVATE_H_ 26 | #define FINIT_PRIVATE_H_ 27 | 28 | #include "svc.h" 29 | #include "plugin.h" 30 | 31 | #define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */ 32 | #define MATCH_CMD(l, c, x) \ 33 | (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) 34 | 35 | #define IS_RESERVED_RUNLEVEL(l) (l == 0 || l == 6 || l == INIT_LEVEL) 36 | 37 | extern char *finit_conf; 38 | extern char *finit_rcsd; 39 | extern svc_t *wdog; 40 | extern int service_interval; 41 | extern char *fsck_mode; 42 | extern char *fsck_repair; 43 | 44 | extern uev_ctx_t *ctx; 45 | 46 | int api_init (uev_ctx_t *ctx); 47 | int api_exit (void); 48 | void conf_flush_events(void); 49 | 50 | void service_monitor (pid_t lost, int status); 51 | 52 | const char *plugin_hook_str (hook_point_t no); 53 | int plugin_exists (hook_point_t no); 54 | 55 | void plugin_run_hook (hook_point_t no, void *arg); 56 | void plugin_run_hooks (hook_point_t no); 57 | void plugin_script_run(hook_point_t no); 58 | 59 | int plugin_init (uev_ctx_t *ctx); 60 | void plugin_exit (void); 61 | 62 | #endif /* FINIT_PRIVATE_H_ */ 63 | 64 | /** 65 | * Local Variables: 66 | * indent-tabs-mode: t 67 | * c-file-style: "linux" 68 | * End: 69 | */ 70 | -------------------------------------------------------------------------------- /src/service.h: -------------------------------------------------------------------------------- 1 | /* Finit service monitor, task starter and generic API for managing svc_t 2 | * 3 | * Copyright (c) 2008-2010 Claudio Matsuoka 4 | * Copyright (c) 2008-2025 Joachim Wiberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifndef FINIT_SERVICE_H_ 26 | #define FINIT_SERVICE_H_ 27 | 28 | #include "svc.h" 29 | 30 | int service_register (int type, char *line, struct rlimit rlimit[], char *file); 31 | void service_unregister (svc_t *svc); 32 | 33 | void service_runtask_clean (void); 34 | void service_update_rdeps (void); 35 | void service_mark_unavail (void); 36 | 37 | void service_ready_script (svc_t *svc); /* XXX: only for pidfile plugin before notify framework */ 38 | 39 | int service_timeout_after (svc_t *svc, int timeout, void (*cb)(svc_t *svc)); 40 | int service_timeout_cancel (svc_t *svc); 41 | 42 | void service_forked (svc_t *svc); 43 | void service_ready (svc_t *svc, int ready); 44 | 45 | int service_stop (svc_t *svc); 46 | int service_step (svc_t *svc); 47 | void service_step_all (int types); 48 | void service_worker (void *unused); 49 | 50 | int service_completed (svc_t **svc); 51 | void service_log_incomplete (void); 52 | void service_notify_reconf (void); 53 | 54 | void service_init (uev_ctx_t *ctx); 55 | 56 | #endif /* FINIT_SERVICE_H_ */ 57 | 58 | /** 59 | * Local Variables: 60 | * indent-tabs-mode: t 61 | * c-file-style: "linux" 62 | * End: 63 | */ 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License Badge][]][License] [![Release Badge][]][Release] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan] 2 | Finit: Fast Init 3 | 4 | Finit is an alternative to [SysV init](https://en.wikipedia.org/wiki/Init) 5 | and [systemd](https://www.freedesktop.org/wiki/Software/systemd/), focused 6 | on small and embedded Linux systems, although fully usable on server and 7 | [desktop systems][finix]. 8 | 9 | > Reverse engineered from the [EeePC fastinit][] 10 | > "gaps filled with frog DNA …" 11 | > — [Claudio Matsuoka][] 12 | 13 | For detailed information, explore our extensive documentation 14 | :books: **** 15 | 16 |
17 | Alpine screenshot
18 |
19 | 20 | For working examples, see the :rocket: [contrib/](contrib/) section or these 21 | tutorials: 22 | 23 | * :hammer_and_wrench: [Buildroot embedded Linux][br2demo], 24 | * :package: [Debian GNU/Linux](contrib/debian/), 25 | * :mountain: [Alpine Linux](contrib/alpine/), and 26 | * :milky_way: [Void Linux](contrib/void/) 27 | 28 | > [!NOTE] 29 | > Support for various Linux distributions does not mean Finit installs 30 | > easily on all architectures. Bundled install scripts are examples for 31 | > standard installations, tested on amd64 (x86_64) systems. Custom 32 | > setups, e.g., for embedded systems, can be found in the following 33 | > [Buildroot][] based examples: [myLinux][], [Infix][], or the plain 34 | > [br2-finit-demo](https://github.com/finit-project/br2-finit-demo). 35 | 36 | [finix]: https://github.com/aanderse/finix 37 | [Buildroot]: https://buildroot.org 38 | [Infix]: https://kernelkit.github.io 39 | [myLinux]: https://github.com/troglobit/myLinux/ 40 | [br2demo]: https://troglobit.com/post/2022-12-26-buildroot-demo-of-fastinit-finit/ 41 | [EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process 42 | [Claudio Matsuoka]: https://github.com/cmatsuoka 43 | [License]: https://en.wikipedia.org/wiki/MIT_License 44 | [License Badge]: https://img.shields.io/badge/License-MIT-teal.svg 45 | [Release]: https://github.com/finit-project/finit/releases 46 | [Release Badge]: https://img.shields.io/github/v/release/finit-project/finit 47 | [GitHub]: https://github.com/finit-project/finit/actions/workflows/build.yml/ 48 | [GitHub Status]: https://github.com/finit-project/finit/actions/workflows/build.yml/badge.svg 49 | [Coverity Scan]: https://scan.coverity.com/projects/3545 50 | [Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg 51 | -------------------------------------------------------------------------------- /plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglibdir = $(plugin_path) 2 | AM_LDFLAGS = -module -avoid-version -shared 3 | AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 4 | AM_CPPFLAGS = -I$(top_srcdir)/src 5 | AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE 6 | AM_CPPFLAGS += -D__FINIT__ $(lite_CFLAGS) $(uev_CFLAGS) 7 | 8 | if STATIC 9 | noinst_LTLIBRARIES = libplug.la 10 | libplug_la_SOURCES = bootmisc.c pidfile.c procps.c sys.c usr.c 11 | 12 | if BUILD_ALSA_UTILS_PLUGIN 13 | libplug_la_SOURCES += alsa-utils.c 14 | endif 15 | 16 | if BUILD_DBUS_PLUGIN 17 | libplug_la_SOURCES += dbus.c 18 | endif 19 | 20 | if BUILD_MODULES_LOAD_PLUGIN 21 | libplug_la_SOURCES += modules-load.c 22 | endif 23 | 24 | if BUILD_MODPROBE_PLUGIN 25 | libplug_la_SOURCES += modprobe.c 26 | endif 27 | 28 | if BUILD_NETLINK_PLUGIN 29 | libplug_la_SOURCES += netlink.c 30 | endif 31 | 32 | if BUILD_RESOLVCONF_PLUGIN 33 | libplug_la_SOURCES += resolvconf.c 34 | endif 35 | 36 | if BUILD_RTC_PLUGIN 37 | libplug_la_SOURCES += rtc.c 38 | endif 39 | 40 | if BUILD_TTY_PLUGIN 41 | libplug_la_SOURCES += tty.c 42 | endif 43 | 44 | if BUILD_URANDOM_PLUGIN 45 | libplug_la_SOURCES += urandom.c 46 | endif 47 | 48 | if BUILD_X11_COMMON_PLUGIN 49 | libplug_la_SOURCES += x11-common.c 50 | endif 51 | 52 | else 53 | pkglib_LTLIBRARIES = bootmisc.la pidfile.la procps.la sys.la usr.la 54 | 55 | if BUILD_ALSA_UTILS_PLUGIN 56 | pkglib_LTLIBRARIES += alsa-utils.la 57 | endif 58 | 59 | if BUILD_DBUS_PLUGIN 60 | pkglib_LTLIBRARIES += dbus.la 61 | endif 62 | 63 | if BUILD_HOOK_SCRIPTS_PLUGIN 64 | pkglib_LTLIBRARIES += hook-scripts.la 65 | endif 66 | 67 | if BUILD_MODULES_LOAD_PLUGIN 68 | pkglib_LTLIBRARIES += modules-load.la 69 | endif 70 | 71 | if BUILD_MODPROBE_PLUGIN 72 | pkglib_LTLIBRARIES += modprobe.la 73 | endif 74 | 75 | if BUILD_NETLINK_PLUGIN 76 | pkglib_LTLIBRARIES += netlink.la 77 | endif 78 | 79 | if BUILD_RESOLVCONF_PLUGIN 80 | pkglib_LTLIBRARIES += resolvconf.la 81 | endif 82 | 83 | if BUILD_RTC_PLUGIN 84 | pkglib_LTLIBRARIES += rtc.la 85 | endif 86 | 87 | if BUILD_TTY_PLUGIN 88 | pkglib_LTLIBRARIES += tty.la 89 | endif 90 | 91 | if BUILD_URANDOM_PLUGIN 92 | pkglib_LTLIBRARIES += urandom.la 93 | endif 94 | 95 | if BUILD_X11_COMMON_PLUGIN 96 | pkglib_LTLIBRARIES += x11-common.la 97 | endif 98 | 99 | # Clean up .la files installed by libtool despite being in -module mode 100 | install-exec-hook: 101 | @(cd $(DESTDIR)$(pkglibdir) && $(RM) $(pkglib_LTLIBRARIES)) 102 | 103 | uninstall-hook: 104 | @(cd $(DESTDIR)$(pkglibdir) && for file in $(pkglib_LTLIBRARIES); do $(RM) `basename $$file .la`.so; done) 105 | endif 106 | -------------------------------------------------------------------------------- /src/cgutil.h: -------------------------------------------------------------------------------- 1 | /* Cgroup utility functions for initctl 2 | * 3 | * Copyright (c) 2019-2025 Joachim Wiberg 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef FINIT_CGUTIL_H_ 25 | #define FINIT_CGUTIL_H_ 26 | 27 | #include 28 | #include 29 | 30 | struct cg { 31 | struct cg *cg_next; 32 | 33 | /* stats */ 34 | char *cg_path; /* path in /sys/fs/cgroup */ 35 | uint64_t cg_prev; /* cpuacct.usage */ 36 | uint64_t cg_rss; /* memory.stat */ 37 | uint64_t cg_vmlib; /* memory.stat */ 38 | uint64_t cg_vmsize; /* memory.current */ 39 | float cg_memshare; /* cg_rss / total_ram * 100 */ 40 | float cg_load; /* curr - prev / 10000000.0 */ 41 | 42 | /* config */ 43 | struct { 44 | char min[32]; 45 | char max[32]; 46 | } cg_mem; 47 | 48 | struct { 49 | char set[16]; 50 | char weight[32]; 51 | char max[32]; 52 | } cg_cpu; 53 | }; 54 | 55 | int cgroup_avail(void); 56 | 57 | char *pid_cmdline (int pid); 58 | char *pid_cgroup (int pid); 59 | 60 | char *cgroup_val(char *path, char *file, char *buf, size_t len); 61 | uint64_t cgroup_memory(char *group); 62 | int cgroup_throttle(char *group, uint64_t *throttled_usec, uint64_t *nr_throttled); 63 | 64 | struct cg *cg_stats(char *path); 65 | struct cg *cg_conf (char *path); 66 | 67 | int cgroup_tree (char *path, char *pfx, int mode, int pos); 68 | 69 | int show_cgroup (char *arg); 70 | int show_cgtop (char *arg); 71 | int show_cgps (char *arg); 72 | 73 | #endif /* FINIT_CGUTIL_H_ */ 74 | 75 | /** 76 | * Local Variables: 77 | * indent-tabs-mode: t 78 | * c-file-style: "linux" 79 | * End: 80 | */ 81 | --------------------------------------------------------------------------------