Compare commits

..
47 Commits
Author SHA1 Message Date
Joachim Wiberg 40042da8ff contrib: debian: add anacron and cron services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:36:05 +02:00
Joachim Wiberg c251304146 Refactor sys condition plugin into a standalone keventd
This patch is a refactor of the prototype sys condition plugin.  It
moves most of the logic to monitor kernel events into a keventd that,
currently only, sets and clears the sys/pwr/ac condition.  The sys
plugin itself is now only a monitor of conditions and ensures Finit
follows them.  This is a lot more secure and moves (at least one piece
of) netlink processing out from PID 1.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:31:08 +02:00
Joachim Wiberg c047e37a79 cgroup: ensure all services in init group remain as leaf nodes
The top-level cgroup init is a leaf group and should be treated as such,
even for user setup tasks/services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:29:29 +02:00
Joachim Wiberg d2c57447f0 initctl: no error if failing to remove non-existing usr condition
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:29:01 +02:00
Joachim Wiberg 79e9dabc11 Fix bug: re-registering a former service as task does not work
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:28:29 +02:00
Joachim Wiberg b04dc4d457 Ensure services in plugins and from finit.c belong to a cgroup
The udevd, dbus, bundled watchdog, and others were started without a
valid cgroup.  This is a workaround to ensure they are assigned one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:27:59 +02:00
Joachim Wiberg f86e7810be sig.c: disable kernel default ctrl-alt-delete handler
This fixes a long-standanding issue with finit not controlling the
reboot when the user presses ctrl-alt-delete (PC systems).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-14 10:34:54 +02:00
Joachim Wiberg 2018c82ea1 plugins: sys: watch uevent to prevent feedback loop
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-13 21:20:08 +02:00
Joachim Wiberg c27f3603e6 plugins: sys: use counting semaphores to handle >1 AC supply
- Track number of ac and ac online
 - Use systemd logic to assert sys/pwr/ac also when no supply

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-13 08:45:49 +02:00
Joachim Wiberg 6f82b806ad plugins: new sys condition event monitor (wip)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-11 07:02:49 +02:00
Joachim Wiberg cbb8935660 New functions: fnread() and fngetint(), companions to fnwrite()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-11 07:02:30 +02:00
Joachim Wiberg 21ebbb942f plugins: bootmisc: minor, whitespace
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 988ecd95e3 plugins: bootmisc: only create /run/lock if missing
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 3f3e70c686 Mount /dev/pts with gid of tty group and set nosuid, noexec flags
Note, this code only runs if /dev/pts was not mounted in /etc/fstab

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 073b040981 Prevent user DoS by mounting /run/lock as separate tmpfs
This patch does several things related to /run and system reliability.

 - Mount /run with MAX 10% of usable RAM
 - Create and mount /run/lock as a separate tmpfs with max 5 MiB

As a spin-off, this patch also fixes permisions on /run/lock to 0777
so regular users can create lock files.

Note: none of this code runs if /run is mounted alread in /etc/fstab

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg b1a058ccdd doc: update TODO a bit, remember -s for initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg cbc7b8c3c7 plugins: bootmisc: add S02sudo setup for Debian systems
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 13340e4200 contrib: debian: start tty's on tty1-tty6 clear 2-6
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 0076c1f218 contrib: debian: add apparmor, avahi, and bluetooth services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 6fc71b7528 Follow-up to a1af8e8: mount /tmp with perms for all users
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg b42f1e99e2 README: use absolute path in sshd example, it requires that
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 22:29:23 +02:00
Joachim Wiberg afd6ad06a6 README: drop urls from initctl help text to sync with app
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 22:28:50 +02:00
Joachim Wiberg ea0bfaa2f3 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit 40425ee9ec.
2021-05-07 21:15:37 +02:00
Joachim Wiberg 5c3ec176c2 Update changelog, issue #173
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 20:19:31 +02:00
Joachim Wiberg 40425ee9ec Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 19:45:28 +02:00
Joachim Wiberg 424db825a3 plugins: netlink: error handling fixes
Handle EAGAIN properly, for both regular and resync flow, on any error
in the regular flow (unless ENOBUFS) we want to check for nl_ifdown on
any of the successfully parsed messages.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 17:02:09 +02:00
Joachim Wiberg b9cc4cd629 Revert "plugins: fix #173, increase size of netlink socket receive buffer"
This reverts commit add55cfc2a.
2021-05-07 16:50:04 +02:00
Joachim Wiberg 243b8f025b plugins: netlink: refactor and reduce recv() buffer
This patch adds support for calling recv() repeatedly to get the netlink
response from the kernel.  As a result, the recv() buffer can be reduced
down to 4k again.

Both the regular flow and the resync flow now follow the exact same code
path, except for the ENOBUFS handling.  If we get ENOBUFS in resync, we
are screwed anyway.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 16:42:42 +02:00
Joachim Wiberg 6c60b67588 plugins: netlink: fix resync request size alignment with kernel
For RTM_GETLINK we need a `struct ifinfomsg`, not `struct rtmsg`,
otherwise the kernel will get 8 extra bytes and complain about it.

This patch makes sure to set the correct iface change mask as well, and
increases the debug logs a bit to get a fix on sizes used.  We increase
the recv() buffer 8k -> 64k to make sure we can get all data in one big
swoop.  Plan is to refactor this mess in a later commit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 15:20:25 +02:00
Joachim Wiberg 910bb13711 plugins: netlink: redesign to handle ENOBUFS with kernel resync
This is a major redesign of the netlink plugin to be able to handle
ENOBUFS¹ properly.  Pending verification, this change replaces the patch
to increase socket buffer size, which in real life turned out to be
insufficient.

When nl_callback() calls recv() and it fails with ENOBUFS, we consider
our cache of the kernel state invalid and thus:

  1. deassert all net/ conditions
  2. open a new (temporary) netlink socket
  3. send RTM_GETLINK  and re-assert all interfaces using nl_link()
  4. send RTM_GETROUTE and re-assert all routes with nl_route()

Like before, the kernel will not send us a RTM_DELROUTE when it removes
the default route, so we still have to track this ourselves.  This patch
also refactors that functionality to only resync routes when the ifindex
associated previosly with the default route goes down or is removed.

The previous change that added nl_default() to recheck, has been dropped
to instead reuse the standard nl_route() callback.
___
¹ see netlink(7) for details.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 13:06:47 +02:00
Joachim Wiberg add55cfc2a plugins: fix #173, increase size of netlink socket receive buffer
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-04 13:19:54 +02:00
Joachim Wiberg 565820325b Follow-up to a1af8e8: nodev is a mount flag as well
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-04 07:36:41 +02:00
Joachim Wiberg 6bb4d67d92 Follow-up to a1af8e8: fix mount options vs mount flags
The nosuid, noexec, and relatime arguments are not mount options, but flags.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-04 07:34:39 +02:00
Joachim Wiberg 534d50c03a Fix diff link
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 16:09:27 +02:00
Joachim Wiberg 9a42883a96 Bump version to 4.1-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 16:09:21 +02:00
Joachim Wiberg 7c77e2c2b6 Update with major bug fixes for 4.1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 15:25:03 +02:00
Joachim Wiberg ea8c46cd30 Fix #170: check for loss of default route when interfaces go down
This patch fixes the problem with Linux not sending netlink route change
notifications when interfaces for these routes goes down.  When an iface
goes down we now send a route request to the kernel and check the return
message, if no default route is found we deassert net/default/route.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 15:18:25 +02:00
Joachim Wiberg fead3b0671 configure.ac: drop grub Makefile.in generation
Not needed anymore.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 09:34:27 +02:00
Joachim Wiberg 081990b49e Update for upcoming v4.1 bug fix release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 03:50:12 +02:00
Joachim Wiberg 6b89f8c6db contrib: debian: update README with new grub setup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 03:39:08 +02:00
Joachim Wiberg 373097040a contrib: debian: update install legend
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 03:03:30 +02:00
Joachim Wiberg a4ab75172c contrib: debian: use SUPPORTED_INITS instead of custom entry
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 01:53:09 +02:00
Joachim Wiberg 5cf9958957 contrib: debian: read parameters from /etc/default/grub
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 15:43:27 +02:00
Joachim Wiberg b137e08e84 contrib: fix install script to be able to run from tarball
The file autogen.sh is not included in tarballs.  Use configure.ac
instead to detect topdir.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 15:30:13 +02:00
Joachim Wiberg a1af8e8687 Follow-up to 4febd28: restore mount of /dev/shm et al for Debian
This patch restores parts of 4febd28, but in a more orderly fashion, in
order to get a standard Debian system to boot properly with X and tools.

The patch adds fs_finalize() which checks if /dev/sh/, /dev/pts, /run,
and /tmp have been mounted properly from /etc/fstab.  If not, then the
function makes sure to mount tmfps (or devpts) file systems as expected
by most modern systems.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 15:21:50 +02:00
Joachim Wiberg 53c7ddcb22 Change SIGUSR1 to be compatible with systemd and SysV init
This patch changes the behavior of SIGUSR1.  Previously USR1 could be
used to halt a running Finit system, similar to BusyBox init.  However,
compatibility with sysvinit and systemd has been deemed more important.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 14:48:49 +02:00
Joachim Wiberg 3872077ff1 plugins: netlink: stricter interface name validation
Coverity suggests validating against only a set of known characters.
However, the kernel allows just about all characters in an interface
name.  This version of valdiate_ifname() is blatantly stolen, more
or less, from linux/net/core/dev.c

 https://code.woboq.org/linux/linux/net/core/dev.c.html#1020

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-29 17:19:19 +02:00
40 changed files with 1047 additions and 152 deletions
+36 -7
View File
@@ -4,6 +4,41 @@ Change Log
All relevant changes are documented in this file.
[4.1][UNRELEASED]
-----------------
Bug fix release.
### Changes
* Change behavior on SIGUSR1 to be compatible with sysvinit and systemd.
Previously SIGUSR1 caused Finit to halt, like BusyBox init. This had
"interesting" side effects on Debian systems when coexisting with
sysvinit (upgrading/reinstalling causes scripts to `kill -USR1 1`)
* Change how `contrib/debian/install.sh` sets up a Grub boot entry for
finit. We now modify the $SUPPORTED_INITS variable in `10_linux`
### Fixes
* Stricter interface name validation in netlink plugin, modeled after
the kernel. Suggested by Coverity Scan
* The `contrib/*/install.sh` scripts failed to run from tarball
* Fix #170: detect loss of default route when interfaces go down. This
emulates the missing kernel netlink message to remove the condition
net/default/route to allow stopping dependent services
* Fix #171: restore automatic mount of `/dev/shm`, `/dev/pts`, `/run`
and `/tmp`, unless mounted already by `/etc/fstab`. This is what most
desktop systems expect PID 1 to do
* Fix #173: netlink plugin runs out of socket buffer space;
finit[1]: nl_callback():recv(): No buffer space available
Fixed by adding support for resync with kernel on `ENOBUFS`. See
netlink(7) for details. As a spin-off the plugin now supports any
number of interfaces and routes on a system. On resync, the following
message is now logged, as a warning:
finit[1]: nl_callback():busy system, resynchronizing with kernel.
[4.0][] - 2021-04-26
--------------------
@@ -875,7 +910,7 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.0...HEAD
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
@@ -907,9 +942,3 @@ Major bug fix release.
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
[libuEv]: https://github.com/troglobit/libuev
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+1 -4
View File
@@ -134,7 +134,7 @@ Some components are optional: runlevel(s), condition(s) and description,
making it easy to create simple start scripts and still possible for more
advanced uses as well:
service sshd -D
service /usr/sbin/sshd -D
Dependencies are handled using [conditions](doc/conditions.md). One of
the most common conditions is to wait for basic networking to become
@@ -460,9 +460,6 @@ Commands:
halt Halt system
poweroff Halt and power off system
suspend Suspend system
Bug report address: https://github.com/troglobit/finit/issues
Project homepage: https://troglobit.com/finit.html
```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
+2 -2
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_INIT(Finit, 4.1-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@@ -8,7 +8,7 @@ AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile
contrib/Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
doc/Makefile
plugins/Makefile
+1 -1
View File
@@ -10,7 +10,7 @@ fi
# Adjust base directory
if [ -e alpine-release ]; then
cd ../..
elif [ ! -e autogen.sh ]; then
elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory."
exit 1
fi
+1 -1
View File
@@ -1,4 +1,4 @@
SUBDIRS = finit.d grub.d
SUBDIRS = finit.d
docsdir := @docdir@/contrib/debian
docs_DATA = README.md finit.conf os-release build.sh install.sh
EXTRA_DIST = $(docs_DATA)
+6 -5
View File
@@ -29,10 +29,10 @@ set up your system to run Finit:
user@debian:~/finit$ contrib/debian/build.sh
However, since `/sbin/init` already exists on your system, the script
creates another entry in your GRUB config, in `/etc/grub.d/40_custom`,
where `init=/sbin/finit` is added to your kernel line. It is of course
also possible to change the default init to Finit, if you do you can
remove the custom Grub entry:
creates another entry in your GRUB config by updating `$SUPPORTED_INITS`
in `/etc/grub.d/10_inux` and run `update-grub`. On reboot you will find
a (finit) entry in the "Advanced options for Debian GNU/Linux" section.
It is of course also possible to change the default init to Finit:
user@debian:~/finit# cd /sbin
user@debian:/sbin# sudo mv init oldinit
@@ -51,7 +51,8 @@ If you want to use the X Window system, `sudo apt install elogind`
(Bullseye and later). Followed by `initctl reload` (enabled by default)
and logout/login again. The elogind daemon ensures a regular non-root
user can start and interact with an X session, otherwise keyboard and
mouse won't work.
mouse won't work. When you're happy, you can enable the lightdm.conf,
change the default runlevel to 3, and presto! you have a desktop again.
Have fun!
+4 -3
View File
@@ -1,8 +1,9 @@
SUBDIRS = available
docsdir := @docdir@/contrib/debian/finit.d
d_LINKS = console-setup.conf elogind.conf getty.conf \
keyboard-setup.conf modules.conf rsyslogd.conf \
sshd.conf
d_LINKS = acpid.conf apparmor.conf avahi-demon.conf \
bluetooth.conf console-setup.conf elogind.conf \
getty.conf keyboard-setup.conf modules.conf \
rsyslogd.conf sshd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available
+3 -1
View File
@@ -1,5 +1,7 @@
docsdir := @docdir@/contrib/debian/finit.d/available
docs_DATA = acpid.conf console-setup.conf elogind.conf getty.conf \
docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \
console-setup.conf elogind.conf getty.conf \
keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \
rsyslogd.conf sshd.conf syslogd.conf xdm.conf
EXTRA_DIST = $(docs_DATA)
@@ -0,0 +1 @@
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
@@ -0,0 +1,2 @@
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
@@ -0,0 +1 @@
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
@@ -0,0 +1,3 @@
# To ignore errors like "org.freedesktop.hostname1", update the env file with
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
@@ -0,0 +1 @@
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
+5 -2
View File
@@ -1,4 +1,7 @@
# Consoles to start getty on when system is up
tty [12345] /dev/tty1 linux noclear nowait
tty [2345] /dev/tty2 linux noclear nowait
tty [2345] /dev/tty3 linux noclear nowait
tty [2345] /dev/tty2 linux nowait
tty [2345] /dev/tty3 linux nowait
tty [2345] /dev/tty4 linux nowait
tty [2345] /dev/tty5 linux nowait
tty [2345] /dev/tty6 linux nowait
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
insmod gzio
insmod part_msdos
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
initrd /initrd.img
}
-3
View File
@@ -1,3 +0,0 @@
docsdir := @docdir@/contrib/debian/grub.d
docs_DATA = 40_custom
EXTRA_DIST = $(docs_DATA)
+14 -8
View File
@@ -11,7 +11,7 @@ fi
grep -q Debian os-release 2>/dev/null
if [ $? -eq 0 ]; then
cd ../..
elif [ ! -e autogen.sh ]; then
elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory."
exit 1
fi
@@ -23,7 +23,7 @@ echo "/sbin/finit - PID 1"
echo "/lib/finit/plugins/* - All enabled Finit plugins"
echo "/etc/finit.conf - Finit configuration file"
echo "/etc/finit.d/ - Finit services"
echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader"
echo "/etc/grub.d/10_linux - Add Finit to Grub's SUPPORTED_INITS"
echo
read -p "Do you want to continue (y/N)? " yorn
echo
@@ -38,12 +38,18 @@ if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
cp -va finit.d /etc/
echo "*** Setting up a GRUB boot entry ..."
MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"`
TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"`
BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'`
cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom
update-grub
fn=/etc/grub.d/10_linux
if [ -e $fn ]; then
if `grep SUPPORTED_INITS $fn |head -1 |grep -q finit`; then
echo "Already installed, done."
else
echo "Adding Finit to list of SUPPORTED_INITS ..."
sed -i 's/SUPPORTED_INITS="[^"]*/& finit:\/sbin\/finit/' $fn
update-grub
fi
else
echo "Cannot find $fn, you'll have to set up your bootloader on your own."
fi
echo
echo "*** Done"
+1 -1
View File
@@ -10,7 +10,7 @@ fi
# Adjust base directory
if [ -e xbps ]; then
cd ../..
elif [ ! -e autogen.sh ]; then
elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory."
exit 1
fi
+6
View File
@@ -26,12 +26,18 @@ Some of these are also registered in the issue tracker on GitHub.
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ...
Initctl
-------
* Add support for timed shutdown in Finit, including cancelled shutdown:
shutdown -h 03:35 "foobar was here"
* Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json`
* Add support for `-s` to redirect all output to syslog, for scripting
Init
+17 -9
View File
@@ -12,28 +12,36 @@ have been adopted:
Same effect as `finit q`, `init q`, or `initctl reload`, reloads all
*.conf files in `/etc/finit.d/`
This also restarts the API (initctl) socket, like SysV init and systemd
does on USR1 with their FIFO/D-Bus.
`SIGUSR1`
---------
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
processes, and unmounts all file systems. Then tells kernel to halt.
Most people these days want `SIGUSR2` though.
Since Finit 4.1 this signal causes Finit to restart its API (initctl)
socket, like SysV init and systemd does on USR1 with their FIFO/D-Bus.
Finit <= 4.0 performed a system halt (like USR2 without power-off), but
this caused compatibility problems with systemd and sysvinit on desktop
systems. Hence, since Finit 4.1 it is no longer possible to halt a
system with a signal.
SysV init and systemd use this to re-open their FIFO/D-Bus.
`SIGUSR2`
---------
Like SIGUSR1, but tell kernel to power-off the system, if ACPI or
similar exists to actually do this. If the kernel fails power-off,
Finit falls back to halt.
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
processes, and unmounts all file systems. Then tells kernel to power
off the system, if ACPI or similar exists to actually do this. If the
kernel fails power-off, Finit falls back to halt.
SysV init N/A, systemd dumps its internal state to log.
`SIGTERM`
---------
Like `SIGUSR1`, but tell kernel to reboot the system when done.
Like `SIGUSR2`, but tell kernel to reboot the system when done.
SysV init N/A, systemd rexecutes itself.
+2 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC
noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c sys.c tty.c urandom.c usr.c
if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
endif
else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la sys.la tty.la urandom.la usr.la
if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la
+12 -5
View File
@@ -119,15 +119,16 @@ static void setup(void *arg)
makedir("/var/lib/urandom",0755);
if (fisdir("/run")) {
_d("System with new /run tmpfs ...");
makedir("/run/lock", 1777);
if (!fisdir("/run/lock"))
makedir("/run/lock", 1777);
symlink("/run/lock", "/var/lock");
symlink("/dev/shm", "/run/shm");
symlink("/dev/shm", "/run/shm");
/* compat only, should really be set up by OS/dist */
symlink("/run", "/var/run");
symlink("/run", "/var/run");
} else {
makedir("/var/lock", 1777);
makedir("/var/run", 0755);
makedir("/var/lock", 1777);
makedir("/var/run", 0755);
}
makedir("/var/log", 0755);
makedir("/var/mail", 0755);
@@ -183,6 +184,12 @@ static void setup(void *arg)
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
/* Debian has /run/sudo, ensure correct perms and SELinux label */
mksubsys("/var/run/sudo", 0711, "root", "root");
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
if (whichp("restorecon"))
run("restorecon /var/run/sudo /var/run/sudo/ts");
umask(prev);
}
+1 -1
View File
@@ -64,7 +64,7 @@ static void setup(void *arg)
remove("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] @%s:%s %s %s -- %s",
snprintf(line, sizeof(line), "[S12345789] cgroup.system @%s:%s %s %s -- %s",
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
+3 -3
View File
@@ -48,17 +48,17 @@ static void setup(void *arg)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd name:udevd log %s "
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
"-- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
} else {
snprintf(cmd, sizeof(cmd), ":1 [S] <pid/udevd> log "
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
"udevadm trigger -c add -t devices "
"-- Requesting device events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), ":2 [S] <pid/udevd> log "
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
+2 -3
View File
@@ -36,7 +36,7 @@
#define MODULES_LOAD_PATH "/etc/modules-load.d"
#endif
#define SERVICE_LINE \
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
"cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
static void load(void *arg)
{
@@ -71,8 +71,7 @@ static void load(void *arg)
mod = strtok_r(mod, " ", &args);
snprintf(cmd, sizeof(cmd), SERVICE_LINE,
index, mod, mod, args, mod);
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
index++;
+227 -42
View File
@@ -30,31 +30,35 @@
#include "cond.h"
#include "helpers.h"
#include "plugin.h"
#include "service.h"
static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
{
if (!NLMSG_OK(nh, len))
return 1;
#define NL_BUFSZ 4096
if (nh->nlmsg_type == NLMSG_DONE) {
_d("Done with netlink messages.");
return 1;
}
struct nl_request {
struct nlmsghdr nh;
union {
struct rtmsg rtm;
struct ifinfomsg ifi;
};
};
if (nh->nlmsg_type == NLMSG_ERROR) {
_d("Netlink reports error.");
return 1;
}
static int nl_defidx;
static int nl_ifdown;
static char *nl_buf;
return 0;
}
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
{
char daddr[INET_ADDRSTRLEN];
char gaddr[INET_ADDRSTRLEN];
struct in_addr ind, ing;
struct rtmsg *r;
struct rtattr *a;
int plen = 0;
int dst = 0;
int idx = 0;
int gw = 0;
int la;
int gw = 0, dst = 0, mask = 0, idx = 0;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
_e("Packet too small or truncated!");
@@ -80,8 +84,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
case RTA_DST:
dst = *((int *)data);
mask = r->rtm_dst_len;
//_d("MASK: 0x%04x", mask);
plen = r->rtm_dst_len;
//_d("Prefix LEN: 0x%04x", plen);
break;
case RTA_OIF:
@@ -93,11 +97,20 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
a = RTA_NEXT(a, la);
}
if ((!dst && !mask) && (gw || idx)) {
if (nlmsg->nlmsg_type == RTM_DELROUTE)
ind.s_addr = dst;
ing.s_addr = gw;
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
if ((!dst && !plen) && (gw || idx)) {
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
cond_clear("net/route/default");
else
nl_defidx = 0;
} else {
cond_set("net/route/default");
nl_defidx = idx;
}
}
}
@@ -117,14 +130,14 @@ static int validate_ifname(const char *ifname)
if (!ifname || !ifname[0])
return 1;
if (strlen(ifname) >= IFNAMSIZ)
if (strnlen(ifname, IFNAMSIZ) == IFNAMSIZ)
return 1;
if (strstr(ifname, ".."))
if (!strcmp(ifname, ".") || !strcmp(ifname, ".."))
return 1;
while (*ifname) {
if (*ifname == '/' || isspace(*ifname))
if (*ifname == '/' || *ifname == ':' || isspace(*ifname))
return 1;
ifname++;
}
@@ -132,12 +145,25 @@ static int validate_ifname(const char *ifname)
return 0;
}
/*
* Check if this interface was associated with the default route
* previously, or if it's been removed. If so, trigger a recheck
* of the system default route.
*/
static void nl_check_default(char *ifname)
{
int idx = (int)if_nametoindex(ifname);
if ((nl_defidx > 0 && nl_defidx == idx) || (idx == 0 && errno == ENODEV))
nl_ifdown = 1;
}
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
{
int la;
char ifname[IFNAMSIZ + 1];
struct rtattr *a;
struct ifinfomsg *i;
struct rtattr *a;
int la;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
_e("Packet too small or truncated!");
@@ -172,6 +198,8 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
nl_check_default(ifname);
break;
case RTM_DELLINK:
@@ -180,6 +208,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
nl_check_default(ifname);
break;
case RTM_NEWADDR:
@@ -197,26 +226,175 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
}
}
static void nl_callback(void *arg, int sd, int events)
static int nl_parse(int sd)
{
static char buf[4096];
struct nlmsghdr *nh;
ssize_t len;
while (1) {
struct nlmsghdr *nh;
ssize_t len;
memset(buf, 0, sizeof(buf));
len = recv(sd, buf, sizeof(buf), 0);
if (len < 0) {
if (errno != EINTR) /* Signal */
_pe("recv()");
while ((len = recv(sd, nl_buf, NL_BUFSZ, 0)) < 0) {
switch (errno) {
case EAGAIN: /* Nothing more right now. */
return 0;
case EINTR: /* Signal */
continue;
case ENOBUFS: /* netlink(7) */
break;
default:
_pe("recv()");
break;
}
return -1;
}
_d("recv %lld bytes", len);
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
struct nlmsgerr *nle;
switch (nh->nlmsg_type) {
case NLMSG_DONE:
_d("Done with netlink messages.");
return 0;
case NLMSG_ERROR:
_d("Kernel netlink comm. error.");
nle = NLMSG_DATA(nh);
if (nle) {
errno = -nle->error;
_pe("Kernel netlink error %d", errno);
}
return -1;
case RTM_NEWROUTE:
case RTM_DELROUTE:
_d("Netlink route ...");
nl_route(nh, len);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
_d("Netlink link ...");
nl_link(nh, len);
break;
default:
_w("unhandled netlink message, type %d", nh->nlmsg_type);
break;
}
}
}
}
static int nl_request(int sd, unsigned int seq, int type)
{
struct nl_request *nlr = (struct nl_request *)nl_buf;
memset(nlr, 0, sizeof(struct nl_request));
nlr->nh.nlmsg_type = type;
nlr->nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
nlr->nh.nlmsg_seq = seq;
nlr->nh.nlmsg_pid = 1;
switch (type) {
case RTM_GETROUTE:
_d("RTM_GETROUTE");
nlr->rtm.rtm_family = AF_INET;
nlr->rtm.rtm_table = RT_TABLE_MAIN;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
break;
case RTM_GETLINK:
_d("RTM_GETLINK");
nlr->ifi.ifi_family = AF_UNSPEC;
nlr->ifi.ifi_change = 0xFFFFFFFF;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
break;
default:
_w("Cannot resync, unhandled message type %d", type);
return -1;
}
if (send(sd, nlr, nlr->nh.nlmsg_len, 0) < 0)
return 1;
return nl_parse(sd);
}
static void nl_resync_routes(int sd, unsigned int seq)
{
if (nl_request(sd, seq, RTM_GETROUTE))
_pe("Failed netlink route request");
}
static void nl_resync_ifaces(int sd, unsigned int seq)
{
if (nl_request(sd, seq, RTM_GETLINK))
_pe("Failed netlink link request");
}
/*
* We've potentially lost netlink events, let's resync with kernel.
*/
static void nl_resync(int all)
{
unsigned int seq = 0;
int sd;
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (sd < 0) {
_pe("netlink socket");
return;
}
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
nl_route(nh, len);
else
nl_link(nh, len);
if (all) {
_d("============================ RESYNC =================================");
/* this doesn't update condtions, and thus does not stop services */
cond_deassert("net/");
nl_resync_ifaces(sd, seq++);
nl_resync_routes(sd, seq++);
/* delayed update after we've corrected things */
service_step_all(SVC_TYPE_ANY);
_d("=========================== RESYNCED ================================");
} else
nl_resync_routes(sd, seq++);
close(sd);
}
static void nl_callback(void *arg, int sd, int events)
{
if (nl_parse(sd) < 0) {
if (errno == ENOBUFS) { /* netlink(7) */
_w("busy system, resynchronizing with kernel.");
nl_resync(1);
return;
}
}
/*
* Linux doesn't send route changes when interfaces go down, so
* we need to check ourselves, e.g. for loss of default route.
*/
if (nl_ifdown) {
_d("interface down, checking default route.");
if (nl_defidx > 0) {
nl_defidx = 0;
nl_resync(0);
if (nl_defidx <= 0) {
cond_clear("net/route/default");
nl_defidx = 0;
}
}
nl_ifdown = 0;
}
}
@@ -236,8 +414,8 @@ static plugin_t plugin = {
PLUGIN_INIT(plugin_init)
{
int sd;
struct sockaddr_nl sa;
int sd;
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sd < 0) {
@@ -247,7 +425,7 @@ PLUGIN_INIT(plugin_init)
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR;
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK;
sa.nl_pid = getpid();
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
@@ -256,6 +434,13 @@ PLUGIN_INIT(plugin_init)
return;
}
nl_buf = malloc(NL_BUFSZ);
if (!nl_buf) {
_pe("malloc()");
close(sd);
return;
}
plugin.io.fd = sd;
plugin_register(&plugin);
}
+197
View File
@@ -0,0 +1,197 @@
/* System condtion event monitor for the Finit condition engine
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <dirent.h>
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_sys;
static int sys_add_path(struct iwatch *iw, char *path)
{
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
}
static void sys_update_conds(char *dir, char *name, uint32_t mask)
{
char fn[PATH_MAX];
char *cond;
paste(fn, sizeof(fn), dir, name);
cond = strstr(fn, COND_BASE);
if (!cond)
return;
cond += strlen(COND_BASE) + 1;
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
cond_update(cond);
}
/* synthesize events in case of new run dirs */
static void sys_scandir(struct iwatch *iw, char *dir, int len)
{
glob_t gl;
size_t i;
char path[len + 6];
int rc;
snprintf(path, sizeof(path), "%s/*", dir);
rc = glob(path, GLOB_NOSORT, NULL, &gl);
if (rc && rc != GLOB_NOMATCH)
return;
for (i = 0; i < gl.gl_pathc; i++) {
_d("scan found %s", gl.gl_pathv[i]);
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
}
globfree(&gl);
}
/*
* create/remove sub-directory in monitored directory
*/
static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
{
char path[strlen(dir) + strlen(name) + 2];
struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name);
_d("path: %s", path);
iwp = iwatch_find_by_path(iw, path);
if (mask & IN_CREATE) {
if (!iwp) {
if (!sys_add_path(iw, path))
sys_scandir(iw, path, sizeof(path));
}
} else if (mask & IN_DELETE) {
if (iwp)
iwatch_del(&iw_sys, iwp);
}
}
static void sys_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_sys, ev->wd);
if (!iwp)
continue;
if (ev->mask & IN_ISDIR) {
sys_handle_dir(&iw_sys, iwp->path, ev->name, ev->mask);
continue;
}
if (ev->mask & (IN_CREATE | IN_DELETE))
sys_update_conds(iwp->path, ev->name, ev->mask);
}
}
static void sys_init(void *arg)
{
char sysdir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
return;
}
path = realpath(_PATH_CONDSYS, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
return;
}
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
iwatch_exit(&iw_sys);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_sys);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = sys_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_sys);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1
View File
@@ -1,6 +1,7 @@
.deps/*
finit
initctl
keventd
logit
reboot
sulogin
+9 -5
View File
@@ -8,18 +8,22 @@ AM_LDFLAGS = -export-dynamic
endif
sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = logit sulogin
pkglibexec_PROGRAMS = keventd logit sulogin
if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd
endif
finit_pkglibdir = $(pkglibdir)
finit_pkglib_SCRIPTS = rescue.conf sample.conf
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
keventd_LDADD = $(lite_LIBS)
sulogin_SOURCES = sulogin.c
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
sulogin_LDADD = -lcrypt
if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd
endif
logit_SOURCES = logit.c logrotate.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS)
+3
View File
@@ -172,6 +172,9 @@ int cgroup_service(char *name, int pid, struct cgroup *cg)
if (!strcmp(cg->name, "root"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
if (!strcmp(cg->name, "init"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs");
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
if (fisdir(path))
group = cg->name;
+52 -12
View File
@@ -163,7 +163,7 @@ int cond_set_path(const char *path, enum cond_state next)
return next != prev;
}
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
/* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */
void cond_update(const char *name)
{
svc_t *svc, *iter = NULL;
@@ -178,13 +178,22 @@ void cond_update(const char *name)
}
}
void cond_set(const char *name)
int cond_set_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
return 1;
if (!cond_set_path(cond_path(name), COND_ON))
return 1;
return 0;
}
void cond_set(const char *name)
{
_d("%s", name);
if (cond_set_noupdate(name))
return;
cond_update(name);
@@ -208,13 +217,22 @@ void cond_set_oneshot(const char *name)
cond_update(name);
}
void cond_clear(const char *name)
int cond_clear_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
return 1;
if (!cond_set_path(cond_path(name), COND_OFF))
return 1;
return 0;
}
void cond_clear(const char *name)
{
_d("%s", name);
if (cond_clear_noupdate(name))
return;
cond_update(name);
@@ -227,7 +245,7 @@ void cond_reload(void)
cond_bump_reconf();
}
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw, int set)
{
char *nm;
@@ -239,25 +257,47 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
nm = strstr((char *)fpath, COND_BASE);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
_e("Incorrect condtion path %s, cannot %sassert", fpath, set ? "re" : "de");
return 1;
}
nm += strlen(COND_BASE);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
if (set)
cond_set(nm);
else
cond_clear_noupdate(nm); /* important, see netlink plugin! */
return 0;
}
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{
return do_assert(fpath, sb, tflg, ftw, 1);
}
static int deassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{
return do_assert(fpath, sb, tflg, ftw, 0);
}
/*
* Used only by netlink plugin atm.
* type: is a one of pid/, net/, etc.
*/
void cond_reassert(const char *type)
void cond_reassert(const char *pat)
{
_d("%s", type);
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
_d("%s", pat);
nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
}
/*
* Used only by netlink plugin atm.
*/
void cond_deassert(const char *pat)
{
_d("%s", pat);
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
}
void cond_init(void)
+10 -2
View File
@@ -5,12 +5,14 @@
#include <svc.h>
#define COND_BASE "finit/cond"
#define COND_USR "usr/"
#define COND_PID "pid/"
#define COND_SYS "sys/"
#define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_CONDPID _PATH_COND COND_PID
#define _PATH_CONDSYS _PATH_COND COND_SYS
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_RECONF _PATH_COND "reconf"
typedef enum cond_state {
@@ -34,7 +36,13 @@ void cond_set (const char *name);
void cond_set_oneshot (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
int cond_set_noupdate (const char *name);
int cond_clear_noupdate(const char *name);
void cond_reassert (const char *pat);
void cond_deassert (const char *pat);
void cond_init (void);
#endif /* FINIT_COND_H_ */
+73 -1
View File
@@ -186,6 +186,69 @@ static void fs_remount_root(int fsckerr)
}
#endif /* SYSROOT */
/*
* Opinionated file system setup. Checks for critical mount points and
* mounts them as most users expect. All file systems are checked with
* /proc/mounts before mounting.
*
* Embedded systems, and other people who want full control, can set up
* their system with /etc/fstab, which is handled before this function
* is called. For systems like Debian/Ubuntu, who only have / and swap
* in their /etc/fstab, this function does all the magic necessary.
*/
static void fs_finalize(void)
{
/*
* Some systems rely on us to both create /dev/shm and, to mount
* a tmpfs there. Any system with dbus needs shared memory, so
* mount it, unless its already mounted, but not if listed in
* the /etc/fstab file already.
*/
if (!fismnt("/dev/shm")) {
makedir("/dev/shm", 0777);
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
}
/* Modern systems use /dev/pts */
if (!fismnt("/dev/pts")) {
char opts[32];
int mode;
int gid;
gid = getgroup("tty");
if (gid == -1)
gid = 0;
/* 0600 is default on Debian, use 0620 to get mesg y by default */
mode = 0620;
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
}
/*
* Modern systems use tmpfs for /run. Fallback to /var/run if
* /run doesn't exist is handled by the bootmisc plugin. It
* also sets up compat symlinks.
*
* The unconditional mount of /run/lock is for DoS prevention.
* To override any of this behavior, add entries to /etc/fstab
* for /run (and optionally /run/lock).
*/
if (fisdir("/run") && !fismnt("/run")) {
mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
makedir("/run/lock", 1777);
mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
}
/* Modern systems use tmpfs for /tmp */
if (!fismnt("/tmp"))
mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
}
static void fs_mount(void)
{
if (!rescue)
@@ -201,6 +264,9 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
_d("Finalize, ensure common file systems are available ...");
fs_finalize();
}
/*
@@ -524,10 +590,16 @@ int main(int argc, char *argv[])
* Start built-in watchdogd as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
service_register(SVC_TYPE_SERVICE, "[123456789] name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
wdog = svc_find_by_nameid("watchdog", "finit");
}
/*
* Start kernel event daemon as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/keventd"))
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
/* Base FS up, enable standard SysV init signals */
sig_setup(&loop);
+1 -1
View File
@@ -345,7 +345,7 @@ static int do_cond_act(char *arg, int creat)
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
err(1, "Failed asserting condition <%s>", &oneshot[off]);
} else {
if (erase(oneshot))
if (erase(oneshot) && errno != ENOENT)
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
}
+278
View File
@@ -0,0 +1,278 @@
/* Listens to kernel events like AC power status and manages sys/ conditions
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include "lite/lite.h"
#include "cond.h"
#include "pid.h"
#include "util.h"
#define _PATH_SYSFS_PWR "/sys/class/power_supply"
static int num_ac_online;
static int num_ac;
int debug; /* debug in other modules as well */
void logit(int prio, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (!debug)
vsyslog(prio, fmt, ap);
else {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
va_end(ap);
}
#define panic(fmt, args...) { logit(LOG_CRIT, fmt ":%s", ##args, strerror(errno)); exit(1); }
#define warn(fmt, args...) { logit(LOG_WARNING, fmt ":%s", ##args, strerror(errno)); }
static void sys_cond(char *cond, int set)
{
char oneshot[256];
snprintf(oneshot, sizeof(oneshot), "%s/%s", _PATH_CONDSYS, cond);
if (set) {
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
warn("failed asserting sys/%s", cond);
} else {
if (erase(oneshot) && errno != ENOENT)
warn("failed asserting sys/%s", cond);
}
}
static int fgetln(char *path, char *buf, size_t len)
{
FILE *fp;
fp = fopen(path, "r");
if (!fp)
return -1;
if (!fgets(buf, len, fp)) {
fclose(fp);
return -1;
}
chomp(buf);
fclose(fp);
return 0;
}
static int check_online(char *online)
{
int val;
if (!online)
return 0;
val = atoi(online);
logit(LOG_INFO, "AC %s", val ? "connected" : "disconnected");
return val;
}
static int is_ac(char *type)
{
char *types[] = {
"Mains",
"USB",
"BrickID",
"Wireless",
NULL
};
int i;
for (i = 0; types[i]; i++) {
if (!strncmp(type, types[i], strlen(types[i])))
return 1;
}
return 0;
}
static void init(void)
{
struct dirent **d = NULL;
char *cond_dirs[] = {
_PATH_CONDSYS,
_PATH_CONDSYS "/pwr",
};
char path[384];
int i, n;
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS,
cond_dirs[i]);
return;
}
}
n = scandir(_PATH_SYSFS_PWR, &d, NULL, alphasort);
for (i = 0; i < n; i++) {
char *nm = d[i]->d_name;
char buf[10];
snprintf(path, sizeof(path), "%s/%s/type", _PATH_SYSFS_PWR, nm);
if (!fgetln(path, buf, sizeof(buf)) && is_ac(buf)) {
num_ac++;
snprintf(path, sizeof(path), "%s/%s/online", _PATH_SYSFS_PWR, nm);
if (!fgetln(path, buf, sizeof(buf))) {
if (check_online(buf))
num_ac_online++;
}
}
free(d[i]);
}
if (n > 0)
free(d);
/* if any power_supply is online, or none can be found */
if (num_ac == 0 || num_ac_online > 0)
sys_cond("pwr/ac", 1);
}
/*
* Started by Finit as soon as possible when base filesystem is up,
* modules have been probed, or insmodded from /etc/finit.conf, so by
* now we should have /sys/class/power_supply/ available for probing.
* If none is found we assert /sys/pwr/ac condition anyway, this is what
* systemd does (ConditionACPower) and also makes most sense.
*/
int main(int argc, char *argv[])
{
struct sockaddr_nl nls = { 0 };
struct pollfd pfd;
char buf[1024];
if (argc > 1) {
if (!strcmp(argv[1], "-d"))
debug = 1;
}
if (!debug) {
openlog("keventd", LOG_CONS | LOG_PID, LOG_DAEMON);
setlogmask(LOG_UPTO(LOG_NOTICE));
}
init();
pfd.events = POLLIN;
pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (pfd.fd == -1)
panic("failed creating netlink socket");
nls.nl_family = AF_NETLINK;
nls.nl_pid = 0;
nls.nl_groups = -1;
if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
panic("bind failed");
logit(LOG_DEBUG, "Waiting for events ...");
while (-1 != poll(&pfd, 1, -1)) {
char *path = NULL;
int ac = 0;
int i, len;
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
if (len == -1) {
switch (errno) {
case EINTR:
continue;
case ENOBUFS:
warn("lost events");
continue;
default:
panic("unhandled");
break;
}
}
/* skip libusb events, focus on kernel events/changes */
if (strncmp(buf, "change@", 7))
continue;
/* XXX: currently limited to monitoring this subsystem */
if (!strstr(buf, "power_supply"))
continue;
i = 0;
while (i < len) {
char *line = buf + i;
logit(LOG_DEBUG, "%s", line);
if (!strncmp(line, "DEVPATH=", 8)) {
path = &line[8];
logit(LOG_DEBUG, "Got path %s", path);
} else if (!strncmp(line, "POWER_SUPPLY_TYPE=", 18)) {
ac = is_ac(&line[18]);
} else if (!strncmp(line, "POWER_SUPPLY_ONLINE=", 20) && ac) {
if (check_online(&line[20])) {
if (!num_ac_online)
sys_cond("pwr/ac", 1);
num_ac_online++;
} else {
if (num_ac_online > 0)
num_ac_online--;
if (!num_ac_online)
sys_cond("pwr/ac", 0);
}
}
i += strlen(line) + 1;
}
}
close(pfd.fd);
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -206,7 +206,7 @@ int reboot_main(int argc, char *argv[])
case CMD_HALT:
if (do_halt(NULL))
do_kill(SIGUSR1, msg);
do_kill(SIGUSR2, msg);
break;
case CMD_POWEROFF:
+3
View File
@@ -1260,6 +1260,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc);
} else {
/* update type, may have changed from service -> task */
svc->type = type;
/* e.g., if missing cmd or env before */
svc_unblock(svc);
}
+21 -13
View File
@@ -31,21 +31,24 @@
* does on USR1 with their FIFO/D-Bus.
*
* SIGUSR1
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
* processes, and unmounts all file systems. Then tells kernel to
* halt. Most people these days want SIGUSR2 though.
* Restarts API (initctl) socket, like SysV init and systemd does
* on USR1 with their FIFO/D-Bus.
*
* (SysV init and systemd use this to re-open their FIFO/D-Bus.)
* Finit <= 4.0 used to perform a system halt on USR1. This caused
* some quite nasty problems on systems with both systemd/sysvinit
* installed alongside finit. For compatibility reasons Finit 4.1
* changed to partial SIGHUP (api_exit/init).
*
* SIGUSR2
* Like SIGUSR1, but tell kernel to power-off the system, if ACPI
* or similar exists to actually do this. If the kernel fails
* power-off, Finit falls back to halt.
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
* processes, and unmounts all file systems. Then tells kernel to
* power-off the system, if ACPI or similar exists to actually do
* this. If the kernel fails power-off, Finit falls back to halt.
*
* (SysV init N/A, systemd dumps its internal state to log.)
*
* SIGTERM
* Like SIGUSR1, but tell kernel to reboot the system when done.
* Like SIGUSR2, but tell kernel to reboot the system when done.
*
* (SysV init N/A, systemd rexecutes itself.)
*
@@ -323,7 +326,7 @@ static void sigint_cb(uev_t *w, void *arg, int events)
}
/*
* SIGUSR1: BusyBox style halt
* SIGUSR1: SysV init/systemd API socket restart
*/
static void sigusr1_cb(uev_t *w, void *arg, int events)
{
@@ -333,8 +336,9 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
return;
}
halt = SHUT_HALT;
service_runlevel(0);
/* Restart initctl API domain socket, similar to systemd/SysV init */
api_exit();
api_init(w->ctx);
}
/*
@@ -517,10 +521,14 @@ void sig_setup(uev_ctx_t *ctx)
erase(SYNC_SHUTDOWN);
erase(SYNC_STOPPED);
/* Standard SysV init calls ctrl-alt-delete handler */
/*
* Standard SysV init calls ctrl-alt-delete handler
* We need to disable kernel default so it sends us SIGINT
*/
reboot(RB_DISABLE_CAD);
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
/* BusyBox init style signals for halt, power-off and reboot. */
/* BusyBox/SysV init style signals for halt, power-off and reboot. */
uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
uev_signal_init(ctx, &sigpwr_watcher, sigusr2_cb, NULL, SIGPWR);
+44
View File
@@ -154,6 +154,35 @@ char *str(char *fmt, ...)
return buf;
}
int fnread(char *buf, size_t len, char *fmt, ...)
{
char path[256];
va_list ap;
FILE *fp;
va_start(ap, fmt);
vsnprintf(path, sizeof(path), fmt, ap);
va_end(ap);
if (!buf || !len) {
struct stat st;
if (stat(path, &st))
return -1;
return (ssize_t)st.st_size;
}
fp = fopen(path, "r");
if (!fp)
return -1;
len = fread(buf, sizeof(char), len, fp);
fclose(fp);
return (int)len;
}
int fnwrite(char *value, char *fmt, ...)
{
char path[256];
@@ -185,6 +214,21 @@ int fnwrite(char *value, char *fmt, ...)
return rc;
}
int fngetint(char *path, int *val)
{
char buf[10];
if (fnread(buf, sizeof(buf), path) <= 0)
return -1;
errno = 0;
*val = strtoul(chomp(buf), NULL, 0);
if (errno)
return -1;
return 0;
}
/*
* musl libc default to /dev/null/utmp and /dev/null/wtmp, respectively.
* See https://www.openwall.com/lists/musl/2012/03/04/4 for reasoning.
+2
View File
@@ -50,7 +50,9 @@ extern char *prognm;
char *progname (char *arg0);
char *str (char *fmt, ...);
int fnread (char *buf, size_t len, char *fmt, ...);
int fnwrite (char *value, char *fmt, ...);
int fngetint (char *path, int *val);
int has_utmp (void);
int strtobytes (char *arg);