Compare 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
Joachim Wiberg 9e85c4f3d1 Bump version for v4.0 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 01:01:20 +02:00
Joachim Wiberg 2531102623 sulogin: ensure Ctrl-D returns OK(0) also for pwd login prompt
sulogin should return OK (exit code 0) when the user presses Ctrl-D,
regardless of the prompt shown.  Otherwise Finit may look for another
sulogin to run straight after this one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 00:59:01 +02:00
Joachim Wiberg 33deada6f0 README: mention new rescue mode in feature list overview
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 00:52:40 +02:00
Joachim Wiberg 9d1506bb7e Revert "Travis-CI: disable clang for coverity scan run"
This reverts commit e011dac98b.
2021-04-26 00:45:57 +02:00
Joachim Wiberg c3f115c9ec sulogin: fix build issue
Keyboard suddenly lost CapsLock -> Ctrl remapping ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 00:39:39 +02:00
Joachim Wiberg 430d402b14 sulogin: check for Ctrl-D also if no pwd to abort and continue boot
Signed-off-by: Joachim Wiberg <joachim.nilsson@westermo.se>
2021-04-26 00:25:24 +02:00
Joachim Wiberg e011dac98b Travis-CI: disable clang for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 23:57:41 +02:00
Joachim Wiberg 42b84d1ccb Update ChangeLog for v4.0 GA
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 23:57:02 +02:00
Joachim Wiberg 288105eac7 sulogin: must fflush() prompt on Alpine Linux (musl libc)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 23:14:05 +02:00
Joachim Wiberg 842b522e68 Drop --disable-logit configure option, no longer in $PATH
Simplify build system.

The logit tool is, as of v4.0, installed into /libexec/finit/logit and
thus not part of the system $PATH.  If a sysadmin or distro wants to
remove it from the system it's entirely possible since Finit always
checks for logit availability before attempting to use it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:53:34 +02:00
Joachim Wiberg e6f36974c8 Document all facets of rescue mode; traditional and fallback
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:46:43 +02:00
Joachim Wiberg 679b4df881 Fallback rescue mode handling
Before 4e5d06b the only way to go into rescue mode was to skip certain
steps and then attempt to load /lib/finit/rescue.conf.  After 4e5d06b
we keep the old handling as a fallback in case early sulogin fails.

The new tty option 'rescue' is added, which recue.conf is updated with.
This option implies notty mode and will try sulogin (again) before it
falls back to start /bin/sh as a login shell.

The reason we keep this is to be able to handle all possible use-cases
and also allow sysadmins to set up the behavior that fits their needs.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:10:42 +02:00
Joachim Wiberg 666cae6f49 service_start(): allow notty TTYs without device to start
A notty TTY can be declared with or without a device, it requires none.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:09:07 +02:00
Joachim Wiberg be2ac1d1b1 sulogin: handle no password and locked account (treated as no pwd)
This patch does two things:

 1. A system with a root account that has no password is now
    allowed to login without password, as one expects.
 2. A systems with a locked root account, is treated as (1).

The latter of the two may very well turn out to be a really bad idea.
Much angst have been poured into it, yet the end decision is to allow
access.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 21:43:39 +02:00
Joachim Wiberg 266e5b30eb Drop all mention of old fallback shell from comments
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 21:43:16 +02:00
Joachim Wiberg 4e5d06b580 Replace experimental emergency shell with actual rescue mode
Most major init systems have a way to start a rescue shell.  This is
usally started when the string `rescue` is read from the kernel's
command line.

The traditional Finit way of handling this has been to skip certain
phases of the standard boot, skip /etc/finit.conf et al, and go for a
/lib/finit/rescue.conf instead.  This pratice has been imroved over
the last few commits with a sulogin.  However, there was no way to
resume boot, like most other init systems offer.

This patch implements a very simple rescue mode, utilizing the new
sulogin, replacing the old (and experimental) emergency shell.  If
Ctrl-D is pressed at the maintenance login prompt the boot is now
resumed.  The same goes for exit/Ctrl-D from the maintenace shell.

The preferred sulogin is the bundled /libexec/finit/sulogin, but
if that isn't installed, the first one in $PATH is used.  If no
sulogin at all is found, Finit proceeds as before this patch, to
skip certains steps and go for a /lib/finit/rescue.conf.  Hence,
al steps can be controlled by an administrator.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 21:32:35 +02:00
Joachim Wiberg fe334135ce Fix small memory leak, use whichp() to query, not which()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 19:47:15 +02:00
Joachim Wiberg aef10fb5e7 Revert "Travis-CI: disable clang for coverity scan run"
This reverts commit 550ae9c784.
2021-04-25 19:47:04 +02:00
Joachim Wiberg 86707c1f0b sulogin: minor refactor, splice out sh() as separate function
- splice out sh() as a separate function
 - make sh() session leader and set controlling TTY (TIOCSCTTY)
   to make it a proper login shell with Ctrl-C functionality
 - simplify call to execl()

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 19:28:03 +02:00
Joachim Wiberg a28d5b3c09 sulogin: fix minor issue found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 19:27:42 +02:00
Joachim Wiberg 550ae9c784 Travis-CI: disable clang for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:45:44 +02:00
Joachim Wiberg cd5b91680a sulogin: home cooked version, can be built -static
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:43:54 +02:00
Joachim Wiberg 221ab20898 tty: add basic security, call sulogin in rescue mode (notty)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:27:26 +02:00
Joachim Wiberg 96534789db Minor, rename LOGIT_PATH -> _PATH_LOGIT for consistency
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:26:52 +02:00
Joachim Wiberg 26128dd788 plugins: skip plugin in rescue mode
These plugins should not run in rescue mode, because the system may be
in a very bad state and we do not want to make the situation any worse
than it already is.

Essentially, only services in rescue.conf should run in rescue mode.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 19:46:43 +02:00
Joachim Wiberg 799e992542 tty: minor, rename local variables for consistency
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 10:33:58 +02:00
Joachim Wiberg fa524eec60 contrib: debian: add elogind.conf and update recommendations
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 10:33:57 +02:00
Joachim Wiberg 6dced29b49 Add another constraint for the built-in watchdog, device exists
- Check if watchdogd *and* `WDT_DEVNODE` exists before registering the
   built-in watchdogd at boot
 - Update bootstrap.md with this additional constraint
 - Update config.md with references to bootstrap and the new constraint

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 10:33:57 +02:00
Joachim Wiberg 2ea613a42b Make sure built-in watchdog is disabled by default
Omitting --without-watchdog caused the watchdog to be enabled.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 09:43:27 +02:00
Joachim Wiberg 985df20519 cgroup: don't warn on cgroup.events EINVAL for top-level groups
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 07:42:31 +02:00
Joachim Wiberg f6161b9be9 doc: Add blurb on bundled vs external watchdog, handover and reset
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 07:41:46 +02:00
Joachim Wiberg 25c5014d3c Also check env: file for changes to detect svc->args_dirty
This patch fixes an issue where services that support SIGHUP are not
properly stop/started on changes to their command line arguments.

A change to a service's env: file, e.g. /etc/default/foo for service
foo, must also be counted as a change to the foo args.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-23 17:12:59 +02:00
Joachim Wiberg b8b7b53d96 External plugins require exporting cgroup.h, svc.h needs it
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-20 01:48:03 +02:00
Joachim Wiberg 7707c4b8e6 initctl: only show key capture in debug mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 15:56:39 +02:00
Joachim Wiberg 437f95f00f Bump version for final (?) release candidate, v4.0-rc4
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 15:40:26 +02:00
Joachim Wiberg 099672fabb configure: change --enable-redirect -> --disable-redirect
This changes the default to redirect output from run/task/services to
/dev/null.  Use --disable-redirect to get the old pre v4.0 behavior.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 15:25:57 +02:00
Joachim Wiberg 860515a716 contrib: load softdog module on Alpine/Void Linux for finit-watchdog
We need at least the softdog watchdog driver loaded for proper watchdog
reset at reboot.  This can safely be replaced with a hardware specific
module that provides /dev/watchdog ... without one Finit will try to
restart the built-in finit-watchdog service 10 times to no avail.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 14:52:28 +02:00
Joachim Wiberg c85551a42e watchdog: wait for WDT reset only if watchdogd is running
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 14:41:48 +02:00
Joachim Wiberg 79dc6e7472 Update Travis, Alpine, and Void build options
- Fallback shell has been removed
 - Built-in watchdog has been renamed

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 13:55:24 +02:00
Joachim Wiberg 06e456384c watchdog: Enable --with-watchdog[=DEV] and improve log messages
This patch changes the configure option to enable the built-in watchdog
from --enable-watchdog to --with-watchdog[=DEV].  This is the convention
for features that take arguments.

Also, improve log messages to aid debugging when finit-watchdogd does
not start properly.  This means flushing logs to syslogd with closelog()

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 13:41:31 +02:00
Joachim Wiberg 38f7d24a80 tty: clean up developer debug messages
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 11:05:07 +02:00
Joachim Wiberg 145c78ef3e Follow-up to 7f76202: Remove fallback-shell from configure summary
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 11:05:07 +02:00
Joachim Wiberg dd437bf0a5 Follow-up to 18ab7d3: restore start of built-in watchdogd
This patch restores the start of the built-in/bundled watchdogd.  It is
tracked in the `wdog` variable and handled as an exception at shutdown.

This is also a follow-up to 7b74c99, ensuring that we only kill/stop the
built-in watchdog, not any external.  External ones can register to be
the controlling watchdogd in the system -- Finit is not the arbiter for
singletons, this is up to the system engineer.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 11:05:07 +02:00
Joachim Wiberg d82d119729 plugins: handle corner case when PID file doesn't exist
If we get a notification and the service dies immediately, and also
removes its pid file, we need to take corrective action.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 09:45:57 +02:00
Joachim Wiberg 7f76202865 Simplify, drop --enable-fallback-shell from configure script
Recommend using `notty` option in tty stanza instead.  See the updated
docs for details.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 23:13:19 +02:00
Joachim Wiberg aee92751ac Minor, add env: and sysv examples to first example .conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 16:24:19 +02:00
Joachim Wiberg 2ea7236d45 Add feature list immediately after screenshot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 14:52:50 +02:00
Joachim Wiberg cedc04e1e3 Revert "Travis-CI: disable clang for coverity scan run"
This reverts commit 0168b765c1.
2021-04-18 12:37:21 +02:00
Joachim Wiberg edc6eb1747 initctl: fix too small destination buffer (unlikely)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 12:24:49 +02:00
Joachim Wiberg be5ec94fdf Fix GCC warnings for unhandled return value
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 12:24:32 +02:00
Joachim Wiberg 23a13fe1b6 initctl: add hidden command line option -d,--debug
For developer use only.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 12:23:58 +02:00
Joachim Wiberg 0168b765c1 Travis-CI: disable clang for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 11:55:25 +02:00
Joachim Wiberg 01358a5a45 doc: update service with pre:script and post:script actions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 11:37:01 +02:00
Joachim Wiberg 106516d12d doc: update tty config, detail <COND> and @console vs console
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 11:34:57 +02:00
Joachim Wiberg ba858743e5 Fix #129: add pre:script and post:script action support
This patch updates the service state machine with two new states: SETUP
and CLEANUP.  If an executable pre:/path/to/script is defined for a
service, it is called every time the task goes to READY state.  If an
executable post:/path/to/script is defined for a service, it is called
when the task goes to HALTED state.

Each of these two scripts default to a three (3) second execution time
before they are SIGKILLed.  This can be adjusted with the `kill:SEC`
option for the service.  There are no execution guarantees, nor are
there any way of detecting if the script was killed before completion or
not -- except for running Finit in debug mode and inspecting the result
printed by system_monitor().

Note: the post:script MUST be idempotent since transitions between READY
      and HALTED can take place any number of times before a task goes
      to its RUNNING state.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 11:33:38 +02:00
Joachim Wiberg 9fd860ef3c initctl: cgroup and status commands should show mem.min numeric
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 10:37:38 +02:00
Joachim Wiberg 46ccdc3820 Fix ordering issue, pid is unset for kill(-pid, SIGKILL) op
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 09:52:48 +02:00
Joachim Wiberg 242fe54afd initctl: standardize on code=signal for signaled/killed cause
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 09:51:53 +02:00
Joachim Wiberg df5d296cd9 initctl: fix too small buffer for extended status
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 09:51:33 +02:00
Joachim Wiberg dc4630f214 contrib: consistent naming across distro examples
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 06:21:25 +02:00
Joachim Wiberg ac07d3bac7 contrib: run klogd in same cgroup as syslogd (same .conf file)
These two BusyBox daemon's run in concert so it makes sense they also
share the same cgroup.  In Finit that means sharing the same .conf file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 06:17:40 +02:00
Joachim Wiberg f641eef8f7 Adjust command length for built-in getty
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 06:08:14 +02:00
Joachim Wiberg f17b5b3cd3 init: move bug report+homepage from -h to -v
Also, detect progname to improve usage text slightly.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 05:52:36 +02:00
Joachim Wiberg d945f4960b initctl: move bug report+homepage from help to version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 05:51:45 +02:00
Joachim Wiberg 6a5a6ac62f Fix tty command line recomposition for initctl -v
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 05:50:49 +02:00
Joachim Wiberg a9ba4300ec contrib: add ttyS0 to list of ttys to start in Alpine Linux
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 05:03:23 +02:00
Joachim Wiberg 7bb649f11a tty: don't block SIGHUP, needed to exit getty gracefully
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 04:57:37 +02:00
Joachim Wiberg 85ada0ac99 initctl: minor, add linefeed at end of top session
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 04:47:22 +02:00
Joachim Wiberg be343a3b34 plugins: call bootmisc:bootclean() in HOOK_MOUNT_POST
Before this patch bootclean() ran first in setup() which caused to to
remove the entire /var/run/finit directory, and other files as well,
created earlier.  Only possible fix is to split clean and setup in
two and make sure to call clean as soon as we've mounted everything.

Note: this introduces a new behavior, and anyone hooking into the
      same point to do good-stuff(tm) may be affected by this.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 04:34:48 +02:00
Joachim Wiberg 0ac0e5c7d3 plugins: create /var/run/finit/cond/pid directory
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 04:34:29 +02:00
Joachim Wiberg 7f272768e4 Log error if reconf cannot be written
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-18 04:31:56 +02:00
Jacques de Laval 7b74c9900f Revert "Only track built-in watchdogd, not external ones"
This reverts commit 9eb8e8dd99.
2021-04-15 11:34:07 +02:00
Joachim Wiberg 36f1a39b31 Revert "Travis-CI: disable clang for coverity scan run"
This reverts commit 8d3917e9fa.
2021-04-15 10:30:05 +02:00
Joachim Wiberg 6d380082eb ttinit: check retun value from tcgetattr(), only restore if OK
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-15 10:11:06 +02:00
Joachim Wiberg 8d3917e9fa Travis-CI: disable clang for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-15 09:35:42 +02:00
Joachim Wiberg c3cd1a5bfa Merge pull request #166 from troglobit/tty-refactor
Tty refactor
2021-04-15 09:28:41 +02:00
Joachim Wiberg eaeddc36c2 Restore tty plugin after tty refactor
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-15 09:02:28 +02:00
Joachim Wiberg 4f40b65f0c svc_find_by_tty(): fix finder fn, check svc->dev, not name
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-15 08:46:21 +02:00
Joachim Wiberg f958aa504e cond_set_path(): minor, improve debug, 'new' -> 'next'
- Avoid using reserved C++ keyword 'new'
 - Rename new -> next
 - Rename old -> prev
 - Add debug for value being set to cond path

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-15 08:44:03 +02:00
Joachim Wiberg 3d1b95aa7a tty: export tty_exists() to rest of finit (for plugins)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-15 08:43:32 +02:00
Joachim Wiberg 4bb67bc06f Restore @console handling and support for external getty
- Drop vhangup(), how did things ever work *with* this in?!
 - setsid() + TIOCSCTTY are best buds, see notty code
 - Allow storing any non-NULL string as tty->dev, expand in service_register()
 - Reorder parse_cmdline_args(), we need to expand @console to current dev
 - Fix tty arg parser, swapparoo for external getty
 - Refactor tty_atcon(), iterated over by service_register()
 - New arg format, need to translate for old run_getty2()

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 23:41:22 +02:00
Joachim Wiberg 0929c93caf Misc. cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 23:41:02 +02:00
Joachim Wiberg c0368d2b16 Refactor fallback shell handling, run as regular service
This patch re-enables the fallback shell handling after the big TTY
refactor.  We do this by allowing the fallback shell to run as a
regular service.

Note: this also adds the "hidden" support for 'notty' option for
      tty configurations stanzas.  This is just to pick up from
      where the kernel left us, reusing stdin + stdout.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 16:29:19 +02:00
Joachim Wiberg 85a212ef93 service_monitor(): cancel any pending SIGKILL timers
As soon as we collect a PID we should cancel any outstanding timer
actions.   Because we may very soon svc_del() it ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 16:29:19 +02:00
Joachim Wiberg 12c3cb3dc0 service_stop(): improve debug, show rc of kill(), and it's args.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 16:29:19 +02:00
Joachim Wiberg 16710e7ab5 Fix possible NULL pointer deref.
Some C-lib, like GLIBC, convert NULL to <nil> when printing %p,
which we shouldn't rely on since others just bite the dust.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 16:29:19 +02:00
Joachim Wiberg b652aaf0cb Rename nologin shell: finit-sh -> finitsh, as fallback shell
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 16:29:19 +02:00
Joachim Wiberg 30020263ba Refactor tty handling to use service backend enabling conditions etc.
Initial refactor of the tty implementation to use the service/run/task
general backend.  This enables all the features of services also for
ttys, except logging because it makes no sense.

Work in progress:

 - plugins/tty.c does not work anymore, could possibly be removed in
   favor of usinga (a new) condition instead (if-tty-exists)
 - fallback tty does not work anymore, should we remove it, or can we
   handle it as an optional built-in with (a new) condition?
 - @console does not work anymore, needs to generate N cloned services

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-14 16:29:19 +02:00
Joachim Wiberg 3cf5d36972 doc: example of timed shutdown
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 16:45:33 +02:00
Joachim Wiberg c9ae96bcf7 doc: status of processes now shown in initctl status foo
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 16:31:25 +02:00
Joachim Wiberg 5955110e67 Revert "Travis-CI: disable gcc for coverity scan run"
This reverts commit 4cfc8fec0e.
2021-04-10 16:26:56 +02:00
Joachim Wiberg 4191e15c77 Travis-CI: adjust install dir, plain /tmp gets crowded
Calling `tree /tmp` causes too long execution time and Travis timing out
the job.  So let's install to a sane directory.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 16:25:39 +02:00
Joachim Wiberg d60baa2b80 initctl: logically dead code, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 15:59:13 +02:00
Joachim Wiberg a50bc331db Fix possible string truncation, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 15:56:44 +02:00
Joachim Wiberg eff4453de9 initctl: add missing comma in signames[], found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 15:54:11 +02:00
Joachim Wiberg 4cfc8fec0e Travis-CI: disable gcc for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 12:06:46 +02:00
Joachim Wiberg c9e1c2f80f initctl: show exit status/signaled like systemctl
- Show if exited/signaled
 - Show status code and the std /NAME
 - Show signal value and the std /NAME

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 11:51:29 +02:00
Joachim Wiberg 93039e40cc initctl: usability improvements to ls/edit family of commands
- List /etc/finit.conf
 - Allow edit of /etc/finit.conf, using finit/finit.conf or NULL
 - Add helpful question to edit /etc/finit.conf if no edit arg.
 - Show available files if delete has no arg, like disable

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-10 08:56:26 +02:00
Joachim Wiberg 015a52a20d sample.conf: update with cgroup help and examples
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 19:52:12 +02:00
Joachim Wiberg d53cc8bfe7 doc: Fix run/task/service cgroup config syntax
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 19:51:47 +02:00
Joachim Wiberg 3c4eca60b7 initctl: fix restart of run/tasks, stuck in DONE state
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 19:51:03 +02:00
Joachim Wiberg c85e28b971 Yield execution in forked children back to PID 1
- Speeds up start-up a bit
 - Ensures the child is moved to its cgroup as soon as possible

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:59:33 +02:00
Joachim Wiberg d373068619 exec: only log when starting getty, not before
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:58:53 +02:00
Joachim Wiberg 997f3d7862 cgroup: fix possible NULL pointer deref.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:58:32 +02:00
Joachim Wiberg f12f53692e cgroup: allow adding self (PID 0) to a leaf group
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:58:11 +02:00
Joachim Wiberg b921b8cf0f cgroup: run all ttys, except rescue sh, in getty cgroup until login
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:32:31 +02:00
Joachim Wiberg 61781c72fe cgroup: Call ifupdown in system cgroup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:21:04 +02:00
Joachim Wiberg 3b5cfc5ecf test: drop checksef.sh from EXTRA_DIST, generated now
Also, silence generation of checkself.sh

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 18:05:02 +02:00
Joachim Wiberg a42a789d29 Refactor, clean up removed top-level cgroups
The main purpose of this patch is to remove top-level cgroups when they
are remvoed from finit.conf.  However, this is tricky, becasue there may
still be supervised services (and children of them) running.  We must
postpone removal until a cgroup.events change.  To make matters worse,
events may arrive out-of-order, i.e. the last-child event for the parent
cgroup before the sub-group.

A spin-off from this patch is that we can now support arbitrarily long
group names and group config.  (Only applies to top-level cgroups, not
run/task/services.)

Limitations:
 - Processes in "removed" cgroups are not migrated/orphaned, the reasons
   are several: overhead and complexity and the fact that memory cannot
   be migrated
 - A lingering child of a moved service may prevent an old top-level
   cgroup from being removed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-09 17:49:55 +02:00
Joachim Wiberg d77c014723 Add example of initctl status foo
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 17:11:35 +02:00
Joachim Wiberg 23fca9c067 Update initctl usage text, three new commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 17:04:13 +02:00
Joachim Wiberg 37eb1901f9 initctl: handle ~ECHONL properly in top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 14:36:15 +02:00
Joachim Wiberg 98c4e7764f initctl: fix boolean logic bug in -p top command option
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 14:27:36 +02:00
Joachim Wiberg d3e4463612 initctl: add missing signal.h include, required for musl libc
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 14:20:48 +02:00
Joachim Wiberg b812045d82 doc: add cgroup documentation
First draft of the new cgroup documentation.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 14:16:25 +02:00
Joachim Wiberg b1bcdc88e0 initctl: fix ordering issue for root task in ps/top/cgroup commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 13:17:40 +02:00
Joachim Wiberg 62c5a9d06d cgroup: add support for reserved cgroup 'root', for RT tasks
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-08 13:17:10 +02:00
Joachim Wiberg dcdc842336 Revert "Travis-CI: disable gcc for coverity scan run"
This reverts commit 73629944c0.
2021-04-08 12:15:08 +02:00
Joachim Wiberg 12eaa3073a fnwrite(): call fclose() separately, found by Coverity Scan
If any of the fputs() calls fails we will not call fclose() and thus
cause resource loss.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 14:32:12 +02:00
Joachim Wiberg 34875f57a5 Fix uninitialized stack variable, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 14:00:08 +02:00
Joachim Wiberg f0bf1343b0 initctl: skip return value for uev_*_init(), optional
We have these neat extras for added value, if they should fail, for
whatever reason, we still want to present 'top' to the user, which may
have to use 'reset' if exited improperly and lost cooked/cursor.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 13:57:43 +02:00
Joachim Wiberg 4e6b9514d5 cgroup: check return value from fnwrite(), found by Coverity Scan
We check it everywhere else, and a log message for failing to move PID 1
to the init cgroup is useful as well.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 13:55:07 +02:00
Joachim Wiberg 3b495d7518 cgroup: fix too small buffer for strlcpy(), found by Coverity Scan
Off by one, of course.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 13:54:34 +02:00
Joachim Wiberg badf8701bf cgroup: fix use before NULL check, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 13:53:56 +02:00
Joachim Wiberg 85fa0d4926 cgroup: fix resource leak, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 13:53:25 +02:00
Joachim Wiberg 3de9643893 fnwrite(): fix resource leak, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 13:26:56 +02:00
Joachim Wiberg 73629944c0 Travis-CI: disable gcc for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 12:06:07 +02:00
Joachim Wiberg c97c83bfb6 cgroup: basic instrumentation of critical functions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 11:58:48 +02:00
Joachim Wiberg 19b18afac9 cgroup: Skip mkdir() and subtree control if group already exists
- No point in doing it twice
 - Don't anger the gods (possible kernel bugs)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 11:58:48 +02:00
Joachim Wiberg e3c424e129 Minor, rename internal variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 11:58:48 +02:00
Joachim Wiberg c0716d7f63 Minor optimization, drop expensive memset() ops
No need to zero out whole buffer for strlcat(), or other string check
ops.  Also drop a few completely useless memset() calls.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 11:58:41 +02:00
Joachim Wiberg d43af8c9c1 Minor optimization, use strdupa() for small alloc ops.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 10:31:59 +02:00
Joachim Wiberg 2825100f77 initctl: add support for 'q' to quit from top (also signals)
- Set TTY in raw mode to be able read a single char at a time
 - Hide cursor
 - Trap 'q', Ctrl-C, Ctrl-\ and SIGTERM to restore TTY and cursor

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 10:08:13 +02:00
Joachim Wiberg b1bee31d61 Helper functions for setting TTY in raw and cooked mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 10:07:26 +02:00
Joachim Wiberg 522276b80d Minor, rename internal functions/variables screen_* -> tt*
- Shorter
 - Follows old UNIX tradition
 - Lines up with next commit

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-07 10:06:01 +02:00
Joachim Wiberg 7bc1470a1e Minor, rearrange functions in mode order
Only moved functions around.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-06 14:38:24 +02:00
Joachim Wiberg ca32cb0dfc cgroup: 'init' is a reserved leaf group with Finit
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-06 13:16:58 +02:00
Joachim Wiberg d59f55b90c initctl: new command 'cgroup' to show cgroup config
This is intended to be used with the 'top' command to aid in setting up
and verifying proper limits for services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-06 12:03:20 +02:00
Joachim Wiberg 02936de993 initctl: initical conversion to use libuEv for top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-05 23:56:39 +02:00
Joachim Wiberg 8ed223343d getty: fix regression introduced in 5342881
Replacing write() with dprint() without thinking leads to duplicate
output in getty.  In particular '\n login: foo login: ".

Must check if length is > 0, or dprint() will calculate it on its own.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-04 12:54:35 +02:00
Joachim Wiberg 90f7ff1c23 initctl: new command 'reload NAME:ID' and new semantics for restart
This patch corrects a logical glitch, or design flaw, in initctl.  The
'restart FOO' command did not stop+start FOO only send SIGHUP (provided
FOO supports SIGHUP).  Hence, a new command 'reload FOO' is introduced,
which does exactly that, and 'restart FOO' now stops and restarts FOO.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-04 12:35:35 +02:00
Joachim Wiberg a56220f158 Mark result of chown() as don't-care for now
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-04 12:35:35 +02:00
Joachim Wiberg d40b426c1b Refactor, check return value from fgets()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-04 12:35:35 +02:00
Joachim Wiberg 5342881bf4 Refactor, wrap write() syscall in new dprint() function
Fixes the GCC warning for unchecked return value from write():

  ... ignoring return value of ‘write’, declared with attribute warn_unused_result

The new dprint() function wraps write() in a bounded retry-loop that
catches EINTR.  The length argument is optional computed with strlen()
if omitted (zero).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-04 12:35:35 +02:00
Joachim Wiberg c84c17ea8c initctl: initial support for -q,--quiet mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-04 12:35:35 +02:00
Joachim Wiberg f976b8261f Merge pull request #165 from troglobit/nohup-on-cmd-line-changed
Do a full restart rather than HUP if command line of a service has changed
2021-04-02 11:31:57 +02:00
Jacques de Laval 10f4c7fa18 Do a full restart rather than HUP if command line of a service has changed
By keeping track of when a full restart is needed, Finit can relieve
the user of having to track modifications to service configuration. This
also makes initctl reload easier to explain and reason about as the effect
of a reload should now be that any configuration updates to services will
be fully "commited" by initctl reload.

Signed-off-by: Jacques de Laval <Jacques.De.Laval@westermo.com>
2021-04-01 16:18:54 +02:00
Joachim Wiberg 665d212bd6 Add support for configuring cgroups and their settings on services
This patch adds support for modifying settings for the default cgroups;
init, user, and system, as well as adding up to a total of eight groups
for the system.

Services can now be assigned to a cgroup, with optional extra settings
for that particular process group.  The syntax is slightly contrivied
but follows the overall Finit syntax of prop:value,prop':value', e.g.

   cgroup maint cpu.weight:123,mem.max:10000

Starting with the introduction of rlimits, a group of services sharing
the same .conf file can share the same (locally "global") rlimits, and
now also the same cgroup, e.g.

    cgroup.maint
    service foo
    service bar cgroup:mem.max:1000

This puts foo and bar in the same top-level cgroup 'maint', with an
extra memory restriction on bar for max 1000 bytes memory.

NOTE: 'mem.' is a Finit extension, a shorthand for cgroups2 'memory.'

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-30 10:21:01 +02:00
Joachim Wiberg 9e6e653c57 plugins: pidfile: fix subtle memory leak
Since the introduction of the iwatch framework we can now safely free
the memory allocated by realpath() and prevent leaks in a more elegant
way than before.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-29 23:20:00 +02:00
Joachim Wiberg bda4da71a6 Fix touch-reload regression from 3bb1e41
When reloading we check if service .conf files have been modified since
the last time we read them.  In 3bb1e41 we changed it so we load only
the enabled/*.conf files, which led to our no longer properly detecting
changed files since we monitor changes in what the symlinks point to.

This patch fixes that by checking the target for changes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-29 23:15:36 +02:00
Joachim Wiberg a24fc941a6 telinit: call initctl with '-b' to prevent screen size probing
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-29 22:23:34 +02:00
Joachim Wiberg f307ad060b Follow-up to 0641b1a, prevent hang regression in bootstrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-28 16:38:07 +02:00
Joachim Wiberg 1845b7be54 The unix(7) man page recommends using SOCK_SEQPACKET
Since Linux 2.6.4 the SOCK_SEQPACKET socket type is available for UNIX
domain sockets on Linux.  It is intended for; "a sequenced-packet socket
that is connection-oriented, preserves message boundaries, and delivers
messages in the order that they were sent." which sounds like us.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-25 17:26:55 +01:00
Joachim Wiberg a260e157eb Move external getty and sh to user cgroup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-25 17:26:31 +01:00
Joachim Wiberg 0641b1a73e Split finalize() in two separate tasks
The finalize() function is a bit too long, which means it may block the
API socket initctl (from the final runlevel switch at the beginning) use
to contact os via.

We should perhaps break it up even more, but at least once like this to
give the event loop a chance to interleave with an API callback.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-25 17:24:32 +01:00
Joachim Wiberg 9ba57f0c9d iwatch: ignore EEXIST errors, we're already watching it
In particular this can happen from cgroup.c when multiple services map
to the same cgroup.events (due to being in the same cgroup)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-25 17:22:42 +01:00
Joachim Wiberg 9c2f0b2dc8 initctl: finit may be busy, wait for it ...
When calling initctl early in the boot process, e.g. just when Finit is
switching to the final runlevel, it may take a few milliseconds for it
to actually be ready to process our request.

This patch adds poll() to both the write(REQ) and the read(RSP) steps
to prevent from getting weird errors when Finit is busy.  The timeout
is currently 2 sec in both directions, which should be enough.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-25 17:18:50 +01:00
Joachim Wiberg d4bbebeb93 plugins: hotplug: ensure we always call setup() *after* bootmisc
The bootmisc plugin creates lots of required system directories which
udev, and possibly also mdev, need to operate.  E.g., a system which
has an empty tmpfs for /var need to populate that before we run.

The plugin loader handled this dependency implicitly before, loading all
plugins in alphabetical order.  We should not rely on that for proper
operation.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-24 13:16:12 +01:00
Joachim Wiberg 1a22b4f0aa plugins: dbus: allow in all runlevels and start already in 'S'
This plugin should be able to start much earlier than on network UP,
it uses a UNIX domain socket to communicate so loopback should not be
needed.

Also, Finit supports runvels up to 9 (0 and 6 are special), so allow
dbus to run in all these runlevels.  It is up to the user/OS to set
any policy for what runlevels to use.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-24 13:13:35 +01:00
Joachim Wiberg 9c4d5341ae No env file, or optional ('-'), are both OK results
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-23 16:24:12 +01:00
Joachim Wiberg b9d498e01c initctl: fall back to .conf in /etc/finit.d if not in available/
For commands like `initctl show foo`, initctl looks in the available/
subdirectory for `foo.conf`.  This patch adds support for falling back
to return `/etc/finit.d/foo.conf` if the service doesn't exist in the
`/etc/finit.d/available/` subdirectory.

Static services, outside of the `enabled/` handling, live in the base
dirctory and this is what you expect from the tool.  "Do what I mean"

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-22 18:57:43 +01:00
Joachim Wiberg a1e38a3e40 cgroup: add missing pointer and result of fopen()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-22 14:28:25 +01:00
Joachim Wiberg c0ae7763a6 initctl: ignore possibly missing cpu.stat in cgroup2 root
Older kernels (4.19) don't have the summary cpu.stat in the cgroup2
root, so let's ignore that problem and return zero.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-22 10:47:52 +01:00
Joachim Wiberg 9f98b1d3d1 Merge pull request #161 from yangfl/upstream
Fix build for some non-x86 archs
2021-03-20 19:45:54 +01:00
yangfl a7e2224f05 Fix build for some non-x86 archs
Ref:
https://buildd.debian.org/status/fetch.php?pkg=finit&arch=mips64el&ver=3.2%7Erc3-2&stamp=1609910244&raw=0
https://buildd.debian.org/status/fetch.php?pkg=finit&arch=sparc64&ver=3.2%7Erc3-2&stamp=1609869348&raw=0
2021-03-20 23:57:08 +08:00
Joachim Wiberg 3bb1e41394 Only check for dangling symlinks, load enabled/*.conf file
In debug mode it's highly confusing to see Finit load available/*.conf
files when you expect it to only load finit.conf and enabled/*.conf,
so let's keep the dangling symlink check to itself and actually load
and parse from enabled/ as it was intended :)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-20 14:40:11 +01:00
Joachim Wiberg e437379939 Minor, style/clarify debug messages
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-20 14:39:49 +01:00
Joachim Wiberg 3ad4f6ef4b In garbage collect, don't clear conditions provided by new instances
Sneaky bug, when switching between different sets of configurations, a
new instance of a previously removed svc can have its condition removed
by svc_gc() of the old instance.  This patch prevents such accidents.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-20 14:27:01 +01:00
Joachim Wiberg 4170a61d0b Merge pull request #160 from troglobit/release
Forward port dirty vs enabled fixes from release branch
2021-03-19 17:23:36 +01:00
Joachim Wiberg b3c5ab41e7 Merge pull request #159 from troglobit/fix-dirty-removed-svc-restarting
Store removal status separate from dirty
2021-03-19 17:11:55 +01:00
Jacques de Laval b3fad1077e Store removal status separate from dirty
Having removal status stored in the dirty status resulted in removal
status being forgotten when a service was marked as dirty, for instance
when a dependency was updated. This side effect of marke_dirty seems a bit
unexpected and instead of adding exceptions to the logic for when marking
a service dirty - let's separate the two things (dirty and removed) from each
other.

Signed-off-by: Jacques de Laval <Jacques.De.Laval@westermo.com>
2021-03-19 16:53:04 +01:00
Joachim Wiberg 522e1f57d7 Merge branch 'release' 2021-03-19 10:01:55 +01:00
Joachim Wiberg d00a53dfbf Bump version for v4.0-rc3
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:53:26 +01:00
Joachim Wiberg ff86f29f41 initctl: add Memory: and CGroup: to process status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:33:52 +01:00
Joachim Wiberg 3107027132 cgutil: new functions to query a cgroup from pid and get memory use
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:33:42 +01:00
Joachim Wiberg 728db73b09 cgutil: export more functions to initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:32:36 +01:00
Joachim Wiberg fcb8ec9967 Refactor, factor out and export memsz() util function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:30:52 +01:00
Joachim Wiberg 5b8fe12502 cgroup: error handling for critical operations
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:29:27 +01:00
Joachim Wiberg 6e24f2ff13 echo(): refactor and rename helper function -> fnwrite()
More often than not, the file to write to in sysfs changes rather than
the value.  This patch changes echo() into a fnwrite(), flipping what
is vsnprintf()'ed, and adds a stupid str() function that converts any
value (float/int/double/uint64_t) to a static string buffer.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-18 18:25:11 +01:00
Joachim Wiberg 74d715243c sanitize(): avoid strlen() to check string for NUL termination
The strlen() function can easiliy go out of bounds.  Use memchr()
instead, we have the max buffer len as argument anyway.

Also fix call to sanitize() which used wrong length.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-18 18:23:50 +01:00
Joachim Wiberg d9993860cb cgroup: add inotify support for cgroup.events
Replaces previous cgreaper.sh functionality for release notification and
cleanup of service groups.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-18 18:23:38 +01:00
Joachim Wiberg 6a6f3a8291 cgroup: refactor for cgroups v2, much cleaner + unified hieararchy
Since the cgroups support in Finit is not yet officially released, we
decided to change the back-end to use cgroups v2 instead of the aging
and rather clumsy cgroups v1.  Even this initial refactor is a lot
easier to read and understand, more can still be done since there's
a lot of concepts, data values and the ilk that can now be shared
between different controllers.

Still ToDo: inotify for cgroup.events to clean up leaf nodes, which
            in cgroups v1 was handled by cgreaper.sh.  This is fixed
	    in the next commit in the series.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-18 18:23:35 +01:00
Joachim Wiberg 99a8cf3eb7 initctl: add -1,--once flag for top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 23:09:01 +01:00
Joachim Wiberg da95c7d80e initctl: ensure lines don't overflow in top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 23:07:14 +01:00
Joachim Wiberg a9f7f692a9 initctl: add more columns to top command; VmSize, RSS, VmLib, %MEM
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 16:49:08 +01:00
Joachim Wiberg ce55f88a00 cgroup: add symlinks for traditional split controller world
Even systemd have collapsed a few of the cgroup controllers into a
semi-unified hierarchy and uses this approach.  We just take it to
the extreme and have collapsed all of them (like cgroup v2).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 16:47:08 +01:00
Joachim Wiberg 74a26f559d initctl: refactor, split into multiple files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 09:23:04 +01:00
Joachim Wiberg 08144df5ea initctl: fix %CPU listing bug and use hash table for faster lookup
Two major bugs: `if (!cg)` and missing `strdup(path)`.

Also convert to use std hash table for lookup of previous cpu load value
for a given cgroup path.  The hash table is sized after the current num.
rows on the screen -- resize currently not supported.  The value given
to hcreate() should be 25% greater than the estimated num of entries,
but we take a wild guess just to make sure we don't run out of space at
runtime.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-15 13:43:37 +01:00
Joachim Wiberg 230c64927b initctl: add very bare-bones cgroup based top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-15 02:21:43 +01:00
Joachim Wiberg d1e56f300d initctl: show cgroup cpu.shares and memory.usage in ps command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 22:16:20 +01:00
Joachim Wiberg 9842786453 cgroups: fix cpu.shares assignment and set memory hierarchy
- Fix obvious refactor mistake in cpu.shares assignment
- For unified memory hierarchy we need to set .use_hierarch=1

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 12:27:46 +01:00
Joachim Wiberg 6d0c679e05 initctl: show comm and cmdline in ps output
The ps command currently only lists processes in the three main control
groups: init, system, user.  Kernel threads are not show at all.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:12:42 +01:00
Joachim Wiberg 5428dea5bf cgroup: refactor, use a unified hierarchy for finit
As of now, the default finit cgroup behavior is to use a unified
hierarchy of controllers under /sys/fs/cgroup/finit.

We mount cpu,cpuacct,cpuset,memory (if available) and gain the
ability to control our three major groups: init, system, user.

The default CPU share setup is ~10% for init and user, and 90% for
system.  These are guaranteed CPU shares to ensure we do not starve
PID 1 or user processes.  Support for configuring these limits will
be added in a later commit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:07:13 +01:00
Joachim Wiberg 4e84ecae89 Set umask(022) early, in fs_init(), and no place else
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:05:12 +01:00
Joachim Wiberg 959c9fe5aa plugins: restore previous umask() after plugin ops
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:04:29 +01:00
Joachim Wiberg 03d8ef9c8f doc: drop malplaced comment
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:45:37 +01:00
Joachim Wiberg 084b4ce8d3 cgroup: minor refactor/simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:40:52 +01:00
Joachim Wiberg 31cae6a56d plugins: dbus: simplify, use mksubsys(), run as messagebus user
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:26:14 +01:00
Joachim Wiberg 1fd45c6f2e plugins: bootmisc: refactor, use mksubys() and simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:26:14 +01:00
Joachim Wiberg 46995fe446 Add mksusbys() to helpers.c, a kind-of tmpfiles.d in C
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 00:37:47 +01:00
Joachim Wiberg 600b2874e6 initctl: add bug report address to usage text, like finit -h
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 17:08:36 +01:00
Joachim Wiberg b080b970b7 initctl: don't warn on missing env file if it's optional
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 17:08:12 +01:00
Joachim Wiberg 6106574e13 initctl: minor refactor, use systemf() instead of snprintf+system
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:49:01 +01:00
Joachim Wiberg 2385a1209b initctl: don't fall back to default command for wrong commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:48:58 +01:00
Joachim Wiberg e8b942cf95 Fix regression in starting built-in services, introduced in 5b9d990
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:28:16 +01:00
Joachim Wiberg ac17d7f5a7 Silence output from gzip in logrotate()
- system may not have gzip (we're opportunistic)
- system may not have the log file (yet)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:26:50 +01:00
Joachim Wiberg 1edc4356a6 initctl: improve error message for unprivileged users
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:26:16 +01:00
Joachim Wiberg 9f06a2db99 Silence developer API debug message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:25:50 +01:00
Joachim Wiberg cfb3a9f2b9 plugins: pidfile: reduce log noise
With the new practise of keeping record of process pid files, we no
longer need to log/update when reading back the same PID we already
have on file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 00:35:13 +01:00
Joachim Wiberg 8aba63a9e3 Track oldpid of services to fix empty pid in restart message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 00:13:15 +01:00
Joachim Wiberg 3d98ad293b Restore telinit symlink to finit, compat only
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 23:59:39 +01:00
Joachim Wiberg 156e86f395 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit d859ecd21d.
2021-03-11 22:28:41 +01:00
Joachim Wiberg d859ecd21d Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:04:56 +01:00
Joachim Wiberg cbec78dbbc initctl: show pid file and condition for foo in 'status foo' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:01:42 +01:00
Joachim Wiberg 572291a1d9 Don't guess pid file name, either there's one declared or not
The previous patch just added dynamic tracking of non-declared pid
files, so we no longer need to make stuff up.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:00:29 +01:00
Joachim Wiberg 280608f30e plugins: pidfile: track PID file in svc, if svc has none declared
Services that create their own PID files usually don't declare one with
Finit.  This patch adds support to track those PID files anywayt at
runtime for the purpose of identifying match svc_t when a PID file is
removed, i.e. when a service exits.

 - On IN_CREATE the pidfile.so plugin saves the pid file name in svc_t
 - On ON_DELETE the pidfile.so plugin finds svc_t based on pid file

Quicker tracking and less dead code, win-win.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:56:49 +01:00
Joachim Wiberg 5c4146cfe4 Export pid_file_set() function, useful to other subsystems as well
Rename, refactor, and export.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:52:44 +01:00
Joachim Wiberg 30e3f5a33c Wait only one second at shutdown/reboot for lingering processes
Transitioning to runlevel 0 or 6 handles graceful shutdown of any
managed run/task/sysv/services.  So we can replace the old 2 sec delay
with a shorter one for any non-managed still lingering process.

Also, some minor cleanup.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:48:49 +01:00
Joachim Wiberg 2c12379a30 Only reload .conf files when not going to shutdown or reboot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:47:22 +01:00
Joachim Wiberg 5f026c8149 initctl: when svc has status missing, highlight what is missing
To aid with debugging, highlight if env file, binary, or both are the
missing component(s) and causing status 'missing'.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:58:36 +01:00
Joachim Wiberg 90005235e8 initctl: minor, rename local variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:11:16 +01:00
Joachim Wiberg 6b688c4211 Refactor, share env helper fns with initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:10:10 +01:00
Joachim Wiberg bdf5f8fc90 initctl: show halted state status reason in bold everywhere
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 14:35:09 +01:00
Joachim Wiberg 79f7a8ccfb Reclaim 900 msec in boot time from final_worker() at bootstrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 14:03:15 +01:00
Joachim Wiberg 3b19db04e3 initctl: add cond, user, group and visual hints for status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:52:05 +01:00
Joachim Wiberg 93402b81d7 initctl: disable ctrl chars also in runlevel_string() when -p
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:51:23 +01:00
Joachim Wiberg 6dec4c9ca0 Set default user/group for services that don't specify any
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:49:14 +01:00
Joachim Wiberg 548acf27cf Minor refactor, rename RESPAWN_MAX -> SVC_RESPAWN_MAX
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:48:44 +01:00
Joachim Wiberg cd0bc57ca8 Clear stale PID conditions when a service stops
Protect against corner cases where pid conditions are not cleaned up.
We don't want such conditions to remain asserted when the process has
terminated.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 11a3318c02 Properly set status 'missing' for missing binaries and/or env files
When we try to start a service/run/task we call whichp() to see if the
binary exists, either tha absolute path given in the .conf file, or in
the $PATH we run with.  If binary, or the env: file, doesn't exist we
now set svc_missing() state.

On `initctl reload` we unblock the service to be able to check again.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 2711b27971 plugins: pidfile: tricky zebra doesn't close its pid file
This patch fixes a few really hard problems wrt PID files:

 1. Listening for IN_CREATE events means we get notified immediately by
    the kernel when someone calls open()/fopen() on a PID file.  Reading
    the contents returns 0, thank you atoi() ... so we drop IN_CREATE
    and instead look for IN_CLOSE_WRITE, there fixed it!  Not quite ...

 2. Some programs, like Zebra, and other Quagga/Frr daemons, don't
    close() their PID files after creation.  Instead they ftruncate()
    and keep them open, and locked.  Presumably to get a mechanism to
    detect already running instances -- messes life up a bit for the
    rest of us though.  So we need to read files after IN_MODIFY too.

 3. We also want to track IN_DELETE so we can deassert conditions when
    services exit gracefully and clean up their PID files

The rest fo the commit is debug instrumentation changes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 05ac9c18c9 Update changelog, new inictcl cond set|clear commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 1a7092f545 doc: update with new 'initctl cond set|clear foo' commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 8000d361b3 initctl: restore 'cond [set|clr] foo' for user-defined conditions
In 7447192 we dropped support for 'cond set' and 'cond clear' commands
with the motivation they were unsafe and sent the wrong message to the
user.  That was true, but mostly because it was too generic and required
the user to call `initctl reload` to apply the changes.

This patch restores the behavior, albeit in a very reduced and simple
format.  All conditions set with this command are constrained to the
'usr/...' namespace.  No subdirectories are allowed.  The argument to
the 'cond set|clear' command is disallowed if it contains '/' or '.'
but anything else is supported, for example:

    initctl cond set foo:2

creates a static/oneshot condition in /run/finit/cond/usr/foo:2

These conditions are static and are fully handled by the user.  The
initctl command is the recommended, and only supported, way of setting
and clearing usr conditions.

This patch also includes a new plugin, usr.so, which is a very simple
inotify plugin for the /run/finit/cond/usr/ directory.  When files are
created or removed here the plugin tells the Finit condition engine to
update and trigger service changes.

For instance, the following service is not started by default at boot:

    service <usr/foo> myservice -- MyService

However, as soon as `initctl cond set foo` is called, myservice starts.
Consequently, it is stopped when `initctl cond clr foo` is called.

Another major difference from the original is that this implementation
doesn't send IPC commands to create/delete the conditions.  This makes
calling these new commands non-blocking so they can be used very early
in the bootstrap, by plugins, if needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg e3c8febe3b Refactor; common nomenclature, ordering, possib. security fix
- Use _PATH_foo for all condition paths, *with* trailing /
- Read condition file first, may not exist, in which case we save time
- Change from libte makepath() to mkpath(), this changes from hard-coded
  0777 perms on all cond dirs to 0755 -- possible security fix

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 32ca117949 initctl: refactor command line parser, use concept from mroutectl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg b966d031c8 initctl: enable plain mode for ls and utmp commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-09 21:30:11 +01:00
Joachim Wiberg 905c2678a3 Fix path to fallback console and handle leading /dev from cmdline
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-08 09:33:16 +01:00
Joachim Wiberg 7d429bdbb1 initctl: restore -p,--plain output for headings and ps listing
This is the same output style as used in the mroutectl and pimctl tools.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 23:30:13 +01:00
Joachim Wiberg ccfea59cb3 Bump version for Finit v4.0-rc2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 18:50:50 +01:00
Joachim Wiberg 7b315364ca Update initctl help text, and output, relocate screenshot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:38:23 +01:00
Joachim Wiberg e9e28ece1c Add Finit4 + Alpine screenshot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:28:32 +01:00
Joachim Wiberg c1f9b4e1f6 Use .svg for Travis-CI status badge
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:17:26 +01:00
Joachim Wiberg 6ebb23e0f0 Travis-CI: Minor, adjust configure line for Coverity
- Drop commented-out sudo:true
- Drop unnecessary configure option

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:16:26 +01:00
Joachim Wiberg 026c229005 Revert "Travis-CI: disable clang and unit tests for Coverity Scan run"
This reverts commit c71bafd495.
2021-03-07 15:14:47 +01:00
Joachim Wiberg 4d05bf9359 Mark affected services as dirty if their rdeps are dirty
Provided a configuration that looks like this:

ospfd.conf:
    service [2345] <!pid/zebra> log ospfd -A 127.0.0.1 -u root -g root -- OSPF daemon

zebra.conf:
    service [2345] <!> log zebra -A 127.0.0.1 -u root -g root -- Zebra Routing daemon

If zebra.conf is changed, we restart it when `initctl reload` is issued.

This change ensures that ospfd is also restarted, because ospfd depends
on zebra, we must restart it too.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 14:52:19 +01:00
Joachim Wiberg 289247dab9 plugins: netlink: slightly stricter ifname validation
Check if ifname contains double periods, this should *not* be a valid
name, though eth0.10 is, et0..10 is not.  Should protect better against
directory traversal attacks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 13:25:40 +01:00
Joachim Wiberg e44dfde54e plugins: tty: validate against read() length, not buffer size
Fix ev->len validation; must check against sz read(), not total buffer
size.  Also, fix off-by-one in comparison.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:55:12 +01:00
Joachim Wiberg 35b200d3a9 Validate inotify event against read() length, not buffer size
Fix ev->len validation; must check against sz read(), not total buffer
size.  Also, fix off-by-one in comparison.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:55:12 +01:00
Joachim Wiberg 5badf4d376 plugins: pidfile: validate against read() length, not buffer size
Fix ev->len validation; must check against sz read(), not total buffer
size.  Also, fix off-by-one in comparison.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:48:42 +01:00
Joachim Wiberg 24f274c126 plugins: netlink: validate interface name
Check for spaces and slashes in interface name to prevent path based
attacks.  Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:23:24 +01:00
Joachim Wiberg 1abd43384b plugins: netlink: minor refactor, collapse for-loop
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:20:59 +01:00
Joachim Wiberg 70a1acc210 Highly unlikekly, but chdir() needs to be checked
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 11:40:25 +01:00
Joachim Wiberg 6af0446490 plugins: netlink: fix untrusted loop bound, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 11:28:39 +01:00
Joachim Wiberg ecaf111a4b plugins: tty: possible out-of-bounds read in inotify_event parser
This is a major refactor to use the same construct as in the pidfile.so
plugin to parse kernel inotify events for when TTYs are added removed
from the system.

Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 10:58:55 +01:00
Joachim Wiberg 32ec25b070 Check return value from remove(), found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 10:34:23 +01:00
Joachim Wiberg 305ad302f2 Refactor, reduce code duplication
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 10:28:54 +01:00
Joachim Wiberg d6390244ad Fix possible out-of-bounds read in inotify_event parser
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 09:57:07 +01:00
Joachim Wiberg 790a316607 Log rotate: improved handling of return values from syscalls
- Align the two implementations in logit.c and utmp-api.c
 - Use libite APIs to handle common constructs
 - If gzip fails, don't remove rotated-to file
 - Don't try gzip if rename fails
 - Issues found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 09:57:07 +01:00
Joachim Wiberg 77aa941e84 Mounting devtmpfs may fail if already mounted
Ignore EBUSY errors, the kernel has the ability to automount /dev as
soon as the rootfs has been mounted.  This may be added to procfs and
sysfs later, so we make this a general change in fs_init().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 09:48:02 +01:00
Joachim Wiberg e0a65f67c9 Minor, staticify, reduce includes and allow debug to kmsg
The loglevel setting should control all logging, regardless of
where we target it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-06 23:55:54 +01:00
Joachim Wiberg 3ed291789c Fix GCC warnings with _FORTIFY_SOURCE=2
- Decleare some return values with (void)fn(), for cases where
  we don't care (dropping table headers), or best effor
- Check return value from fgets() and chdir() in some cases that
  are valid, i.e., continuing execution is pointless

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 12:36:48 +01:00
Joachim Wiberg 774a24cdd8 Allow building with -D_FORTIFY_SOURCE[=1,2]
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 11:17:59 +01:00
Joachim Wiberg 6011f91f22 Fix possible out-of-bounds read in inotify_event parser
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 11:16:37 +01:00
Joachim Wiberg 07a364e570 initctl: fix use of possibly unterminated string, found by Coverity
No point in using rq.data, which could have been modified, instead use
the input argument to the function in the error message.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:51:25 +01:00
Joachim Wiberg 5b9d99011b Fix long-standing bug in reset of rlimits between reconf
While skimming through the results of the latest Coverity Scan, I
discovered that that the reset logic of global rlimits was broken.
This it seems to have been since its first introduction in Finit.

We fix this by reading initial rlimits at bootstrap, then for each
reconf, including the first, we seed global rlimits with the initial
ones -- thus resetting between each reconf.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:44:01 +01:00
Joachim Wiberg f4f773b4c7 Initialize fallback svc_t in client comms, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:35:12 +01:00
Joachim Wiberg 9d949c4186 Fix possible NULL ptr deref in cmdline option parser
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:21:58 +01:00
Joachim Wiberg 1434561a46 initctl: fix cut-and-paste error in new 'show' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:17:52 +01:00
Joachim Wiberg cf3d3f6f8e Properly check return value from mount(2) and display error message
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:15:46 +01:00
Joachim Wiberg 621da582c2 Fix obvious bug in ismnt() found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:10:45 +01:00
Joachim Wiberg 9167d9255d Work around weird finding from Coverity Scan
The utmp_set() function allows id==NULL, so default the line to NULL and
avoid parsing empty lines.  Which is better code anyway.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 09:59:58 +01:00
Joachim Wiberg c71bafd495 Travis-CI: disable clang and unit tests for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 08:25:32 +01:00
Joachim Wiberg b3b7a33252 initctl: developer mode, hidden command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 08:22:38 +01:00
Joachim Wiberg d1efc60c84 initctl: add show command to cat foo.conf, default finit.conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 08:06:01 +01:00
Joachim Wiberg 9a24dfd98d initctl: refactor command composition for status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 00:13:01 +01:00
Joachim Wiberg 0e3fe5e3bf initctl: fix too long args list to status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 00:06:36 +01:00
Joachim Wiberg 224f42c344 cgreaper.sh: drop log message when cleaning up
Mostly used during development, doesn't really provide any value to the
user at normal runtime.  Leave it commented out though so user can do
debug themselves if needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:57:48 +01:00
Joachim Wiberg e60eb94810 initctl: complete rewrite of cgroup dumper in 'ps' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:49:42 +01:00
Joachim Wiberg 464bc831a5 Minor refactor, create FINIT_CGPATH in finit.h to reduce duplicaiton
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:48:57 +01:00
Joachim Wiberg e84911fe52 cgroup: create 'name' or 'name:id' entries, not 'name:'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:46:25 +01:00
Joachim Wiberg 4bcfd606b4 Refactor screen_init(), use methods developed in pimd project
- Refactor screen_init()
  - use native impl. of TTY probing from pimd project
    - check if TIOCWINSZ works
    - check if we're running in watch(1), for initctl
  - check if ANSI goto(999,999) escape seq. works (invasive)
  - fallback to 80x24
- Drop screen_exit()
- Rename screen_init() to get_width(), for now, matching pimd
- Relocate call in main() to banner(), first fn to write to TTY

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:23:31 +01:00
Joachim Wiberg fe27ed17eb initctl: status command formatting updates, add origin .conf file
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 07:27:06 +01:00
Joachim Wiberg b3ea23ed26 initctl: drop old 'job:id' format support, canonical is 'name:id'
With the recent changes to the condition handling it has become more and
more evident that the canonical reference for a task/run/service is the
NAME:ID representation.  Up until now we've kept the older JOB:ID for
some sort of compatibility fallback.

This patch removes the support to simplify maintenance going forward.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 07:23:19 +01:00
114 changed files with 5935 additions and 2023 deletions
+10 -13
View File
@@ -5,9 +5,6 @@ language: c
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false
# Use non-docker builds atm. to install libuEv and libite from source
#sudo: true
# Test build with both GCC and Clang (LLVM)
compiler:
- gcc
@@ -30,7 +27,7 @@ addons:
name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
build_command_prepend: "./autogen.sh && ./configure --prefix= --disable-doc --disable-contrib --with-watchdog"
build_command: "make"
branch_pattern: dev
@@ -45,20 +42,20 @@ install:
# Custom build script for the time being, no "make test" yet
script:
- ./autogen.sh
- ./configure --prefix=/tmp --enable-static
- ./configure --prefix= --enable-static
- make clean
- make V=1 -j5
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog
- cat config.log
- make clean
- make V=1 -j5
- make install-strip
- tree /tmp
- ldd /tmp/sbin/finit
- size /tmp/sbin/finit
- size /tmp/sbin/initctl
- size /tmp/sbin/reboot
- LD_LIBRARY_PATH=/tmp/lib /tmp/sbin/initctl -h
- make install-strip DESTDIR=/tmp/finit
- tree /tmp/finit
- ldd /tmp/finit/sbin/finit
- size /tmp/finit/sbin/finit
- size /tmp/finit/sbin/initctl
- size /tmp/finit/sbin/reboot
- LD_LIBRARY_PATH=/tmp/lib /tmp/finit/sbin/initctl -h
- unshare -V
- nsenter -V
- chroot --version
+81 -21
View File
@@ -4,12 +4,47 @@ Change Log
All relevant changes are documented in this file.
[4.0][UNRELEASED] - 2021-02-21
------------------------------
[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
--------------------
This release became v4.0, and not v3.2, because of incompatible changes
to service conditions. There are other significant changes as well, so
make sure to read the whole changelog when upgrading.
make sure to read the whole change log when upgrading.
### Changes
* The stand-alone `reboot` tool has been replaced with a symlink to
@@ -18,31 +53,53 @@ make sure to read the whole changelog when upgrading.
with a fallback to sending signals as per traditional SysV init. The
`-f` (force) flag remains, where `reboot(2)` is called directly
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
* The `inictl cond set|clear COND` have changed completely. Constrained
to a flat `<usr/...>` namespace and automatically activated by a new
`usr.so` plugin that checks services for usr condition changes
* Removed built-in inetd super server. If you need this functionality,
use an external inetd, like xinetd, instead. A pull request for a
stand-alone inetd, like watchdogd and getty, is most welcome!
* Incompatible `configure` script changes, i.e., you must give proper
path arguments to the script, no more guessing just GNU defaults.
There are examples in the documentation and the `contrib/` section
* Change service conditions from the non-obvious `<svc/path/to/foo>` to
`<pid/foo:id>`. This to hopefully make it clear that one service's
'pid:!foo' pidfile is another service's `<pid/foo>` condition
* Initial support for cgroups, every service now runs in its own cgroup
https://twitter.com/b0rk/status/1214341831049252870?s=20
* Incompatible `configure` script changes, i.e., no guessing `--prefix`
and other paths. Also, many options have been changed, renamed, or
flipped defaults, or even dropped altogether. There are examples in
the documentation and the `contrib/` section
* Service conditions change from the non-obvious `<svc/path/to/foo>` to
`<pid/foo:id>`. Not only does this give simpler internal semantics,
it hopefully also makes it clear that one service's `pid:!foo` pidfile
is another service's `<pid/foo>` condition, issue #143
* Initial support for cgroups v2:
- services runs in a cgroup named after their respective *.conf file
- top-level groups are; init, user, and system
- all top-level groups can be configured from finit *.conf files
- each service can tweak the cgroup settings
- Use `initctl [top|ps|cgroup]` commands to inspect runtime state
- https://twitter.com/b0rk/status/1214341831049252870?s=20
* Major refactor of Finit's `main()` function to be able to start the
event loop earlier. This also facilitated factoring out functionality
previously hard-coded in Finit, e.g., starting the bundled watchdogd,
various distro packed udevd and other hotplugging tools
* A proper rescue mode has been added. It is started extremely early
and is protected with a bundled `suslogin`. Exiting rescue mode now
brings up the system as a normal boot, as one expects
* Support for `sysv` start/stop scripts as well as monitoring forking
services, stared using `sysv` or `service` stanza
* Support for custom `kill:DELAY`, default 3 sec.
* Support for custom `halt:SIGNAL`, default SIGTERM
* Support for `pre:script` and `post:script`, allows for setup and
teardown/cleanup before and after a service runs, issue #129
* Support for `env:file` in `/etc/default/foo` or `/etc/conf.d/foo`, see
the contrib section for examples that utilize this feature. Variables
expanded from env files, and the env files themselves, are tracked for
changes to see if a service .conf file is "dirty" and needs restart on
`initctl reload`
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
useful with new `sysv` or `service` which fork to background
* Support starting run/task/services without absolute path, trust `$PATH`
* Add support for `--disable-doc` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds
* Add support for `@console` also for external getty
* Support for `@console` also for external getty
* Support for `notty` option to built-in getty, for board bring-up
* Support for `rescue` option to built-in getty, for rescue shells
* Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog
@@ -92,6 +149,8 @@ make sure to read the whole changelog when upgrading.
* Always append `:ID` qualifier to conditions if set for a service
* The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket`
officially only supported for use by the `initctl` tool
* The IPC socket now uses `SOCK_SEQPACKET` instead of `SOCK_STREAM`.
Recommend using watchdogd v3.4, or later, which support this
* Improved support for modern `/etc/network/interfaces`, which has
include statements. No more native `ifup` of individual interfaces,
Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to
@@ -109,16 +168,21 @@ make sure to read the whole changelog when upgrading.
that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access
* Fix #101: Built-in inetd removed
* Fix #102: Start built-in watchdogd as a regular service
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
Fixed by Jonas Johansson, Westermo
* Fix #109: Support for PID files in sub-directories to `/var/run`
* Handle rename of PID files, by Robert Andersson, Atlas Copco
* Fix #110: automatic modprobe of RTC devices, built-in hwclock
* Fix #120: Redirect `stdin` to `/dev/null` for services by default
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
fixed by Jacques de Laval, Westermo
* Fix #124: Lingering processes in process group when session leader
exits. E.g., lingering `logit` processes when parent dies
* Fix #127: Show all runparts scripts as they start, like rc.local, fixed
by Jacques de Laval, Westermo
* Fix service name matching, e.g. for condition handling, may match with
wrong service, by Jonas Holmberg, Westermo
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard
@@ -137,6 +201,8 @@ make sure to read the whole changelog when upgrading.
* Provide service description for built-in watchdog daemon
* Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system
* Drop the '%m' GNUism, for compat with older musl libc
* Fix #139: call `tzset()` on `initctl reload` to activate system
timezone changes (for logging)
[3.1][] - 2018-01-23
@@ -745,14 +811,14 @@ Major bug fix release.
* Makefile fixes for installation, paths encoded wrong
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip
* Default install is now to `/sbin/finit` and `/usr/`
* Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir`
* Note change in `$PLUGIN_DIR` environment variable to `$plugindir`
[1.2][] - 2012-09-27
--------------------
### Changes
* Update README with section on building and enviroment variables
* Update README with section on building and environment variables
### Fixes
* Fix installation paths encoded in finit binary
@@ -844,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
@@ -876,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:
-->
-16
View File
@@ -29,19 +29,3 @@ release: distcheck
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done
run:
srcdir=@srcdir@/test \
top_builddir=@top_builddir@ \
TENV_ROOT=$(tenv_root) \
@srcdir@/test/setup-root.sh
mkdir -p $(dir $(tenv_root)/@FINIT_CONF@)
echo 'tty [12345] @console linux noclear nologin' > $(tenv_root)/@FINIT_CONF@
-TENV_ROOT=$(tenv_root) \
TESTENV_PATH=@sbindir@:/bin \
FINIT_RCSD=@FINIT_RCSD@ \
test/tenv/start.sh finit
run : tenv_root ?= @abs_top_builddir@/run-rootfs
+182 -54
View File
@@ -26,6 +26,26 @@ Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
was reverse engineered from the [EeePC fastinit][] over ten years ago
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
![Alpine screenshot](img/finit4-screenshot.png)
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
Features include:
* Runlevels
* One-shot tasks, daemons, or SysV init scripts
* Runparts and rc.local
* Process supervision
* Sourcing environment files
* Conditions for network/process/custom dependencies
* Pre/Post script actions
* Tooling to enable/disable services
* Built-in getty
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
* Built-in support for Debian/BusyBox `/etc/network/interfaces`
* Cgroups v2, both configuration and monitoring in `initctl top`
* Plugin support for customization
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section with tutorials for:
@@ -34,9 +54,6 @@ on server and desktop systems as well. For working examples, see the
* [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/)
![Alpine screenshot](img/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
Example
-------
@@ -58,10 +75,10 @@ runlevel 2
log size=100k count=4
# Services to be monitored and respawned as needed
service [S12345] watchdogd -L -f -- System watchdog daemon
service [S12345] syslogd -n -b 3 -D -- System log daemon
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
service [2345] lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
service [S12345] env:-/etc/conf.d/watchdog watchdog $WATCHDOG_OPTS $WATCHDOG_DEV -- System watchdog daemon
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- System log daemon
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
service [2345] env:-/etc/conf.d/lldpd lldpd -d $LLDPD_OPTS -- LLDP daemon (IEEE 802.1ab)
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
@@ -76,24 +93,24 @@ service :80 [2345] merecat -n -p 80 /var/www -- Web server
service :8080 [2345] merecat -n -p 8080 /var/www -- Old web server
# Alternative method instead of below runparts, can also use /etc/rc.local
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd start -- Preparing console
#sysv [S] /etc/init.d/keyboard-setup -- Setting up preliminary keymap
#sysv [S] /etc/init.d/acpid -- Starting ACPI Daemon
#task [S] /etc/init.d/kbd -- Preparing console
# Run start scripts from this directory
# runparts /etc/start.d
# Virtual consoles run BusyBox getty, keep kernel default speed
tty [12345] /sbin/getty -L 0 /dev/tty1 linux
tty [12345] /sbin/getty -L 0 /dev/tty2 linux
tty [12345] /sbin/getty -L 0 /dev/tty3 linux
tty [12345] /sbin/getty -L 0 /dev/tty1 linux nowait noclear
tty [2345] /sbin/getty -L 0 /dev/tty2 linux nowait noclear
tty [2345] /sbin/getty -L 0 /dev/tty3 linux nowait noclear
# Use built-in getty for serial port and USB serial
tty [12345] /dev/ttyAMA0 noclear nowait
tty [12345] /dev/ttyUSB0 noclear
#tty [12345] /dev/ttyAMA0 noclear nowait
#tty [12345] /dev/ttyUSB0 noclear
# Just give me a shell, I need to debug this embedded system!
tty [12345] @console noclear nologin
#tty [12345] console noclear nologin
```
The `service` stanza, as well as `task`, `run` and others are described
@@ -117,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
@@ -232,8 +249,11 @@ For more information, see [doc/plugins.md](doc/plugins.md).
By default, Finit monitors `/etc/finit.d/` and `/etc/finit.d/enabled/`
registering any changes to `.conf` files. To activate a change the user
must call `initctl reload`, which reloads all modified files, stops any
removed services, starts new ones, and restarts any modified ones, with
SIGHUP if the process supports it.
removed services, starts new ones, and restarts any modified ones. If the
command line arguments of a service have changed, the process will be
terminated and then started again with the updated arguments. If the arguments
have not been modified and the process supports SIGHUP, the process will
receive a SIGHUP rather than being terminated and started.
For some use-cases the extra step of calling `initctl reload` creates an
unnecessary overhead, which can be removed at build-time using:
@@ -241,6 +261,46 @@ unnecessary overhead, which can be removed at build-time using:
configure --enable-auto-reload
**Cgroups**
Finit supports cgroups v2 and comes with the following default groups
in which services and user sessions are placed in:
/sys/fs/cgroup
|-- init/ # cpu.weight:100
|-- system/ # cpu.weight:9800
`-- user/ # cpu.weight:100
Finit itself and its helper scripts and services are placed in the
top-level leaf-node group `init/`, which also is _reserved_.
All run/task/service/sysv processes are placed in their own sub-group
in `system/`. The name of each sub-group is taken from the respective
`.conf` file from `/etc/finit.d`.
All getty/tty processes are placed in their own sub-group in `user/`.
The name of each sub-group is taken from the username.
A fourth group also exists, the `root` group. It is also _reserved_ and
primarily intended for RT tasks. If you have RT tasks they need to be
declared as such in their service stanza like this:
service [...] <...> cgroup.root /path/to/foo args -- description
or
cgroup.root
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
See [doc/config.md](doc/config.md#cgroups) for more information, e.g.,
how to configure per-group limits.
The `initctl` tool has three commands to help debug and optimize the
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
commands for details.
Runparts & /etc/rc.local
------------------------
@@ -348,11 +408,18 @@ Finit also implements a modern API to query status, and start/stop
services, called `initctl`. Unlike `telinit` the `initctl` tool does
not return until the given command has fully completed.
```shell
~ $ initctl -h
```
alpine:~# initctl help
Usage: initctl [OPTIONS] [COMMAND]
Options:
-b, --batch Batch mode, no screen size probing
-c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt
-1, --once Only one lap in commands like 'top'
-p, --plain Use plain table headings, no ctrl chars
-q, --quiet Silent, only return status of command
-t, --no-heading Skip table headings
-v, --verbose Verbose output
-h, --help This help text
@@ -360,29 +427,39 @@ Commands:
debug Toggle Finit (daemon) debug
help This help text
version Show Finit version
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes)
cond show Show condition status
ls | list List all .conf in /etc/finit.d
create <CONF> Create .conf in /etc/finit.d/available
delete <CONF> Delete .conf in /etc/finit.d/available
show <CONF> Show .conf in /etc/finit.d/available
edit <CONF> Edit .conf in /etc/finit.d/available
touch <CONF> Change .conf in /etc/finit.d/available
enable <CONF> Enable .conf in /etc/finit.d/available
disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined condition +usr/COND
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
cond status Show condition status, default cond command
cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
status <JOB|NAME>[:ID] Show service status, by job# or name
status | show Show status of services, default command
start <NAME>[:ID] Start service by name, with optional ID
stop <NAME>[:ID] Stop/Pause a running service by name
reload <NAME>[:ID] Reload service by name (SIGHUP or restart)
restart <NAME>[:ID] Restart (stop/start) service by name
status <NAME>[:ID] Show service status, by name
status Show status of services, default command
cgroup List cgroup config overview
ps List processes based on cgroups
top Show top-like listing based on cgroups
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system
halt Halt system
poweroff Halt and power off system
utmp show Raw dump of UTMP/WTMP db
suspend Suspend system
```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -390,22 +467,73 @@ file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
these will also affect how a service is maintained.
**Note:** even though it is possible to start services not belonging in
the current runlevel these services will not be respawned automatically
by Finit if they exit (crash). Hence, if the runlevel is 2, the below
Dropbear SSH service will not be restarted if it is killed or exits.
> **Note:** even though it is possible to start services not belonging in
> the current runlevel these services will not be respawned automatically
> by Finit if they exit (crash). Hence, if the runlevel is 2, the below
> Dropbear SSH service will not be restarted if it is killed or exits.
```shell
~ $ initctl status -v
1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
3 running 478 [S12345] /sbin/klogd -n
4:1 inetd 0 [2345] internal time allow *:37,!eth0
4:2 inetd 0 [2345] internal time allow eth0:3737
5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
The `status` command is the default, it displays a quick overview of all
monitored run/task/services. Here we call `initctl -p`, suitable for
scripting and documentation:
```
alpine:~# initctl -p
PID IDENT STATUS RUNLEVELS DESCRIPTION
======================================================================
1506 acpid running [--2345----] ACPI daemon
1509 crond running [--2345----] Cron daemon
1489 dropbear running [--2345----] Dropbear SSH daemon
1511 klogd running [S12345----] Kernel log daemon
1512 ntpd running [--2345----] NTP daemon
1473 syslogd running [S12345----] Syslog daemon
alpine:~# initctl -pv
PID IDENT STATUS RUNLEVELS COMMAND
======================================================================
1506 acpid running [--2345----] acpid -f
1509 crond running [--2345----] crond -f -S $CRON_OPTS
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
1473 syslogd running [S12345----] syslogd -n
```
The environment variables to each of the services above are read from,
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
have other directories, e.g., Debian use `/etc/default/`.
The `status` command takes an optional `NAME:ID` argument. Here we
check the status of `dropbear`, which only has one instance in this
system:
```
alpine:~# initctl -p status dropbear
Status : running
Identity : dropbear
Description : Dropbear SSH daemon
Origin : /etc/finit.d/enabled/dropbear.conf
Environment : -/etc/conf.d/dropbear
Condition(s):
Command : dropbear -R -F $DROPBEAR_OPTS
PID file : !/run/dropbear.pid
PID : 1485
User : root
Group : root
Uptime : 2 hour 46 min 56 sec
Runlevels : [--2345----]
Memory : 1.2M
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|- 1485 dropbear -R -F
|- 2634 dropbear -R -F
|- 2635 ash
`- 2652 initctl -p status dropbear
Apr 8 12:19:49 alpine authpriv.info dropbear[1485]: Not backgrounding
Apr 8 12:37:45 alpine authpriv.info dropbear[2300]: Child connection from 192.168.121.1:47834
Apr 8 12:37:46 alpine authpriv.notice dropbear[2300]: Password auth succeeded for 'root' from 192.168.121.1:47834
Apr 8 12:37:46 alpine authpriv.info dropbear[2300]: Exit (root) from <192.168.121.1:47834>: Disconnect received
Apr 8 15:02:11 alpine authpriv.info dropbear[2634]: Child connection from 192.168.121.1:48576
Apr 8 15:02:12 alpine authpriv.notice dropbear[2634]: Password auth succeeded for 'root' from 192.168.121.1:48576
```
@@ -473,6 +601,6 @@ and proposed extensions.
[License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[Travis]: https://travis-ci.org/troglobit/finit
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+23 -45
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_INIT(Finit, 4.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
@@ -32,7 +32,7 @@ AC_PROG_MKDIR_P
# Configuration.
AC_HEADER_STDC
AC_CHECK_HEADERS([fstaby.h termios.h])
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
AC_CHECK_FUNCS([strstr getopt getfsenty])
# Check for uint[8,16,32]_t
@@ -48,30 +48,13 @@ PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
# Check for configured Finit features
AC_ARG_ENABLE(emergency_shell,
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
Use only for debugging very early boot problems with Finit]),,[
enable_emergency_shell=no])
AC_ARG_ENABLE(fallback_shell,
AS_HELP_STRING([--enable-fallback-shell], [Start a shell if no TTYs can start]),,[
enable_fallback_shell=no])
AC_ARG_ENABLE(auto_reload,
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
enable_auto_reload=no])
AC_ARG_ENABLE(watchdog,
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_watchdog=no])
AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
enable_redirect_output=no])
AC_ARG_ENABLE(logit,
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
enable_logit=yes])
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
enable_redirect=yes])
AC_ARG_ENABLE(logrotate,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
@@ -128,31 +111,25 @@ AC_ARG_WITH(random-seed,
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
[random_seed=$withval], [random_seed=yes])
AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval], [with_watchdog=no watchdog=])
### Enable features ###########################################################################
# Create config.h from selected features and fallback defautls
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
AS_IF([test "x$enable_fallback_shell" = "xyes"], [
AC_DEFINE(FALLBACK_SHELL, 1, [Start /bin/sh on console if no TTYs.])])
# Create config.h from selected features and fallback defaults
AS_IF([test "x$enable_auto_reload" = "xyes"], [
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
AS_IF([test "x$enable_watchdog" = "xyes"], [
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
### Disable features ###########################################################################
AS_IF([test "x$enable_logit" != "xno"], [
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
AS_IF([test "x$enable_logrotate" != "xno"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ##############################################################################
AS_IF([test "x$with_config" != "xno"], [
AS_IF([test "x$conf" = "xyes"], [
@@ -202,10 +179,16 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_EXPAND_DIR(random_path, "$random_seed")
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
AS_IF([test "x$with_watchdog" != "xno"], [
AS_IF([test "x$watchdog" = "xyes"], [
watchdog=/dev/watchdog])
with_watchdog=yes
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
watchdog=])
# Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
@@ -224,9 +207,6 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
AC_EXPAND_DIR(path_stdpath, "$sbindir:$bindir:/usr/sbin:/usr/bin:/sbin:/bin")
AC_DEFINE_UNQUOTED(_PATH_STDPATH, "$path_stdpath", [Default PATH])
# Generate all files
AC_OUTPUT
@@ -235,7 +215,7 @@ cat <<EOF
------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $prefix
Exec prefix...........: `eval echo $exec_prefix`
Exec prefix...........: $eprefix
Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir`
Finit config file.....: $conf_path
@@ -254,11 +234,9 @@ Behavior:
Optional features:
Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib
Built-in watchdogd....: $enable_watchdog
Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate
Scripting tool logit..: $enable_logit
Emergency shell.......: $enable_emergency_shell
Fallback shell........: $enable_fallback_shell
Redirect output.......: $enable_redirect
Default hostname......: $hostname
Default runlevel......: $runlevel
+1 -1
View File
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --enable-watchdog \
--enable-alsa-utils-plugin --with-watchdog \
--with-heading="Alpine Linux 3.13"
echo
+1 -2
View File
@@ -1,5 +1,4 @@
docsdir := @docdir@/contrib/alpine/finit.d/available
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
lxdm.conf modules.conf ntpd.conf klogd.conf syslog.conf \
watchdog.conf
lxdm.conf modules.conf ntpd.conf syslog.conf watchdog.conf
EXTRA_DIST = $(docs_DATA)
+4 -3
View File
@@ -1,4 +1,5 @@
# Consoles to start getty on when system is up
tty [12345] /dev/tty1 noclear nowait
tty [2345] /dev/tty2 noclear nowait
tty [2345] /dev/tty3 noclear nowait
tty [12345] /dev/ttyS0 noclear
tty [12345] /dev/tty1 noclear nowait
tty [2345] /dev/tty2 noclear nowait
tty [2345] /dev/tty3 noclear nowait
@@ -1,2 +0,0 @@
# Start klogd as soon as possible, after syslogd, should always run
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
@@ -4,3 +4,4 @@
#module evdev
#module mousedev
#module 8139cp
module softdog
+3 -1
View File
@@ -1,2 +1,4 @@
# Start syslogd as soon as possible, should always run
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
# Start klogd as soon as possible, but after syslogd
service [S12345] env:-/etc/conf.d/syslog syslogd -n $SYSLOGD_OPTS -- Syslog daemon
service [S12345] <pid/syslogd> env:-/etc/conf.d/klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
+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)
+19 -7
View File
@@ -20,29 +20,41 @@ tools pre-systemd work as intended.
root@debian:~# apt install initscripts console-setup
> **Note:** as of Debian 11 (Bullseye), libuev and libite are part of
> the main section of Debian. So just install the -dev packages :)
The following script can then be used to configure, build, install and
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 rm init
user@debian:/sbin# sudo mv init oldinit
user@debian:/sbin# sudo ln -s finit init
Before rebooting, check the default [/etc/finit.conf](finit.conf) and
`/etc/finit.d/*.conf` files. The build + install script above provides
a few sample `.conf` files. See `initctl list` after boot for a list of
enabled and available services.
a few sample `.conf` files. See `initctl ls` after boot for a list of
enabled and available services, you can then use `enable` and `disable`
comands to `initctl`, followed by `reload` to activate your changes.
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
and initialization like keyboard language etc.
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. 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!
/Joachim ツ
+4 -2
View File
@@ -1,7 +1,9 @@
SUBDIRS = available
docsdir := @docdir@/contrib/debian/finit.d
d_LINKS = console-setup.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
+5 -3
View File
@@ -1,5 +1,7 @@
docsdir := @docdir@/contrib/debian/finit.d/available
docs_DATA = acpid.conf console-setup.conf getty.conf keyboard-setup.conf \
lightdm.conf modules.conf rsyslogd.conf sntpd.conf \
sshd.conf syslogd.conf xdm.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
@@ -0,0 +1,4 @@
# On some systems, bootstrap can be really quick and cause elogind
# to not start before a user has managed to log in. To prevent
# this, add <pid/elogind> as condition to your TTYs in getty.conf
service [2345] <pid/syslogd> /lib/elogind/elogind -- Login manager
+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"
+29 -2
View File
@@ -8,6 +8,18 @@ module evdev
module loop
module psmouse
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9700 cpu.max:50000
# Example extra cgroup
cgroup maint cpu.weight:100
# Runlevel to start after bootstrap, runlevel 'S'
runlevel 2
@@ -16,7 +28,7 @@ network service networking start
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size=100k count=4
log size:100k count:4
# Virtual consoles to start getty on
tty /dev/tty1
@@ -30,9 +42,24 @@ tty /dev/tty3
#run [2] /etc/init.d/networking start -- Start networking
# Services to be monitored and respawned as needed
# klgod and syslogd are placed in the maint cgroup
cgroup.maint
service [2345] <pid/syslogd> klogd -n -- Kernel logging server
service [2345] syslogd -n -- Syslog server
service [3] gdm -- GNOME Display Manager
# gdm in the system cgroup (default when read from it's own .conf file)
cgroup.system
service [3] cgroup:cpu.weight:250,mem.max:10M gdm -- GNOME Display Manager
# Start SSH daemon with opts from /etc/default/ssh (if available), move
# to user cgroup with cpu.weight:250. Default for services is system
# group. Default weight in user group is 100 (above). Max length of
# cgroup argument is (currently) 63 chars.
#
# Notice alternative syntax where current cgroup is only set for this
# particular service.
service [2345] cgroup.user:cpu.weight:250,mem.max:10M env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
+1 -1
View File
@@ -17,7 +17,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --enable-watchdog \
--enable-alsa-utils-plugin --with-watchdog \
--with-heading="Void Linux"
echo
+2 -2
View File
@@ -1,5 +1,5 @@
docsdir := @docdir@/contrib/void/finit.d/available
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
sshd.conf syslogd.conf uuidd.conf
keymap.conf lxdm.conf modules.conf ntpd.conf \
sshd.conf syslog.conf uuidd.conf
EXTRA_DIST = $(docs_DATA)
@@ -1,2 +0,0 @@
# Start klogd as soon as possible, should always run
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
@@ -4,3 +4,4 @@
#module evdev
#module mousedev
#module 8139cp
module softdog
@@ -0,0 +1,4 @@
# Start syslogd as soon as possible, should always run
# Start klogd as soon as possible, but after syslogd
service [S12345] syslogd -n -- Syslog daemon
service [S12345] <pid/syslogd> klogd -n -- Kernel log daemon
@@ -1,2 +0,0 @@
# Start syslogd as soon as possible, should always run
service [S12345] syslogd -n -- Syslog daemon
+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
+10 -4
View File
@@ -19,9 +19,6 @@ Some of these are also registered in the issue tracker on GitHub.
* Add new (complimentary) .svc file format to slowly migrate away
from the very terse one-liner format currently used
* Allow running as non-pid1 => read .conf and RCSD form cmdline
* When a process dies, and Finit does not restart it, we should collect
a `siginfo_t` from the SIGCHLD signal and supply to the user in case
`initctl status <process>` is called to debug the issue.
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
@@ -29,9 +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 ...
* Add support for timed shutdown in Finit, including cancelled shutdown
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
+6 -4
View File
@@ -9,9 +9,11 @@ Bootstrap
6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type
9. Parse `/etc/finit.conf` and all `/etc/finit.d/*.conf` files
10. Start built-in watchdog, if enabled
11. Set hostname
9. Parse `/etc/finit.conf`
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
means any WDT that requires a kernel module need to be either
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
11. Load all `/etc/finit.d/*.conf` files and set hostname
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
14. Mount all file systems listed in `/etc/fstab` and swap, if available
@@ -34,7 +36,7 @@ Bootstrap
24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs, or a fallback shell on `/dev/console`
27. Start all configured TTYs
In (19) and (22) tasks and services defined in `/etc/finit.conf` and
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task`
+10 -7
View File
@@ -153,19 +153,22 @@ kernel messages and add `debug` to enable Finit debug messages.
append="init=/sbin/finit debug"
```
To debug startup issues, in particular issues with getty/login, try
`configure --enable-fallback-shell`. When no TTYs are detected, and
Finit is configured with this option, Finit will try to start a bare
`/bin/sh` on the boot console. Remember, this is only for debugging
and would leave your production system potentially wide open.
To debug startup issues, in particular issues with getty/login, add
the following to your Finit .conf file:
tty [12345789] notty noclear
The `notty` option ensures reusing the stdin/stdout set up by the
kernel. Remember, this is only for debugging and would leave your
production system potentially wide open.
There is also a rescue shell available, in case Finit crashes and the
kernel usually reboots: `configure --enable-emergency-shell`. However,
the behavior of Finit is severely limited when this is enabled, so use
it only for debugging start up issues when Finit crashes.
**NOTE:** Neither of these two configure options should be enabled on
production systems since they can potentially give a user root access.
**NOTE:** Neither of these options should be enabled on production
systems since they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz
+7 -5
View File
@@ -30,10 +30,12 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
* `quiet`: Suppress kernel logging to console, except for warnings and
errors.
* `rescue`: Start Finit rescue mode; no network, no `fsck` of file
systems in `/etc/fstab`, no `/etc/rc.local`, no `runparts`. The
configuration is read from `/lib/finit/rescue.conf`, and can be
modified by the operator.
* `rescue`: Start rescue/maintenance mode. If your system comes with
the bundled `sulogin` program (Finit, or from util-linux/Busybox),
you will be given a root login to a maintenance shell. However, if
`sulogin` is missing, the file `/lib/finit/rescue.conf` is read and
the system booted in a limited fallback mode. See [config.md][]
for more information.
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
debug problems with the regular runlevel. All services and TTYs in
@@ -42,5 +44,5 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
For more on kernel boot paramaters, see the man page [bootparam(7)][].
[config.md][]: config.md#rescue-mode
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
+27 -1
View File
@@ -45,7 +45,31 @@ being created.
Triggering
----------
Conditions can only be triggered by built-in plugins.
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands:
initctl cond set foo
initctl cond clear foo
The purpose of user-defined conditions is to be able to start or stop
services, or run/task jobs, on external site-dependent stimuli.
Example:
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
For convenience, prefixing with `usr/` is allowed, but any other slashes
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
the same as above, can also be achieved like this:
initctl cond set usr/foo
initctl cond clear usr/foo
Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the
@@ -57,6 +81,8 @@ unchanged) state of it.
> **Note:** For `pid/` conditions it is expected that services "touch"
> or recreate their PID file on `SIGHUP`.
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions
-------------------
+224 -28
View File
@@ -2,7 +2,9 @@ Configuration
=============
* [Introduction](#introduction)
* [Service Environment](#service-environment)
* [Service Wrapper Scripts](#service-wrapper-scripts)
* [Cgroups](#cgroups)
* [Configuration File Syntax](#configuration-file-syntax)
* [Hostname](#hostname)
* [Kernel Modules](#kernel-modules)
@@ -19,9 +21,11 @@ Configuration
* [TTYs and Consoles](#ttys-and-consoles)
* [Non-privileged Services](#non-privileged-services)
* [Redirecting Output](#redirecting-output)
* [Rescue Mode](#rescue-mode)
* [Limitations](#limitations)
* [/etc/finit.conf](#etcfinitconf)
* [/etc/finit.d](#etcfinitd)
* [Watchdog](#watchdog)
Introduction
@@ -35,8 +39,8 @@ Linux distributions -- each package can provide its own "script" file.
- `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf`
and some directives are only available at bootstrap, runlevel `S`, see
the section [Limitations](#limitations) below for details.
and some directives are only available at [bootstrap][], runlevel `S`,
see the section [Limitations](#limitations) below for details.
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
@@ -128,6 +132,67 @@ example employs a wrapper script in `/etc/start.d`.
> instead of sending SIGHUP at restart/reload events.
Cgroups
-------
There are three major cgroup configuration directives:
1. Global top-level group: init, system, user, or a custom group
2. Selecting a top-level group for a set of run/task/services
3. Per run/task/service limits
Top-level group configuration.
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9800
Adding an extra cgroup `maint/` will require you to adjust the weight of
the above three. We leave `init/` and `user /` as-is reducing weight of
`system/` to 9700.
cgroup system cpu.weight:9700
# Example extra cgroup 'maint'
cgroup maint cpu.weight:100
By default, the `system/` cgroup is selected for almost everything. The
`init/` cgroup is reserved for PID 1 itself and its closest relatives.
The `user/` cgroup is for local TTY logins spawned by getty.
To select a different top-level cgroup, e.g. `maint/`, one can either
define it for a group of run/task/service directives in a `.conf` or per
each stanza:
cgroup.maint
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
or
service [...] <...> cgroup.maint /path/to/foo args -- description
The latter form also allows per-stanza limits on the form:
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
Notice the comma separation and the `mem.` exception to the rule: every
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is not filtering,
except for expanding the shorthand `mem.` to `memory.`, if the file is
not available, either the cgroup controller is not available in your
Linux kernel, or the name is misspelled.
> Linux cgroups and details surrounding values are not explained in the
> Finit documentation. The Linux admin-guide cover this well:
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
Configuration File Syntax
-------------------------
@@ -140,7 +205,7 @@ the contents of that file is used.
Deprecated. We recommend using `/etc/hostname` instead.
> **Note:** only read and executed in runlevel S (bootstrap).
> **Note:** only read and executed in runlevel S ([bootstrap][]).
### Kernel Modules
@@ -158,7 +223,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
> **Note:** only read and executed in runlevel S (bootstrap).
> **Note:** only read and executed in runlevel S ([bootstrap][]).
### Networking
@@ -171,7 +236,7 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
> **Note:** only read and executed in runlevel S (bootstrap).
> **Note:** only read and executed in runlevel S ([bootstrap][]).
### Resource Limits
@@ -205,13 +270,13 @@ stanzas can share the same rlimits if they are in the same .conf.
**Syntax:** `runlevel <N>`
Defines the system runlevel to go to after bootstrap (S) has completed.
N is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
The system runlevel to go to after [bootstrap][] (S) has completed. `N`
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt.
Default: 2
> **Note:** only read and executed in runlevel S (bootstrap).
> **Note:** only read and executed in runlevel S ([bootstrap][]).
### One-shot Commands (sequence)
@@ -333,6 +398,28 @@ process using a different signal, use the option `halt:SIGNAL`, e.g.,
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
sending `SIGKILL`.
Services support `pre:script` and `post:script` actions as well. These
run as the same `@USER:GROUP` as the service itself, with any `env:file`
sourced. The scripts must use an absolute path, but are executed from
the `$HOME` of the given user. The scripts are not called with any
argument (currently), but both get the `SERVICE_IDENT=foo` environment
variable set. Here `foo` denotes the identity of the service, which if
there are multiple services named `foo`, may be `foo:1`, or any unique
identifier specified in the .conf file. The `post:script` is called
with an additional set of environment variables:
- `EXIT_CODE=[exited,signal]`: set to one of `exited` or `signal`
- `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from
the program, if it exited normally, or the signal name (`HUP`,
`TERM`, etc.) if it exited due to signal
The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
> **Note:** the `pre:script` _must_ be idempotent, because a service
> can transition between READY and HALTED states any number
> of times before going to RUNNING.
### Run-parts Scripts
@@ -341,7 +428,7 @@ sending `SIGKILL`.
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel
`S`, bootstrap.
`S`, [bootstrap][].
It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing
@@ -351,7 +438,10 @@ Similar to the `/etc/rc.local` shell script, make sure that all your
services and programs either terminate or start in the background or
you will block Finit.
> **Note:** only read and executed in runlevel S (bootstrap).
> **Note:** only read and executed in runlevel S ([bootstrap][]).
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
### Including Finit Configs
@@ -377,8 +467,9 @@ size limit is reached.
### TTYs and Consoles
**Syntax:** `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
`tty [LVLS] <COND> [notty] [rescue]`
The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. The DEV may be the special
@@ -386,15 +477,29 @@ keyword `@console`, or `console`, useful on embedded systems.
The default baud rate is 0, i.e., keep kernel default.
> The `tty` stanza inherits runlevel, condition (and other feature)
> parsing from the `service` stanza. So TTYs can run in one or many
> runlevels and depend on any condition supported by Finit. This is
> useful e.g. to depend on `<pid/elogind>` before starting a TTY.
**Example:**
tty [12345] /dev/ttyAMA0 115200 noclear vt220
The second `tty` syntax variant is for using an external getty, like
agetty or the BusyBox getty.
By default both variants *clear* the TTY and *wait* for the user to
press enter before starting getty.
The third variant is for board bringup and the `rescue` boot mode. No
device node is required in this variant, the same output that the kernel
uses is reused for stdio. If the `rescue` option is omitted, a shell is
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
option is set the bundled `/libexec/finit/sulogin` is started to present
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
not have a password set, no rescue is possible. For more information,
see the [Rescue Mode](#rescue-mode) section.
By default, the first two syntax variants *clear* the TTY and *wait* for
the user to press enter before starting getty.
**Example:**
@@ -423,16 +528,23 @@ uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`. The speed
can be omitted to keep the kernel default.
> Most systems get by fine by just using `console`, which will evaluate
> to `/dev/console`. If you have to use `@console` to get any output,
> you may have some issue with your kernel config.
**Example:**
tty [12345] @console noclear vt220
On really bare bones systems Finit offers a fallback shell, which
should not be enabled on production systems since. This because it
may give a user root access without having to log in. However, for
bringup and system debugging it can come in handy:
On really bare bones systems, or for board bringup, Finit can give you a
shell prompt as soon as [bootstrap][] is done, without opening any
device node:
configure --enable-fallback-shell
tty [12345789] notty
This should of course not be enabled on production systems. Because it
may give a user root access without having to log in. However, for
board bringup and system debugging it can come in handy.
One can also use the `service` stanza to start a stand-alone shell:
@@ -483,8 +595,58 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
Rescue Mode
-----------
Finit supports a rescue mode which is activated by the `rescue` option
on the kernel command line. See [cmdline docs](cmdline.md) for how to
activate it.
The rescue mode comes in two flavors; *traditional* and *fallback*.
### Traditional
This is what most users expect. A very early maintenance login prompt,
served by the bundled `/libexec/finit/sulogin` program, or the standard
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
rescue mode is ended and the system boots up normally.
> **Note:** if the user (UID 0 and GID 0) does not have a password, or
> __the account is locked__, the user is presented with a password-less
> `"Press enter to enter maintenance mode."`, prompt which opens up a
> root shell.
### Fallback
If no `sulogin` program is found, Finit tries to bring up as much of its
own functionality as possible, yet limiting many aspects, meaning; no
network, no `fsck` of file systems in `/etc/fstab`, no `/etc/rc.local`,
no `runparts`, and most plugins are skipped (except those that provide
functionality for the condition subsystem).
Instead of reading `/etc/finit.conf` et al, system configuration is read
from `/lib/finit/rescue.conf`, which can be freely modified by the
system administrator.
The bundled default `rescue.conf` contains nothing more than:
runlevel 1
tty [12345] rescue
The `tty` has the `rescue` option set, which works similar to the board
bring-up tty option `notty`. The major difference being that `sulogin`
is started to query for root/admin password. If `sulogin` is not found,
`rescue` behaves like `notty` and gives a plain root shell prompt.
If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
tty [12345] rescue
There is no way to exit the *fallback* rescue mode.
Limitations
@@ -496,15 +658,49 @@ from `/etc/finit.conf`, since it is the first `.conf` file Finit reads.
Originally, `/etc/finit.conf` was the only way to set up a Finit system.
Today it is mainly used for bootstrap settings like system hostname,
network bringup and system shutdown. These can now also be set in any
`.conf` file in `/etc/finit.d`.
early module loading for watchdogd, network bringup and system shutdown.
These can now also be set in any `.conf` file in `/etc/finit.d`.
There is, however, nothing preventing you from having all configuration
settings in `/etc/finit.conf`.
> **Note:** The `/etc/finit.d` directory was previously the default
> Finit `runparts` directory. Finit no longer has a default
> `runparts`, make sure to update your setup, or the finit
> configuration, accordingly.
> Finit [runparts](#run-parts-scripts) directory. Finit no
> longer has a default `runparts`, make sure to update your
> setup, or the finit configuration, accordingly.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
Watchdog
--------
When built `--with-watchdog` a separate service is built and installed
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
device node exists, Finit will start it and treat it as the elected
watchdog service to delegate its reboot to. See [bootstrap][] for
details. This delegation is to ensure that the system is rebooted by a
hardware watchdog timer -- on many embedded systems this is crucial to
ensure all circuits on the board are properly reset for the next boot,
in effect ensuring the system works the same after both a power-on and
reboot event.
The delegation is performed at the very last steps of system shutdown,
if reboot has been selected and an elected watchdog is known, first a
`SIGPWR` is sent to advise watchdogd of the pending reboot. Then, when
the necessary steps of preparing the system for shutdown (umount etc.)
are completed, Finit sends `SIGTERM` to watchdogd and puts itself in a
10 sec timeout loop waiting for the WDT to reset the board. If a reset
is not done before the timeout, Finit falls back to`reboot(RB_AUTOBOOT)`
which tells the kernel to do the reboot.
An external watchdog service can also be used. The more advanced cousin
[watchdogd](https://github.com/troglobit/watchdogd/) is the recommended
option here. It can register itself with Finit using the same IPC as
`initctl`. If the bundled watchdogd is running a hand-over takes place,
so it's safe to have both services installed on a system. For the
hand-over to work it requires that the WDT driver supports the safe exit
functionality where `"V"` is written to the device before closing the
device descriptor. If the kernel driver has been built without this,
the only option is to remove `/libexec/finit/watchdogd` or build without
it at configure time.
[bootstrap]: bootstrap.md
+1 -1
View File
@@ -18,7 +18,7 @@ The current state depends on the two following conditions:
A service may be stopped, or blocked, for several reasons:
- The user has manually stopped the service using `initctl stop JOB`
- The user has manually stopped the service using `initctl stop NAME`
- The program exits immediately. I.e. keeps crashing (make sure to use
the 'run this service in the foreground' command line option)
- The binary is missing in the filesystem
+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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

+2 -2
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
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
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
+10
View File
@@ -30,6 +30,11 @@
static void save(void *arg)
{
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (whichp(ALSACTL)) {
_d("Saving sound settings ...");
run_interactive(ALSACTL " -g store", "Saving sound settings");
@@ -38,6 +43,11 @@ static void save(void *arg)
static void restore(void *arg)
{
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (whichp(ALSACTL)) {
_d("Restoring sound settings ...");
run_interactive(ALSACTL " -g restore", "Restoring sound settings");
+43 -44
View File
@@ -65,7 +65,7 @@ static int is_tmpfs(char *path)
return tmpfs;
}
static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{
if (ftw->level == 0)
return 1;
@@ -76,8 +76,14 @@ static int do_clean(const char *fpath, const struct stat *sb, int tflag, struct
return 0;
}
/* We can safely skip tmpfs, nothing to clean from previous boot there */
static void bootclean(void)
/*
* Cleanup stale files from previous boot, if any still linger on.
* Some systems, e.g. Alpine Linux, still have a persistent /run and
* /tmp, i.e. not tmpfs.
*
* We can safely skip tmpfs, nothing to clean there.
*/
static void clean(void *arg)
{
char *dir[] = {
"/tmp/",
@@ -90,7 +96,7 @@ static void bootclean(void)
if (is_tmpfs(dir[i]))
continue;
nftw(dir[i], do_clean, 20, FTW_DEPTH);
nftw(dir[i], bootclean, 20, FTW_DEPTH);
}
}
@@ -99,13 +105,11 @@ static void bootclean(void)
*/
static void setup(void *arg)
{
int uid, gid;
mode_t prev;
/* Cleanup stale files, if any still linger on. */
bootclean();
prev = umask(0);
_d("Setting up FHS structure in /var ...");
umask(0);
makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755);
@@ -115,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);
@@ -133,19 +138,21 @@ static void setup(void *arg)
makedir("/var/tmp", 0755);
makedir("/var/empty", 0755);
/*
* If /etc/group or "utmp" group is missing,
* default to "root"/"wheel" group
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
/*
* UTMP actually needs multiple db files
*/
if (has_utmp()) {
int gid;
/*
* If /etc/group or "utmp" group is missing, default to
* "root", or "wheel", group.
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
@@ -165,39 +172,31 @@ static void setup(void *arg)
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
makedir("/var/run/frr", 0755); /* Needed by FRR */
makedir("/var/log/frr", 0755); /* Needed by FRR */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
mksubsys("/var/run/frr", 0755, "frr", "frr");
mksubsys("/var/log/frr", 0755, "frr", "frr");
makedir("/var/run/sshd", 01755); /* OpenSSH */
gid = getgroup("tty");
if (gid < 0)
gid = 0;
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid);
if (!fexist("/etc/mtab"))
symlink("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
uid = getuser("uuidd", NULL);
if (uid >= 0) {
gid = getgroup("uuidd");
if (gid < 0)
gid = 0;
makedir("/var/run/uuidd", 0755);
chown("/var/run/uuidd", uid, gid);
}
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
umask(022);
/* 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);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.hook[HOOK_MOUNT_POST] = { .cb = clean },
.hook[HOOK_BASEFS_UP] = { .cb = setup },
.depends = { "pidfile" },
};
+18 -11
View File
@@ -32,13 +32,21 @@
#include "conf.h"
#define DAEMON "dbus-daemon"
#define ARGS "--nofork --system"
#define ARGS "--nofork --system --syslog-only"
#define DESC "D-Bus message bus daemon"
#define DAEMONUSER "messagebus"
static void setup(void *arg)
{
char *cmd;
char line[256];
mode_t prev;
char *cmd;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
cmd = which(DAEMON);
if (!cmd) {
@@ -46,29 +54,28 @@ static void setup(void *arg)
return;
}
umask(0);
prev =umask(0);
makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
if (whichp("dbus-uuidgen"))
run("dbus-uuidgen --ensure");
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
/* Clean up from any previous pre-bootstrap run */
erase("/var/run/dbus/pid");
remove("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
snprintf(line, sizeof(line), "[S12345789] 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);
free(cmd);
umask(022);
umask(prev);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_NETWORK_UP] = {
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc", },
+9 -3
View File
@@ -34,6 +34,11 @@ static void setup(void *arg)
char cmd[256];
char *path;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
@@ -43,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);
@@ -90,6 +95,7 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc" },
};
PLUGIN_INIT(plugin_init)
+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++;
+291 -71
View File
@@ -17,6 +17,7 @@
*/
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h>
@@ -29,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)
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!");
@@ -63,8 +68,14 @@ static void nl_route(struct nlmsghdr *nlmsg)
r = NLMSG_DATA(nlmsg);
a = RTM_RTA(r);
la = RTM_PAYLOAD(nlmsg);
if (la >= len) {
_e("Packet too large!");
return;
}
while (RTA_OK(a, la)) {
void *data = RTA_DATA(a);
switch (a->rta_type) {
case RTA_GATEWAY:
gw = *((int *)data);
@@ -73,8 +84,8 @@ static void nl_route(struct nlmsghdr *nlmsg)
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:
@@ -86,11 +97,20 @@ static void nl_route(struct nlmsghdr *nlmsg)
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;
}
}
}
@@ -105,12 +125,45 @@ static void net_cond_set(char *ifname, char *cond, int set)
cond_clear(msg);
}
static void nl_link(struct nlmsghdr *nlmsg)
static int validate_ifname(const char *ifname)
{
if (!ifname || !ifname[0])
return 1;
if (strnlen(ifname, IFNAMSIZ) == IFNAMSIZ)
return 1;
if (!strcmp(ifname, ".") || !strcmp(ifname, ".."))
return 1;
while (*ifname) {
if (*ifname == '/' || *ifname == ':' || isspace(*ifname))
return 1;
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!");
@@ -120,68 +173,228 @@ static void nl_link(struct nlmsghdr *nlmsg)
i = NLMSG_DATA(nlmsg);
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
if (la >= len) {
_e("Packet too large!");
return;
}
while (RTA_OK(a, la)) {
if (a->rta_type == IFLA_IFNAME) {
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK:
/*
* New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN
*/
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
break;
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) {
if (a->rta_type != IFLA_IFNAME)
continue;
case RTM_DELLINK:
/* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname);
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
break;
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
if (validate_ifname(ifname)) {
_d("Invalid interface name '%s', skipping ...", ifname);
continue;
}
case RTM_NEWADDR:
_d("%s: New Address", ifname);
break;
switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK:
/*
* New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN
*/
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
nl_check_default(ifname);
break;
case RTM_DELADDR:
_d("%s: Deconfig Address", ifname);
case RTM_DELLINK:
/* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname);
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
nl_check_default(ifname);
break;
case RTM_NEWADDR:
_d("%s: New Address", ifname);
break;
case RTM_DELADDR:
_d("%s: Deconfig Address", ifname);
break;
default:
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
break;
}
}
}
static int nl_parse(int sd)
{
while (1) {
struct nlmsghdr *nh;
ssize_t len;
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:
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
_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;
}
}
a = RTA_NEXT(a, la);
}
}
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;
}
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)
{
ssize_t len;
static char buf[4096];
struct nlmsghdr *nh;
memset(buf, 0, sizeof(buf));
len = recv(sd, buf, sizeof(buf), 0);
if (len < 0) {
if (errno != EINTR) /* Signal */
_pe("recv()");
return;
if (nl_parse(sd) < 0) {
if (errno == ENOBUFS) { /* netlink(7) */
_w("busy system, resynchronizing with kernel.");
nl_resync(1);
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);
else
nl_link(nh);
/*
* 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;
}
}
@@ -201,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) {
@@ -212,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) {
@@ -221,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);
}
+36 -17
View File
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
}
}
return iwatch_add(iw, path, IN_ONLYDIR);
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
}
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
svc_t *svc;
paste(fn, sizeof(fn), dir, name);
_d("path: %s, mask: %08x", fn, mask);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return;
_d("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn);
if (!svc) {
_d("No matching svc for %s", fn);
@@ -82,15 +82,25 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc);
if (!svc_has_pidfile(svc))
pid_file_set(svc, fn, 1);
if (svc_is_forking(svc)) {
pid_t pid;
pid = pid_file_read(pid_file(svc));
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
if (pid == -1) {
cond_clear(cond);
return;
}
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
}
cond_set(cond);
@@ -151,7 +161,8 @@ static void pidfile_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz, off;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
@@ -160,15 +171,19 @@ static void pidfile_callback(void *arg, int fd, int events)
}
ev_buf[sz] = 0;
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
if (!iwp)
@@ -179,12 +194,7 @@ static void pidfile_callback(void *arg, int fd, int events)
continue;
}
if (ev->mask & IN_DELETE) {
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
continue;
}
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
pidfile_update_conds(iwp->path, ev->name, ev->mask);
}
}
@@ -226,8 +236,14 @@ static void pidfile_reconf(void *arg)
static void pidfile_init(void *arg)
{
char piddir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
return;
}
/*
* The bootmisc plugin is responsible for setting up /var/run.
* and/or /run, with proper symlinks etc. We depend on bootmisc
@@ -235,6 +251,7 @@ static void pidfile_init(void *arg)
*/
path = realpath(_PATH_VARRUN, NULL);
if (!path) {
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL);
if (!path) {
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
@@ -244,6 +261,8 @@ static void pidfile_init(void *arg)
if (pidfile_add_path(&iw_pidfile, path))
iwatch_exit(&iw_pidfile);
free(path);
}
/*
+8
View File
@@ -23,6 +23,9 @@
#include <glob.h>
#include <lite/lite.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
static void setup(void *arg)
@@ -31,6 +34,11 @@ static void setup(void *arg)
glob_t gl;
char buf[160];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
+10
View File
@@ -84,6 +84,11 @@ static void rtc_save(void *arg)
int fd, rc = 0;
char tz[128];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
fd = rtc_open();
if (fd < 0)
return;
@@ -121,6 +126,11 @@ static void rtc_restore(void *arg)
int fd, rc = 0;
char tz[128];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
fd = rtc_open();
if (fd < 0)
return;
+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:
*/
+46 -25
View File
@@ -32,13 +32,14 @@
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "service.h"
#include "tty.h"
static void watcher(void *arg, int fd, int events);
static void tty_watcher(void *arg, int fd, int events);
static plugin_t plugin = {
.io = {
.cb = watcher,
.cb = tty_watcher,
.flags = PLUGIN_IO_READ,
},
};
@@ -53,33 +54,53 @@ static void setup(void)
_pe("Failed starting TTY watcher");
}
static void watcher(void *arg, int fd, int events)
static void do_tty(char *tty, size_t len, int creat)
{
char buf[EVENT_SIZE];
int len = 0;
char name[len + 6];
svc_t *svc;
while ((len = read(fd, buf, sizeof(buf)))) {
struct inotify_event *notified = (struct inotify_event *)buf;
char name[notified->len + 6];
struct tty *entry;
if (-1 == len) {
if (errno == EINVAL)
setup();
if (errno == EINTR)
continue;
break; /* Likely EAGAIN */
snprintf(name, sizeof(name), "/dev/%s", tty);
svc = svc_find_by_tty(name);
if (svc) {
if (svc_is_blocked(svc) && creat) {
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
svc_start(svc);
} else if (svc->pid) {
_d("%s: missing ...", svc_ident(svc, NULL, 0));
svc_missing(svc);
}
snprintf(name, sizeof(name), "/dev/%s", notified->name);
entry = tty_find(name);
if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE)
tty_start(entry);
else if (entry->pid)
tty_stop(entry);
}
service_step_all(SVC_TYPE_TTY);
}
}
static void tty_watcher(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
_d("tty %s, event: 0x%08x", ev->name, ev->mask);
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
}
}
+19 -4
View File
@@ -35,12 +35,18 @@
static void setup(void *arg)
{
#ifdef RANDOMSEED
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (!fexist(RANDOMSEED)) {
int ret = 1;
mode_t prev;
FILE *fp;
print_desc("Bootstrapping random seed", NULL);
umask(077);
prev = umask(077);
fp = fopen(RANDOMSEED, "w");
if (fp) {
int iter = 128;
@@ -57,7 +63,7 @@ static void setup(void *arg)
ret = fclose(fp);
}
print_result(ret);
umask(0);
umask(prev);
}
print_desc("Initializing random number generator", NULL);
@@ -68,10 +74,19 @@ static void setup(void *arg)
static void save(void *arg)
{
#ifdef RANDOMSEED
umask(077);
mode_t prev;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
prev = umask(077);
print_desc("Saving random seed", NULL);
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
umask(0);
umask(prev);
#endif
}
+133
View File
@@ -0,0 +1,133 @@
/* User-defined condtion event monitor for the Finit condition engine
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_usr;
static void usr_cond(char *name, uint32_t mask)
{
char cond[MAX_COND_LEN] = COND_USR;
strlcat(cond, name, sizeof(cond));
cond_update(cond);
}
static void usr_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
if (ev->mask & IN_ISDIR)
continue; /* unsupported */
if (ev->mask & IN_DELETE)
sync(); /* dont ask */
usr_cond(ev->name, ev->mask);
}
}
static void usr_init(void *arg)
{
char usrdir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
return;
}
path = realpath(_PATH_CONDUSR, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
return;
}
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
iwatch_exit(&iw_usr);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_usr);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = usr_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_usr);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+10 -4
View File
@@ -32,13 +32,19 @@
static void setup(void *arg)
{
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE
int fd;
#endif
char line[LINE_SIZE];
const char *username = "nobody";
umask(0);
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
prev = umask(0);
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
@@ -58,7 +64,7 @@ static void setup(void *arg)
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
umask(022);
umask(prev);
}
static plugin_t plugin = {
+2
View File
@@ -1,8 +1,10 @@
.deps/*
finit
initctl
keventd
logit
reboot
sulogin
watchdogd
Makefile
Makefile.in
+29 -21
View File
@@ -1,6 +1,5 @@
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
EXTRA_DIST = rescue.conf sample.conf
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC
AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs
@@ -8,21 +7,27 @@ else
AM_LDFLAGS = -export-dynamic
endif
sbin_PROGRAMS = finit initctl
pkglibexec_SCRIPTS = cgreaper.sh
sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = keventd logit sulogin
if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd
endif
finit_pkglibdir = $(pkglibdir)
finit_pkglib_SCRIPTS = rescue.conf sample.conf
if WATCHDOGD
pkglibexec_PROGRAMS = watchdogd
endif
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
keventd_LDADD = $(lite_LIBS)
if LOGIT
pkglibexec_PROGRAMS = logit
logit_SOURCES = logit.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
endif
sulogin_SOURCES = sulogin.c
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
sulogin_LDADD = -lcrypt
logit_SOURCES = logit.c logrotate.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS)
logit_LDADD = $(lite_LIBS)
finit_SOURCES = api.c cgroup.c cgroup.h \
cond.c cond-w.c cond.h \
@@ -43,7 +48,11 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
tty.c tty.h \
util.c util.h \
utmp-api.c utmp-api.h
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
if LOGROTATE
finit_SOURCES += logrotate.c
endif
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
@@ -54,20 +63,19 @@ else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c client.c client.h \
reboot.c serv.c serv.h svc.h \
cond.c cond.h util.c util.h
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
client.c client.h cond.c cond.h reboot.c \
serv.c serv.h svc.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS)
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
INIT_LNKS = init
INIT_LNKS = init telinit
REBOOT_LNKS = reboot shutdown halt poweroff suspend
if WATCHDOGD
watchdogd_SOURCES = watchdog.c watchdog.h
watchdogd_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
#log.c log.h
endif
# Hook in install to add finit and reboot symlink(s)
+47 -17
View File
@@ -74,7 +74,24 @@ static int start(svc_t *svc)
return 0;
}
/*
* NOTE: this does not wait for svc to be stopped first, that is the
* responsibility of initctl to do. Otherwise we'd block PID 1,
* or introduce some nasty race conditions.
*/
static int restart(svc_t *svc)
{
if (!svc)
return 1;
svc_mark_dirty(svc);
svc_start(svc);
service_step(svc);
return 0;
}
static int reload(svc_t *svc)
{
if (!svc)
return 1;
@@ -88,9 +105,10 @@ static int restart(svc_t *svc)
return 0;
}
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
static int do_stop (char *buf, size_t len) { return call(stop, buf, len); }
static int do_start (char *buf, size_t len) { return call(start, buf, len); }
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
static char query_buf[368];
static int missing(char *job, char *id)
@@ -110,7 +128,7 @@ static int missing(char *job, char *id)
static int do_query(char *buf, size_t len)
{
memset(query_buf, 0, sizeof(query_buf));
query_buf[0] = 0;
if (svc_parse_jobstr(buf, len, NULL, missing)) {
memcpy(buf, query_buf, len);
return 1;
@@ -237,7 +255,7 @@ ev_t ev_list[] = {
static void send_svc(int sd, svc_t *svc)
{
svc_t empty;
svc_t empty = { 0 };
size_t len;
if (!svc) {
@@ -252,10 +270,10 @@ static void send_svc(int sd, svc_t *svc)
static void api_cb(uev_t *w, void *arg, int events)
{
int sd, lvl;
svc_t *svc;
static svc_t *iter = NULL;
struct init_request rq;
int sd, lvl;
svc_t *svc;
sd = accept(w->fd, NULL, NULL);
if (sd < 0) {
@@ -327,16 +345,22 @@ static void api_cb(uev_t *w, void *arg, int events)
result = do_start(rq.data, sizeof(rq.data));
break;
case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data));
break;
case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_stop(rq.data, sizeof(rq.data));
break;
case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
case INIT_CMD_RELOAD_SVC:
_d("reload %s", rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data));
result = do_reload(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_RUNLEVEL:
@@ -364,24 +388,30 @@ static void api_cb(uev_t *w, void *arg, int events)
}
_e("Request to hand-over wdog ... to PID %d", rq.runlevel);
if (!svc_find_by_pid(rq.runlevel)) {
svc = svc_find_by_pid(rq.runlevel);
if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
break;
}
/* Disable and allow Finit to collect bundled watchdog */
if (wdog) {
logit(LOG_NOTICE, "Stopping and removing %s (PID:%d)", wdog->cmd, wdog->pid);
stop(wdog);
if (wdog && wdog != svc) {
char name[32];
svc_ident(svc, name, sizeof(name));
logit(LOG_NOTICE, "Handing over wdog ctrl from %s[%d] to %s[%d]",
svc_ident(wdog, NULL, 0), wdog->pid, name, svc->pid);
if (wdog->protect) {
wdog->protect = 0;
wdog->runlevels = 0;
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
stop(wdog);
svc_del(wdog);
}
}
wdog = svc;
break;
case INIT_CMD_SVC_ITER:
_d("svc iter, first: %d", rq.runlevel);
// _d("svc iter, first: %d", rq.runlevel);
/*
* XXX: This severly limits the number of
* simultaneous client connections, but will
@@ -444,7 +474,7 @@ int api_init(uev_ctx_t *ctx)
};
_d("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
if (-1 == sd) {
_pe("Failed starting external API socket");
return 1;
-11
View File
@@ -1,11 +0,0 @@
#!/bin/sh
# Called by kernel when last child in cgroup exists
BASE=/sys/fs/cgroup/finit
PROC=`basename $1`
DIR="$BASE$1"
/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir $DIR
exit 0
+430 -80
View File
@@ -25,111 +25,461 @@
#include <stdio.h>
#include <string.h>
#include <lite/lite.h>
#include <lite/queue.h>
#include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */
#include "cgroup.h"
#include "finit.h"
#include "iwatch.h"
#include "log.h"
#include "util.h"
static int cg_init = 0;
struct cg {
TAILQ_ENTRY(cg) link;
/*
* Called by Finit at early boot to mount initial cgroups
*/
void cgroup_init(void)
char *name; /* top-level group name */
char *cfg; /* kernel settings */
int active; /* for mark & sweep */
int protected; /* for init/, user/, & system/ */
};
static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups);
static char controllers[256];
static struct iwatch iw_cgroup;
static uev_t cgw;
static void cgset(const char *path, char *ctrl, char *prop)
{
FILE *fp;
char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char *val;
fp = fopen("/proc/cgroups", "r");
if (!fp) {
_d("No cgroup support");
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
if (!path || !ctrl) {
_e("Missing path or controller, skipping!");
return;
}
if (mount("none", "/sys/fs/cgroup", "tmpfs", opts, NULL)) {
_d("Failed mounting cgroups");
goto fail;
if (!prop) {
prop = strchr(ctrl, '.');
if (!prop) {
_e("Invalid cgroup ctrl syntax: %s", ctrl);
return;
}
*prop++ = 0;
}
/* Skip first line, header */
fgets(buf, sizeof(buf), fp);
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup;
char rc[80];
val = strchr(prop, ':');
if (!val) {
_e("Missing cgroup ctrl value, prop %s", prop);
return;
}
*val++ = 0;
cgroup = strtok(buf, "\t ");
if (!cgroup)
continue;
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
if (mkdir(rc, 0755) && EEXIST != errno)
continue;
if (mount("cgroup", rc, "cgroup", opts, cgroup))
_d("Failed mounting %s cgroup on %s", cgroup, rc);
/* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) {
_e("Possible security violation; '..' not allowed in cgroup config!");
return;
}
/* Default cgroups for process monitoring */
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
goto fail;
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
_pe("Failed mounting Finit cgroup hierarchy");
goto fail;
}
/* Set up reaper and enable callbacks */
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
/* Default groups, PID 1, services, and user/login processes */
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
goto fail;
/* Move ourselves to init */
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
/* We have signal, main screen turn on! */
cg_init = 1;
fail:
fclose(fp);
_d("%s/%s.%s <= %s", path, ctrl, prop, val);
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
}
static int move_pid(char *group, char *name, char *id, int pid)
/*
* Settings for a cgroup are on the form: cpu.weight:1234,mem.max:4321,...
* Finit supports the short-form 'mem.', replacing it with 'memory.' when
* writing the setting to the file system.
*/
static void group_init(char *path, int leaf, const char *cfg)
{
char *ptr, *s;
_d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
if (!fisdir(path)) {
if (mkdir(path, 0755)) {
_pe("Failed creating cgroup %s", path);
return;
}
/* enable detected controllers on domain groups */
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
_pe("Failed enabling %s for %s", controllers, path);
}
if (!cfg || !cfg[0])
return;
s = strdupa(cfg);
if (!s) {
_pe("Failed activating cgroup cfg for %s", path);
return;
}
_d("%s <=> %s", path, s);
ptr = strtok(s, ",");
while (ptr) {
_d("ptr: %s", ptr);
if (!strncmp("mem.", ptr, 4))
cgset(path, "memory", &ptr[4]);
else
cgset(path, ptr, NULL);
ptr = strtok(NULL, ",");
}
}
static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
{
char path[256];
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
if (mkdir(path, 0755) && errno != EEXIST)
return 1;
strlcat(path, "/cgroup.procs", sizeof(path));
return echo(path, 0, "%d", pid);
}
int cgroup_user(char *name)
{
if (!cg_init)
return 0;
return move_pid("finit/user", name, "0", getpid());
}
int cgroup_service(char *nm, char *id, int pid)
{
if (!cg_init)
return 0;
if (pid <= 0) {
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
if (pid < 0 || pid == 1) {
errno = EINVAL;
return 1;
}
return move_pid("finit/system", nm, id, pid);
/* create and initialize new group */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
group_init(path, 1, cfg);
/* move process to new group */
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
_pe("Failed moving pid %d to group %s", pid, path);
strlcat(path, "/cgroup.events", sizeof(path));
return iwatch_add(&iw_cgroup, path, 0);
}
int cgroup_user(char *name, int pid)
{
return cgroup_leaf_init("user", name, pid, NULL);
}
int cgroup_service(char *name, int pid, struct cgroup *cg)
{
char *group = "system";
if (cg && cg->name[0]) {
char path[256];
if (!strcmp(cg->name, "root"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
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;
}
return cgroup_leaf_init(group, name, pid, cg ? cg->cfg : NULL);
}
static void append_ctrl(char *ctrl)
{
if (controllers[0])
strlcat(controllers, " ", sizeof(controllers));
strlcat(controllers, "+", sizeof(controllers));
strlcat(controllers, ctrl, sizeof(controllers));
}
static void cgroup_handle_event(char *event, uint32_t mask)
{
char path[strlen(event) + 1];
char buf[80];
char *ptr;
FILE *fp;
_d("event: '%s', mask: %08x", event, mask);
if (!(mask & IN_MODIFY))
return;
fp = fopen(event, "r");
if (!fp) {
_d("Failed opening %s, skipping ...", event);
return;
}
while (fgets(buf, sizeof(buf), fp)) {
if (strncmp(buf, "populated", 9))
continue;
chomp(buf);
if (atoi(&buf[10]))
break;
strlcpy(path, event, sizeof(path));
ptr = strrchr(path, '/');
if (ptr) {
*ptr = 0;
if (!cgroup_del(path)) {
/*
* try with parent, top-level group, we
* may get events out-of-order *sigh*
*/
ptr = strrchr(path, '/');
if (!ptr)
break;
*ptr = 0;
cgroup_del(path);
}
}
break;
}
fclose(fp);
}
static void cgroup_events_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_cgroup, ev->wd);
if (!iwp || !iwp->path)
continue;
cgroup_handle_event(iwp->path, ev->mask);
}
#ifdef ENABLE_AUTO_RELOAD
if (conf_any_change())
service_reload_dynamic();
#endif
}
static struct cg *cgroup_find(char *name)
{
struct cg *cg;
TAILQ_FOREACH(cg, &cgroups, link) {
if (strcmp(cg->name, name))
continue;
return cg;
}
return NULL;
}
/*
* Marks all unprotected cgroups for deletion (during reload)
*/
void cgroup_mark_all(void)
{
struct cg *cg;
TAILQ_FOREACH(cg, &cgroups, link) {
if (cg->protected)
continue;
cg->active = 0;
}
}
/*
* Remove (try to) all unused cgroups
*/
void cgroup_cleanup(void)
{
struct cg *cg, *tmp;
char path[256];
TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) {
if (cg->active)
continue;
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
cgroup_del(path);
}
}
/*
* Add, or update, settings for top-level cgroup
*/
int cgroup_add(char *name, char *cfg, int protected)
{
struct cg *cg;
if (!name)
return -1;
if (!cfg)
cfg = "";
cg = cgroup_find(name);
if (!cg) {
cg = malloc(sizeof(struct cg));
if (!cg) {
_pe("Failed allocating 'struct cg' for %s", name);
return -1;
}
cg->name = strdup(name);
if (!cg->name) {
_pe("Failed setting cgroup name %s", name);
free(cg);
return -1;
}
TAILQ_INSERT_TAIL(&cgroups, cg, link);
} else
free(cg->cfg);
cg->cfg = strdup(cfg);
if (!cg->cfg) {
_pe("Failed add/update of cgroup %s", name);
TAILQ_REMOVE(&cgroups, cg, link);
free(cg->name);
free(cg);
return -1;
}
cg->protected = protected;
cg->active = 1;
return 0;
}
/*
* Remove inactive top-level cgroup
*/
int cgroup_del(char *dir)
{
struct cg *cg;
char path[256];
TAILQ_FOREACH(cg, &cgroups, link) {
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
if (strcmp(path, dir))
continue;
if (cg->active)
return -1;
break;
}
if (rmdir(dir) && errno != ENOENT) {
_d("Failed removing %s: %s", dir, strerror(errno));
return -1;
}
if (cg) {
TAILQ_REMOVE(&cgroups, cg, link);
free(cg->name);
free(cg->cfg);
free(cg);
}
return 0;
}
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
void cgroup_config(void)
{
struct cg *cg;
TAILQ_FOREACH(cg, &cgroups, link) {
char path[256];
int leaf = 0;
if (!cg->active)
continue;
if (!strcmp(cg->name, "init"))
leaf = 1; /* reserved */
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, cg->name);
group_init(path, leaf, cg->cfg);
strlcat(path, "/cgroup.events", sizeof(path));
iwatch_add(&iw_cgroup, path, 0);
}
}
/*
* Called by Finit at early boot to mount initial cgroups
*/
void cgroup_init(uev_ctx_t *ctx)
{
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char buf[80];
FILE *fp;
int fd;
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
_pe("Failed mounting cgroup v2");
return;
}
/* Find available controllers */
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
if (!fp) {
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
return;
}
if (fgets(buf, sizeof(buf), fp)) {
char *cgroup;
cgroup = strtok(chomp(buf), "\t ");
while (cgroup) {
append_ctrl(cgroup);
cgroup = strtok(NULL, "\t ");
}
}
fclose(fp);
/* Enable all controllers */
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control"))
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
/* Default (protected) groups, PID 1, services, and user/login processes */
cgroup_add("init", "cpu.weight:100", 1);
cgroup_add("system", "cpu.weight:9800", 1);
cgroup_add("user", "cpu.weight:100", 1);
cgroup_config();
/* Move ourselves to init (best effort, otherwise run in 'root' group */
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs"))
_pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
/* prepare cgroup.events watcher */
fd = iwatch_init(&iw_cgroup);
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
_pe("Failed setting up cgroup.events watcher");
close(fd);
}
}
/**
+17 -3
View File
@@ -24,9 +24,23 @@
#ifndef FINIT_CGROUP_H_
#define FINIT_CGROUP_H_
void cgroup_init (void);
#include <uev/uev.h>
int cgroup_user (char *name);
int cgroup_service (char *cmd, char *id, int pid);
struct cgroup {
char name[16];
char cfg[128];
};
void cgroup_mark_all(void);
void cgroup_cleanup (void);
int cgroup_add (char *name, char *cfg, int protected);
int cgroup_del (char *dir);
void cgroup_config (void);
void cgroup_init (uev_ctx_t *ctx);
int cgroup_user (char *name, int pid);
int cgroup_service (char *name, int pid, struct cgroup *cg);
#endif /* FINIT_CGROUP_H_ */
+654
View File
@@ -0,0 +1,654 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <err.h>
#include <dirent.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <search.h>
#include <inttypes.h>
#include <lite/lite.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include "cgutil.h"
#include "initctl.h"
#define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m"
#define NONE " "
#define PIPE plain ? "| " : "│ "
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
uint64_t total_ram; /* From sysinfo() */
struct cg dummy; /* empty result "NULL" */
struct cg *list;
char *pid_cmdline(int pid, char *buf, size_t len)
{
size_t i, sz;
char *ptr;
FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) {
buf[0] = 0;
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
fclose(fp);
if (!sz)
return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
}
for (i = 0; i < sz; i++) {
if (buf[i] == 0)
buf[i] = ' ';
}
return buf;
}
char *pid_comm(int pid, char *buf, size_t len)
{
char *ptr = NULL;
FILE *fp;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
if (fgets(buf, len, fp))
ptr = chomp(buf);
fclose(fp);
return ptr;
}
char *pid_cgroup(int pid, char *buf, size_t len)
{
char *ptr = NULL;
FILE *fp;
fp = fopenf("r", "/proc/%d/cgroup", pid);
if (!fp)
return NULL;
if (fgets(buf, len, fp))
ptr = chomp(buf);
fclose(fp);
if (ptr)
ptr = strchr(buf, '/');
return ptr;
}
static char *cgroup_val(char *path, char *file, char *buf, size_t len)
{
char *val = NULL;
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (fp) {
if (fgets(buf, len, fp)) {
val = chomp(buf);
len = strcspn(val, " \t");
val[len] = 0;
}
fclose(fp);
}
return val;
}
static uint64_t cgroup_uint64(char *path, char *file)
{
uint64_t val = 0;
char buf[42];
if (cgroup_val(path, file, buf, sizeof(buf)))
val = strtoull(buf, NULL, 10);
return val;
}
static char *cgroup_memval(char *path, char *file, char *buf, size_t len)
{
char data[42];
if (cgroup_val(path, file, data, sizeof(data))) {
if (!strcmp(data, "max"))
strlcpy(buf, data, len);
else
memsz(strtoull(data, NULL, 10), buf, len);
} else
buf[0] = 0;
return buf;
}
static uint64_t cgroup_memuse(struct cg *cg)
{
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
if (fp) {
cg->cg_rss = 0;
cg->cg_vmlib = 0;
while (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
if (!strncmp(buf, "anon", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "slab", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "kernel_stack", 12)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "pagetables", 10)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "percpu", 6)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "sock", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "file", 4)) {
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "file_mapped", 11)) {
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
}
}
fclose(fp);
}
cg->cg_memshare = (float)(cg->cg_rss * 100 / total_ram);
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.current");
}
uint64_t cgroup_memory(char *group)
{
char path[256];
paste(path, sizeof(path), FINIT_CGPATH, group);
return cgroup_uint64(path, "memory.current");
}
static float cgroup_cpuload(struct cg *cg)
{
char fn[256];
char buf[64];
FILE *fp;
snprintf(fn, sizeof(fn), "%s/cpu.stat", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
while (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
chomp(buf);
if (strncmp(buf, "usage_usec", 10))
continue;
curr = strtoull(&buf[11], NULL, 10);
if (cg->cg_prev != 0) {
uint64_t diff = curr - cg->cg_prev;
/* this expects 1 sec poll interval */
cg->cg_load = (float)(diff / 1000000);
cg->cg_load *= 100.0;
}
cg->cg_prev = curr;
break;
}
fclose(fp);
return cg->cg_load;
}
static struct cg *append(char *path)
{
struct cg *cg;
char fn[256];
ENTRY item;
snprintf(fn, sizeof(fn), "%s/cpu.stat", path);
if (access(fn, F_OK)) {
/* older kernels, 4.19, don't have summary cpu.stat in root */
if (strcmp(path, FINIT_CGPATH))
warn("not a cgroup path with cpu controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
cg->cg_next = list;
list = cg;
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
return cg;
}
static struct cg *find(char *path)
{
ENTRY *ep, item = { path, NULL };
ep = hsearch(item, FIND);
if (ep)
return ep->data;
return append(path);
}
/* update stats */
struct cg *cg_stats(char *path)
{
struct cg *cg;
cg = find(path);
if (!cg)
return &dummy;
cgroup_cpuload(cg);
cgroup_memuse(cg);
return cg;
}
/* query config */
struct cg *cg_conf(char *path)
{
static struct cg cg;
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
return &cg;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
int cgroup_tree(char *path, char *pfx, int mode, int pos)
{
struct dirent **namelist = NULL;
char s[32], r[32], l[32];
char row[ttcols + 9]; /* + control codes */
size_t rlen = sizeof(row) - 1;
size_t rplen = rlen - 9;
struct stat st;
struct cg *cg;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (pos >= ttrows)
return 0;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
switch (mode) {
case 1:
cg = cg_stats(path);
snprintf(row, rplen, "\r %6.6s %6.6s %6.6s %5.1f %5.1f %s",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_memshare, cg->cg_load, path);
break;
case 2:
cg = cg_conf(path);
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
memsz(cg->cg_vmsize, s, sizeof(s)),
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
cg->cg_cpu.weight, cg->cg_cpu.max, path);
break;
default:
strlcpy(row, "\r", rplen);
strlcat(row, path, rplen);
break;
}
puts(row);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 };
pid_t pid;
pid = atoi(chomp(buf));
if (pid <= 0)
continue;
/* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf))) {
char proc[ttcols];
int len;
switch (mode) {
case 1:
snprintf(row, rplen, "\r%37s", " ");
break;
case 2:
snprintf(row, rplen, "\r --.-- [ ] [ ] ");
break;
default:
strlcpy(row, "\r", rplen);
break;
}
strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen);
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
if (plain) {
strlcat(row, proc, rplen);
} else {
strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen);
len = strlen(row) + strlen(CRST);
if (len > (int)rlen)
row[rlen - strlen(CRST)] = 0;
strlcat(row, CRST, sizeof(row));
}
puts(row);
}
if (mode == 1) {
pos += i;
if (pos >= ttrows)
break;
}
}
}
fclose(fp);
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
switch (mode) {
case 1:
cg = cg_stats(buf);
snprintf(row, rplen,
"\r %6.6s %6.6s %6.6s %5.1f %5.1f ",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_memshare, cg->cg_load);
break;
case 2:
cg = cg_conf(buf);
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
memsz(cg->cg_vmsize, s, sizeof(s)),
cg->cg_mem.min, cg->cg_mem.max,
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
break;
default:
strlcpy(row, "\r", rplen);
break;
}
strlcat(row, pfx, rplen);
if (i + 1 == n) {
strlcat(row, END, rplen);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
strlcat(row, FORK, rplen);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
}
strlcat(row, " ", rplen);
strlcat(row, nm, rplen);
strlcat(row, "/ ", rplen);
puts(row);
rc += cgroup_tree(buf, prefix, mode, pos + i);
free(namelist[i]);
}
free(namelist);
}
return rc;
}
int show_cgps(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return cgroup_tree(arg, NULL, 0, 0);
}
static void cgtop(uev_t *w, void *arg, int events)
{
(void)w;
(void)events;
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
cgroup_tree(arg, NULL, 1, 0);
}
static void cleanup(void)
{
ttcooked();
showcursor();
puts("");
}
static void leave(uev_t *w, void *arg, int events)
{
(void)arg;
(void)events;
uev_exit(w->ctx);
}
static void key(uev_t *w, void *arg, int events)
{
char ch;
(void)arg;
(void)events;
if (read(w->fd, &ch, sizeof(ch)) != -1) {
switch (ch) {
case 'q':
uev_exit(w->ctx);
break;
default:
dbg("Got char 0x%02x", ch);
break;
}
}
}
int show_cgtop(char *arg)
{
struct sysinfo si = { 0 };
uev_t timer, input, sigint, sigterm, sigquit;
char path[512];
uev_ctx_t ctx;
int flags;
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
if (!hcreate(ttrows + 25))
err(1, "failed creating hash table");
sysinfo(&si);
total_ram = si.totalram * si.mem_unit;
uev_init(&ctx);
uev_timer_init(&ctx, &timer, cgtop, arg, 1, ionce ? 0 : 1000);
if (!ionce && !plain) {
atexit(cleanup);
ttraw();
hidecursor();
flags = fcntl(STDIN_FILENO, F_GETFL);
if (flags != -1)
(void)fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
(void)uev_io_init(&ctx, &input, key, NULL, STDIN_FILENO, UEV_READ);
(void)uev_signal_init(&ctx, &sigint, leave, NULL, SIGINT);
(void)uev_signal_init(&ctx, &sigterm, leave, NULL, SIGTERM);
(void)uev_signal_init(&ctx, &sigquit, leave, NULL, SIGQUIT);
}
return uev_run(&ctx, 0);
}
int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
/* memory.current memory.min memory.max cpuset.cpus cpu.weight cpu.max */
if (heading)
print_header(" MEM [MIN MAX] CPU [WEIGHT MAX] GROUP");
return cgroup_tree(arg, NULL, 2, 0);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+77
View File
@@ -0,0 +1,77 @@
/* Cgroup utility functions for initctl
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_CGUTIL_H_
#define FINIT_CGUTIL_H_
#include <stdint.h>
#include <stdlib.h>
struct cg {
struct cg *cg_next;
/* stats */
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
uint64_t cg_rss; /* memory.stat */
uint64_t cg_vmlib; /* memory.stat */
uint64_t cg_vmsize; /* memory.current */
float cg_memshare; /* cg_rss / total_ram * 100 */
float cg_load; /* curr - prev / 10000000.0 */
/* config */
struct {
char min[32];
char max[32];
} cg_mem;
struct {
char set[16];
char weight[32];
char max[32];
} cg_cpu;
};
char *pid_cmdline (int pid, char *buf, size_t len);
char *pid_comm (int pid, char *buf, size_t len);
char *pid_cgroup (int pid, char *buf, size_t len);
uint64_t cgroup_memory(char *group);
struct cg *cg_stats(char *path);
struct cg *cg_conf (char *path);
int cgroup_tree (char *path, char *pfx, int mode, int pos);
int show_cgroup (char *arg);
int show_cgtop (char *arg);
int show_cgps (char *arg);
#endif /* FINIT_CGUTIL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+30 -16
View File
@@ -21,7 +21,9 @@
* THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -36,19 +38,14 @@ int client_connect(void)
.sun_path = INIT_SOCKET,
};
sd = socket(AF_UNIX, SOCK_STREAM, 0);
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (-1 == sd)
goto error;
err(1, "Failed creating UNIX domain socket");
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
close(sd);
goto error;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
err(1, "Failed connecting to finit");
return sd;
error:
perror("Failed connecting to finit");
return -1;
}
int client_disconnect(void)
@@ -68,27 +65,44 @@ int client_disconnect(void)
int client_send(struct init_request *rq, ssize_t len)
{
struct pollfd pfd = { 0 };
int sd, result = 255;
sd = client_connect();
if (-1 == sd)
return -1;
if (write(sd, rq, len) != len)
goto error;
pfd.fd = sd;
pfd.events = POLLOUT;
if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for Finit, errno %d", errno);
goto exit;
}
if (read(sd, rq, len) != len)
goto error;
if (write(sd, rq, len) != len) {
warn("Failed communicating with Finit, errno %d", errno);
goto exit;
}
pfd.fd = sd;
pfd.events = POLLIN;
if (poll(&pfd, 1, 2000) <= 0) {
warn("Timed out waiting for reply from Finit, errno %d", errno);
goto exit;
}
if (read(sd, rq, len) != len) {
warn("Failed reading reply from Finit, errno %d", errno);
goto exit;
}
if (rq->cmd == INIT_CMD_NACK)
result = 1;
else
result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit:
client_disconnect();
return result;
}
+73 -50
View File
@@ -99,13 +99,14 @@ static void cond_bump_reconf(void)
unsigned int rgen;
/*
* If %COND_RECONF does not exist, cond_get_gen() returns 0
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want.
*/
rgen = cond_get_gen(COND_RECONF);
rgen = cond_get_gen(_PATH_RECONF);
rgen++;
cond_set_gen(COND_RECONF, rgen);
if (cond_set_gen(_PATH_RECONF, rgen))
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen);
}
static int cond_checkpath(const char *path)
@@ -119,7 +120,7 @@ static int cond_checkpath(const char *path)
return 1;
}
if (makepath(dir) && errno != EEXIST) {
if (mkpath(dir, 0755) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1;
}
@@ -127,22 +128,22 @@ static int cond_checkpath(const char *path)
return 0;
}
int cond_set_path(const char *path, enum cond_state new)
int cond_set_path(const char *path, enum cond_state next)
{
enum cond_state old;
enum cond_state prev;
unsigned int rgen;
_d("%s", path);
_d("%s <= %d", path, next);
rgen = cond_get_gen(COND_RECONF);
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
}
old = cond_get_path(path);
prev = cond_get_path(path);
switch (new) {
switch (next) {
case COND_ON:
if (cond_checkpath(path))
return 0;
@@ -159,30 +160,11 @@ int cond_set_path(const char *path, enum cond_state new)
return 0;
}
return new != old;
return next != prev;
}
/* Has condition in configuration and cond is allowed? */
static int svc_has_cond(svc_t *svc)
{
if (!svc->cond[0])
return 0;
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
return 1;
default:
break;
}
return 0;
}
static void cond_update(const char *name)
/* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */
void cond_update(const char *name)
{
svc_t *svc, *iter = NULL;
@@ -196,13 +178,22 @@ static 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);
@@ -221,17 +212,27 @@ void cond_set_oneshot(const char *name)
if (cond_checkpath(path))
return;
symlink(COND_RECONF, path);
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
_pe("Failed creating onshot cond %s", name);
cond_update(name);
}
int cond_clear_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
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 (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_OFF))
if (cond_clear_noupdate(name))
return;
cond_update(name);
@@ -244,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;
@@ -254,35 +255,57 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F)
return 0;
nm = strstr((char *)fpath, COND_DIR);
nm = strstr((char *)fpath, COND_BASE);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
_e("Incorrect condtion path %s, cannot %sassert", fpath, set ? "re" : "de");
return 1;
}
nm += sizeof(COND_DIR);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
nm += strlen(COND_BASE);
_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)
{
char path[MAX_ARG_LEN];
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", COND_PATH);
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND);
return;
}
+6 -6
View File
@@ -45,15 +45,15 @@ const char *cond_path(const char *name)
static char path[256];
char tmp[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
return pid_runpath(tmp, path, sizeof(path));
}
unsigned int cond_get_gen(const char *file)
{
unsigned int gen;
char *ptr, path[256];
unsigned int gen;
FILE *fp;
int ret;
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
{
int cgen, rgen;
rgen = cond_get_gen(COND_RECONF);
if (!rgen)
return COND_OFF;
cgen = cond_get_gen(path);
if (!cgen)
return COND_OFF;
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen)
return COND_OFF;
return (cgen == rgen) ? COND_ON : COND_FLUX;
}
+17 -3
View File
@@ -4,9 +4,16 @@
#include <paths.h>
#include <svc.h>
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf"
#define COND_BASE "finit/cond"
#define COND_PID "pid/"
#define COND_SYS "sys/"
#define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
#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 {
COND_OFF = 0,
@@ -23,12 +30,19 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names);
void cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_set_oneshot (const char *name);
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_ */
+108 -33
View File
@@ -23,6 +23,7 @@
#include "config.h" /* Generated by configure script */
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#include <sys/inotify.h>
@@ -47,8 +48,11 @@
int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
struct rlimit global_rlimit[RLIMIT_NLIMITS];
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
struct conf_change {
TAILQ_ENTRY(conf_change) link;
char *name;
@@ -78,14 +82,14 @@ static void hide_args(int argc, char *argv[])
}
}
int get_bool(char *ptr, int default_value)
int get_bool(char *arg, int default_value)
{
if (!ptr)
if (!arg)
goto fallback;
if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
return 1;
if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
return 0;
fallback:
return default_value;
@@ -97,19 +101,24 @@ fallback:
*/
static void parse_finit_opts(char *opt)
{
char *ptr;
char *arg;
ptr = strchr(opt, '=');
if (ptr)
*ptr++ = 0;
arg = strchr(opt, '=');
if (arg)
*arg++ = 0;
if (string_compare(opt, "debug")) {
debug = get_bool(ptr, 1);
debug = get_bool(arg, 1);
return;
}
if (string_compare(opt, "status_style")) {
if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
if (!arg) {
_e("status_style option requires an argument, skipping.");
return;
}
if (string_compare(arg, "old") || string_compare(arg, "classic"))
show_progress(PROGRESS_CLASSIC);
else
show_progress(PROGRESS_MODERN);
@@ -117,7 +126,7 @@ static void parse_finit_opts(char *opt)
}
if (string_compare(opt, "show_status")) {
show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
return;
}
}
@@ -346,7 +355,7 @@ char *lim2str(struct rlimit *rlim)
char tmp[25];
static char buf[42];
memset(buf, 0, sizeof(buf));
buf[0] = 0;
if (RLIM_INFINITY == rlim->rlim_cur)
snprintf(tmp, sizeof(tmp), "unlimited, ");
else
@@ -417,6 +426,29 @@ error:
logit(LOG_WARNING, "rlimit: parse error");
}
/* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */
static void conf_parse_cgroup(char *line)
{
char config[strlen(line) + 1];
char *ptr, *name;
name = strtok(line, " \t");
if (!name)
return;
if (strstr(name, "..") || strchr(name, '/'))
return; /* illegal */
config[0] = 0;
while ((ptr = strtok(NULL, " \t"))) {
if (config[0])
strlcat(config, ",", sizeof(config));
strlcat(config, ptr, sizeof(config));
}
cgroup_add(name, config, 0);
}
static void parse_static(char *line, int is_rcsd)
{
char cmd[CMD_SIZE];
@@ -546,9 +578,21 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return;
}
/* Read contrl group limits */
if (MATCH_CMD(line, "cgroup ", x)) {
conf_parse_cgroup(x);
return;
}
/* Set current cgroup for the following services/run/tasks */
if (MATCH_CMD(line, "cgroup.", x)) {
strlcpy(cgroup_current, x, sizeof(cgroup_current));
return;
}
/* Regular or serial TTYs to run getty */
if (MATCH_CMD(line, "tty ", x)) {
tty_register(strip_line(x), rlimit, file);
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file);
return;
}
}
@@ -574,9 +618,11 @@ static int parse_conf(char *file, int is_rcsd)
if (!fp)
return 1;
/* Prepare default limits for each service in /etc/finit.d/ */
if (is_rcsd)
/* Prepare default limits and group for each service in /etc/finit.d/ */
if (is_rcsd) {
memcpy(rlimit, global_rlimit, sizeof(rlimit));
cgroup_current[0] = 0;
}
_d("*** Parsing %s", file);
while (!feof(fp)) {
@@ -616,25 +662,22 @@ int conf_reload(void)
daylight, timezone, tzname[0], tzname[1]);
/* Mark and sweep */
cgroup_mark_all();
svc_mark_dynamic();
tty_mark();
/*
* Get current global limits, which may be overridden from both
* finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there.
* Reset global rlimit to bootstrap values from conf_init().
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
if (rescue) {
int rc;
char line[80] = "tty [12345] @console noclear nologin";
char line[80] = "tty [12345] rescue";
/* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF, 0);
if (rc)
tty_register(line, global_rlimit, NULL);
service_register(SVC_TYPE_TTY, line, global_rlimit, NULL);
print(rc, "Entering rescue mode");
goto done;
@@ -674,7 +717,7 @@ int conf_reload(void)
/* Check for dangling symlinks */
if (S_ISLNK(st.st_mode)) {
path = rp = realpath(path, NULL);
rp = realpath(path, NULL);
if (!rp) {
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
continue;
@@ -684,7 +727,7 @@ int conf_reload(void)
/* Check that file ends with '.conf' */
len = strlen(path);
if (len < 6 || strcmp(&path[len - 5], ".conf"))
_d("Skipping %s, not a valid .conf ... ", path);
_d("Skipping %s, not a Finit .conf file ... ", path);
else
parse_conf(path, 1);
@@ -694,7 +737,15 @@ int conf_reload(void)
globfree(&gl);
/* Mark any reverse deps as chenaged. */
service_update_rdeps();
/* Set up top-level cgroups */
cgroup_config();
done:
/* Remove all unused top-level cgroups */
cgroup_cleanup();
/* Drop record of all .conf changes */
drop_changes();
@@ -787,20 +838,29 @@ int conf_any_change(void)
int conf_changed(char *file)
{
int rc = 0;
char *rp;
if (!file)
return 0;
if (conf_find(file))
return 1;
rp = realpath(file, NULL);
if (!rp)
return 0;
return 0;
if (conf_find(rp))
rc = 1;
free(rp);
return rc;
}
static void conf_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz, off;
ssize_t sz;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
@@ -809,10 +869,16 @@ static void conf_cb(uev_t *w, void *arg, int events)
}
ev_buf[sz] = 0;
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
@@ -868,7 +934,7 @@ int conf_monitor(void)
}
/*
* Prepare .conf parser and load all .conf files
* Prepare .conf parser and load /etc/finit.conf for global settings
*/
int conf_init(uev_ctx_t *ctx)
{
@@ -882,8 +948,17 @@ int conf_init(uev_ctx_t *ctx)
* finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there.
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (getrlimit(i, &initial_rlimit[i]))
logit(LOG_WARNING, "rlimit: Failed reading setting %s: %s",
rlim2str(i), strerror(errno));
}
/* Initialize global rlimits, e.g. for built-in services */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
/* Read global rlimits and global cgroup setup from /etc/finit.conf */
parse_conf(FINIT_CONF, 0);
/* prepare /etc watcher */
fd = iwatch_init(&iw_conf);
+2
View File
@@ -24,12 +24,14 @@
#ifndef FINIT_CONF_H_
#define FINIT_CONF_H_
#include "cgroup.h"
#include "svc.h"
extern int logfile_size_max;
extern int logfile_count_max;
extern struct rlimit global_rlimit[];
extern char cgroup_current[];
int str2rlim(char *str);
char *rlim2str(int rlim);
+35 -49
View File
@@ -35,6 +35,7 @@
#include <lite/lite.h>
#include "finit.h"
#include "cgroup.h"
#include "conf.h"
#include "helpers.h"
#include "sig.h"
@@ -244,10 +245,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
char name[80];
int fd;
/* Detach from initial controlling TTY and become session leader */
vhangup();
setsid();
fd = open(tty, O_RDWR);
if (fd < 0) {
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
@@ -259,6 +256,11 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
dup2(fd, STDERR_FILENO);
close(fd);
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
@@ -288,7 +290,6 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
@@ -315,7 +316,7 @@ static int activate_console(int noclear, int nowait)
return 1;
if (!noclear)
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
dprint(STDERR_FILENO, "\e[r\e[H\e[J", 9);
/* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &orig)) {
@@ -339,16 +340,16 @@ static int activate_console(int noclear, int nowait)
continue;
}
(void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, msg, strlen(msg));
dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r')
continue;
if (fexist(SYNC_STOPPED))
continue;
(void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, cup, strlen(cup));
dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, cup, strlen(cup));
ret = 1;
break;
}
@@ -381,62 +382,47 @@ static int activate_console(int noclear, int nowait)
*/
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
speed_t speed;
int rc = 1;
pid = fork();
if (!pid) {
speed_t speed;
int rc = 1;
speed = stty_parse_speed(baud);
speed = stty_parse_speed(baud);
prepare_tty(tty, speed, "tty", rlimit);
if (activate_console(noclear, nowait)) {
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
prepare_tty(tty, speed, "tty", rlimit);
if (activate_console(noclear, nowait))
rc = getty(tty, speed, term, NULL);
_exit(rc);
rc = getty(tty, speed, term, NULL);
}
return pid;
return rc;
}
/*
* Start external getty as defined by user in .conf file
*/
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
int rc = 1;
pid = fork();
if (!pid) {
int rc = 1;
/* Dunno speed, tell stty() to not mess with it */
logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0);
prepare_tty(tty, B0, "getty", rlimit);
if (activate_console(noclear, nowait))
rc = execv(cmd, args);
vhangup();
_exit(rc);
/* Dunno speed, tell stty() to not mess with it */
prepare_tty(tty, B0, "getty", rlimit);
if (activate_console(noclear, nowait)) {
logit(LOG_INFO, "Starting external getty on %s", tty);
rc = execv(cmd, args);
}
return pid;
vhangup();
return rc;
}
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
int rc = 1;
pid = fork();
if (!pid) {
int rc = 1;
prepare_tty(tty, B0, "finitsh", rlimit);
if (activate_console(noclear, nowait))
rc = sh(tty);
prepare_tty(tty, B0, "finit-sh", rlimit);
if (activate_console(noclear, nowait))
rc = sh(tty);
_exit(rc);
}
return pid;
return rc;
}
int run_parts(char *dir, char *cmd)
+169 -96
View File
@@ -52,6 +52,7 @@
#include "util.h"
#include "utmp-api.h"
#include "schedule.h"
#include "watchdog.h"
int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
@@ -59,6 +60,7 @@ int prevlevel = -1;
int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootrapping (for TTYs) */
char *sdown = NULL;
char *network = NULL;
char *hostname = NULL;
@@ -179,10 +181,74 @@ static void fs_remount_root(int fsckerr)
* XXX: Untested, in the initramfs age we should
* probably use switch_root instead.
*/
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
_pe("Failed %s / MS_MOVE");
}
#endif /* SYSROOT */
/*
* 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)
@@ -198,7 +264,9 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
_d("Finalize, ensure common file systems are available ...");
fs_finalize();
}
/*
@@ -206,6 +274,9 @@ static void fs_mount(void)
* for early multi-console, and /sys for the cgroups support. Any
* occurrence of these file systems in /etc/fstab will replace these
* mounts later in fs_mount()
*
* Ignore any mount errors with EBUSY, kernel likely alread mounted
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
*/
static void fs_init(void)
{
@@ -218,53 +289,19 @@ static void fs_init(void)
};
size_t i;
for (i = 0; i < NELEMS(fs); i++)
mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
}
/* mask writable bit for g and o */
umask(022);
/*
* If everything goes south we can use this to give the operator an
* emergency shell to debug the problem -- Finit should not crash!
*
* Note: Only use this for debugging a new Finit setup, don't use
* this in production since it gives a root shell to anyone
* if Finit crashes.
*
* This emergency shell steps in to prevent "Aieee, PID 1 crashed"
* messages from the kernel, which usually results in a reboot, so
* that the operator instead can debug the problem.
*/
static void emergency_shell(void)
{
#ifdef EMERGENCY_SHELL
pid_t pid;
for (i = 0; i < NELEMS(fs); i++) {
int rc;
pid = fork();
if (pid) {
while (1) {
pid_t id;
/* Reap 'em (prevents Zombies) */
id = waitpid(-1, NULL, WNOHANG);
if (id == pid)
break;
}
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
if (rc && errno != EBUSY)
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
}
#endif /* EMERGENCY_SHELL */
}
/*
* Handle bootstrap transition to configured runlevel, start TTYs
*
@@ -276,20 +313,8 @@ static void emergency_shell(void)
* finit.conf, or *.conf in finit.d/, run to completion before we
* finalize the bootstrap process by calling this function.
*/
static void finalize(void)
static void finalize(void *unused)
{
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel, start all services ...");
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap();
@@ -311,9 +336,9 @@ static void finalize(void)
/* Disable progress output at normal runtime */
enable_progress(0);
/* Delayed start of TTYs at bootstrap */
_d("Launching all getty services ...");
tty_runlevel();
/* System bootrapped, launch TTYs et al */
bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN);
}
/*
@@ -331,12 +356,17 @@ static void crank_worker(void *unused)
/*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
* allowed to complete their work in [S], or timeout, before we call
* finalize(), should not take more than 120 sec.
* allowed to complete their work in [S], or timeout, before we switch
* to the configured runlevel and call finalize(), should not take more
* than 120 sec.
*/
static void final_worker(void *work)
static void bootstrap_worker(void *work)
{
static int cnt = 120;
static struct wq final = {
.cb = finalize,
.delay = 10
};
_d("Step all services ...");
service_step_all(SVC_TYPE_ANY);
@@ -352,12 +382,36 @@ static void final_worker(void *work)
else
_d("Timeout, resuming bootstrap.");
finalize();
_d("Starting runlevel change finalize ...");
schedule_work(&final);
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(cfglevel);
}
static int version(int rc)
{
puts(PACKAGE_STRING);
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc;
}
static int usage(int rc)
{
printf("Usage: telinit [OPTIONS] [q | Q | 0-9]\n\n"
printf("Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n"
// " -a Ignored, compat SysV init\n"
// " -b Ignored, compat SysV init\n"
@@ -375,12 +429,7 @@ static int usage(int rc)
" q, Q Reload /etc/finit.conf and/or any *.conf in /etc/finit.d/\n"
" if modified, same as initctl reload or SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n"
"\n");
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
"\n", prognm);
return rc;
}
@@ -393,6 +442,7 @@ static int telinit(int argc, char *argv[])
{
int c;
progname(argv[0]);
while ((c = getopt(argc, argv, "abe:h?st:vVz:")) != EOF) {
switch(c) {
case 'a': case 'b': case 'e': case 's': case 'z':
@@ -402,7 +452,7 @@ static int telinit(int argc, char *argv[])
break;
case 'v': case 'V':
return puts("v" VERSION) == EOF;
return version(0);
case 'h':
case '?':
@@ -414,13 +464,13 @@ static int telinit(int argc, char *argv[])
int req = (int)argv[optind][0];
if (isdigit(req))
return systemf("initctl runlevel %c", req);
return systemf("initctl -b runlevel %c", req);
if (req == 'q' || req == 'Q')
return systemf("initctl reload");
return systemf("initctl -b reload");
if (req == 's' || req == 'S')
return systemf("initctl runlevel %c", req);
return systemf("initctl -b runlevel %c", req);
}
/* XXX: add non-pid1 process monitor here
@@ -435,11 +485,12 @@ static int telinit(int argc, char *argv[])
int main(int argc, char *argv[])
{
struct wq crank = {
.cb = crank_worker
.cb = crank_worker,
.delay = 10
};
struct wq final = {
.cb = final_worker,
.delay = 1000
struct wq bootstrap = {
.cb = bootstrap_worker,
.delay = 100
};
uev_ctx_t loop;
@@ -470,33 +521,43 @@ int main(int argc, char *argv[])
ctx = &loop;
/*
* Set PATH, SHELL, PWD, and umask early to something sane
* Set PATH, SHELL, and PWD early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
chdir("/");
umask(0);
/* Set up canvas */
if (!rescue && !debug)
screen_init();
/* Allow progress, if enabled */
enable_progress(1);
if (chdir("/"))
_pe("Failed cd /");
/*
* In case of emergency.
*/
emergency_shell();
if (rescue) {
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
/* Load plugins early, the first hook is in banner(), so we
* need plugins loaded before calling it. */
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
rescue = 0;
break;
}
}
/*
* Load plugins early, the first hook is in banner(), so we
* need plugins loaded before calling it.
*/
plugin_init(&loop);
/*
* Hello world.
*/
enable_progress(1); /* Allow progress, if enabled */
banner();
/*
@@ -507,8 +568,7 @@ int main(int argc, char *argv[])
/*
* Initialize default control groups, if available
*/
cgroup_init();
cgroup_init(&loop);
/* Check and mount filesystems. */
fs_mount();
@@ -526,6 +586,20 @@ int main(int argc, char *argv[])
*/
conf_init(&loop);
/*
* Start built-in watchdogd as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
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);
@@ -540,13 +614,12 @@ int main(int argc, char *argv[])
_d("Starting initctl API responder ...");
api_init(&loop);
umask(022);
_d("Starting the big state machine ...");
schedule_work(&crank);
_d("Starting bootstrap finalize timer ...");
schedule_work(&final);
schedule_work(&bootstrap);
/*
* Enter main loop to monitor /dev/initctl and services
+17 -5
View File
@@ -30,17 +30,28 @@
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <lite/lite.h>
#include <uev/uev.h>
#ifndef _PATH_VARRUN
#define _PATH_VARRUN "/var/run/"
/* just in case */
#ifndef _PATH_STDPATH
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
#endif
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
#ifndef _PATH_VARRUN
#define _PATH_VARRUN "/var/run/"
#endif
#ifndef FINIT_CGPATH
#define FINIT_CGPATH "/sys/fs/cgroup"
#endif
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
#define CMD_SIZE 256
#define LINE_SIZE 1024
@@ -62,10 +73,10 @@
/* Finit extensions over std SysV */
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
#define INIT_CMD_START_SVC 10
#define INIT_CMD_START_SVC 10 /* START service */
#define INIT_CMD_STOP_SVC 11
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */
#define INIT_CMD_GET_RUNLEVEL 16
@@ -96,6 +107,7 @@ extern int prevlevel;
extern int debug;
extern int rescue;
extern int single;
extern int bootstrap;
extern char *rcsd;
extern char *sdown;
extern char *network;
+17 -18
View File
@@ -41,9 +41,6 @@
#define _PATH_LOGIN "/bin/login"
#endif
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
/*
* Read one character from stdin.
*/
@@ -54,7 +51,7 @@ static int readch(char *tty)
st = read(STDIN_FILENO, &ch1, 1);
if (st == 0) {
print("\n");
dprint(STDOUT_FILENO, "\n", 0);
_exit(0);
}
@@ -74,31 +71,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
s0 = line;
for (s = line; *s != 0; s++) {
if (*s == '\\') {
(void)write(1, s0, s - s0);
if ((s - s0) > 0)
dprint(STDOUT_FILENO, s0, s - s0);
s0 = s + 2;
switch (*++s) {
case 'l':
print(tty);
dprint(STDOUT_FILENO, tty, 0);
break;
case 'm':
print(uts->machine);
dprint(STDOUT_FILENO, uts->machine, 0);
break;
case 'n':
print(uts->nodename);
dprint(STDOUT_FILENO, uts->nodename, 0);
break;
#ifdef _GNU_SOURCE
case 'o':
print(uts->domainname);
dprint(STDOUT_FILENO, uts->domainname, 0);
break;
#endif
case 'r':
print(uts->release);
dprint(STDOUT_FILENO, uts->release, 0);
break;
case 's':
print(uts->sysname);
dprint(STDOUT_FILENO, uts->sysname, 0);
break;
case 'v':
print(uts->version);
dprint(STDOUT_FILENO, uts->version, 0);
break;
case 0:
goto leave;
@@ -109,7 +107,8 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
}
leave:
(void)write(1, s0, s - s0);
if ((s - s0) > 0)
dprint(STDOUT_FILENO, s0, s - s0);
}
/*
@@ -159,7 +158,7 @@ static void do_getty(char *tty, char *name, size_t len)
while ((ch = readch(tty)) != '\n') {
if (ch == CTRL('U')) {
while (np > name) {
(void)write(1, "\b \b", 3);
dprint(STDOUT_FILENO, "\b \b", 3);
np--;
}
continue;
@@ -186,12 +185,12 @@ static int do_login(char *name)
{
struct stat st;
cgroup_user(name);
cgroup_user(name, 0);
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/*
* Failed to exec login, should be impossible. Try a starting a
* fallback shell instead.
* Failed to exec login, should not happen on normal systems.
* Try a starting a rescue shell instead.
*
* Note: Add /etc/securetty handling.
*/
@@ -215,7 +214,7 @@ int getty(char *tty, speed_t speed, char *term, char *user)
/* Set up TTY, re-enabling ISIG et al. */
stty(STDIN_FILENO, speed);
(void)write(STDERR_FILENO, cln, strlen(cln));
dprint(STDERR_FILENO, cln, strlen(cln));
/* The getty process is responsible for the UTMP login record */
utmp_set_login(tty, NULL);
+88 -16
View File
@@ -103,6 +103,9 @@ static void add_console(char *cons)
if (ptr)
*ptr = 0;
if (!strncmp(cons, "/dev/", 5))
cons += 5;
snprintf(path, sizeof(path), "/dev/%s", cons);
/* check for and skip duplicates */
@@ -121,7 +124,7 @@ static void add_console(char *cons)
num_cons++;
}
void console_init(void)
static void find_active_consoles(void)
{
char *cons, *ptr, *tok;
char buf[512];
@@ -131,7 +134,7 @@ void console_init(void)
if (!fp) {
fallback:
_d("Using fallback /dev/console");
add_console("/dev/console");
add_console("console");
return;
}
@@ -148,6 +151,12 @@ void console_init(void)
}
}
void console_init(void)
{
find_active_consoles();
ttinit();
}
ssize_t cprintf(const char *fmt, ...)
{
const size_t len = strlen(fmt) * 2;
@@ -161,7 +170,7 @@ ssize_t cprintf(const char *fmt, ...)
va_end(ap);
for (i = 0; i < num_cons; i++)
(void)write(fds[i], buf, size);
dprint(fds[i], buf, size);
return size;
}
@@ -246,20 +255,20 @@ static char *pad(char *buf, size_t len, char *ch, size_t width)
void print_banner(const char *heading)
{
char buf[4 * SCREEN_WIDTH];
char buf[4 * ttcols];
if (progress_style == PROGRESS_SILENT)
return;
memset(buf, 0, sizeof(buf));
buf[0] = 0;
strlcat(buf, "\r\e[2K", sizeof(buf));
if (progress_style == PROGRESS_CLASSIC) {
strlcat(buf, "\e[1m", sizeof(buf));
strlcat(buf, heading, sizeof(buf));
pad(buf, sizeof(buf), "=", SCREEN_WIDTH + 10);
pad(buf, sizeof(buf), "=", ttcols + 10);
} else {
size_t wmax = 80 <= SCREEN_WIDTH ? 80 : SCREEN_WIDTH;
size_t wmax = 80 <= ttcols ? 80 : ttcols;
/* • • • Foo System ═══════════════════════════════════════════════ */
/* ⬤ ⬤ ⬤ Foo System ═══════════════════════════════════════════════ */
@@ -322,7 +331,7 @@ static char *status(int rc)
void printv(const char *fmt, va_list ap)
{
char buf[SCREEN_WIDTH];
char buf[ttcols];
size_t len;
if (!fmt || progress_style == PROGRESS_SILENT)
@@ -330,7 +339,7 @@ void printv(const char *fmt, va_list ap)
delline();
memset(buf, 0, sizeof(buf));
buf[0] = 0;
len = print_timestamp(buf, sizeof(buf));
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
@@ -404,6 +413,64 @@ int getgroup(char *group)
#endif
}
/* get current user */
int getcuser(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct passwd *pw = getpwuid(getuid());
if (pw)
ptr = pw->pw_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
/* get current group */
int getcgroup(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct group *gr = getgrgid(getgid());
if (gr)
ptr = gr->gr_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
int uid, gid;
int rc = 0;
omask = umask(0);
uid = getuser(user, NULL);
if (uid >= 0) {
gid = getgroup(group);
if (gid < 0)
gid = 0;
rc = makedir(dir, mode);
if (chown(dir, uid, gid))
_pe("Failed chown(%s, %d, %d)", dir, uid, gid);
}
umask(omask);
return rc;
}
void set_hostname(char **hostname)
{
FILE *fp;
@@ -426,14 +493,16 @@ void set_hostname(char **hostname)
return;
}
fgets(*hostname, st.st_size, fp);
chomp(*hostname);
if (fgets(*hostname, st.st_size, fp))
chomp(*hostname);
fclose(fp);
}
done:
if (*hostname)
sethostname(*hostname, strlen(*hostname));
if (*hostname) {
if (sethostname(*hostname, strlen(*hostname)))
_pe("Failed sethostnaem(%s)", *hostname);
}
}
/*
@@ -471,6 +540,7 @@ void networking(int updown)
run("ifdown -a");
_exit(0);
}
cgroup_service("network", pid, NULL);
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
}
@@ -557,10 +627,12 @@ int ismnt(char *file, char *dir, char *mode)
while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, dir)) {
if (mode && hasopt(mnt->mnt_opts, mode))
found = 1;
else
if (mode) {
if (hasopt(mnt->mnt_opts, mode))
found = 1;
} else
found = 1;
break;
}
}
+26 -1
View File
@@ -26,6 +26,7 @@
#define FINIT_HELPERS_H_
#include <mntent.h>
#include <sched.h> /* sched_yield() */
#include <stdarg.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -70,7 +71,7 @@ void show_progress (pstyle_t style);
int getty (char *tty, speed_t speed, char *term, char *user);
int sh (char *tty);
int stty (int fd, speed_t speed);
void stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud);
void print_banner (const char *heading);
@@ -81,6 +82,12 @@ int print_result (int fail);
int getuser (char *username, char **home);
int getgroup (char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname);
void networking (int updown);
@@ -103,6 +110,24 @@ static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid)
return 0;
}
static inline int dprint(int fd, const char *s, size_t len)
{
size_t loop = 3;
int rc = -1;
if (!len)
len = strlen(s);
while (loop--) {
rc = write(fd, s, len);
if (rc == -1 && errno == EINTR)
continue;
break;
}
return rc;
}
int ismnt (char *file, char *dir, char *mode);
int fismnt (char *dir);
+458 -257
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_INITCTL_H_
#define FINIT_INITCTL_H_
#include "finit.h"
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
extern int ionce; /* initctl -1 */
extern int heading; /* initctl -t */
extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */
extern void print_header(const char *fmt, ...);
#endif /* FINIT_INITCTL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+20 -3
View File
@@ -81,10 +81,10 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
return -1;
if (!fexist(file)) {
_d("No such file or directory, skipping %s", file);
_d("skipping %s: no such file or directory", file);
return 0;
}
_d("Adding new watcher for path %s", file);
_d("adding new watcher for path %s", file);
path = strdup(file);
if (!path) {
@@ -94,7 +94,24 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
if (wd < 0) {
_pe("Failed adding watcher for %s", path);
switch (errno) {
case EEXIST:
break;
case EINVAL:
/*
* some older kernels, notably 4.19, don't like
* inotify on top-level cgroups in cgroup v2
*/
if (!strncmp(path, "/sys/fs/cgroup", 14))
break; /* ignore error */
/* fallthrough */
default:
_pe("Failed adding watcher for %s", path);
break;
}
free(path);
return -1;
}
+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:
*/
+6 -9
View File
@@ -22,7 +22,6 @@
*/
#include "config.h"
#include <sched.h> /* sched_yield() */
#include <stdio.h>
#include <stdarg.h>
#include <lite/lite.h>
@@ -50,15 +49,13 @@ void log_exit(void)
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (debug)
screen_exit();
else
screen_init();
if (!debug)
ttinit();
enable_progress(1);
}
void log_open(void)
static void log_open(void)
{
int opts = LOG_PID;
@@ -79,10 +76,9 @@ void log_debug(void)
if (debug) {
loglevel = LOG_DEBUG;
screen_exit();
} else {
loglevel = LOG_NOTICE;
screen_init();
ttinit();
}
log_open();
@@ -103,11 +99,12 @@ void logit(int prio, const char *fmt, ...)
if (up || fexist("/dev/log")) {
if (!up)
log_open();
vsyslog(prio, fmt, ap);
goto done;
}
if (!debug && prio > loglevel)
if (prio > loglevel)
goto done;
fp = fopen("/dev/kmsg", "w");
+2 -65
View File
@@ -30,75 +30,12 @@
#include <syslog.h>
#include <unistd.h>
#include <sys/stat.h>
#include <lite/lite.h>
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
extern int logrotate(char *file, int num, off_t sz);
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
{
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
static int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
if (cnt == 2 && !access(nfile, F_OK)) {
size_t len = 5 + strlen(nfile) + 1;
char cmd[len];
snprintf(cmd, len, "gzip %s", nfile);
system(cmd);
remove(nfile);
}
}
if (rename(file, nfile))
(void)truncate(file, 0);
else
create(file, st.st_mode, st.st_uid, st.st_gid);
} else {
if (truncate(file, 0))
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
}
}
return 0;
}
static int checksz(FILE *fp, off_t sz)
{
struct stat st;
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <lite/lite.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
{
if (mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid)) {
syslog(LOG_ERR, "Failed recreating %s: %s", path, strerror(errno));
return -1;
}
return 0;
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
if (rename(ofile, nfile) && errno != ENOENT)
syslog(LOG_ERR, "Failed logrotate %s: %s",
ofile, strerror(errno));
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
if (rename(ofile, nfile) && errno != ENOENT) {
syslog(LOG_ERR, "Failed logrotate %s: %s",
ofile, strerror(errno));
continue;
}
if (cnt == 2 && fexist(nfile)) {
if (systemf("gzip %s 2>/dev/null", nfile))
continue; /* no gzip, probably */
(void)remove(nfile);
}
}
if (rename(file, nfile))
goto fallback;
recreate(file, st.st_mode, st.st_uid, st.st_gid);
} else {
fallback:
if (truncate(file, 0))
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
file, strerror(errno));
}
}
return 0;
}
+10 -12
View File
@@ -95,18 +95,15 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
char *pid_file(svc_t *svc)
{
static char path[256];
char fn[MAX_ARG_LEN];
if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!')
return &svc->pidfile[1];
return svc->pidfile;
}
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
errno = ENOENT;
return pid_runpath(fn, path, sizeof(path));
return NULL;
}
pid_t pid_file_read(const char *fn)
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
return fclose(fp);
}
static int pid_realpath(svc_t *svc, char *file)
int pid_file_set(svc_t *svc, char *file, int not)
{
int not = 0;
if (!file)
if (!file) {
file = pid_file(svc);
if (!file)
return -1;
}
if (file[0] == '!') {
not = 1;
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_realpath(svc, arg);
return pid_file_set(svc, arg, 0);
if (arg[0] == '!') {
arg++;
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
if (len > 4 && strcmp(&path[len - 4], ".pid"))
strlcat(path, ".pid", sizeof(path));
return pid_realpath(svc, path);
return pid_file_set(svc, path, 0);
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid"))
return pid_realpath(svc, NULL);
return pid_file_set(svc, NULL, 0);
return 1;
}
+1
View File
@@ -32,6 +32,7 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc);
int pid_file_set (svc_t *svc, char *file, int not);
pid_t pid_file_read (const char *fn);
int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
+1 -1
View File
@@ -196,7 +196,7 @@ void plugin_run_hook(hook_point_t no, void *arg)
PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL");
p->hook[no].cb(arg ? arg : p->hook[no].arg);
}
}
+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:
+1 -2
View File
@@ -1,3 +1,2 @@
runlevel 1
hostname rescue
tty [12345] @console noclear nologin
tty [12345] rescue
+21 -3
View File
@@ -1,4 +1,12 @@
# sample.conf: template run/task/service stanza for finit
# sample.conf: template cgroup/run/task/service stanza for finit
#
# The top-level cgroups can be defined anywhere, but share the same
# namespace. It is up to the user to ensure groups are defined only
# once, otherwise the last read .conf wins. See below for assigning
# run/task/services to cgroups. The below example creates the cgroup
# NAME with the following cpu and memory settings:
#
# cgroup NAME cpu.weight:1000 mem.max:65
#
# The one-shot commands 'run' and 'task' are called only once per
# runlevel. Finit does not restart them when they exit.
@@ -25,6 +33,16 @@
# | | `--------------------------------------------------------------- Optional conditions
# | `---------------------------------------------------------------------- Optional Runlevels
# `------------------------------------------------------------------------------ Monitored application
#
# Each stanza can also hold a 'cgroup' argument, or be prefixed with
# cgroup.NAME to place all following stanzas in the same group. In
# the following example, foo and bar share the cgroup foo but baz
# runs in its own cgroup baz:
#
# cgroup.foo
# service cgroup:cpu.weight:250,mem.max:655350 foo args -- foo desc
# service cgroup:cpu.weight:150,mem.max:655350 bar args -- bar desc
# service cgroup.baz:cpu.weight:300 baz args -- baz desc
# Debian GNU/Linux: start SSH daemon as soon as basic networking comes up
#service [2345] <net/lo/up> env:-/etc/default/ssh /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
@@ -33,11 +51,11 @@
#task [S] env:/etc/conf.d/loadkmap zcat $KEYMAP | loadkmap -- Loading keymap
# Alpine Linux: start SSH daemon with $DROPBEAR_OPTS from /etc/conf.d
#service [2345] env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
#service [2345] cgroup.user env:-/etc/conf.d/dropbear dropbear -R -F $DROPBEAR_OPTS -- Dropbear SSH daemon
# Handle PWR button to shutdown/reboot -- useful in Qemu (virt-manager)
# Depends on syslogd having started. Redirect any output to log.
#service [2345] <pid/syslogd> log acpid -f -- ACPI daemon
#service [2345] <pid/syslogd> cgroup:mem.max:32000,cpu.max:1000 log acpid -f -- ACPI daemon
# Start rsyslogd as soon as possible, should always run
# Provides pid/syslogd condition
+87 -20
View File
@@ -30,10 +30,7 @@
#include <stdlib.h>
#include <lite/lite.h>
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
#include "initctl.h"
static int calc_width(char *arr[], size_t len)
@@ -53,27 +50,66 @@ static int calc_width(char *arr[], size_t len)
static void do_list(const char *path)
{
char buf[screen_cols];
static int once = 0;
int width, num, prev;
glob_t gl;
size_t i;
chdir(path);
if (chdir(path)) {
char *dir, *ptr;
if (!fexist(path))
return;
dir = strdupa(path);
if (!dir)
return;
ptr = strrchr(dir, '/');
if (ptr)
*ptr++ = 0;
else
ptr = dir;
if (heading)
print_header("%s%s ", once ? "\n" : "", dir);
puts(ptr);
puts("");
return;
}
if (glob("*.conf", 0, NULL, &gl))
return;
if (gl.gl_pathc <= 0)
goto done;
snprintf(buf, sizeof(buf), "%s ", path);
printheader(NULL, buf, 0);
if (plain) {
if (heading)
print_header("%s%s ", once ? "\n" : "", path);
once++;
for (i = 0; i < gl.gl_pathc; i++) {
if (!heading) {
char buf[512];
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
puts(buf);
} else
puts(gl.gl_pathv[i]);
}
goto done;
}
if (heading)
print_header("%s ", path);
width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0)
goto done;
num = (screen_cols - 2) / width;
if ((num - 1) * 2 + num * width > screen_cols)
num = (ttcols - 2) / width;
if ((num - 1) * 2 + num * width > ttcols)
num--;
prev = 0;
@@ -118,15 +154,19 @@ int serv_list(char *arg)
if (fisdir(FINIT_RCSD))
do_list(FINIT_RCSD);
if (fexist(FINIT_CONF))
do_list(FINIT_CONF);
return 0;
}
/*
* Return path to configuration file for 'name', relative to FINIT_RCSD.
* This may be any of the following, provided sysconfdir is /etc:
* Return path to configuration file for 'name'. This may be any of the
* following, provided sysconfdir is /etc:
*
* - /etc/finit.d/$name.conf
* - /etc/finit.d/available/$name.conf
* - /etc/finit.d/$name.conf
* - /etc/finit.conf
*
* The system *may* have a /etc/finit.d/available/ directory, or it may
* just use a plain /etc/finit.d/ -- we do not set policy.
@@ -138,6 +178,11 @@ static char *conf(char *path, size_t len, char *name, int creat)
{
char corr[40];
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
strlcpy(path, FINIT_CONF, len);
return path;
}
if (!strstr(name, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", name);
name = corr;
@@ -155,6 +200,10 @@ static char *conf(char *path, size_t len, char *name, int creat)
} else
strlcat(path, name, len);
/* fall back to static service unless edit/create */
if (!creat && !fexist(path))
paste(path, len, FINIT_RCSD, name);
return path;
}
@@ -208,7 +257,8 @@ int do_disable(char *arg, int check)
if (chdir(FINIT_RCSD))
err(1, "failed cd %s", FINIT_RCSD);
chdir("enabled"); /* System *may* have enabled/ dir. */
if (chdir("enabled")) /* System *may* have enabled/ dir. */
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno));
if (check && stat(arg, &st))
errx(1, "%s not (an) enabled (service).", arg);
@@ -250,6 +300,20 @@ int serv_touch(char *arg)
return 0;
}
int serv_show(char *arg)
{
char path[256];
char *fn;
fn = conf(path, sizeof(path), arg, 0);
if (!fexist(fn)) {
warnx("Cannot find %s", arg);
return 1;
}
return systemf("cat %s", fn);
}
/*
* Try to open an editor for the given file, if creat is given we
* create a new file based on /lib/finit/sample.conf
@@ -279,7 +343,7 @@ static int do_edit(char *arg, int creat)
if (!fexist(fn)) {
if (!creat) {
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
return serv_list("available");
return serv_list(NULL);
}
#ifdef SAMPLE_CONF
@@ -301,8 +365,8 @@ static int do_edit(char *arg, int creat)
int serv_edit(char *arg)
{
if (!arg || !arg[0]) {
warnx("missing argument to edit, may be one of:");
return serv_list("available");
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF))
return 0;
}
return do_edit(arg, icreate);
@@ -346,8 +410,10 @@ int serv_delete(char *arg)
char buf[256];
char *fn;
if (!arg || !arg[0])
errx(1, "missing argument to delete");
if (!arg || !arg[0]) {
warnx("missing argument to delete, may be one of:");
return serv_list("available");
}
fn = conf(buf, sizeof(buf), arg, 0);
if (!fn)
@@ -358,7 +424,8 @@ int serv_delete(char *arg)
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
do_disable(arg, 0);
remove(fn);
if (remove(fn))
err(1, "Failed removing %s", fn);
}
return 0;
+1
View File
@@ -28,6 +28,7 @@ int serv_list (char *arg);
int serv_enable (char *arg);
int serv_disable (char *arg);
int serv_touch (char *arg);
int serv_show (char *arg);
int serv_edit (char *arg);
int serv_creat (char *arg);
int serv_delete (char *arg);
+484 -139
View File
@@ -25,7 +25,6 @@
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */
#include <sched.h> /* sched_yield() */
#include <string.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -49,8 +48,6 @@
#include "utmp-api.h"
#include "schedule.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static struct wq work = {
.cb = service_worker,
};
@@ -198,6 +195,8 @@ static int lredirect(svc_t *svc)
char *tag = basename(svc->cmd);
char *prio = "daemon.info";
sched_yield();
fds = open(ptsname(fd), O_RDONLY);
close(fd);
if (fds == -1)
@@ -207,8 +206,8 @@ static int lredirect(svc_t *svc)
/* Reset signals */
sig_unblock();
if (!whichp(LOGIT_PATH)) {
logit(LOG_INFO, LOGIT_PATH " missing, using syslog for %s instead", svc->name);
if (!whichp(_PATH_LOGIT)) {
logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
fallback_logger(tag, prio);
_exit(0);
}
@@ -219,7 +218,7 @@ static int lredirect(svc_t *svc)
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp(LOGIT_PATH, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
execlp(_PATH_LOGIT, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
_exit(1);
}
@@ -228,7 +227,7 @@ static int lredirect(svc_t *svc)
if (svc->log.prio[0])
prio = svc->log.prio;
execlp(LOGIT_PATH, "logit", "-t", tag, "-p", prio, NULL);
execlp(_PATH_LOGIT, "logit", "-t", tag, "-p", prio, NULL);
_exit(1);
}
@@ -262,37 +261,6 @@ static int redirect(svc_t *svc)
return 0;
}
/*
* non-zero env, no checking if file exists or not
*/
static char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static int check_env(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env)
return 1;
return fexist(env);
}
/*
* Source environment file, if it exists
* Note: must be called from privsepped child
@@ -309,7 +277,7 @@ static void source_env(svc_t *svc)
if (!fn)
return;
/* Warning in service_start() after check_env() */
/* Warning in service_start() after svc_checkenv() */
fp = fopen(fn, "r");
if (!fp)
return;
@@ -384,6 +352,84 @@ static int is_norespawn(void)
fexist("/tmp/norespawn");
}
/* used for process group name, derived from originating filename,
* so to group multiple services, place them in the same .conf
*/
static char *group_name(svc_t *svc, char *buf, size_t len)
{
char *ptr;
if (!svc->file[0])
return svc_ident(svc, buf, len);
ptr = strrchr(svc->file, '/');
if (ptr)
ptr++;
else
ptr = svc->file;
strlcpy(buf, ptr, len);
ptr = strstr(buf, ".conf");
if (ptr)
*ptr = 0;
return buf;
}
static pid_t service_fork(svc_t *svc)
{
pid_t pid;
pid = fork();
if (pid == 0) {
char *home = NULL;
#ifdef ENABLE_STATIC
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
int gid = 0;
#else
int uid = getuser(svc->username, &home);
int gid = getgroup(svc->group);
#endif
sched_yield();
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &svc->rlimit[i]) == -1)
logit(LOG_WARNING,
"%s: rlimit: Failed setting %s",
svc->cmd, rlim2str(i));
}
/* Set desired user+group */
if (gid >= 0) {
if (setgid(gid))
_pe("%s: failed setgid(%d)", svc->cmd, gid);
}
if (uid >= 0) {
if (setuid(uid))
_pe("%s: failed setuid(%d)", svc->cmd, uid);
/* Set default path for regular users */
if (uid > 0)
setenv("PATH", _PATH_DEFPATH, 1);
if (home) {
setenv("HOME", home, 1);
if (chdir(home)) {
if (chdir("/"))
_pe("%s: failed chdir(%s) and chdir(/)", svc->cmd, home);
}
}
}
/* Source any environment from env:/path/to/file */
source_env(svc);
}
return pid;
}
/**
* service_start - Start service
* @svc: Service to start
@@ -395,6 +441,7 @@ static int service_start(svc_t *svc)
{
int result = 0, do_progress = 1;
sigset_t nmask, omask;
char grnam[80];
pid_t pid;
size_t i;
@@ -407,17 +454,28 @@ static int service_start(svc_t *svc)
/* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd)) {
print(1, "%s: does not exist", svc->cmd);
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
svc_missing(svc);
return 1;
}
/* Unlike systemd we do not allow starting service if env is missing, unless - */
if (!check_env(svc)) {
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
if (!svc_checkenv(svc)) {
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
svc_missing(svc);
return 1;
}
if (svc_is_tty(svc) && !svc->notty) {
char *dev = tty_canonicalize(svc->dev);
if (!dev || !tty_exists(dev)) {
_d("TTY %s missing or invalid, halting service.", svc->dev);
svc_missing(svc);
return 1;
}
}
if (svc_is_sysv(svc))
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
@@ -439,47 +497,10 @@ static int service_start(svc_t *svc)
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork();
cgroup_service(svc->name, svc->id, pid);
pid = service_fork(svc);
if (pid == 0) {
int status;
char *home = NULL;
#ifdef ENABLE_STATIC
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
int gid = 0;
#else
int uid = getuser(svc->username, &home);
int gid = getgroup(svc->group);
#endif
char *args[MAX_NUM_SVC_ARGS + 1];
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &svc->rlimit[i]) == -1)
logit(LOG_WARNING,
"%s: rlimit: Failed setting %s",
svc->cmd, rlim2str(i));
}
/* Set desired user+group */
if (gid >= 0)
setgid(gid);
if (uid >= 0) {
setuid(uid);
/* Set default path for regular users */
if (uid > 0)
setenv("PATH", _PATH_DEFPATH, 1);
if (home) {
setenv("HOME", home, 1);
chdir(home);
}
}
/* Source any environment from env:/path/to/file */
source_env(svc);
int status;
if (!svc_is_sysv(svc)) {
wordexp_t we = { 0 };
@@ -543,7 +564,8 @@ static int service_start(svc_t *svc)
}
args[i] = NULL;
redirect(svc);
if (!svc_is_tty(svc))
redirect(svc);
sig_unblock();
/*
@@ -561,6 +583,8 @@ static int service_start(svc_t *svc)
if (svc_is_runtask(svc))
status = exec_runtask(args[0], &args[1]);
else if (svc_is_tty(svc))
status = tty_exec(svc);
else
status = execvp(args[0], &args[1]);
@@ -578,6 +602,11 @@ static int service_start(svc_t *svc)
_d("Starting %s %s", svc->cmd, buf);
}
if (svc_is_tty(svc))
cgroup_user("getty", pid);
else
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid, &svc->cgroup);
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
svc->pid = pid;
@@ -646,12 +675,19 @@ static void service_cleanup(svc_t *svc)
{
char *fn;
/* PID collected, cancel any pending SIGKILL */
service_timeout_cancel(svc);
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
if (fn && remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
basename(svc->cmd), fn);
/* No longer running, update books. */
if (svc_is_tty(svc) && svc->pid > 1)
utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */
svc->oldpid = svc->pid;
svc->start_time = svc->pid = 0;
}
@@ -695,7 +731,7 @@ static int service_stop(svc_t *svc)
if (svc->pid > 1) {
/* Kill all children in the same proess group, e.g. logit */
rc = kill(-svc->pid, svc->sighalt);
_d("kill(-%d, %d) => rc %d", svc->pid, svc->sighalt, rc);
/* PID lost or forking process never really started */
if (rc == -1 && ESRCH == errno)
service_cleanup(svc);
@@ -862,6 +898,30 @@ static void parse_env(svc_t *svc, char *env)
strlcpy(svc->env, env, sizeof(svc->env));
}
static void parse_cgroup(svc_t *svc, char *cgroup)
{
char *ptr = cgroup;
if (!cgroup)
return;
if (cgroup[0] == '.') {
ptr = strchr(cgroup, ':');
if (ptr)
*ptr++ = 0;
strlcpy(svc->cgroup.name, &cgroup[1], sizeof(svc->cgroup.name));
if (!ptr)
return;
}
if (strlen(ptr) >= sizeof(svc->cgroup)) {
_e("%s: cgroup settings too long (>%d chars)", svc->cmd, sizeof(svc->cgroup));
return;
}
strlcpy(svc->cgroup.cfg, ptr, sizeof(svc->cgroup.cfg));
}
static void parse_sighalt(svc_t *svc, char *arg)
{
int signo;
@@ -888,6 +948,14 @@ static void parse_killdelay(svc_t *svc, char *delay)
svc->killdelay = (int)(sec * 1000);
}
static void parse_script(char *type, char *script, char *buf, size_t len)
{
if (access(script, X_OK))
logit(LOG_WARNING, "%s: %s:%s is missing or not executable, skipping.", type, script);
else
strlcpy(buf, script, len);
}
/*
* name:<name>
*/
@@ -903,13 +971,10 @@ static void parse_name(svc_t *svc, char *arg)
}
strlcpy(svc->name, name, sizeof(svc->name));
/* Warn if svc generates same condition as an existing service */
svc_validate(svc);
}
/**
* parse_cmdline_args - Update the command line args in the svc struct
/*
* Update the command line args in the svc struct
*
* strtok internal pointer must be positioned at first command line arg
* when this function is called.
@@ -918,23 +983,29 @@ static void parse_name(svc_t *svc, char *arg)
*/
static void parse_cmdline_args(svc_t *svc, char *cmd)
{
int diff = 0;
char sep = 0;
char *arg;
int i = 0;
if (strcmp(svc->args[i], cmd))
diff++;
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while (i < MAX_NUM_SVC_ARGS)
svc->args[i++][0] = 0;
/*
* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
* array to be zero-terminated.
*/
for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1);) {
while ((arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1)) {
char prev[sizeof(svc->args[0])] = { 0 };
char ch = arg[0];
size_t len;
if (!sep) {
strlcpy(prev, svc->args[i], sizeof(prev));
svc->args[i][0] = 0;
}
/* XXX: ugly string arg re-concatenation, fixme */
if (ch == '"' || ch == '\'')
sep = ch;
@@ -951,6 +1022,13 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
continue;
}
/* replace any @console arg with the expanded device name */
if (svc_is_tty(svc) && tty_isatcon(svc->args[i]))
strlcpy(svc->args[i], svc->dev, sizeof(svc->args[i]));
if (strcmp(svc->args[i], prev))
diff++;
sep = 0;
i++;
}
@@ -959,15 +1037,22 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* Clear remaining args in case they were set earlier.
* This also zero-terminates the args array.
*/
while (i < MAX_NUM_SVC_ARGS)
svc->args[i++][0] = 0;
#if 0
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
if (!svc->args[i][0])
break;
_d("%s ", svc->args[i]);
while (i < MAX_NUM_SVC_ARGS) {
if (svc->args[i++][0]) {
svc->args[i-1][0] = 0;
diff++;
}
}
#endif
/*
* Check also for changes to /etc/default/foo, because this
* also constitutes changes to command line args.
*/
diff += conf_changed(svc_getenv(svc));
if (diff)
_d("Modified args for %s detected", cmd);
svc->args_dirty = (diff > 0);
}
@@ -1031,7 +1116,10 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
char *username = NULL, *log = NULL, *pid = NULL;
char *name = NULL, *halt = NULL, *delay = NULL;
char *id = NULL, *env = NULL;
char *id = NULL, *env = NULL, *cgroup = NULL;
char *pre_script = NULL, *post_script = NULL;
struct tty tty = { 0 };
char *dev = NULL;
int levels = 0;
int manual = 0;
char *line;
@@ -1042,7 +1130,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
return errno = EINVAL;
}
line = strdup(cfg);
line = strdupa(cfg);
if (!line)
return 1;
@@ -1064,15 +1152,17 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
}
cmd = strtok(line, " ");
cmd = strtok(line, " \t");
if (!cmd) {
incomplete:
_e("Incomplete service '%s', cannot register", cfg);
free(line);
return errno = ENOENT;
}
while (cmd) {
if (type == SVC_TYPE_TTY && cmd[0] == '@')
break; /* @console */
if (cmd[0] == '@') /* @username[:group] */
username = &cmd[1];
else if (cmd[0] == '[') /* [runlevels] */
@@ -1093,8 +1183,16 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
halt = &cmd[5];
else if (!strncasecmp(cmd, "kill:", 5))
delay = &cmd[5];
else if (!strncasecmp(cmd, "pre:", 4))
pre_script = &cmd[4];
else if (!strncasecmp(cmd, "post:", 5))
post_script = &cmd[5];
else if (!strncasecmp(cmd, "env:", 4))
env = &cmd[4];
else if (!strncasecmp(cmd, "cgroup:", 7))
cgroup = &cmd[7]; /* only settings */
else if (!strncasecmp(cmd, "cgroup.", 7))
cgroup = &cmd[6]; /* with group */
else
break;
@@ -1107,25 +1205,66 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
levels = conf_parse_runlevels(runlevels);
if (runlevel > 0 && !ISOTHER(levels, 0)) {
_d("Skipping %s, bootstrap is completed.", cmd);
free(line);
return 0;
}
if (!id)
id = "";
svc = svc_find(cmd, id);
if (type == SVC_TYPE_TTY) {
size_t i, len = 0;
if (tty_parse_args(cmd, &tty))
return errno;
if (tty.cmd)
len += strlen(tty.cmd);
else
len += 3;
len += tty.num + 1;
for (i = 0; i < tty.num; i++)
len += strlen(tty.args[i]) + 1;
line = alloca(len);
if (!line)
return errno;
snprintf(line, len, "%s", tty.cmd ?: "tty");
for (i = 0; i < tty.num; i++) {
strlcat(line, " ", len);
strlcat(line, tty.args[i], len);
}
cmd = strtok(line, " \t");
if (!cmd)
return errno;
if (tty_isatcon(tty.dev))
dev = tty_atcon();
else
dev = tty.dev;
next:
svc = svc_find_by_tty(dev);
} else
svc = svc_find(cmd, id);
if (!svc) {
_d("Creating new svc for %s id #%s type %d", cmd, id, type);
svc = svc_new(cmd, id, type);
if (!svc) {
_e("Out of memory, cannot register service %s", cmd);
free(line);
return errno = ENOMEM;
}
if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc);
} else {
/* update type, may have changed from service -> task */
svc->type = type;
/* e.g., if missing cmd or env before */
svc_unblock(svc);
}
/* Always clear svc PID file, for now. See TODO */
@@ -1142,42 +1281,103 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
strlcpy(svc->group, ptr, sizeof(svc->group));
}
strlcpy(svc->username, username, sizeof(svc->username));
} else {
getcuser(svc->username, sizeof(svc->username));
getcgroup(svc->group, sizeof(svc->group));
}
parse_cmdline_args(svc, cmd);
svc->runlevels = levels;
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
_d("Service %s runlevel 0x%02x", svc->cmd, svc->runlevels);
conf_parse_cond(svc, cond);
parse_name(svc, name);
if (type == SVC_TYPE_TTY) {
char *ptr;
if (dev)
strlcpy(svc->dev, dev, sizeof(svc->dev));
if (tty.baud)
strlcpy(svc->baud, tty.baud, sizeof(svc->baud));
if (tty.term)
strlcpy(svc->term, tty.term, sizeof(svc->term));
svc->noclear = tty.noclear;
svc->nowait = tty.nowait;
svc->nologin = tty.nologin;
svc->notty = tty.notty;
svc->rescue = tty.rescue;
/* TTYs cannot be redirected */
log = NULL;
/* Create name:id tuple for identiy, e.g., tty:S0 */
ptr = strrchr(svc->dev, '/');
if (ptr) {
ptr++;
if (!strncmp(ptr, "tty", 3))
ptr += 3;
} else
ptr = svc->dev;
if (!id || id[0] == 0)
id = ptr;
strlcpy(svc->name, "tty", sizeof(svc->name));
strlcpy(svc->id, id, sizeof(svc->id));
} else
parse_name(svc, name);
parse_cmdline_args(svc, cmd);
/*
* Warn if svc generates same condition (based on name:id)
* as an existing service.
*/
svc_validate(svc);
if (halt)
parse_sighalt(svc, halt);
if (delay)
parse_killdelay(svc, delay);
if (pre_script)
parse_script("pre", pre_script, svc->pre_script, sizeof(svc->pre_script));
if (post_script)
parse_script("post", post_script, svc->post_script, sizeof(svc->post_script));
if (log)
parse_log(svc, log);
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
else if (type == SVC_TYPE_TTY)
snprintf(svc->desc, sizeof(svc->desc), "Getty on %s", svc->dev);
if (env)
parse_env(svc, env);
if (file)
strlcpy(svc->file, file, sizeof(svc->file));
/* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
/* Seed with currently active group, may be empty */
strlcpy(svc->cgroup.name, cgroup_current, sizeof(svc->cgroup.name));
if (cgroup)
parse_cgroup(svc, cgroup);
/* New, recently modified or unchanged ... used on reload. */
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)))
svc_mark_dirty(svc);
else
svc_mark_clean(svc);
svc_enable(svc);
/* for finit native services only, e.g. plugins/hotplug.c */
if (!file)
svc->protect = 1;
/* Free duped line, from above */
free(line);
/* continue expanding any 'tty @console ...' */
if (tty_isatcon(tty.dev)) {
dev = tty_atcon();
if (dev)
goto next;
}
return 0;
}
@@ -1205,36 +1405,47 @@ void service_monitor(pid_t lost, int status)
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return;
if (tty_respawn(lost))
return;
svc = svc_find_by_pid(lost);
if (!svc) {
_d("collected unknown PID %d", lost);
return;
}
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
svc->status = status;
switch (svc->state) {
case SVC_CLEANUP_STATE:
case SVC_SETUP_STATE:
_d("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script,
lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
kill(-svc->pid, SIGKILL);
goto done;
default:
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
svc->status = status;
break;
}
/* Forking sysv/services declare themselves with pid:!/path/to/pid.file */
if (svc_is_starting(svc) && svc_is_forking(svc))
return;
/* Terminate any children in the same proess group, e.g. logit */
kill(-svc->pid, SIGKILL);
/* Try removing PID file (in case service does not clean up after itself) */
if (svc_is_daemon(svc)) {
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
service_cleanup(svc);
} else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */
if (WIFEXITED(status) && !WEXITSTATUS(status))
svc->started = 1;
else
svc->started = 0;
}
/* Terminate any children in the same proess group, e.g. logit */
kill(-svc->pid, SIGKILL);
done:
/* No longer running, update books. */
svc->start_time = svc->pid = 0;
@@ -1247,6 +1458,112 @@ void service_monitor(pid_t lost, int status)
sm_step(&sm);
}
static void svc_mark_affected(char *cond)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc))
continue;
if (cond_affects(cond, svc->cond))
svc_mark_dirty(svc);
}
}
/*
* Called on conf_reload() to update service reverse dependencies.
* E.g., if ospfd depends on zebra and the zebra Finit conf has
* changed, we need to mark the ospfd Finit conf as changed too.
*/
void service_update_rdeps(void)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char cond[MAX_COND_LEN];
if (!svc_is_changed(svc))
continue;
svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
}
}
static void service_kill_script(svc_t *svc)
{
if (svc->pid <= 1)
return;
kill(-svc->pid, SIGKILL);
}
static void service_pre_script(svc_t *svc)
{
svc->pid = service_fork(svc);
if (svc->pid < 0) {
_pe("Failed forking off %s pre-script %s", svc_ident(svc, NULL, 0), svc->pre_script);
return;
}
if (svc->pid == 0) {
char *argv[4] = {
"sh",
"-c",
svc->pre_script,
NULL
};
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR);
}
/* Short hard-coded timeout to prevent locking up Finit */
service_timeout_after(svc, svc->killdelay, service_kill_script);
}
static void service_post_script(svc_t *svc)
{
svc->pid = service_fork(svc);
if (svc->pid < 0) {
_pe("Failed forking off %s post-script %s", svc_ident(svc, NULL, 0), svc->post_script);
return;
}
if (svc->pid == 0) {
char *argv[4] = {
"sh",
"-c",
svc->post_script,
NULL
};
int rc, sig;
rc = WEXITSTATUS(svc->status);
sig = WTERMSIG(svc->status);
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
if (WIFEXITED(svc->status)) {
char val[4];
setenv("EXIT_CODE", "exited", 1);
snprintf(val, sizeof(val), "%d", rc & 0xff);
setenv("EXIT_STATUS", val, 1);
} else if (WIFSIGNALED(svc->status)) {
setenv("EXIT_CODE", "signal", 1);
setenv("EXIT_STATUS", sig2str(sig), 1);
}
execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR);
}
/* Short hard-coded timeout to prevent locking up Finit */
service_timeout_after(svc, svc->killdelay, service_kill_script);
}
static void service_retry(svc_t *svc)
{
int timeout;
@@ -1256,12 +1573,13 @@ static void service_retry(svc_t *svc)
if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) {
_d("%s not crashing anymore", svc->cmd);
if (!svc_is_tty(svc))
_d("%s not crashing anymore", svc->cmd);
*restart_cnt = 0;
return;
}
if (*restart_cnt >= RESPAWN_MAX) {
if (*restart_cnt >= SVC_RESPAWN_MAX) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0));
svc_crashing(svc);
@@ -1274,12 +1592,12 @@ static void service_retry(svc_t *svc)
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
svc_unblock(svc);
service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
service_timeout_after(svc, timeout, service_retry);
}
@@ -1290,7 +1608,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
*state = new;
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if ((*state == SVC_STOPPING_STATE)) {
if (*state == SVC_STOPPING_STATE) {
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
svc->cmd, svc->killdelay / 1000);
service_timeout_cancel(svc);
@@ -1322,8 +1640,13 @@ restart:
switch (svc->state) {
case SVC_HALTED_STATE:
if (enabled)
svc_set_state(svc, SVC_READY_STATE);
if (enabled) {
if (svc_has_pre(svc)) {
svc_set_state(svc, SVC_SETUP_STATE);
service_pre_script(svc);
} else
svc_set_state(svc, SVC_READY_STATE);
}
break;
case SVC_DONE_STATE:
@@ -1333,13 +1656,20 @@ restart:
case SVC_STOPPING_STATE:
if (!svc->pid) {
/* PID was collected normally, no need to kill it */
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
char cond[MAX_COND_LEN];
_d("%s: stopped, cleaning up timers and conditions ...", svc->cmd);
service_timeout_cancel(svc);
cond_clear(mkcond(svc, cond, sizeof(cond)));
switch (svc->type) {
case SVC_TYPE_SERVICE:
svc_set_state(svc, SVC_HALTED_STATE);
case SVC_TYPE_TTY:
if (svc_has_post(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE);
service_post_script(svc);
} else
svc_set_state(svc, SVC_HALTED_STATE);
break;
case SVC_TYPE_TASK:
@@ -1355,6 +1685,16 @@ restart:
}
break;
case SVC_CLEANUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_HALTED_STATE);
break;
case SVC_SETUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_READY_STATE);
break;
case SVC_READY_STATE:
if (!enabled) {
svc_set_state(svc, SVC_HALTED_STATE);
@@ -1365,6 +1705,10 @@ restart:
err = service_start(svc);
if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++;
break;
}
@@ -1382,7 +1726,7 @@ restart:
}
if (!svc->pid) {
if (svc_is_daemon(svc)) {
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
svc_restarting(svc);
svc_set_state(svc, SVC_HALTED_STATE);
@@ -1420,7 +1764,9 @@ restart:
case COND_ON:
if (svc_is_changed(svc)) {
if (svc->sighup) {
if (svc_nohup(svc))
service_stop(svc);
else {
/*
* wait until all processes have been
* stopped before continuing...
@@ -1428,8 +1774,7 @@ restart:
if (sm_is_in_teardown(&sm))
break;
service_restart(svc);
} else
service_stop(svc);
}
svc_mark_clean(svc);
}
@@ -1500,7 +1845,7 @@ void service_step_all(int types)
void service_worker(void *unused)
{
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK);
}
/**
+1
View File
@@ -33,6 +33,7 @@ void service_unregister (svc_t *svc);
void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_update_rdeps (void);
int service_step (svc_t *svc);
void service_step_all (int types);
+33 -23
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.)
*
@@ -138,7 +141,9 @@ static struct sigmap {
/* Nonstandard signals found in all modern POSIX systems
(including both BSD and Linux). */
{ SIGWINCH, "SIGWINCH" },
#ifdef SIGSTKFLT
{ SIGSTKFLT, "SIGSTKFLT" },
#endif
{ SIGPWR, "SIGPWR" },
/* Archaic names for compatibility. */
@@ -192,10 +197,10 @@ void do_kill(int signo)
if (fgets(file, sizeof(file), fp)) {
if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file);
else if (file[0] != '@')
kill(pid, signo);
else
else if (file[0] == '@')
_d("Skipping %s ...", &file[1]);
else
kill(pid, signo);
}
fclose(fp);
}
@@ -214,11 +219,11 @@ void do_shutdown(shutop_t op)
utmp_set_halt();
/*
* Tell all remaining non-monitored processes to exit, give them
* some time to exit gracefully, 2 sec is customary.
* Tell remaining non-monitored processes to exit, give them
* time to exit gracefully, 2 sec was customary, we go for 1.
*/
do_kill(SIGTERM);
do_sleep(2);
do_sleep(1);
do_kill(SIGKILL);
/* Exit plugins and API gracefully */
@@ -265,7 +270,7 @@ void do_shutdown(shutop_t op)
/* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) {
if (wdog) {
if (wdog && wdog->pid > 1) {
int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */
@@ -274,15 +279,15 @@ void do_shutdown(shutop_t op)
do_sleep(1);
}
_e("Rebooting ...");
_d("Rebooting ...");
reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) {
_e("Powering down ...");
_d("Powering down ...");
reboot(RB_POWER_OFF);
}
/* Also fallback if any of the other two fails */
_e("Halting ...");
_d("Halting ...");
reboot(RB_HALT_SYSTEM);
}
@@ -321,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)
{
@@ -331,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);
}
/*
@@ -515,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);
+11 -13
View File
@@ -156,9 +156,11 @@ restart:
/* Disable login in single-user mode as well as shutdown/reboot */
nologin();
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
if (runlevel != 0 && runlevel != 6) {
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
}
/* Reset once flag of runtasks */
service_runtask_clean();
@@ -204,10 +206,6 @@ restart:
break;
}
/* No TTYs run at bootstrap, they have a delayed start. */
if (prevlevel > 0)
tty_runlevel();
sm->state = SM_RUNNING_STATE;
break;
@@ -222,8 +220,7 @@ restart:
_d("Stopping services not allowed after reconf ...");
sm->in_teardown = 1;
cond_reload();
service_step_all(SVC_TYPE_SERVICE);
tty_reload(NULL);
service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_WAIT_STATE;
break;
@@ -240,16 +237,17 @@ restart:
}
sm->in_teardown = 0;
/* Cleanup stale services */
svc_clean_dynamic(service_unregister);
_d("Starting services after reconf ...");
service_step_all(SVC_TYPE_SERVICE);
service_step_all(SVC_TYPE_ANY);
/* Cleanup stale services */
svc_clean_dynamic(service_unregister);
_d("Calling reconf hooks ...");
plugin_run_hooks(HOOK_SVC_RECONF);
service_step_all(SVC_TYPE_SERVICE);
service_step_all(SVC_TYPE_ANY);
_d("Reconfiguration done");
sm->state = SM_RUNNING_STATE;
+6 -2
View File
@@ -27,6 +27,8 @@
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
#include <helpers.h>
/* B0 means; keep kernel default */
speed_t stty_parse_speed(char *baud)
{
@@ -61,10 +63,12 @@ speed_t stty_parse_speed(char *baud)
{ 1152000, B1152000 },
{ 1500000, B1500000 },
{ 2000000, B2000000 },
#ifdef B4000000
{ 2500000, B2500000 },
{ 3000000, B3000000 },
{ 3500000, B3500000 },
{ 4000000, B4000000 },
#endif
};
if (!baud || !baud[0])
@@ -121,10 +125,10 @@ void stty(int fd, speed_t speed)
tcsetattr(fd, TCSAFLUSH, &term);
/* Show cursor again, if it was hidden previously */
write(fd, "\033[?25h", 6);
dprint(fd, "\033[?25h", 6);
/* Enable line wrap, if disabled previously */
write(fd, "\033[7h", 4);
dprint(fd, "\033[7h", 4);
}
/**
+213
View File
@@ -0,0 +1,213 @@
/* poor man's sulogin, may be statically linked
*
* 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 <crypt.h>
#include <err.h>
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* getpwnam() cannot be used when statically linked */
static int get_passwd(struct passwd *pw)
{
char *ptr, *tmp;
char buf[256];
FILE *fp;
fp = fopen("/etc/passwd", "r");
if (!fp)
return 1;
while ((tmp = fgets(buf, sizeof(buf), fp))) {
if (!strstr(buf, ":0:0:"))
continue; /* not uid 0 */
ptr = strsep(&tmp, ":");
if (!ptr)
break;
pw->pw_name = strdup(ptr);
ptr = strsep(&tmp, ":");
if (!ptr)
break;
/* x : password in /etc/shadow, see passwd(5) */
if (ptr[0] != 'x')
pw->pw_passwd = strdup(ptr);
break; /* done, check it */
}
fclose(fp);
if (!pw->pw_name)
return 1; /* no uid 0 here */
if (pw->pw_passwd)
return 0; /* passwd in passwd */
fp = fopen("/etc/shadow", "r");
if (!fp) {
if (pw->pw_name)
free(pw->pw_name);
return 1;
}
while ((tmp = fgets(buf, sizeof(buf), fp))) {
ptr = strsep(&tmp, ":");
if (!ptr)
break;
if (!strcmp(tmp, pw->pw_name))
continue;
ptr = strsep(&tmp, ":");
if (!ptr)
break;
pw->pw_passwd = strdup(ptr);
break;
}
fclose(fp);
if (!pw->pw_passwd) {
if (pw->pw_name)
free(pw->pw_name);
return 1; /* nopass => nologin */
}
return 0; /* passwd in shadow */
}
static void freepw(struct passwd *pw)
{
if (pw->pw_passwd) {
free(pw->pw_passwd);
pw->pw_passwd = NULL;
}
free(pw->pw_name);
}
static int sh(void)
{
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
setenv("PS1", "# ", 1);
return execl(_PATH_BSHELL, "-sh", NULL);
}
int main(void)
{
struct termios old, raw;
struct passwd pw = { 0 };
int rc = 1, ch;
if (get_passwd(&pw))
err(1, "reading credentials for UID 0");
switch (pw.pw_passwd[0]) {
case '*': case '!': /* login disabled, allow */
printf("The %s account is locked, allowing entry.\n", pw.pw_name);
/* fallthrough */
case 0: /* no password at all? */
printf("Press enter for maintenance (Ctrl-D to continue) ");
fflush(stdout);
ch = getchar();
if (ch == 4 || ch == EOF) { /* Ctrl-D */
rc = 0;
goto done;
}
goto nopass;
case '$': /* md5/shaN or similar, original DES not supported */
break;
default:
break;
}
if (tcgetattr(1, &old) < 0)
goto done; /* failed reading tty */
raw = old;
raw.c_lflag &= ~ICANON;
raw.c_lflag &= ~ECHO;
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if (tcsetattr(1, TCSANOW, &raw) < 0) {
tcsetattr(1, TCSANOW, &old);
goto done; /* failed setting raw/pw mode */
}
while (1) {
char pwd[256]; /* reasonable max? */
size_t i = 0;
char *passwd;
printf("Give %s password for maintenance (Ctrl-D to continue): ", pw.pw_name);
fflush(stdout);
do {
ch = getchar();
if (ch == '\n' || ch == '\r')
break;
if (ch == 4 || ch == EOF) { /* Ctrl-D */
rc = 0;
goto exit;
}
pwd[i++] = ch;
} while (i + 1 < sizeof(pwd));
pwd[i] = 0;
puts("");
passwd = crypt(pwd, pw.pw_passwd);
if (passwd && !strcmp(passwd, pw.pw_passwd)) {
tcsetattr(1, TCSANOW, &old); /* restore tty */
nopass:
freepw(&pw);
return sh();
}
}
exit:
tcsetattr(1, TCSANOW, &old); /* restore tty */
puts("");
done:
freepw(&pw);
return rc;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/

Some files were not shown because too many files have changed in this diff Show More