Compare commits

..
244 Commits
Author SHA1 Message Date
Joachim Wiberg ceb2945da9 Update ChangeLog and bump version for v4.3-rc2
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-09 06:55:21 +02:00
Joachim Wiberg 1b8a0951f5 initctl: update usage text with missing initctl cond get <COND>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-08 14:09:27 +02:00
Joachim Wiberg a27b8462d1 Fix #260: drop limit on device name in "Checking filesystem" output
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-08 09:59:51 +02:00
Joachim Wiberg edc2d53923 cgroup: reduce loglevel for missing/disallowed cgroup support
A user with no cgroup support in kernel does not know what they are
missing, and a user running in a container has someone else managing
cgroups.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-08 09:57:18 +02:00
Joachim Wiberg 4aae3403f5 test: new test, verify default runlevel from command line
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-08 09:57:18 +02:00
Joachim Wiberg 73423699fd test: add support for overriding default runlevel
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-08 09:57:18 +02:00
Joachim Wiberg 0cce69892a Fix #261: support for overriding default runlevel from command line
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-08 09:57:18 +02:00
Joachim Wiberg 3ca6256f87 doc: update modules-load plugin description, recommend .conf files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-07 16:29:18 +02:00
Joachim Wiberg eca3098c0e plugins: refactor modules-load to read files in lexicographic order
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-07 16:29:18 +02:00
Joachim Wiberg 7af3461e3c Merge pull request #262 from wkz/hook-scripts
plugins: hook-scripts: new plugin for run-parts scripts on hooks

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 15:04:27 +02:00
Tobias Waldekranz b62c975dc4 plugins: hook-scripts: Add
Add a plugin that will allow the user to trigger the execution of
scripts from plugin hook points. This is particularly useful for early
boot debugging that needs to take place before regular services are
available.

For example, let's say that you want to enable some kernel tracing
before modules are loaded. With hook-scripts, you can just drop in a
shell script in /libexec/finit/hook/mount/all/ that will poke the
right control files in tracefs.
2022-05-06 10:36:37 +02:00
Joachim Wiberg b368bd1420 Document hidden HOOK_BANNER
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 08:22:01 +02:00
Joachim Wiberg 060e97d64e test: minor, drop comment within comment confusing shellcheck
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 08:07:44 +02:00
Joachim Wiberg 9b17ce0b7c Fix restart logic of sysv start/stop scripts and forking services
Init script return pretty quickly after launch, so we cannot use that to
detect if they, or the daemon they launched, have crashed.  So we start
the service_retry() with a 2 sec delay, which should be ample time to
allow the pidfile plugin to detect a forking service's new PID.

In release-cycle regression, no public issue needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:08 +02:00
Joachim Wiberg 50f2cbf600 Export service timeout APIs, for pidfile plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:08 +02:00
Joachim Wiberg 837bca3947 test: new test, verify sysv services are restarted properly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:08 +02:00
Joachim Wiberg 16180675db test: minor coding style fixups
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:08 +02:00
Joachim Wiberg d3c6351ed8 Fix nasty service matcher bug
In some conditions, typically when the same command is used for multiple
services, e.g. the modules-load plugin, the svc_find() function returned
an existing "similar" entry instead of NULL, causing loss of config.

When creating, and searching for, a run/task/service we must follow the
new name:id paradigm to the letter.  Always create based on name:id and
always search for matching name:id.  The name may be derived from the
command, but they cannot be used interchangably.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:08 +02:00
Joachim Wiberg 7e37ac348e Refactor service name: parser slightly
This change makes it possible to see the new service's resulting name
and id before we create it with svc_new().  We extend the debug output
slightly to see what's going on and why we sometimes lose modules from
the modules-load plugin ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:07 +02:00
Joachim Wiberg 30e68658ae Use name:id instead consistently in debug messages
For many usecases, e.g. the modules-load plugin, the same command can be
used to create multple run/task/services makeing it real difficult to
debug such systems when Finit does not show the correct task name.  It
also hides nasty bugs, as will be shown in a later commit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:07 +02:00
Joachim Wiberg a179308819 Simplify, tabstospaces() have already been called
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:07 +02:00
Joachim Wiberg 4cdd5f8ccc Export tabstospaces() from conf.c to helpers.[ch]
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:07 +02:00
Joachim Wiberg 7f19474d8d plugins: skip backup files in modules-load plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:07 +02:00
Joachim Wiberg a5941165a0 plugins: add support for 'set noindex' to modules-load plugin
This adds support for a `set` directive in the modules-load plugin.
Currently only `set noindex` and `set index = NUM` are supported and
either disable indexing (:ID) of modules, or reset the index to NUM,
for the given file.  For each new file read the index is reset to
the value it would have had.  E.g.

01-foo.conf:          => name:id
   esp4.ko            => modprobe.esp4.ko:1
   set noindex
   deflate.ko         => modprobe.deflate.ko
02-bar.conf:
   nls_iso8859-15.ko  => modprobe.nls_iso8859-15.ko:3
   nls_utf8.ko        => modprobe.nls_utf8.ko:4

Fixes issue #257

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-06 07:57:07 +02:00
Joachim Wiberg 181d9556a1 Drop config.h include from helpers.h, exported header file
Drop the config.h include from helpers.h after a report from a colleague
trying to build an external plugin from the latest GIT sources.

Instead, make sure config.h is included, and properly commented, in all
.c files that have configure #ifdefs and other deps.  Also, move more
ot the includes from helpers.h to their respective .c file instead to
reduce the amount of headers an external plugin pulls in.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-05 14:48:30 +02:00
Joachim Wiberg bf72e6b9c8 Make bundled sulogin optional, use ./configure --with-sulogin
The bundled sulogin could be considered insecure, so leave it up to the
administrator, or system integrator, to decide which sulogin(8) is best
suited.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-03 05:58:57 +02:00
Joachim Wiberg 8db521d379 Fix minor memory leak in sulogin() handling
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-03 05:58:24 +02:00
Joachim Wiberg f46ac20fb0 doc: document support for global environment variables
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-02 00:16:04 +02:00
Joachim Wiberg d70d86e9fe test: new test, verify behavior of global environment variables
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-02 00:03:07 +02:00
Joachim Wiberg 38db52aa4e Add support for setting environment variables from finit.conf
Finit can now track and set environment variables from finit.conf.  The
syntax is pretty much what you'd expect:

    foo=bar
    baz="qux"

On reload of .conf files, all tracked environment variables are cleared
so if `foo=bar` is removed from finit.conf, or any finit.d/*.conf file,
it will no longer be used by Finit or any new (!) started run/tasks or
services.

The only variables reset to sane defaults on .conf reload are:

    PATH=_PATH_STDPATH
    SHELL=_PATH_BSHELL
    LOGNAME=root
    USER=root

It is entirely possible to override these as well from the .conf files,
but be careful.  Changing SHELL changes the behavior of system() and a
lot of other commands as well.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-02 00:02:44 +02:00
Joachim Wiberg bf62bee440 Update project homepage
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-01 22:43:39 +02:00
Joachim Wiberg e50fe3b060 plugins: use reentrant API getmntent_r()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-01 22:12:02 +02:00
Joachim Wiberg d6c55282ef Fix #253: use reentrant getmntent_r() API
When iterating over the system fstab file to call fsck, Finit calls the
helper function ismnt(), which opens /proc/mounts to make sure mounted
file systems are not fsck'ed.  Both the main function and ismnt() used
the same non-reentrant getmntent() API which caused ismnt() to set the
fstab pointer for the first out of whack.

This change replaces getmntent() in the two critical functions with the
getmntent_r() API instead.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-01 21:43:13 +02:00
Joachim Wiberg 1015124530 Use sys/sysmacros.h, if available, for minor() and major()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-01 20:50:17 +02:00
Joachim Wiberg 38885aaa67 Issue #253: handle fsck of magic /dev/root device
Some systems have /dev/root declared in their /etc/fastb as /.  This is
as special device node created by the kernel based on the root=, and a
few other variables.  This may be a symlink, and if so, and on a mdev
system, the symlink may point to the wrong device node ... so we must
try to figure out the major:minor from / and then traverse /sys/block
to find our root device.

Note: this commit also does a bit of refactoring for readability and
      also adds a few debug lines that may be dropped before release.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-01 19:31:51 +02:00
Joachim Wiberg a685b698c2 Follow-up to 5593372f, regression in remounting read-write at boot
A subtle regression was introduced in 5593372f, when moving to get
getmntent() family of APIs.  Finding the 'ro' flag (or not) in the
system fstab broke and Finit never tried to remount / read-write.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-01 19:28:50 +02:00
Joachim Wiberg 9bce8f9584 initctl: add cond get foo, supports -v option
Fix issue #255

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-30 10:28:47 +02:00
Joachim Wiberg d04b04d73a Follow-up to 7212543, fix args to svc_ident()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-30 10:20:53 +02:00
Joachim Wiberg 72125430e6 Fix #252: run/task success+failure conditions do not use :ID
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-30 08:50:14 +02:00
Joachim Wiberg 561387ce57 test: new test, verify pre:post: scripts with optional env:file
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-30 08:46:05 +02:00
Joachim Wiberg e2d1930e6c Fix #248: source env:file also in pre:post: scripts
This change basically add this for pre/post scripts:

    sh -ac '. /path/to/env; exec ./pre-post.sh'

Meaning the env file can be a simple foo=bar file, which Finit reads and
call setenv() on all variables, and the user do not need to call export
on all variables -- additionally, any functions defined in an env file
can be used by the pre-post scripts, but are ignored by Finit.  I.e.,
the service only gets the variables set, no functions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-30 08:43:44 +02:00
Joachim Wiberg 6f2a124240 doc: update documentation for modules-load plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-27 07:50:21 +02:00
Joachim Wiberg 10392e9639 plugins: modules-load: skip lines starting with '#'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-27 07:46:03 +02:00
Joachim Wiberg 605c489937 Follow-up to 57c97d8, issue #224: add swapon/swapoff fstab wrappers
The swapon and swapoff commands do not support a custom fstab, they
assume /etc/fstab is the only true source of swap devices and files.

This change adds rudimentary (!) support for figuring out any swap
device or file to use from any finit.fstab.  Please note, options are
not supported at the moment.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 22:57:22 +02:00
Joachim Wiberg 26409bc860 Follow-up to 57c97d8, issue #224: fsck needs FSTAB_FILE env. set
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 21:59:11 +02:00
Joachim Wiberg b01f9f38f2 Improve logging of forking services having started and stopped
Since a forking service, e.g. a sysv init script, will exit very early
we cannot log the same as for regular services "Starting foo[123]", so
instead we log "Started bar[321]" when we get the pidfile update and
the same when stopping a forking service, log "Stopped bar[321]" when
we collect the PID.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 20:58:57 +02:00
Joachim Wiberg 2ddbb805ca Improve logging of sysv start/stop scripts, include full command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 20:58:57 +02:00
Joachim Wiberg ed999d9a80 Fix log regression introduced in 82cc10be
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 20:58:57 +02:00
Joachim Wiberg 82cc10be88 Fix #246: conditions for running/halted services
This change adds the following service conditions, which can be used to
synchronize other stanzas:

  - service/foo/running
  - service/foo/halted
  - service/foo/missing
  - service/foo/crashed
  - service/foo/stopped
  - service/foo/busy
  - service/foo/restart

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 00:07:44 +02:00
Joachim Wiberg 30fb9780d7 initctl: slight improvement to status output for sysv services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 00:07:44 +02:00
Joachim Wiberg 09f46f62b4 Support args to sysv-like scripts, e.g. bridge-stp br0 start
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-26 00:07:44 +02:00
Joachim Wiberg bc4e7a8d8d Revert "Fix starting and stopping of SysV init scripts"
This reverts commit c63279630b which
clearly was not needed to fix the problem.  After this revert the
test(s) still pass, but now we can also start bridge-stp, e.g.

     bridge-stp br0 start

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 23:38:52 +02:00
Joachim Wiberg e2753d75f8 plugins: silence netlink developer debug messages
Default debug level for the netlink plugin is just too noisy.  Let's
drop debug messages (mostly) intended for developers.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 23:35:02 +02:00
Joachim Wiberg addb8c4019 Fix #247: fall back to reboot -f in sulogin mode
When logged in from sulogin we cannot send IPC to Finit, not even
signals, to delegate reboot.  For usability, attempt to tell reboot that
it's in sulogin mode -- provided the bundled sulogin is used -- to let
reboot trigger over to forced reboot.

We cannot do much about initctl, just document this as a limitation.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 21:44:33 +02:00
Joachim Wiberg 0c4a9b62c2 initctl: allow client_connect() to return error
This lets any callers of client_connect() decide what to do wrt. error
recovery and fallback mechanisms.  E.g., reboot may want to check if
it's in sulogin mode, or fall back to sending signals to PID 1.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 21:44:33 +02:00
Joachim Wiberg 57c97d8190 Fix #224: support for cmdline -- finit.fstab=/etc/fstab.secondary
This adds support for bringing up the system with an alternate fstab at
boot.  E.g., when using a primary/secondary setup for boot partitions.

By default /etc/fstab is read, like before, this can now be changed
using configure --with-fstab=/path/to/fstab.primary, which sets the
default that can be overridden using finit.fstab=/etc/fstab.secondary

If mounting, or fsck, fails in any way, Finit calls its own bundled
sulogin, or the system sulogin(8), to let the user handle the issue.
If there is no sulogin available, Finit will try to start up in its
rescue.conf boot mode.

Please note, in either of these rescue modes, use `reboot -f` to get the
system to reboot.  Finit is on pause in the background in rescue mode
and cannot be relied on (since there may not be any writable filesystems
available.).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 21:44:33 +02:00
Joachim Wiberg b0d0ca76cf Minor, coding style
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 21:37:59 +02:00
Joachim Wiberg 5240c0cf19 Follow-up to a91e197: allow umount fail on EBUSY, skip error message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 18:52:16 +02:00
Joachim Wiberg a02f369a89 Minor, rename local variable
The iterator works on any fstab like file, but to keep confusion to a
minimum, rename local variable.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-25 18:51:17 +02:00
Joachim Wiberg fa2a113cbf test: fix path to Finit rcs.d in distcheck
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 15:22:35 +02:00
Joachim Wiberg 4c5ac38e2c test: attempt to use /etc/finit.conf instead, azure 5/N
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 14:42:49 +02:00
Joachim Wiberg f89ebb91a8 test: even more azure debug 4/N
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 14:39:03 +02:00
Joachim Wiberg 477bc064d5 test: more debug azure 3/N
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 14:28:57 +02:00
Joachim Wiberg 96376126b3 test: debug azure 2/N
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 14:21:58 +02:00
Joachim Wiberg 1c150928cb test: debug cond test in azure
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 14:14:35 +02:00
Joachim Wiberg 7a8d4e2c6b test: wait for Finit to reach runlevel 2 before starting tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 13:19:10 +02:00
Joachim Wiberg fb956fd2f5 test: silence Hint:, does not work anymore, need to debug it
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 13:18:41 +02:00
Joachim Wiberg e6e4d814f3 test: extend initial wait for azure env.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 12:55:37 +02:00
Joachim Wiberg 544d44886a test: let Finit start up properly before starting tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-24 11:36:30 +02:00
Joachim Wiberg cec2d5a4db test: add missing sbin/task.sh to skeleton dist.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-22 18:21:19 +02:00
Joachim Wiberg 539d215d0e test: add missing finit.d to skeleton rootfs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-22 17:51:04 +02:00
Joachim Wiberg 10ef0e7de6 doc: document previously secret service option respawn
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-22 07:21:19 +02:00
Joachim Wiberg 05e6b3e752 test: new test, verify task behavior with conditions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-21 20:52:34 +02:00
Joachim Wiberg 3e4be9a122 man: document command line options, complementing bootparam(7)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-20 07:51:58 +02:00
Joachim Wiberg 5593372ffb Refactor, replace deprecated setfsent() & C:o with setmntent()
The setfsent() family of APIs was never standardized and on Linux only
GLIBC implement them.  We've tried to overcome this limitation, and to
support uClibc/uClibc-ng and musl libc, by providing replacements APIs
in helpers.c.

However, and since we want to support alternative /etc/fstab files, the
setmntent() family of APIs is more widespread and supports reading from
any fstab or mtab file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-20 07:20:33 +02:00
Joachim Wiberg 05be281301 Rely on (now working) fstab.h check in configure
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-19 20:04:03 +02:00
Joachim Wiberg 108bbf56dd Update copyright years
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-19 19:35:49 +02:00
Joachim Wiberg a61aa73419 configure: check for fstab.h, no fstaby.h
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-19 19:29:01 +02:00
Joachim Wiberg f39626f132 Minor, fix format string to systemf()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 14:25:32 +02:00
Joachim Wiberg c0b6f3af08 .github: build with latest libite version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 14:22:09 +02:00
Joachim Wiberg 5d8fcfdf13 .github: allow release builds for -rcN
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 12:06:10 +02:00
Joachim Wiberg e4830c5614 Don't try to restart forking daemons
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 11:43:33 +02:00
Joachim Wiberg f33261af6b Fix fnread() string fmt, no format args.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 11:30:43 +02:00
Joachim Wiberg e7516f2b63 Bump version for v4.3-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 11:08:15 +02:00
Joachim Wiberg d18167fe1d Update ChangeLog, ref cmdline.doc and fix libuEv linking
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 11:07:13 +02:00
Joachim Wiberg 7d3f95c0a9 doc: minor, emphasize 'debug' like all other kernel cmdline keywords
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 11:06:31 +02:00
Joachim Wiberg 86c5bbb03d doc: fix markdown syntax
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 11:06:07 +02:00
Joachim Wiberg 7530bb74f6 Update ChangeLog for upcoming v4.3 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 00:56:54 +02:00
Joachim Wiberg 54dd726d16 Follow-up to bd9bb92, missing API change
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 00:01:10 +02:00
Joachim Wiberg bd9bb92ca0 Replace reboot(RB_SW_SUSPEND) w/ internal /sys/power/state API
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-16 23:59:32 +02:00
Joachim Wiberg 01cb088e51 Fix initctl debug toggle regression introduced in ae09272b
Calling `initctl debug` is supposed to toggle Finit debug messages on
the boot console.  This broke in ae09272b when improving support for
running Finit in containers.

Part of this change is a slight refactor of who calles log_init() when
starting up, and when to call ttinit().  We now call ttinit() every time
we toggle debug.

Also, toggling back to normal logging had a bug.  The new default log
level for Finit is LOG_INFO, but toggling back set it to LOG_NOTICE.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 11:55:17 +02:00
Joachim Wiberg 3d91112e0d Rename finit command line option finit.show_status -> finit.status
Usability.

Remains backwards compatible for now, but finit.show_status is as of 4.3
deprecated.  Likely to be removed int 5.0

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 10:55:48 +02:00
Joachim Wiberg 16aef80682 Silence Coverity Scan NULL ptr deref warning
Explicitly set arg to non-NULL value.  The call to conf() later will
steer up the value to point to the full path of the user's finit.conf

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 10:02:11 +02:00
Joachim Wiberg fd34914936 Fix unchecked return value, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 09:57:04 +02:00
Joachim Wiberg 21a890a04e Fix NULL ptr deref, introduced in 8e2d0060 for issue #189
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 09:53:58 +02:00
Joachim Wiberg 7f5f7e8ef6 Fix resource leak in shutdown, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 09:48:42 +02:00
Joachim Wiberg ff937576ea Fix possible NULL deref in run() error handling
When we added support for using popen() to run(), to log the output, we
forgot to update the error handling path.  Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 09:45:41 +02:00
Joachim Wiberg 7d831170ae plugins: close/exit iwatch *after* event loop has been detached
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 08:33:24 +02:00
Joachim Wiberg 6a21ac55ad Enable more printf format attributes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 08:32:18 +02:00
Joachim Wiberg a0820c7f20 Fix printf format specifier and formatting issues in use of log fns
Almost all related to and (less likely) possible causes of issue #236.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 00:26:14 +02:00
Joachim Wiberg 6d58c9ed0b Enable printf format attributes to logit() and flog()
We don't want repeats of issue #236, so let's start by tracking down any
bugs hidden in logging functions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 00:17:15 +02:00
Joachim Wiberg bc2ffbc767 Fix printf style function missing argument to %s
This is very likely the root cause of issue #236, let's see.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-14 00:10:30 +02:00
Joachim Wiberg 4c1fc2410e Show progress output also for unmount
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 22:41:12 +02:00
Joachim Wiberg 70e1aa889a Ignore errors from the first two remount,ro they are just decoys
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 22:28:10 +02:00
Joachim Wiberg c8c5e4e88a Revert "Simplify shutdown, no need for workarounds to remount / read-only"
This reverts commit 7c42986800 since we
seem to still need this werid thing on some systems.  The key seems
to use 'any' device at remount[1].  Anyway, the key issue before was
the noisy output, but like Debian we'll just redirect to /dev/null.

[1]: https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/29187

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 22:22:18 +02:00
Joachim Wiberg 86c1398778 Run swapoff interactively, let user know what's happening.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 22:21:52 +02:00
Joachim Wiberg b37fa2b417 Silence process killer at shutdown, too noisy on regular systems
On regular desktop systems, like Debian, the dbus-daemon forks off a
dbus-launch process to start up things like your desktop for you.  This
process is not known to Finit and lingers in the background since it's
been reparented to init, and to top things off it seems to ignore any
SIGTERMs send to it.

Obviously this makes a shutdown on such systems very noisy, so this
patch changes the print() to a _d() so anyone debugging a system can see
it with `-- finit.debug` on the kernel command line, or `initctl debug`.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 22:16:57 +02:00
Joachim Wiberg a91e197553 Avoid looping forever trying to unmount busy tmpfs
This split unmount is a bit crude, unfortunately.  A user may have set
up a bind or overlayfs mount on top of a tmpfs -- so unmounting tmpfs
first will then always result in EBUSY.

Avoid logging busy errors and fake OK.  We'll catch it later in the
second stage unmount.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 21:57:14 +02:00
Joachim Wiberg 5adb92ca1b Catch and print any errors when unmounting filesystems at reboot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 21:45:37 +02:00
Joachim Wiberg cfc99c4e8b Follow-up to ef9e9ec, run ifdown also in background, with --force
Turns out that on Debian `ifdown -a` can block for quite a while at
shutdown/reboot.  So we'd like to use the --force option.  However,
the BusyBox ifdown tool doesn't support --force, only -f, which in
turn the regular ifdown tool doesn't support.

Regardless, we can allow network shutdown to run in the background
like bring-up, at reboot we want to reboot quickly and don't care
so much, and on runlevel change to single-user mode we can allow
for some lagging behind in the background.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 21:45:37 +02:00
Joachim Wiberg 3e51ed1c55 Run swapon interactively, let user know what's happening.
Let's try this, should be OK for all types of systems.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 21:45:37 +02:00
Joachim Wiberg b5df0a6274 Fix swapoff at shutdown, does not support -e flag
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 21:33:54 +02:00
Joachim Wiberg cbe8d56042 Mention lack of mainline distro testing on non-x86 archs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 08:35:13 +02:00
Joachim Wiberg a3ec792b44 doc: minor updates to build script, again
- Mention why configure script is missing from GIT
  - Adjust command line for enabling Finit debug
  - Plus some minor formatting issues

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-12 08:33:50 +02:00
Joachim Wiberg c24855d4d5 keventd: add support for toggling debug messages w/ SIGUSR1
Useful to debug system events after the system has started up and you
don't want to rebuild Finit to add the `-d` command line flag.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-11 22:38:35 +02:00
Joachim Wiberg 014e457fda doc: note which plugins are enabled by default
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-11 21:35:16 +02:00
Joachim Wiberg 21d89c7e24 doc: mention kernel command line option loglevel=7
This is needed since 5ff27c4f when support for debugging Finit and the
kernel independently was added.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-11 21:32:28 +02:00
Joachim Wiberg 639fb76f5a doc: update build ref, add "or later" to required libraries
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-11 21:14:35 +02:00
Joachim Wiberg 30c189b116 initctl: add helpful hint when showing/editing built-in services
Built-in services, like keventd, do not have a .conf file to edit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-11 20:51:46 +02:00
Joachim Wiberg e2a8803f7a test: minor, extend log message a bit
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-10 22:46:45 +02:00
Joachim Wiberg 209e140ddd test: let Finit's bootmisc plugin create the /var/run symlink
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-10 22:46:15 +02:00
Joachim Wiberg 2e3fa72924 Revert "plugins: bootmisc: use relative symlink for /var/run"
This reverts commit f0e328edc9.
2022-04-10 22:38:27 +02:00
Joachim Wiberg 1502838a0d Fix regression in _pe() log macro, introduced in 685f332
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-10 12:39:20 +02:00
Joachim Wiberg 377c8bce8c test: fix dist and tenv-root/ regression from refactor
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-09 17:52:48 +02:00
Joachim Wiberg a39d958d25 Follow-up to 7dc7f9a, protect against stalling on non-existing pids
A forking service, e.g. a SysV init script, that returns is no longer
running, even though the monitored service may still be starting.  We
must mark the svc->pid as terminated until we know more -- otherwise
we may stall on a shutdown at that exact point.

Also, when shutting down, and stopping all services, ensure we do not
start the carousel for non-existing PIDs.  This might also cause our
stalling at shutdown.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-08 07:27:42 +02:00
Joachim Wiberg 7f8a96a73e test: refactor test framework to use a rootfs skeleton
Instead of having to create files, and copying them in place for each
test, we move all static test files to a skeleton rootfs.  This makes
it a lot easier to get an overview of how things and how they work.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-08 07:22:33 +02:00
Joachim Wiberg 5c2577214d test: ask BusyBox its tools and recommended paths
Note: we need to add /usr/bin and /usr/sbin to the standard PATH
      for tests and the test environment itself.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-06 22:42:49 +02:00
Joachim Wiberg f03206a694 .github: due to regression in ubuntu-latest, use older ubuntu runner
In https://github.com/actions/virtual-environments/commit/12fa229 the
ubuntu-latest runner was updated to Ubuntu 20.04.4, with Linux kernel
version: 5.13.0-1014-azure.  This broke the Finit tests completely
and the only possible solution, for now, seems to be reverting back
to the ubuntu-18.04 runner instead.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-06 21:16:46 +02:00
Joachim Wiberg f0e328edc9 plugins: bootmisc: use relative symlink for /var/run
In an upside world, much like the Finit test cases, the root may be
relocated.  This make /var/run relative to /var, instead of /.  Which
hopefully is safer and covers more use-cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-06 21:13:43 +02:00
Joachim Wiberg 13342f2bd9 .github: debug failing tests again, see what setup-root does
All tests except the first two now suddenly started failing.  Might be
the new caching support for the busybox binary, but that works on other
clean clones -- working theory now is that there's something subtle
with the setup-root.sh test -- which PASSes ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 19:43:43 +02:00
Joachim Wiberg c926160853 Issue #223: add type:forking service option, with updated docs
- Add `type:forking` service option to trigger guessing pidfile to
   watch for, instead of `pid:!foo` option, which is not intuitive.
   This option may likely also survive into the new file format :)
 - Update docs and add examples
 - Update start-stop-serv.sh test case with this new variant

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 19:13:28 +02:00
Joachim Wiberg 462d5b22b4 Fix parse bug, always set service's respawn flag
Before this fix, a service declared with respawn could not be changed at
runtime to remove the respawn flag.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 18:06:35 +02:00
Joachim Wiberg b1bfab08f3 Coding style, reverse Christmas tree ordering
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 08:26:13 +02:00
Joachim Wiberg aeb48ef04b test: add support for caching busybox-x86_64 binary in ~/.cache
While without a working Internet connection today I ran into the issue
of not being able to run the tests.  This adds a basic caching mechanism
to setup-root.sh which saves busybox-x86_64 in ~/.cache, if available.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 08:20:48 +02:00
Joachim Wiberg b8b200ad75 doc: move state machine to inside its section
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 12:13:42 +02:00
Joachim Wiberg c148d8d783 doc: document supported types of forking/non-forking daemon's
Issue #223

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 12:03:21 +02:00
Joachim Wiberg 27ba3b27f5 test: extend start-stop-serv test w/ all supported combinations
serv: add support for foregrounding, running with and without PID file,
      including custom PID filename.

test: verify Finit can start & monitor services that:
      1) Fork and creates a PID file in a known location
      2) Don't fork and don't create a PID file, but Finit does
      3) Don't fork but create a PID file
      4) Don't fork and create custom named PID file

Note: Finit cannot support a service that forks and doesn't create a PID
      file.  This combination is impossible to support without tracking
      all processes created in /proc -- which Finit does not do atm.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 11:56:42 +02:00
Joachim Wiberg b4af86255d test: rename fserv -> serv, more features to be added
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 11:55:11 +02:00
Joachim Wiberg 01a007b388 .github: detect Finit version and figure out build dir on failure
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:38:44 +02:00
Joachim Wiberg 0e28741dcb test: fserv is built in another directory
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:38:20 +02:00
Joachim Wiberg c993feeb44 .github: dump start-stop-fserv.log on make check error
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:21:01 +02:00
Joachim Wiberg ec17f0aa23 test: add new test to dist, fixes CI build
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:14:32 +02:00
Joachim Wiberg 6baf2990e8 test: new test to verify support for forking services
Issue #223

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:08:10 +02:00
Joachim Wiberg 6416e2a1a4 test: minor refactor in build system
Add Makefile.am in tenv and common for EXTRA_DIST.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:07:34 +02:00
Joachim Wiberg 7c42986800 Simplify shutdown, no need for workarounds to remount / read-only
Calling sync is not needed, remount does this for us.

Remount with 'dummydev' causes warnings and is not needed.  It appears
sysvinit used this to try and fix a sparc related bug.  Instead, use the
rootfs keyword to ensure we don't accidentally remount a bind mounted /.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 11:09:29 +02:00
Joachim Wiberg 6f802a7ff1 test: whitespace changes only
Coding style for shell scripts:

  - Tabs for indent (change in Emacs needed)
  - Braces on their own line, like C functions

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 10:45:30 +02:00
Joachim Wiberg 879721839e test: slight formatting change in log() output
Encase (stripped) test name in [] and use decreased intensity ANSI coloring.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 10:26:07 +02:00
Joachim Wiberg 20290a4166 Clear any outstanding restart timer when svc reaches running state
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:32:42 +02:00
Joachim Wiberg ba77e4f217 Ensure we clear the 'starting' flag when cleaning up an svc_t
Prevent crashing/restarting tasks from winding up in the wrong state.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:31:19 +02:00
Joachim Wiberg e7b18feca5 Don't block PID 1 waiting for start/stop scripts, fork and forget
No point waiting for SysV start/stop scripts to finish, PID 1 should not
risk getting blocked forever by broken scripts. Instead we fork them off
and forget about them until they terminate. If they are buggy and don't
finish, it's the problem of the admin.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:27:46 +02:00
Joachim Wiberg 4a90a3e8ef test: drop plugins we don't need in test
E.g., a failing urandom.so does no good for ppl reading test results.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:22:20 +02:00
Joachim Wiberg 6d14ff84d3 test: add missing script helpers to dist archive
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 15:41:24 +02:00
Joachim Wiberg 89cc6017df .github: distcheck runs in another subdir, fix test logs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:51:36 +02:00
Joachim Wiberg f6fafdbd23 .github: show individual test logs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:28:51 +02:00
Joachim Wiberg 3480e42687 .github: 2nd test-suite.log debug attempt
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:23:02 +02:00
Joachim Wiberg 053c1643a7 .github: debug test-suite.log
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:09:22 +02:00
Joachim Wiberg 2018cac050 test: at first run, oldpid doesn't exist
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 13:47:11 +02:00
Joachim Wiberg f9090f535d test: drop cat $tty, seems to not be needed
I'll have to ask Jacques what this was for, because it doesn't seem to
be needed to run and monitor the test.  Also, it lingers at shutdown,
causing some unintended side effects in Finit.

Commenting out for now, as a reminder to myself.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 13:46:54 +02:00
Joachim Wiberg 10577f3e48 Allow matching of svc_t and pid file, prefer PID over file lookup
When a SysV init script starts a daemon, Finit knows nothing of the PID
it should monitor.  The PID is written, by start-stop-daemon or the
daemon itself, to the PID file.  Finit monitors for new PID files and
can match the PID in such files with an svc_t.

For the regular use-case, we prefer first looking up the matching svc_t
based on the PID -- assuming we start and monitor the service.  As a
fallback we resort to mathching the svc_t's declared PID file with the
new file we just discovered.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:53:59 +01:00
Joachim Wiberg 07f7caef0d Allow setting custom pid: file for SysV init scripts
We want to find the PID of the daemon the init script starts, so we need
a way to declare this to Finit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:52:03 +01:00
Joachim Wiberg 093b3ba467 Fix problem with restarting stopped SysV init scripts
SysV init scripts should not go to "done" state but "halted" so we can
do: `initctl stop foo; initctl start foo`, like we do for our regular
monitored services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:50:40 +01:00
Joachim Wiberg c63279630b Fix starting and stopping of SysV init scripts
Due to lack of tests the SysV start/stop script stopped working.
Unclear when.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:45:55 +01:00
Joachim Wiberg 4755e5206b test: new test, verify SysV start/stop daemon support
Install start-stop-daemon in test root.  Add S01-service.sh, which uses
start-stop-daemon to start service.sh.  Modify service.sh to respect
signals, and not exit immediately when sleep exits due to SIGTERM.
Remove PID file in signal callback and make sure to exit OK

The test itself is basically a copy of the start-stop-service.sh test.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-24 22:11:24 +01:00
Joachim Wiberg 2002157032 Simplify shutdown in container, call _exit()
Calling _exit(2) lets libc do most of the cleanup we want when shutting
down while in a container.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-24 21:56:47 +01:00
Joachim Wiberg 094e00d405 test: update test to use kill(1) replacement slay.sh
The slay script checks for common errors and gives some logs and status
of Finit when something goes wrong.  Helps detect issue #226 when the
start-kill-service.sh test runs at 100000 laps.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 07:02:10 +01:00
Joachim Wiberg e01836c506 Clarify debug messages for respawning daemons/ttys
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:36:46 +01:00
Joachim Wiberg 685f332c6e logit(): avoid extra \n when logging to stderr
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:33:21 +01:00
Joachim Wiberg ae09272bcc logit(): detect running in container to use stderr for logging
When running in a container we still want to use any syslog daemon
available for our logging needs.  However, the time between the first
logit() in Finit and any such daemon having started can be long.  In a
normal (non-containerized) setup we log to the kernel ring buffer, but
that's not available in a container scenario.  At least not for
unprivileged containers.  So we need to detect all these cases and be
prepared to fall back to log to the console, either using these LOG_CONS
flag to openlog(), or by simply calling vfprintf() to stderr.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:29:40 +01:00
Joachim Wiberg d16f7e0c88 test: help finit know its under test
This trick can also be used by others who want to run Finit in an
unshare.  Set the container environment variable to 'unshare',
like lxc and docker do for their products.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:27:33 +01:00
Joachim Wiberg 063d645d95 in_container(): cache positive results
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:25:43 +01:00
Joachim Wiberg f87e9d8880 Follow-up to 6fa3aa4, log cmd not args[0] which may be NULL
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-22 22:30:59 +01:00
Joachim Wiberg e8099f1bfa The kill(2) syscall returns ENOENT, not ESRCH on missing PIDs
Turns out the kill(2) syscall returns ENOENT, not ESRCH, in our test
suite.  Don't know why, the man page never mentions ENOENT, only the
ESRCH code.  Let's check for both, either way ithe PID is not there.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-22 07:22:15 +01:00
Joachim Wiberg 7dc7f9a968 Fix #227: set svc state late when stopping services, nay be dead
A service may have unexpectedly died, and we never got the signal, so
when stopping services we must set the new state after we've tried to
stop the service.  Otherwise the svc_set_state() function starts a
background timer for the SIGKILL job, which may block a reboot.

The kill() syscall tells us if the service was there or not, if not we
must clean up and go to HALTED state.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-22 07:18:37 +01:00
Joachim Wiberg 996ab8c6c6 Minor, fix local var shadowing outer var
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-21 22:04:09 +01:00
Joachim Wiberg 16c2198304 .github: dump test-suite.log on failure
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-20 15:45:12 +01:00
Joachim Wiberg ecca01dea0 Fix GitHub action status links
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-20 15:37:29 +01:00
Joachim Wiberg bda3cdd3ad Increase inotify socket receive buffer size
At startup (and reconf) of systems with lots of services there is a risk
of losing inotify events, e.g., PID file creation/delete events.  This
patch increase the receive buffer (doubles it).

On Linux the getsockopt() for SO_RCVBUF returns double the set size, due
to housekeeping in the kernel.  So we don't have to do any adjustments
when setting it.

Issue #226

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-20 15:33:56 +01:00
Joachim Wiberg 4811f76294 test: refactor service kill/respawn test
- Allow service to be respawned immediately (hidden option)
 - Stress test by killing 1000 times

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:50:23 +01:00
Joachim Wiberg 0aba704312 test: install awk, grep, and tail busybox tools as well
We may need them in test scripts.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:49:45 +01:00
Joachim Wiberg 57329832d0 initctl: skip cgroup in status output pid_cgruop() fails
In some (error) cases the PID known to Finit may no longer exist, or may
not have been added to a cgroup (yet).  Handle this case by skipping the
output of cgroup info in such conditions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:48:15 +01:00
Joachim Wiberg 078a1d0c95 initctl: if the system doesn't have logs, skip it in status output
On, e.g., a container based system logs may not be available so check
also that the messages fallback exists instead of causing confusing
error in the status output for the service.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:43:58 +01:00
Joachim Wiberg 0e695aa0c7 Only run swapon/swapoff if they exist
To silence warnings at startup/shutdown, check for existance of swapon
and swapoff before calling them.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 14:01:25 +01:00
Joachim Wiberg f7a1514131 test: update with info on running a single/few tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 11:26:17 +01:00
Joachim Wiberg 11bc87ecc3 Merge pull request #233 from liuming50/fix-a-return-value-of-initctl-status
initctl: fix return status of run tasks

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-05 11:59:57 +01:00
Ming Liu e1c59a2515 Fix some wrong return values of run tasks
When a run task is started with svc->started = 1, it should be
considered started successfully or failed on the other hand.

Signed-off-by: Sergio Morlans <sergio.morlans@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-03-05 11:53:25 +01:00
Joachim Wiberg 2faefd44a2 Simplify shutdown/reboot when running in a container
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 6fa3aa41df run(): add support for logging/redirect
This patch adds support for optional logging of output from all run()
commands.  For run_interactive() we've opted to log instead of just
redirect, meaning output on error is till on console but also in log.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg ef9e9ec786 networking: log output from 'ifup -a', run 'ifdown -a' interactively
We've been discussing, over the years, that we'd like to have an easier
way to debug bringup with Finit.  Network bringup is one such case where
it's hard to debug if/what you've misspelled in /etc/network/interfaces

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg d77e3a6113 networking: when ifup is missing, fall back to bring up loopback
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 0b1258b44c Add very basic in_container() function, from sysklogd project
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 62dad82ba8 Bring networking down also in runlevel 6, reboot
This fixes a seemingly long-running bug; we only brought down networking
in shutdown/halt and for runlevel 1, single-user mode -- not reboot.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 5423b5b0a2 Merge pull request #232 from liuming50/tweak-done-conditions
service.c: create conditions according to runtask exit status

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 16:27:52 +01:00
Ming Liu 6b10174bcb service.c: create conditions according to runtask exit status
In commit f0f358a13:
[ service.c: set/clear condition 'done' for run tasks ]

a runtask done condition would be set/cleared when entering DONE/HALTED
states, but it did not cover all the user cases, for instance, sometimes
an end user may want to know if a runtask has finished sucessfully or
to decide what to do on its failures.

So we now change the conditions to: tsktype/tskname/success and
tsktype/tskname/failure.

And this change not only applies to run/task types, but also applies to
sysv type, in case it fails, a sysv/tskname/failure condition would be
set.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-26 14:05:49 +01:00
Joachim Wiberg ec94f4d321 Fix sig.c build warning
Refactor new kill/shutdown implementation from 3e0063e to fix the
regression in compiler output:

sig.c: In function ‘kill_callback’:
sig.c:222:12: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  222 |  kill(pid, (int)context);
      |            ^

Also, some minor renames and simplificactions to match project style.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:52:42 +01:00
Joachim Wiberg 69800537f2 Minor, rename local variable for consistency
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:18:54 +01:00
Joachim Wiberg 61faedb0b5 Minor refactor
- Reversed christmas tree
  - Avoid assignments inside if/while
  - Use established nomenclature

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:16:19 +01:00
Joachim Wiberg 61014431aa Minor refactor
- Simplify, no need for additional local variable
  - Use established nomenclature

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:15:43 +01:00
Joachim Wiberg 49bd63994c Minor refactor
- Check all pointers
  - Declaratons always at top of func/scope
  - Use established variable nomenclature
  - Skip useless if() stmt

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:15:03 +01:00
Joachim Wiberg 85b29d9407 Minor whitespace and comment cleanup
- Comments preferably at beginning of func/sect
  - Reorder code slightly, add whitespace for readability
  - Drop useless comment

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:15:03 +01:00
Joachim Wiberg b83fdea49b Merge pull request #229 from jorgensigvardsson/issue/255/initctl-signal
Fix #225: add support for initctl signal

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-20 09:50:21 +01:00
Jörgen Sigvardsson beec0ee590 Test case for initctl signal
This commit implements a test for the signal feature of initctl. The
existing service used in tests (`common/service.sh`) has been extended
with a signal trap handler. When SIGUSR1 is received, it will write the
string `'USR1'` to `/tmp/usr1.log`. The test will run `initctl signal
service.sh SIGUSR1` and assert that the contents of the file
`/tmp/usr1.log` really is `USR1`.
2022-02-15 12:43:07 +01:00
Jörgen Sigvardsson 4121113688 Added implementation for the new command INIT_CMD_SIGNAL.
The implementation looks up the named service by using
`svc_parse_jobstr`. The callbacks for `svc_parse_jobstr` has been
augmented to accept a user data parameter. For this use case,
a carrier for the actual signal was needed. The address of the
signal parameter is taken and passed on as a `void *`. The
callback then simply deferences it as an int - the signal number.
2022-02-15 12:29:40 +01:00
Jörgen Sigvardsson 62da379472 initctl signal command parsing: done 2022-02-15 12:29:40 +01:00
Joachim Wiberg 3c9053b055 Merge pull request #231 from jorgensigvardsson/fix/absolute-var-lock-symlink-breaks-tests
Remove test code that may break tests

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-15 10:47:47 +01:00
Jörgen Sigvardsson 71b07ce2bd Remove test code that may break tests
In the test framework for finit, a set of shell scripts are used to
setup a test harness, run tests, and finalize tests. In the setup, a
file system is created for the virtualized/containerized environment in
which all tests are run, located at `$FINIT_SRC/test/tenv-root`. In the
tear down phase of each test, the file `/var/lock` is made readable
using `chmod +r`. It is not clear _why_ the teardown code does this, as
there are no references in the test framework to this path anywhere
else. It is conceivable that the teardown phase attempted to "reset the
state" for next test.

The code that that this commit removes does not always work. When
`/var/lock` is a symlink, and resolves to an absolute path, the test
framework does not function properly. The teardown code is run in the
context of the host computer, and touching files outside of the
virtualized environment is not ok.

The removal of the offending code does not seem to affect tests: all
tests pass without it, so its existence is questionable.

Signed-off-by: Jörgen Sigvardsson <jorgen.sigvardsson@gmail.com>
2022-02-15 10:30:04 +01:00
Joachim Wiberg ebea3d273e test: mention need for proper configure first
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg f3a76b8a63 Clarify debug message slightly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg ba93791077 Revert 5cdbd57, causes prolonged delays at shutdown/reboot
Blocking SIGTERM means Finit will wait another two seconds before it
sends SIGKILL, which casues a delayed [WARN] Killing ... before we can
proceed to shutdown/reboot -- not OK.  Don't know what I was smoking
back in 2017, but it must've been good.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg b994858907 initctl: exit immediately on signal (from Finit)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg 74ad34a0c5 Another case for when we need to silently disable cgroups
When running in a container we might not have the necessary privileges
to mount cgroups.  Don't cause error in this case, just log it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg 1b5b37c06b plugin: allow modprobe and tty plugins to be disabled
This patch allows disabling (enabled by default) the modprobe and tty
plugins.  They are not particularly useful in container use-cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg c8e1bd0e9a test: add new kill test to dist
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 13:37:55 +01:00
Joachim Wiberg e9c8d3d740 test: verify finit restarts crashing services & registers new PID
We extend the service.sh to emulate a well behaving service that creates
and removes its own PID file.

The new test emulates a crash by sending SIGKILL to service.sh.  We then
verify that Finit restarts it, and eventually registers the new PID when
the services recreates the PID file.

Issue #226

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 13:30:05 +01:00
Joachim Wiberg f567b866a0 test: drop plugins we don't want in tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 13:29:27 +01:00
Joachim Wiberg 3471d3ee2d test: add service descriptions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:51:28 +01:00
Joachim Wiberg 6e197f5751 test: add /var and /run to rootfs, add empty fstab
- We need a working /var and /run for some features, e.g. PID
   monitoring and status files for urandom.so
 - Add empty fstab to silence mount errors from finit

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:49:34 +01:00
Joachim Wiberg b568c18346 initctl: fix usage text for -V,--version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:32:53 +01:00
Joachim Wiberg 1b29876ba8 initctl: adjust indentation in usage
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:32:44 +01:00
Joachim Wiberg 4c6f3d2d69 Minor markdown syntax fixes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-11 17:30:09 +01:00
Joachim Wiberg 8d3f7eded2 contrib/patches: publish inotify IN_MASK_CREATE patch for Linux 4.9
Issue #196.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-11 17:30:09 +01:00
Joachim Wiberg 0222feeda1 Prepare ChangeLog for 4.3 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-08 09:22:00 +01:00
Joachim Wiberg f10cf1a9a0 Merge pull request #220 from liuming50/fix-a-regression
service.c: fix a regression
2022-02-07 12:18:41 +01:00
Ming Liu 85b2089fd3 initctl.c: fix a incosistent coding sytle
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-07 11:37:56 +01:00
Ming Liu 885808c64c service.c: fix a regression
A regression was introduced by commit f0f358a1:
[ service.c: set/clear condition 'done' for run tasks ]

svc->type is a integer but mistakenly being used as string, which will
cause crash.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-07 11:15:47 +01:00
Joachim Wiberg 78069b42e7 Merge pull request #219 from liuming50/fix-filesystems-not-unmounted-at-shutdown
Fix filesystems not unmounted at shutdown
2022-02-07 10:27:45 +01:00
Ming Liu 3e0063e874 Fix filesystems not unmounted at shutdown
Instead of unconditionally waiting 2 seconds for processes to die,
check continuously for remaining processes, and break the loop when
none remain.

Turn PID 1 to a RT process with highest priority 99 during shutdown,
this ensures it would not be preempted by other RT processes.

Signed-off-by: Robert Andersson <robert.m.andersson@atlascopco.com>
Signed-off-by: Mathias Thore <mathias.thore@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-07 10:06:36 +01:00
Joachim Wiberg 6ec1ab3d4f Merge pull request #218 from liuming50/fix-some-string-comparing-issues
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 07:04:10 +01:00
Joachim Wiberg 33b4540ad9 Merge pull request #207 from liuming50/set-condition-done-for-oneshot-tasks
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:59:28 +01:00
Joachim Wiberg a434b51653 Follow-up to #189: change SERVICE_TYPE, add SERVICE_SCRIPT_TYPE
- SERVICE_TYPE for svc->type
 - SERVICE_SCRIPT_TYPE for pre/post

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:53:34 +01:00
Joachim Wiberg 222d8d3575 New helper function, resolves svc_t type to string
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:51:20 +01:00
Joachim Wiberg 8e2d006048 Fix #189: add type/env/exist envs to pre/post scripts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:29:05 +01:00
Joachim Wiberg fd9f204b6d Minor refactor, split out pre/post envs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:12:37 +01:00
Ming Liu f0f358a138 service.c: set/clear condition 'done' for run tasks
We like to know when a runtask (run/task/sysv)is done, so as to start
the services that depending on it, set a oneshot condition 'done' for
that case and clear it when runtask services enter HALTED state.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-05 18:18:53 +01:00
Ming Liu cfffcd4513 svc.c: change strncmp to string_compare
strncmp can not handle the case when name is a substring of cmd.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-05 13:54:54 +01:00
Ming Liu e51587c66e initctl.c: change string_match to string_compare
It should not use relaxed comparison in some places, otherwise it mess
up with scenarios like:
```
$ initctl status ntpdpre
$ initctl status ntpd
```

the output might be same for both of the above commands without this
fix.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-05 13:54:15 +01:00
Joachim Wiberg f930953e6a Fix #214: change WARN color in progress to yellow
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-01 08:54:12 +01:00
Joachim Wiberg ebdc10d1c5 Bump version for 4.3 development cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-30 11:03:10 +01:00
Joachim Wiberg d12370568f initctl: add -v,--version command line option
This patch adds the -v,--version option to initctl, which acts as an
alias to the 'version' command.  For now they end up in the same old
function, printing the program version.  However, in time this will be
changed so that the 'version' command asks a running Finit for its
version instead of assuming the daemon is of the same version.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-30 11:02:38 +01:00
Joachim Wiberg 176125deca Strip module args from progress output
A 'module foo args ...' line in finit.conf can be looooooong.  This
patch drops the module args from the progress output for brevity.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-30 10:37:14 +01:00
Joachim Wiberg cb934ca39f Fix issue with "reboot doesn't work on 32-bit targets"
With the introduction of GLIBC v2.34, all 32-bit devices running Linux
get the option of having a 64-bit time_t to survice the UNIX 2038
apocalypse.

Support for 64-bit time_t was added to libuev in v2.4.0.  The build
flags for linking against libuev are defined in the library's .pc
files, which basically gives you -D_TIME_BITS=64 for your CPPFLAGS.

However, it turns out the $(uev_CFLAGS) from pkg-config were only used
when building the main finit binary -- not for the plugins, causing
some really wacky behavior as a result.  All plugins and Finit itself
had some very different opinions on what `struct plugin` looked like,
including offsets and size.  This led to all I/O plugins initializing
their sockets at the wrong offset -- little-endian 32-bit targets set
the HOOK_SHUTDOWN callback hook to address 0x1 -- which the reboot
code then tried to execute, of course leading to a segfault.

This is quite possibly the root cause of issue #216

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-27 18:50:46 +01:00
Joachim Wiberg 4fd95fdcbe Fix #215: disable cgroup support when cpu controller is missing
Previous patches only checked if cgroups v2 support was available in
the kernel.  If mounting failed cgroups support was disabled.

However, much of the core logic to cgroups in Finit revolve around the
CPU controller.  This patch adds checks to ensure that the kernel has
basic controller support as well.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-27 18:50:46 +01:00
Joachim Wiberg cb62349054 plugins/bootmisc: only ignore EEXIST errors in ln()
This patch prevents bogus error messages from Finit for when
directories or symlinks already exist.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-27 18:45:00 +01:00
Joachim Wiberg 1b72119673 Merge pull request #217 from liuming50/fix-a-iwatcher-initializing-issue
cgroup.c: fix a iwatcher initializing issue
2022-01-27 11:02:08 +01:00
Ming Liu add474e825 cgroup.c: fix a iwatcher initializing issue
iwatcher of cgroup needs to be initialized earlier before any
cgroup_add functions being called.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-01-27 10:16:14 +01:00
160 changed files with 3699 additions and 976 deletions
+7 -5
View File
@@ -15,7 +15,7 @@ jobs:
build:
# Verify we can build on latest Ubuntu with both gcc and clang
name: ${{ matrix.compiler }}
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
strategy:
matrix:
compiler: [gcc, clang]
@@ -29,11 +29,11 @@ jobs:
sudo apt-get -y update
sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
tar xf libuev-2.3.2.tar.xz
tar xf libite-2.3.1.tar.gz
tar xf libite-2.5.2.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v2
- name: Static Finit
@@ -60,7 +60,9 @@ jobs:
/tmp/sbin/initctl -h
- name: Run Unit Tests
run: |
make -j1 distcheck
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/setup-root.log; cat $dir/test/test-suite.log; false)
- name: Upload Test Results
uses: actions/upload-artifact@v2
with:
+1 -1
View File
@@ -3,7 +3,7 @@ name: Release General
on:
push:
tags:
- '[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+*'
jobs:
release:
+3
View File
@@ -45,3 +45,6 @@ GTAGS
/.autoconf
/.automake
/.built
# VS Code user settings
.vscode
+103 -2
View File
@@ -4,6 +4,106 @@ Change Log
All relevant changes are documented in this file.
[4.3][UNRELEASED]
--------------------
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
you *need* to upgrade!
> **Note:** system verbosity on console at start and shutdown has been
> increased. Now the output of all commands is logged to the
> system logger, for early services `/dev/kmsg` is used.
>
> **Also:** please notice the updated support for enabling and disabling
> kernel and Finit debug messages on the system console. Very
> useful when debugging either of them, e.g., a kernel module.
> For details, see [cmdline.md](doc/cmdline.md).
### Changes
* Support for overriding default runlevel from kernel command line. Any
runlevel `[1-9]` may be selected, except 6 (reboot). Issue #261
* New command line option: `finit.fstab=/etc/fstab.custom`, with full
support for mounting, mount helpers, fsck, and swapon/off, issue #224
* Support for special device `/dev/root`, which may not exist in `/dev`.
Finit now looks up the matching block device for `/` in `/sys/block/`
* Loading `module`s no longer shows arguments in progress output
* Warning messages in progress output now in yellow, not red, issue #214
* `initctl`, new command line option `-V,--version` for ease of use
* New condition `done` for run task, issue #207 by Ming Liu, Atlas Copco
* Refactor parts of shutdown and reboot sequence for PREEMPT-RT kernels,
by Robert Andersson, Mathias Thore, and Ming Liu, Atlas Copco
* Conditions for run/task/sysv status, e.g. `run/foo/success` and
`task/bar/failure`. Issue #232, by Ming Liu, Atlas Copco
* Conditions for services, can be used to synchronize other stanzas:
- `service/foo/running`
- `service/foo/halted`
- `service/foo/missing`
- `service/foo/crashed`
- `service/foo/stopped`
- `service/foo/busy`
- `service/foo/restart`
* `initctl signal` support, by Jörgen Sigvardsson, issue #225
* `initctl cond get` support to match `cond [set | clear]`, issue #255
* `[WARN]` messages on console now printed in yellow, issue #214
* Network services now also stopped when going to runlevel 6 (reboot),
not just runlevel 0 (shutdown) or 1 (single-user)
* When `ifup` is missing on the system, bring at least `lo` up at boot
* Log output from `ifup -a` (and `ifdown -a`), to syslog
* Avoid blocking PID 1 when starting SysV init scripts
* Allow custom `pid:` for SysV init scripts
* Document supported types of forking/non-forking services
* Auto-detect running in some common forms of containers
* Simplify shutdown/reboot when running in a container
* Log to `stderr` when running in a container w/o syslog daemon
* Add support for `type:forking` to services, already supported but
with a very difficult `pid:` syntax, issue #223. Docs updated
* Support for setting global environment variables in `finit.conf`,
please note: this also affects Finit itself, be careful!
* Extended environment variables for pre/post scripts, issue #189
* Document secret service option `respawn`, which bypasses the crash
semantics, allowing endless restarts
* Document secret `HOOK_BANNER`, the first hook point before the banner
* New plugin: `hook-scripts`, allows [run-parts(8)][] style scripts to
run on any hook point. Contributed by Tobias Waldekranz
* `initctl` (`reboot`) falls back to `-f` when it detects it is in
`sulogin` recovery mode, issue #247
* The bundled `sulogin` is no longer enabled by default, in favor of
distribution versions. Enable with `./configure --with-sulogin`
* Support args to sysv-like scripts, e.g. `bridge-stp br0 start`
* The `modules-load` plugin now skips all lines starting with `#` and
`;`. Furthermore, files in `/etc/modules-load.d/*.conf` are now read
in lexicographic order and UNIX backup files (`foo.conf~`) are skipped
* The `name:id` tuple is now more consistently used in all log and debug
messages instead of the basename of the command
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
### Fixes
* Fix nasty 32/64-bit alignment issue between finit and its plugins,
applicable to 32-bit targets with GLIBC 2.34 and later. External
plugins must make sure to use, at least: `-D_TIME_BITS=64`
* Fix #215: disable cgroup support at runtime if kernel lacks support
or does not have the required controllers (cpu)
* Fix #217: iwatcher initialization issue, by Ming Liu, Atlas Copco
* Fix #218: initctl matches too many services, by Ming Liu, Atlas Copco
* Fix #219: not all filesystems unmounted at shutdown, by Ming Liu,
Mathias Thore, and Robert Andersson, Atlas Copco
* Fix #226: initctl shows wrong PID for crashing services
* Fix #227: reboot stalls if process stopped with `[WARN]`
* Fix #233: initctl shows wrong status for run/task, by Sergio Morlans
and Ming Liu, Atlas Copco
* Fix #248: source `env:file` also in `pre:` and `post` scripts
* Fix #260: drop limit on device name in `Checking filesystem`...` output
* Fix start/stop and monitoring (restart) of SysV init scripts and
forking services, see the updated documentation for details
* Fix call to `swapoff` at shutdown, does not support `-e` flag
* Fix suspend to RAM issue. Previously `reboot(RB_SW_SUSPEND)` was
used, now the modern `/sys/power/state` API is used instead.
* Fix nasty run/task/service matcher bug, triggered by stanzas using the
same basename of a command but different `:ID`. Caused Finit to match
with already registered but different run/task/service
[4.2][] - 2022-01-16
--------------------
@@ -756,7 +856,7 @@ The inetd release.
### Changes
* Add support for built-in inetd super server -- launch services on
demand. Supports filtering per interface and custom Inet ports.
* Upgrade to [libuEv] v1.1.0 to better handle error conditions.
* Upgrade to [libuEv][] v1.1.0 to better handle error conditions.
* Allow mixed case config directives in `finit.conf`
* Add support for RFC 868 (rdate) time plugin, start as inetd service.
* Load plugins before parsing `finit.conf`, this makes it possible to
@@ -787,7 +887,7 @@ The inetd release.
[1.11][] - 2015-01-24 [YANKED]
------------------------------
The [libuEv] release.
The [libuEv][] release.
**Note:** This release has been *yanked* from distribution due to a
regression in launching background processes and TTY's. Fixed in
@@ -1039,6 +1139,7 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
Copyright (c) 2008-2022 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
+2
View File
@@ -29,3 +29,5 @@ release: distcheck
printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done
DISTCHECK_CONFIGURE_FLAGS = --with-fstab=/etc/fstab
+9 -3
View File
@@ -1,4 +1,4 @@
[![License Badge][]][License] [![GitHub Status]][GitHub] [![Coverity Status]][Coverity Scan]
[![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
<img align="right" src="img/finit3.png" alt="Finit: Fast Init">
* [Introduction](#introduction)
@@ -54,6 +54,11 @@ on server and desktop systems as well. For working examples, see the
* [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/)
> **Note:** support for various Linux distributions does not mean Finit
> installs easily on all architectures. The bundled install scripts are
> examples for standard installations, tested on x86_64 systems. Custom
> setups, targeted at embedded systems, can be found in [myLinux][].
Example
-------
@@ -598,7 +603,8 @@ and proposed extensions.
[Joachim Wiberg]: https://troglobit.com
[License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[GitHub]: https://travis-ci.org/troglobit/finit
[GitHub Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
[GitHub]: https://github.com/troglobit/myLinux/
[GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
[Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+37 -6
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_INIT(Finit, 4.3-rc2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/projects/finit/)
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@@ -14,8 +14,8 @@ AC_CONFIG_FILES([Makefile
man/Makefile
plugins/Makefile
src/Makefile
test/test.env
test/Makefile])
test/test.env test/Makefile
test/tenv/Makefile test/common/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4])
@@ -33,8 +33,8 @@ AC_PROG_MKDIR_P
# Configuration.
AC_HEADER_STDC
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
AC_CHECK_FUNCS([strstr getopt getfsenty])
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
# Check for uint[8,16,32]_t
AC_TYPE_UINT8_T
@@ -86,14 +86,21 @@ enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
AC_PLUGIN([modprobe], [yes], [Coldplug modules using modalias magic])
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
AC_PLUGIN([hook-scripts], [no], [Trigger script execution from hook points])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
AC_PLUGIN([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial])
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
# Check for extra arguments or packages
AC_ARG_WITH(fstab,
AS_HELP_STRING([--with-fstab=FILE], [System default fstab, default $sysconfdir/fstab]),
[fstab=$withval], [fstab=yes])
AC_ARG_WITH(heading,
AS_HELP_STRING([--with-heading=GREET], [Heading for boot progress. Default: PRETTY_NAME from /etc/os-release, fallback to "Finit vX.YY"]),
[heading=$withval], [heading=yes])
@@ -123,7 +130,7 @@ AC_ARG_WITH(hostname,
[hostname=$withval], [hostname=yes])
AC_ARG_WITH(runlevel,
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap, default: 2]),
AS_HELP_STRING([--with-runlevel=N], [Runlevel to switch to after bootstrap [1-9], default: 2]),
[runlevel=$withval], [runlevel=yes])
AC_ARG_WITH(random-seed,
@@ -133,10 +140,18 @@ AC_ARG_WITH(random-seed,
AC_ARG_WITH(keventd,
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
AC_ARG_WITH(sulogin,
AS_HELP_STRING([--with-sulogin], [Enable built-in sulogin, default: no.]),, [with_sulogin=no])
AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval], [with_watchdog=no watchdog=])
AC_ARG_WITH(hook-scripts-path,
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
[hook_scripts_path=$withval], [hook_scripts_path=yes])
### Enable features ###########################################################################
# Create config.h from selected features and fallback defaults
@@ -162,6 +177,12 @@ AS_IF([test "x$enable_logrotate" != "xno"], [
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ##############################################################################
AS_IF([test "x$with_fstab" != "xno"], [
AS_IF([test "x$fstab" = "xyes"], [fstab=$sysconfdir/fstab])
AC_EXPAND_DIR(fstab_path, "$fstab")
AC_DEFINE_UNQUOTED(FINIT_FSTAB, "$fstab_path", [Built-in default: /etc/fstab])],[
AC_DEFINE_UNQUOTED(FINIT_FSTAB, NULL, [No built-in default fstab])])
AS_IF([test "x$with_config" != "xno"], [
AS_IF([test "x$conf" = "xyes"], [
conf=$sysconfdir/finit.conf])])
@@ -216,6 +237,8 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
AS_IF([test "x$with_sulogin" != "xno"], [with_sulogin=yes])
AS_IF([test "x$with_watchdog" != "xno"], [
AS_IF([test "x$watchdog" = "xyes"], [
watchdog=/dev/watchdog])
@@ -223,9 +246,15 @@ AS_IF([test "x$with_watchdog" != "xno"], [
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
watchdog=])
AS_IF([test "x$enable_hook_scripts_plugin" = "xyes" -a "x$with_hook_scripts_path" != "xno"], [
AS_IF([test "x$hook_scripts_path" = "xyes"], [hook_scripts_path=$libexecdir/finit/hook])
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
# Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "xno"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
@@ -259,6 +288,7 @@ cat <<EOF
Exec prefix...........: $eprefix
Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir`
Default fstab.........: `eval echo $fstab`
Finit config file.....: $conf_path
Finit config.d path...: $rcsd_path
Finit plugin path.....: $plugin_path
@@ -276,6 +306,7 @@ Optional features:
Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib
Built-in keventd......: $with_keventd
Built-in sulogin......: $with_sulogin
Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate
Skip fsck check.......: $enable_fastboot
@@ -0,0 +1,75 @@
In Finit issue #196 we were reminded [1] of a missing inotify flag in
Linux older kernels, IN_MASK_CREATE. Finit makes use of this internally
for monitoring PID conditions, and more. This is a central mechanism to
synchronize services at boot and reload at runtime.
The following patch for Linux 4.9.0, was provided by Andy Savage for
other people that may want to run Finit on slightly older kernels.
[1]: https://github.com/troglobit/finit/issues/196
Index: fs/notify/inotify/inotify_user.c
===================================================================
--- fs.orig/notify/inotify/inotify_user.c
+++ fs/notify/inotify/inotify_user.c
@@ -522,6 +522,7 @@ static int inotify_update_existing_watch
__u32 old_mask, new_mask;
__u32 mask;
int add = (arg & IN_MASK_ADD);
+ int create = (arg & IN_MASK_CREATE);
int ret;
mask = inotify_arg_to_mask(arg);
@@ -529,6 +530,8 @@ static int inotify_update_existing_watch
fsn_mark = fsnotify_find_inode_mark(group, inode);
if (!fsn_mark)
return -ENOENT;
+ else if (create)
+ return -EEXIST;
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
@@ -726,6 +729,10 @@ SYSCALL_DEFINE3(inotify_add_watch, int,
if (unlikely(!f.file))
return -EBADF;
+ /* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
+ if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE)))
+ return -EINVAL;
+
/* verify that this is indeed an inotify instance */
if (unlikely(f.file->f_op != &inotify_fops)) {
ret = -EINVAL;
@@ -814,7 +821,7 @@ static int __init inotify_user_setup(voi
BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
- BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
+ BUG_ON(hweight32(ALL_INOTIFY_BITS) != 22);
inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC);
Index: include/linux/inotify.h
===================================================================
--- include.orig/linux/inotify.h
+++ include/linux/inotify.h
@@ -17,6 +17,6 @@ extern struct ctl_table inotify_table[];
IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | \
IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | \
IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \
- IN_ISDIR | IN_ONESHOT)
+ IN_MASK_CREATE | IN_ISDIR | IN_ONESHOT)
#endif /* _LINUX_INOTIFY_H */
Index: include/uapi/linux/inotify.h
===================================================================
--- include.orig/uapi/linux/inotify.h
+++ include/uapi/linux/inotify.h
@@ -52,6 +52,7 @@ struct inotify_event {
#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */
#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */
#define IN_EXCL_UNLINK 0x04000000 /* exclude events on unlinked objects */
+#define IN_MASK_CREATE 0x10000000 /* only create watches */
#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */
#define IN_ISDIR 0x40000000 /* event occurred against dir */
#define IN_ONESHOT 0x80000000 /* only send event once */
+2
View File
@@ -1,6 +1,8 @@
Bootstrap
=========
0. Get bearings, disable kernel logs, initialize TTY and print banner,
unless `HOOK_BANNER` is defined, then call that instead
1. Prepare emergency shell, if enabled
2. Set up initial signal handlers
3. Set up default $PATH early
+26 -10
View File
@@ -29,8 +29,8 @@ optional plugins to enable. It depends on two external libraries:
The configure script checks for all dependencies, including the correct
version of the above mentioned libraries. Currently required versions:
- libite v2.2.0
- libuEv v2.2.0
- libite v2.2.0, or later
- libuEv v2.2.0, or later
Configure
@@ -54,7 +54,7 @@ Below are a few of the main switches to configure:
will be linked statically.
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from
`/proc/cmdline`, this is not recommended since Finit may be running as the
`/proc/cmdline`, this is *not recommended* since Finit may be running as the
init for container apps that can see the host's `/proc` filesystem
* `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin.
@@ -65,8 +65,19 @@ Below are a few of the main switches to configure:
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
* `--with-sulgin`: Enable bundled `sulogin` program. Default is to use the
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
login if the `root` user is disabled or has no password at all.
For more configure flags, see <kbd>./configure --help</kbd>
> **Note:** the configure script is not available in the GIT sources. It is
> however included in (officially supported) released tarballs. The
> idea is that you should not need GNU autotools to build, only the
> above mentioned dependencies, a POSIX shell, a C compiler and make.
> Any contributing to Finit can generate it from `configure.ac` using
> the `autogen.sh` script.
Example
-------
@@ -74,8 +85,8 @@ Example
First, unpack the archive:
```shell
$ tar xf finit-4.1.tar.gz
$ cd finit-4.1/
$ tar xf finit-4.3.tar.gz
$ cd finit-4.3/
```
Then configure, build and install:
@@ -83,7 +94,7 @@ Then configure, build and install:
```shell
$ ./configure --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \
--with-keventd --with-watchdog
--with-keventd --with-watchdog
$ make
.
.
@@ -91,7 +102,7 @@ $ make
$ DESTDIR=/tmp/finit make install
```
In this example the [finit-4.1.tar.gz][1] archive is unpacked to the
In this example the [finit-4.3.tar.gz][1] archive is unpacked to the
user's home directory, configured, built and installed to a temporary
staging directory. The environment variable `DESTDIR` controls the
destination directory when installing, very useful for building binary
@@ -146,17 +157,22 @@ enabled. The default Finit rescue mode configuration is installed into
By default the a root shell, without login, is started.
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
> force `reboot`, `shutdown`, or `poweroff`.
Debugging
---------
Edit, or append to, the kernel command line: remove `quiet` to enable
kernel messages and add `debug` to enable Finit debug messages.
kernel messages and add `finit.debug` to enable Finit debug messages.
```shell
append="init=/sbin/finit debug"
append="init=/sbin/finit -- finit.debug"
```
Notice the `--` separator.
To debug startup issues, in particular issues with getty/login, add
the following to your Finit .conf file:
@@ -175,6 +191,6 @@ it only for debugging start up issues when Finit crashes.
systems since they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-4.1.tar.gz
[1]: ftp://troglobit.com/finit/finit-4.3.tar.gz
[libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite
+42 -7
View File
@@ -10,13 +10,32 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
> parameters to init. E.g., `init=/sbin/finit -- finit.debug rescue`
* `debug`: Enable kernel debug. Debug messages are printed to the
console until Finit starts up.
console until Finit starts up, unless `loglevel=7` (below) is used.
* `finit.debug[=bool]`: Enable finit debug. This is operated
independently of the kernel debug setting. New as of Finit v4.
independently of the kernel `debug` setting. New as of Finit v4.
* `finit.show_status[=bool]`: Control finit boot progress, including
banner.
* `finit.fstab=</path/to/etc/fstab.alternative>`: Tell Finit to use an
alternate `fstab` to mount the file system from. Remember, this file
must be on the `root=...` file system provided to Finit from the
kernel. By default the built-in fstab is used, which itself defaults
to `/etc/fstab`, but can be changed at build time with:
./configure --with-fstab=/path/to/fstab
It is even possible to disable a built-in default using:
./configure --without-fstab
Making `finit.fstab=/path/to/fstab` a *mandatory* command line option.
Note, if the command line fstab is missing, Finit falls back to the
built-in fstab, and if both are missing, the system treats this as a
bad `fsck` and thus calls `sulogin`. If, in turn, `sulogin` is not
available on the system, Finit calls reboot, which is also what will
happen when a user exits from `sulogin`.
* `finit.status[=bool]`: Control finit boot progress, including banner.
(Used to be `finit.show_status`, which works but is deprecated.)
* `finit.status_style=<classic,modern>`: Set Finit boot progress style,
when enabled.
@@ -26,25 +45,41 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
signals and has no job control. Recommend using, and modifying,
`rescue` mode instead.
* `loglevel=<0-7>`, sets the kernel's log level, which is more granular
than `debug`. Also, when `loglevel=7`, Finit will *not disable*
kernel logs to the console. This is very useful when debugging the
kernel at system bring-up. Since `loglevel=7` is the same as `debug`
this means you have to use `quiet` for a quiet boot, until sysklogd
takes over logging of kernel events.
* `panic=SEC`: By default the kernel does not reboot after a kernel
panic. This setting will cause a kernel reboot after SEC seconds.
* `quiet`: Suppress kernel logging to console, except for warnings and
errors.
errors. Also, see `loglevel` and `quiet` above.
* `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.
for more information.
**Note:** in this mode `initctl` will not work. Correct the problem
and use `reboot -f` to force reboot.
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
debug problems with the regular runlevel. All services and TTYs in
`[1]` will be started, so a `tty [1] @console nologin` configuration
presents you with a root console without login.
* `1-9`, except `6`: override the configured `runlevel`. Like the `S`
and `rescue`, giving a single number on the kernel command line tells
Finit to ignore any `runlevel` in `/etc/finit.conf` as well as the
configure fallback `--with-runlevel=N` setting. Remember, `6` is the
reboot runlevel and is not permitted. Any other values are ignored.
For more on kernel boot parameters, see the man page [bootparam(7)][].
[config.md][]: config.md#rescue-mode
[config.md]: config.md#rescue-mode
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
+58 -5
View File
@@ -73,6 +73,36 @@ document [Finit Services](service.md).
> examples in the `contrib/` directory, which can be used as a base.
### Environment Variables
In Finit v4.3 support for setting environment variables in `finit.conf`,
and any `*.conf`, was added. It is worth noting that these are global
and *shared with all* services -- the only way to have service-local
environment is detailed in the next section.
The syntax for global environment variables is straight forward:
foo=bar
baz="qux"
On reload of .conf files, all tracked environment variables are cleared
so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
file, it will no longer be used by Finit or any new (!) started
run/tasks or services. The environment of already started processes can
not be changed.
The only variables reset to sane defaults on .conf reload are:
PATH=_PATH_STDPATH
SHELL=_PATH_BSHELL
LOGNAME=root
USER=root
It is entirely possible to override these as well from the .conf files,
but be careful. Changing SHELL changes the behavior of `system()` and a
lot of other commands as well.
Service Environment
-------------------
@@ -323,6 +353,10 @@ calls `init-script restart` on `initctl reload`. Similar to how
Forking services started with `sysv` scripts can be monitored by Finit
by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
Notice the leading `!`, it signifies Finit should not try to create the
file, but rather watch that file for the resulting forked-off PID. This
syntax also works for forking daemons that do not have a command line
option to run it in the foreground, more on this below in `service`.
> `<COND>` is described in the [Services](#services) section.
@@ -337,6 +371,8 @@ default 10 times before giving up and marking them as *crashed*.
After which they have to be manually restarted with `initctl restart
NAME`. The limits controling this are configurable, see the options
below.
> **Tip:** to allow endless restarts, see below option `respawn`
For daemons that support it, we recommend appending `--foreground`,
`--no-background`, `-n`, `-F`, or similar command line argument to
@@ -344,9 +380,19 @@ prevent them from forking off a sub-process in the background. This is
the most reliable way to monitor a service.
However, not all daemons support running in the foreground, or they may
start logging to the foreground as well, these are called forking
services and are supported using the same syntax as forking `sysv`
services, using the `pid:!/path/to/pidfile.pid` syntax.
start logging to the foreground as well, these are forking daemons and
are supported using the same syntax as forking `sysv` services, using
the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax
that may be more intuitive, where Finit can also guess the PID file
based on the daemon's command name:
service type:forking ntpd -- NTP daemon
This example lets BusyBox `ntpd` daemonize itself. Finit uses the
basename of the binary to guess the PID file to watch for the PID:
`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
full `pid:!/path/to/file.pid`.
**Example:**
@@ -397,13 +443,17 @@ As mentioned previously, services are automatically restarted, this is
configurable with the following options:
* `restart:NUM` -- number of times Finit tries to restart a crashing
service, default: 10. When this limit is reached the service is
marked *crashed* and must be restarted manually with `initctl`
service, default: 10, max: 255. When this limit is reached the
service is marked *crashed* and must be restarted manually with
`initctl restart NAME`
* `restart_sec:SEC` -- number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
* `norestart` -- dont restart on failures, same as `restart:0`
* `respawn` -- bypasses the `restart` mechanism completely, allows
endless restarts. Useful in many use-cases, but not what `service`
was originally designed for so not the default behavior
* `oncrash:reboot` -- when all retries have failed, and the service
has *crashed*, if this option is set the system is rebooted.
Note, future releases may include other `oncrash:` actions
@@ -625,6 +675,9 @@ activate it.
The rescue mode comes in two flavors; *traditional* and *fallback*.
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to
> force `reboot`, `shutdown`, or `poweroff`.
### Traditional
+34 -5
View File
@@ -34,26 +34,50 @@ For your convenience a set of *optional* plugins are available:
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
_Optional plugin._
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook
points (see [Hooks](#hooks)). Scripts are located in
`/libexec/finit/hook` by default, this can be build-time customized
by using the `--with-hook-scripts-path=PATH` argument to
`configure`. _Optional plugin._
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
available.
available. Enabled by default.
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
Enabled by default.
* *modules-load.so*: Scans /etc/modules-load.d for modules to modprobe.
* *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to
load using `modprobe`. Each file can contain multiple lines with the
name of the module to load. Any line starting with the standard UNIX
comment character, `#`, is skipped.
Modules are loaded when entering runlevel `[2345]` using the `task`
stanza. Each module gets a unique `name:modprobe.foo`, and
optional`:ID`. The `:ID` is a globally incremented index, which can
be disabled per file (anywhere) using the following config line:
set noindex
**Note:** unlike the traditional .conf `module` directive, which load
any listed module immediately, this plugin creates standard `taks`
directives which load the module(s) in the background. As long as
the `modprobe` program is found in the path, these tasks will always
return `[ OK ]` at boot. Check the actual status using `initctl`.
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor
for services that may want to be SIGHUP'ed on new default route or
interfaces going up/down.
interfaces going up/down. Enabled by default.
* *resolvconf.so*: Setup necessary files for `resolvconf` at startup.
_Optional plugin._
* *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's)
to start/stop getty consoles on them on demand. Useful when plugging
in a usb2serial converter to login to your embedded device.
in a usb2serial converter to login to your embedded device. Enabled
by default.
* *urandom.so*: Setup random seed at startup.
* *urandom.so*: Setup random seed at startup. Enabled by default.
* *x11-common.so*: Setup necessary files for X-Window. _Optional plugin._
@@ -69,6 +93,11 @@ Hooks
### Bootstrap Hooks
* `HOOK_BANNER`: The very first point at which a plugin can run.
Intended to be used as a banner replacement. Essentially this runs
just before entering runlevel S. Assume nothing is available, so
be prepared to use absolute paths, etc.
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
mounted — very early
+55 -1
View File
@@ -1,7 +1,59 @@
Finit Services
==============
![The service state machine](svc-machine.png "The service state machine")
Starting & Monitoring
---------------------
Finit can start and monitor the following types of daemons:
* Forks to background, creates a PID file
* Runs in foreground:
- creates a PID file
- does not create a PID file -- Finit can create it for you (optional)
Finit can *not* start and monitor a daemon that:
* Forks to background and does *not* create a PID file
| | Forking | Creates PID File | Finit creates PID File |
|---|---------|------------------|------------------------|
| ✔ | Yes | Yes | No |
| ✔ | No | Yes | No |
| ✔ | No | No | Yes, optionally |
| ✘ | Yes | No | No |
> **Note:** PID files is one mechanism used to assert conditions to
> synchronize the start and stop of other, dependent, services.
### Forks to Background w/ PID File
There are two syntax variants, type 1 and type 2. The former is the
traditional one used also for `sysv` start/stop scripts, and the latter
is inspired by systemd, with a twist -- it lets Finit guess the pifdile
to look for based on the standard path and the basename of the command.
service pid:!/run/serv.pid serv -- Forking service, type 1
service type:forking serv -- Forking service, type 2
In this example the resulting files to watch for are `/run/serv.pid` and
`/var/run/serv.pid`, respectively. On most modern Linux systems this is
the same directory (`/var/run` is a symlink to `../run`).
### Runs in Foreground w/ PID File
service serv -n -p -- Foreground service w/ PID file
### Runs in Foreground w/o PID File
Same as previous, but we tell Finit to create the PID file, because we
need it to synchronize start/stop of a dependent service.
service pid:/run/serv.pid serv -n -- Foreground service w/o PID file
### Runs in Foreground w/ Custom PID File
service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
State Machine
-------------
@@ -11,6 +63,8 @@ For `run`s and `task`s there is an additional end state called `DONE`,
not shown in the diagram for brevity. Services start in the `HALTED`
state.
![The service state machine](svc-machine.png "The service state machine")
The current state depends on the two following conditions:
* `E`: Service enabled. In order for `E` to be satisfied, the service
+85 -2
View File
@@ -11,9 +11,11 @@
.Nd Fast init for Linux systems
.Sh SYNOPSIS
.Nm /sbin/finit
.Op finit.status_style=classic
.Op finit.show_status=[on,off]
.Op --
.Op finit.debug
.Op finit.fstab=/etc/fstab.alt
.Op finit.status=<on,off>
.Op finit.status_style=<classic,modern>
.Op rescue | recover
.Op single | S
.Nm /sbin/telinit
@@ -61,12 +63,93 @@ Plugin support for customization
.It
Proper rescue mode with bundled `sulogin` for protected maintenance shell
.El
.Sh BOOTPARAM
.Nm
support the following command line parameters to alter the behavior
at boot. Notice the
.Cm --
separator needed to tell the kernel which paramters should be given to
PID 1. For more information on this, and available kernel command line
options, see
.Xr bootparam 7 .
.Bl -tag -width pid -offset indent
.It Cm finit.debug
Enable Finit debug messages on system console and log. Sometimes useful
when doing board bringup. Before the system log daemon has started,
Finit use the kernel ring buffer,
.Pa /dev/kmsg .
.It Cm finit.fstab=/path/to/fstab.alt
Tell
.Nm
to use an alternate
.Cm fstab
to mount the file system from. Remember, this file must be on the
.Cm root=...
file system provided to Finit from the kernel. If this file is missing,
.Nm
falls back to its built-in default, which can be changed at build time
using the
.Cm --with-fstab=/path/to/footab
configure option. It is even possible to completely disable the
built-in using
.Cm --without-fstab ,
meaning
.Nm
.Sy must
be started with the
.Cm finit.fstab
option.
.Pp
Should
.Nm
discover that there is no fstab file available, including the fallback,
it triggers the same failure mode as when
.Cm fsck
fails, starting
.Xr sulogin 8 ,
if that is availble. When
.Cm sulogin
is missing, or when the user exits from
.Cm sulogin ,
the system is rebooted.
.Pp
Default fstab, unless changed at build-time:
.Pa /etc/fstab
.It Cm finit.status=<on,off>
Control finit boot progress, including banner. This used to be called
.Cm finit.show_status ,
which still works but is deprecated.
.It Cm finit.status_style=<classic,modern>
Set Finit boot progress style, when enabled.
.It Cm rescue | recover
Start rescue/maintenance mode. If your system comes with the bundled
.Cm sulogin
program (Finit, or from util-linux/Busybox), you will be given a root
login to a maintenance shell. However, if
.Cm sulogin
is missing, the file
.Pa /lib/finit/rescue.conf
is read and the system booted in a limited fallback mode. See
.Xr finit.conf 5
for more information.
.It Cm single | S
Single user mode, runlevel 1, in Finit. Useful to debug problems with
the regular runlevel. All services and TTYs in
.Cm [1]
will be started, e.g., a
.Cm tty [1] @console nologin
configuration presents you with a root console without login.
.El
.Sh BOOTING
.Pp
Before laucnhing services, the system needs to be bootstrapped. This
involves mounting all filesystems (not already mounted by an initramfs)
in
.Pa /etc/fstab ,
or any alternate
.Pa fstab
file provided on the Finit command line using
.Cm finit.fstab ,
ensuring that necessary filesystems like:
.Pa /dev ,
.Pa /proc ,
+28 -1
View File
@@ -181,6 +181,12 @@ After which they have to be restarted manually using
.Cm initctl restart NAME .
The number of restarts, the delay between each restart, and more is
configurable, see the options below.
.Pp
.Bd -filled -offset indent
.Sy Tip:
to allow endless restarts, see below option
.Cm respawn
.Ed
.Pp
For daemons that support it, we recommend appending
.Cm --foreground , --no-background , -n , -F ,
@@ -194,7 +200,21 @@ services and are supported using the same syntax as forking
.Cm sysv
services, using the
.Cm pid:!/path/to/pidfile.pid
command modifier syntax.
command modifier syntax. There is an alternative syntax that may be
more intuitive, where Finit can also guess the PID file based on the
daemon's command name:
.Bd -unfilled -offset indent
service type:forking ntpd -- NTP daemon
.Ed
.Pp
Here we let BusyBox
.Nm ntpd
daemonize itself. Finit uses the basename of the binary to guess the
PID file to watch for the PID:
.Pa /var/run/ntpd.pid .
If Finit guesses wrong, you have to submit the full
.Cm pid:!/path/to/file.pid
option to your service stanza.
.Pp
.Sy Example: in the case of
.Cm ospfd
@@ -287,6 +307,13 @@ and the above (2 and 5) will be used
.It Cm norestart
dont restart on failures, same as
.Cm restart:0
.It Cm respawn
bypasses the
.Cm restart
mechanism completely, allows endless restarts. Useful in many
use-cases, but not what
.Cm service
was originally designed for so not the default behavior.
.It Cm oncrash:reboot
when all retries have failed, and the service
has
+11 -3
View File
@@ -11,7 +11,7 @@
.Nd Control tool for Finit
.Sh SYNOPSIS
.Nm /sbin/initctl
.Op Fl bcfhlpqtv
.Op Fl bcfhlpqtvV
.Op COMMAND
.Sh DESCRIPTION
.Nm
@@ -41,16 +41,20 @@ Silent, only return status of command.
Skip table headings.
.It Fl v, -verbose
Verbose output, where applicable.
.It Fl V, -version
Show program version.
.El
.Sh COMMANDS
.Bl -tag -width Ds
.It Nm Ar debug
Toggle finit (daemon) debug to
Toggle
.Nm finit
(daemon) debug to
.Pa /dev/console
.It Nm Ar help
Show built-in help text
.It Nm Ar version
Show Finit (daemon) version
Show program version
.It Nm Ar ls | list
List all
.Cm *.conf
@@ -136,6 +140,10 @@ Start service by name, with optional ID, e.g.,
Stop/Pause a running service by name
.It Nm Ar reload Cm NAME[:ID]
Reload service by name (SIGHUP or restart)
.It Nm Ar signal Cm NAME:[ID] S
Send signal S to service, by name.
.Cm S
may be a complete signal name such as SIGHUP, or short name such as HUP, or the signal number such as 1 (SIGHUP).
.It Nm Ar restart Cm NAME[:ID]
Restart (stop/start) service by name
.It Nm Ar status Cm NAME[:ID]
+23 -3
View File
@@ -3,11 +3,11 @@ AM_LDFLAGS = -module -avoid-version -shared
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
AM_CPPFLAGS += $(lite_CFLAGS)
AM_CPPFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
if STATIC
noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c pidfile.c procps.c sys.c tty.c usr.c
libplug_la_SOURCES = bootmisc.c pidfile.c procps.c sys.c usr.c
if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c
@@ -25,6 +25,10 @@ if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c
endif
if BUILD_MODPROBE_PLUGIN
libplug_la_SOURCES += modprobe.c
endif
if BUILD_NETLINK_PLUGIN
libplug_la_SOURCES += netlink.c
endif
@@ -37,6 +41,10 @@ if BUILD_RTC_PLUGIN
libplug_la_SOURCES += rtc.c
endif
if BUILD_TTY_PLUGIN
libplug_la_SOURCES += tty.c
endif
if BUILD_URANDOM_PLUGIN
libplug_la_SOURCES += urandom.c
endif
@@ -46,7 +54,7 @@ libplug_la_SOURCES += x11-common.c
endif
else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la pidfile.la procps.la sys.la tty.la usr.la
pkglib_LTLIBRARIES = bootmisc.la pidfile.la procps.la sys.la usr.la
if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la
@@ -56,6 +64,10 @@ if BUILD_DBUS_PLUGIN
pkglib_LTLIBRARIES += dbus.la
endif
if BUILD_HOOK_SCRIPTS_PLUGIN
pkglib_LTLIBRARIES += hook-scripts.la
endif
if BUILD_HOTPLUG_PLUGIN
pkglib_LTLIBRARIES += hotplug.la
endif
@@ -64,6 +76,10 @@ if BUILD_MODULES_LOAD_PLUGIN
pkglib_LTLIBRARIES += modules-load.la
endif
if BUILD_MODPROBE_PLUGIN
pkglib_LTLIBRARIES += modprobe.la
endif
if BUILD_NETLINK_PLUGIN
pkglib_LTLIBRARIES += netlink.la
endif
@@ -76,6 +92,10 @@ if BUILD_RTC_PLUGIN
pkglib_LTLIBRARIES += rtc.la
endif
if BUILD_TTY_PLUGIN
pkglib_LTLIBRARIES += tty.la
endif
if BUILD_URANDOM_PLUGIN
pkglib_LTLIBRARIES += urandom.la
endif
+1 -1
View File
@@ -1,6 +1,6 @@
/* Save and restore ALSA sound settings using alsactl
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+16 -10
View File
@@ -1,6 +1,6 @@
/* Setup necessary system files for, e.g. UTMP (tracking logins)
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
@@ -21,8 +21,12 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <ftw.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <string.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
@@ -39,10 +43,12 @@
static int is_tmpfs(char *path)
{
struct mntent mount;
struct mntent *mnt;
char buf[256];
int tmpfs = 0;
FILE *fp;
char *dir;
struct mntent *mnt;
/* If path is a symlink, check what it resolves to */
dir = realpath(path, NULL);
@@ -55,7 +61,7 @@ static int is_tmpfs(char *path)
return 0; /* Dunno, maybe not */
}
while ((mnt = getmntent(fp))) {
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
if (!strcmp(dir, mnt->mnt_dir))
break;
}
@@ -104,9 +110,9 @@ static void clean(void *arg)
}
}
static void ln(const char *target, const char *linkpath, int ignerr)
static void ln(const char *target, const char *linkpath)
{
if (symlink(target, linkpath) && !ignerr)
if (symlink(target, linkpath) && errno != EEXIST)
_pe("Failed creating %s -> %s symlink", target, linkpath);
}
@@ -131,11 +137,11 @@ static void setup(void *arg)
_d("System with new /run tmpfs ...");
if (!fisdir("/run/lock"))
makedir("/run/lock", 1777);
ln("/run/lock", "/var/lock", 0);
ln("/dev/shm", "/run/shm", 0);
ln("/run/lock", "/var/lock");
ln("/dev/shm", "/run/shm");
/* compat only, should really be set up by OS/dist */
ln("/run", "/var/run", 1);
ln("/run", "/var/run");
} else {
makedir("/var/lock", 1777);
makedir("/var/run", 0755);
@@ -189,7 +195,7 @@ static void setup(void *arg)
makedir("/var/run/sshd", 01755); /* OpenSSH */
if (!fexist("/etc/mtab"))
ln("../proc/self/mounts", "/etc/mtab", 1);
ln("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
@@ -198,7 +204,7 @@ static void setup(void *arg)
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");
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
umask(prev);
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* Setup and start system message bus, D-Bus
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+116
View File
@@ -0,0 +1,116 @@
/* Trigger scripts to run from finit's hooks
*
* Copyright (c) 2022 Tobias Waldekranz <tobias@waldekranz.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 "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "config.h"
#define CHOOSE(x, y) y
static const char *hscript_paths[] = HOOK_TYPES;
#undef CHOOSE
static void hscript_run_parts(hook_point_t hook)
{
char path[CMD_SIZE] = "";
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
strlcat(path, hscript_paths[hook] + 4, sizeof(path));
run_parts(path, NULL);
}
static void hscript_banner(void *arg)
{
hscript_run_parts(HOOK_BANNER);
}
static void hscript_rootfs_up(void *arg)
{
hscript_run_parts(HOOK_ROOTFS_UP);
}
static void hscript_mount_error(void *arg)
{
hscript_run_parts(HOOK_MOUNT_ERROR);
}
static void hscript_mount_post(void *arg)
{
hscript_run_parts(HOOK_MOUNT_POST);
}
static void hscript_basefs_up(void *arg)
{
hscript_run_parts(HOOK_BASEFS_UP);
}
static void hscript_network_up(void *arg)
{
hscript_run_parts(HOOK_NETWORK_UP);
}
static void hscript_svc_up(void *arg)
{
hscript_run_parts(HOOK_SVC_UP);
}
static void hscript_system_up(void *arg)
{
hscript_run_parts(HOOK_SYSTEM_UP);
}
static void hscript_shutdown(void *arg)
{
hscript_run_parts(HOOK_SHUTDOWN);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BANNER] = { .cb = hscript_banner },
.hook[HOOK_ROOTFS_UP] = { .cb = hscript_rootfs_up },
.hook[HOOK_MOUNT_ERROR] = { .cb = hscript_mount_error },
.hook[HOOK_MOUNT_POST] = { .cb = hscript_mount_post },
.hook[HOOK_BASEFS_UP] = { .cb = hscript_basefs_up },
.hook[HOOK_NETWORK_UP] = { .cb = hscript_network_up },
.hook[HOOK_SVC_UP] = { .cb = hscript_svc_up },
.hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up },
.hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown },
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -1,6 +1,6 @@
/* Heuristically find and initialize a suitable hotplug daemon
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* Coldplug modules using modalias magic
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2021-2022 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
+103 -43
View File
@@ -21,6 +21,29 @@
* THE SOFTWARE.
*/
/*
* This plugin scans files in /etc/modules-load.d/, for each line in the
* file it assumes the name of a kernel module, with optional arguments.
* Each module is loaded when entering runlevel 2, 3, 4, or 5, using the
* modprobe tool.
*
* Loading is done by inserting a `task` stanza in Finit, ensuring all
* modules are loaded in parallel, with other modules and programs.
* Each `task` stanza is by default given the name:modprobe.module and
* indexed with `:ID`, starting with 1.
*
* Indexing can be disabled per file in /etc/modules-load.d/, anywwhere
* in a file, by putting the keyword `noindex` on a line of its own. A
* noindex command can appear anywhere in the file, and up to the point
* it is read, tasks will be indexed with an incrementing `:ID`. When
* `noindex` is read indexing is disabled and all subseequent tasks will
* not have any `:ID` at all.
*
* Please note, the :ID is there for your benefit, it ensures that tasks
* in Finit are unique. If you have two tasks with the same name and ID
* (or no ID), the last one read replaces any preceeding one!
*/
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
@@ -40,57 +63,94 @@
#define MODULES_LOAD_PATH "/etc/modules-load.d"
#endif
#define SERVICE_LINE \
"cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
"cgroup.init name:modprobe.%s :%d [2345] /sbin/modprobe %s %s -- Kernel module: %s"
#define SERVICE_LINE_NOINDEX \
"cgroup.init name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
static int load_file(const char *file, int index)
{
char module_path[PATH_MAX];
char line[256];
int num = 0;
FILE *fp;
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, file, sizeof(module_path));
fp = fopen(module_path, "r");
if (!fp)
return 0;
while (fgets(line, sizeof(line), fp)) {
char cmd[CMD_SIZE], *mod, *args;
chomp(line);
mod = strip_line(line);
if (!mod[0])
continue;
if (mod[0] == '#' || mod[0] == ';')
continue;
if (!strcmp(mod, "set noindex")) {
index = 0;
continue;
}
if (!strncmp(mod, "set index", 9)) {
mod += 9;
args = strchr(mod, '=');
if (args) /* optional */
mod = args + 1;
index = atoi(mod);
continue;
}
mod = strtok_r(mod, " ", &args);
if (!mod)
continue;
if (!index)
snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, mod, args, mod);
else
snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
num++;
}
fclose(fp);
return num;
}
/* XXX: check here for .conf only? */
static int module_filter(const struct dirent *d)
{
if (d->d_type == DT_DIR)
return 0;
if (d->d_name[0] == '.')
return 0; /* skip dot files */
if (d->d_name[strlen(d->d_name) - 1] == '~')
return 0; /* skip backup files */
return 1;
}
static void load(void *arg)
{
struct dirent *d;
char line[256];
struct dirent **dentry;
int index = 1;
FILE *fp;
DIR *dir;
int i, num;
_d("Scanning " MODULES_LOAD_PATH " for config files ...");
dir = opendir(MODULES_LOAD_PATH);
if (!dir)
return;
while ((d = readdir(dir))) {
char module_path[PATH_MAX];
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, d->d_name, sizeof(module_path));
fp = fopen(module_path, "r");
if (!fp)
continue;
while (fgets(line, sizeof(line), fp)) {
char cmd[CMD_SIZE], *mod, *args;
mod = strip_line(line);
if (!*mod)
continue;
mod = chomp(mod);
if (!mod || !*mod)
continue;
mod = strtok_r(mod, " ", &args);
if (!mod)
continue;
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
index++;
num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort);
if (num > 0) {
for (i = 0; i < num; i++) {
index += load_file(dentry[i]->d_name, index);
free(dentry[i]);
}
fclose(fp);
free(dentry);
}
closedir(dir);
}
static plugin_t plugin = {
+10 -8
View File
@@ -1,7 +1,8 @@
/* Netlink plugin for IFUP/IFDN and GW events
*
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
* Copyright (C) 2009-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (C) 2009-2022 Joachim Wiberg <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -252,18 +253,18 @@ static int nl_parse(int sd)
return -1;
}
_d("recv %lld bytes", len);
// _d("recv %zd bytes", len);
l = (size_t)len;
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
struct nlmsgerr *nle;
switch (nh->nlmsg_type) {
case NLMSG_DONE:
_d("Done with netlink messages.");
// _d("Done with netlink messages.");
return 0;
case NLMSG_ERROR:
_d("Kernel netlink comm. error.");
// _d("Kernel netlink comm. error.");
nle = NLMSG_DATA(nh);
if (nle) {
errno = -nle->error;
@@ -273,13 +274,13 @@ static int nl_parse(int sd)
case RTM_NEWROUTE:
case RTM_DELROUTE:
_d("Netlink route ...");
// _d("Netlink route ...");
nl_route(nh, len);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
_d("Netlink link ...");
// _d("Netlink link ...");
nl_link(nh, len);
break;
@@ -303,14 +304,14 @@ static int nl_request(int sd, unsigned int seq, int type)
switch (type) {
case RTM_GETROUTE:
_d("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");
// _d("RTM_GETLINK");
nlr->ifi.ifi_family = AF_UNSPEC;
nlr->ifi.ifi_change = 0xFFFFFFFF;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
@@ -449,6 +450,7 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
close(plugin.io.fd);
}
/**
+8 -2
View File
@@ -1,7 +1,7 @@
/* Simple pidfile event monitor for the Finit condition engine
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -98,10 +98,16 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
return;
}
/* Service is not crashing, let's tell state machine! */
service_forked(svc);
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
/* Complement log in service.c for non-forking services */
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
}
}
@@ -305,8 +311,8 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_pidfile);
plugin_unregister(&plugin);
iwatch_exit(&iw_pidfile);
}
/**
+7 -5
View File
@@ -1,6 +1,6 @@
/* Set kernel variables from /etc/sysctl.conf et al
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -34,9 +34,7 @@
static void setup(void *arg)
{
size_t i;
glob_t gl;
char buf[160];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
@@ -51,9 +49,13 @@ static void setup(void *arg)
glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl);
if (gl.gl_pathc > 0) {
size_t i;
for (i = 0; i < gl.gl_pathc; i++) {
snprintf(buf, sizeof(buf), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
run(buf);
char cmd[160];
snprintf(cmd, sizeof(cmd), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
run(cmd, "sysctl");
}
globfree(&gl);
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* Setup necessary files for resolvconf
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* Save and restore RTC using hwclock
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+2 -2
View File
@@ -1,6 +1,6 @@
/* System condition event monitor for the Finit condition engine
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2021-2022 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
@@ -197,8 +197,8 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_sys);
plugin_unregister(&plugin);
iwatch_exit(&iw_sys);
}
/**
+2 -3
View File
@@ -1,7 +1,7 @@
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
*
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2013-2022 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
@@ -116,10 +116,9 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
if (plugin.io.fd)
close(plugin.io.fd);
plugin_unregister(&plugin);
}
/**
+1 -1
View File
@@ -1,6 +1,6 @@
/* Setup and save random seed at boot/shutdown
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+2 -2
View File
@@ -1,6 +1,6 @@
/* User-defined condition event monitor for the Finit condition engine
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2021-2022 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
@@ -123,8 +123,8 @@ PLUGIN_INIT(plugin_init)
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_usr);
plugin_unregister(&plugin);
iwatch_exit(&iw_usr);
}
/**
+3 -3
View File
@@ -1,6 +1,6 @@
/* Console setup (for X)
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
@@ -58,7 +58,7 @@ static void setup(void *arg)
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username));
close(fd);
run("pam_console_apply");
run("pam_console_apply", "pam-console");
}
#endif
@@ -66,7 +66,7 @@ static void setup(void *arg)
makedir("/tmp/.ICE-unix", 01777);
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix", "restorecon");
umask(prev);
}
+4 -1
View File
@@ -8,7 +8,10 @@ AM_LDFLAGS = -export-dynamic
endif
sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = getty logit sulogin
pkglibexec_PROGRAMS = getty logit
if SULOGIN
pkglibexec_PROGRAMS += sulogin
endif
if KEVENTD
pkglibexec_PROGRAMS += keventd
endif
+42 -14
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket.
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2015-2022 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
@@ -21,6 +21,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -36,7 +38,6 @@
#endif
#include <uev/uev.h>
#include "config.h"
#include "finit.h"
#include "cond.h"
#include "conf.h"
@@ -51,12 +52,12 @@
extern svc_t *wdog;
static uev_t api_watcher;
static int call(int (*action)(svc_t *), char *buf, size_t len)
static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
{
return svc_parse_jobstr(buf, len, action, NULL);
return svc_parse_jobstr(buf, len, NULL, action, NULL);
}
static int stop(svc_t *svc)
static int stop(svc_t *svc, void *user_data)
{
if (!svc)
return 1;
@@ -67,7 +68,7 @@ static int stop(svc_t *svc)
return 0;
}
static int start(svc_t *svc)
static int start(svc_t *svc, void *user_data)
{
if (!svc)
return 1;
@@ -83,7 +84,7 @@ static int start(svc_t *svc)
* responsibility of initctl to do. Otherwise we'd block PID 1,
* or introduce some nasty race conditions.
*/
static int restart(svc_t *svc)
static int restart(svc_t *svc, void *user_data)
{
if (!svc)
return 1;
@@ -95,7 +96,7 @@ static int restart(svc_t *svc)
return 0;
}
static int reload(svc_t *svc)
static int reload(svc_t *svc, void *user_data)
{
if (!svc)
return 1;
@@ -114,8 +115,28 @@ 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 int do_signal_svc(svc_t *svc, void *user_data)
{
int signo;
if (!svc || !user_data || !svc_is_running(svc))
return 1;
signo = *(int *)user_data;
return !!kill(svc->pid, signo);
}
static int do_signal(char *buf, size_t len, int sig)
{
/* Sanity check: do we know this signal? */
if (!*sig2str(sig))
return 1;
return svc_parse_jobstr(buf, len, &sig, do_signal_svc, NULL);
}
static char query_buf[368];
static int missing(char *job, char *id)
static int missing(char *job, char *id, void *user_data)
{
char buf[20];
@@ -133,7 +154,7 @@ static int missing(char *job, char *id)
static int do_query(char *buf, size_t len)
{
query_buf[0] = 0;
if (svc_parse_jobstr(buf, len, NULL, missing)) {
if (svc_parse_jobstr(buf, len, NULL, NULL, missing)) {
memcpy(buf, query_buf, len);
return 1;
}
@@ -177,7 +198,7 @@ static svc_t *do_find(char *buf, size_t len)
if (!id)
return svc_named_iterator(&iter, 1, input);
return svc_find_by_nameid(input, id);
return svc_find(input, id);
}
static svc_t *do_find_byc(char *buf, size_t len)
@@ -230,10 +251,10 @@ static int do_reboot(int cmd, char *buf, size_t len)
case INIT_CMD_SUSPEND:
_d("suspend");
sync();
rc = reboot(RB_SW_SUSPEND);
rc = suspend();
if (rc) {
if (errno == EINVAL)
snprintf(buf, len, "Kernel does not support suspend.");
snprintf(buf, len, "Kernel does not support suspend to RAM.");
else
snprintf(buf, len, "Failed: %s", strerror(errno));
}
@@ -407,7 +428,7 @@ static void api_cb(uev_t *w, void *arg, int events)
if (wdog->protect) {
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
stop(wdog);
stop(wdog, NULL);
svc_del(wdog);
}
}
@@ -443,6 +464,13 @@ static void api_cb(uev_t *w, void *arg, int events)
send_svc(sd, do_find_byc(rq.data, sizeof(rq.data)));
goto leave;
case INIT_CMD_SIGNAL:
/* runlevel is reused for signal */
_d("svc signal %d: %s", rq.runlevel, rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
break;
default:
_d("Unsupported cmd: %d", rq.cmd);
break;
+29 -11
View File
@@ -1,6 +1,6 @@
/* Finit control group support functions
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2019-2022 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
@@ -465,9 +465,11 @@ void cgroup_init(uev_ctx_t *ctx)
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
if (errno == ENOENT)
logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling.");
logit(LOG_INFO, "Kernel does not support cgroups v2, disabling.");
else if (errno == EPERM) /* Probably inside an unpriviliged container */
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
else
_pe("Failed mounting cgroup v2, disabling.");
_pe("Failed mounting cgroup v2");
avail = 0;
return;
}
@@ -477,6 +479,7 @@ void cgroup_init(uev_ctx_t *ctx)
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
if (!fp) {
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
abort:
umount(FINIT_CGPATH);
avail = 0;
return;
@@ -494,9 +497,26 @@ void cgroup_init(uev_ctx_t *ctx)
fclose(fp);
/* Check for cpu controller, abort if missing */
if (!strstr(controllers, "+cpu")) {
logit(LOG_NOTICE, "Missing CPU controller, disabling cgroup support.");
goto abort;
}
/* Enable all controllers */
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control"))
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) {
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
goto abort;
}
/* 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");
iwatch_exit(&iw_cgroup);
close(fd);
goto abort;
}
/* Default (protected) groups, PID 1, services, and user/login processes */
cgroup_add("init", "cpu.weight:100", 1);
@@ -505,14 +525,12 @@ void cgroup_init(uev_ctx_t *ctx)
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");
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
_pe("Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
uev_io_stop(&cgw);
iwatch_exit(&iw_cgroup);
close(fd);
goto abort;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* Finit control group support functions
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2019-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2019-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* Cgroup utility functions for initctl
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2019-2022 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
+16 -8
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket.
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2015-2022 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
@@ -39,11 +39,17 @@ int client_connect(void)
};
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (-1 == sd)
err(1, "Failed creating UNIX domain socket");
if (-1 == sd) {
warn("Failed creating UNIX domain socket");
return -1;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
err(1, "Failed connecting to finit");
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
if (errno != ENOENT)
warnx("Failed connecting to finit");
close(sd);
return -1;
}
return sd;
}
@@ -86,11 +92,13 @@ int client_send(struct init_request *rq, ssize_t len)
}
pfd.fd = sd;
pfd.events = POLLIN;
pfd.events = POLLIN | POLLERR | POLLHUP;
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
if (rc)
if (rc) {
if (errno == EINTR) /* shutdown/reboot */
goto exit;
warn("poll(), errno %d", errno);
else
} else
warnx("Timed out waiting for reply from Finit.");
goto exit;
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket.
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2015-2022 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
+32 -2
View File
@@ -21,6 +21,7 @@
* THE SOFTWARE.
*/
#include <dirent.h>
#include <ftw.h>
#include <libgen.h>
#include <stdio.h>
@@ -132,6 +133,25 @@ static int cond_checkpath(const char *path)
return 0;
}
/* Intended for 'service/foo/`, nothing else */
static void cond_delpath(const char *path)
{
char fn[PATH_MAX];
struct dirent *d;
DIR *dir;
dir = opendir(path);
if (!dir)
return;
while ((d = readdir(dir))) {
paste(fn, sizeof(fn), path, d->d_name);
remove(fn);
}
closedir(dir);
unlink(path);
}
int cond_set_path(const char *path, enum cond_state next)
{
enum cond_state prev;
@@ -155,8 +175,18 @@ int cond_set_path(const char *path, enum cond_state next)
break;
case COND_OFF:
if (unlink(path) && errno != ENOENT)
_pe("Failed removing condition '%s'", path);
if (unlink(path)) {
switch (errno) {
case ENOENT:
break;
case EISDIR:
cond_delpath(path);
break;
default:
_pe("Failed removing condition '%s'", path);
break;
}
}
break;
default:
+193 -56
View File
@@ -1,6 +1,6 @@
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
@@ -54,6 +54,12 @@
int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
struct env_entry {
TAILQ_ENTRY(env_entry) link;
char *name;
};
static TAILQ_HEAD(, env_entry) env_list = TAILQ_HEAD_INITIALIZER(env_list);
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
struct rlimit global_rlimit[RLIMIT_NLIMITS];
@@ -67,7 +73,7 @@ struct conf_change {
static struct iwatch iw_conf;
static uev_t etcw;
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static int parse_conf(char *file, int is_rcsd);
static void drop_changes(void);
@@ -86,8 +92,9 @@ fallback:
}
/*
* finit.debug = [on,off]
* finit.show_status = [on,off]
* finit.debug = [on,off]
* finit.fstab = /path/to/etc/fstab.aternative
* finit.status = [on,off] (compat finit.show_status)
* finit.status_style = [old,classic,modern]
*/
static void parse_finit_opts(char *opt)
@@ -103,6 +110,11 @@ static void parse_finit_opts(char *opt)
return;
}
if (string_compare(opt, "fstab")) {
fstab = arg;
return;
}
if (string_compare(opt, "status_style")) {
if (!arg) {
_e("status_style option requires an argument, skipping.");
@@ -116,12 +128,39 @@ static void parse_finit_opts(char *opt)
return;
}
if (string_compare(opt, "show_status")) {
if (string_compare(opt, "status") || string_compare(opt, "show_status")) {
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
return;
}
}
/*
* When runlevel (single integer) is given on the command line,
* it overrides the runlevel in finit.conf and the built-in
* default (from configure). It do however have to pass the
* same sanity checks.
*/
static int parse_runlevel(char *arg)
{
const char *err = NULL;
char *ptr = arg;
long long num;
/* Sanity check the token is just digit(s) */
while (*ptr) {
if (!isdigit(*ptr++))
return 0;
}
num = strtonum(arg, 1, 9, &err);
if (err || num == 6) {
_d("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg);
return 0;
}
return (int)num;
}
static void parse_arg(char *arg)
{
if (!strncmp(arg, "finit.", 6)) {
@@ -129,11 +168,19 @@ static void parse_arg(char *arg)
return;
}
if (string_compare(arg, "rescue") || string_compare(arg, "recover"))
if (string_compare(arg, "rescue") || string_compare(arg, "recover")) {
rescue = 1;
return;
}
if (string_compare(arg, "single") || string_compare(arg, "S"))
if (string_compare(arg, "single") || string_compare(arg, "S")) {
single = 1;
return;
}
/* Put any new command line options before this line. */
cmdlevel = parse_runlevel(arg);
}
#ifdef KERNEL_CMDLINE
@@ -205,28 +252,114 @@ void conf_parse_cmdline(int argc, char *argv[])
parse_kernel_cmdline();
parse_kernel_loglevel();
}
log_init();
/*
* Clear all environment variables read in parse_env(), they may be
* removed now so let the next call to parse_env() restore them.
*/
void conf_reset_env(void)
{
struct env_entry *node, *tmp;
TAILQ_FOREACH_SAFE(node, &env_list, link, tmp) {
TAILQ_REMOVE(&env_list, node, link);
if (node->name) {
unsetenv(node->name);
free(node->name);
}
free(node);
}
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1);
}
/*
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line
* from finit.conf, or other .conf file. Note, PATH is always reset in
* the conf_reset_env() function.
*/
static void parse_env(char *line)
{
struct env_entry *node;
char *key, *val, *end;
/* skip any leading whitespace */
key = line;
while (isspace(*key))
key++;
/* find end of line */
end = key;
while (*end)
end++;
/* strip trailing whitespace */
if (end > key) {
end--;
while (isspace(*end))
*end-- = 0;
}
val = strchr(key, '=');
if (!val)
return;
*val++ = 0;
/* strip leading whitespace from value */
while (isspace(*val))
val++;
/* unquote value, if quoted */
if (val[0] == '"' || val[0] == '\'') {
char q = val[0];
if (*end == q) {
val = &val[1];
*end = 0;
}
}
/* find end of key */
end = key;
while (*end)
end++;
/* strip trailing whitespace */
if (end > key) {
end--;
while (isspace(*end))
*end-- = 0;
}
setenv(key, val, 1);
node = malloc(sizeof(*node));
if (!node) {
_pe("Out of memory cannot track env vars");
return;
}
node->name = strdup(key);
TAILQ_INSERT_HEAD(&env_list, node, link);
}
static int kmod_exists(char *mod)
{
char buf[256];
int found = 0;
FILE *fp;
char buf[256];
size_t len;
for (len = 0; mod[len]; len++) {
if (mod[len] == ' ' || mod[len] == '\t')
break;
}
fp = fopen("/proc/modules", "r");
if (!fp)
return 0;
while (!found && (fgets(buf, sizeof(buf), fp))) {
if (!strncmp(buf, mod, len))
while (!found && fgets(buf, sizeof(buf), fp)) {
char *kmod = strtok(buf, " \t");
if (kmod && !strcmp(kmod, mod))
found = 1;
}
fclose(fp);
@@ -234,22 +367,26 @@ static int kmod_exists(char *mod)
return found;
}
static void kmod_load(char *line)
static void kmod_load(char *mod)
{
char *mod;
char module[64] = { 0 };
char cmd[CMD_SIZE];
if (runlevel != 0)
return;
mod = strip_line(line);
if (kmod_exists(mod))
/* Strip args for progress below and kmod_exists() */
strlcpy(module, mod, sizeof(module));
if (!strtok(module, " \t"))
return;
if (kmod_exists(module))
return;
strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
run_interactive(cmd, "Loading kernel module %s", module);
}
/* Convert optional "[!123456789S]" string into a bitmask */
@@ -480,7 +617,7 @@ static void conf_parse_cgroup(char *line)
cgroup_add(name, config, 0);
}
static void parse_static(char *line, int is_rcsd)
static int parse_static(char *line, int is_rcsd)
{
char cmd[CMD_SIZE];
char *x;
@@ -488,7 +625,7 @@ static void parse_static(char *line, int is_rcsd)
if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
if (hostname) free(hostname);
hostname = strdup(strip_line(x));
return;
return 0;
}
if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) {
@@ -497,25 +634,25 @@ static void parse_static(char *line, int is_rcsd)
strcpy(cmd, "mknod ");
strlcat(cmd, dev, sizeof(cmd));
run_interactive(cmd, "Creating device node %s", dev);
return;
return 0;
}
/* Kernel module to load */
if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) {
kmod_load(x);
return;
kmod_load(strip_line(x));
return 0;
}
if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) {
if (network) free(network);
network = strdup(strip_line(x));
return;
return 0;
}
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
if (runparts) free(runparts);
runparts = strdup(strip_line(x));
return;
return 0;
}
if (MATCH_CMD(line, "include ", x)) {
@@ -524,11 +661,10 @@ static void parse_static(char *line, int is_rcsd)
strlcpy(cmd, file, sizeof(cmd));
if (!fexist(cmd)) {
_e("Cannot find include file %s, absolute path required!", x);
return;
return 1;
}
parse_conf(cmd, is_rcsd);
return;
return parse_conf(cmd, is_rcsd);
}
if (MATCH_CMD(line, "log ", x)) {
@@ -549,12 +685,14 @@ static void parse_static(char *line, int is_rcsd)
logfile_size_max = size;
if (count >= 0)
logfile_count_max = count;
return 0;
}
if (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown);
sdown = strdup(strip_line(x));
return;
return 0;
}
/*
@@ -571,7 +709,7 @@ static void parse_static(char *line, int is_rcsd)
cfglevel = RUNLEVEL;
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
cfglevel = 2; /* Fallback */
return;
return 0;
}
/*
@@ -591,72 +729,65 @@ static void parse_static(char *line, int is_rcsd)
if (disabled)
service_init();
}
return;
return 0;
}
return 1;
}
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
static int parse_dynamic(char *line, struct rlimit rlimit[], char *file)
{
char *x;
/* Monitored daemon, will be respawned on exit */
if (MATCH_CMD(line, "service ", x)) {
service_register(SVC_TYPE_SERVICE, x, rlimit, file);
return;
return 0;
}
/* One-shot task, will not be respawned */
if (MATCH_CMD(line, "task ", x)) {
service_register(SVC_TYPE_TASK, x, rlimit, file);
return;
return 0;
}
/* Like task but waits for completion, useful w/ [S] */
if (MATCH_CMD(line, "run ", x)) {
service_register(SVC_TYPE_RUN, x, rlimit, file);
return;
return 0;
}
/* Similar to task but is treated like a SysV init script */
if (MATCH_CMD(line, "sysv ", x)) {
service_register(SVC_TYPE_SYSV, x, rlimit, file);
return;
return 0;
}
/* Read resource limits */
if (MATCH_CMD(line, "rlimit ", x)) {
conf_parse_rlimit(x, rlimit);
return;
return 0;
}
/* Read control group limits */
if (MATCH_CMD(line, "cgroup ", x)) {
conf_parse_cgroup(x);
return;
return 0;
}
/* Set current cgroup for the following services/run/tasks */
if (MATCH_CMD(line, "cgroup.", x)) {
strlcpy(cgroup_current, x, sizeof(cgroup_current));
return;
return 0;
}
/* Regular or serial TTYs to run getty */
if (MATCH_CMD(line, "tty ", x)) {
service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file);
return;
return 0;
}
}
static void tabstospaces(char *line)
{
if (!line)
return;
for (int i = 0; line[i]; i++) {
if (line[i] == '\t')
line[i] = ' ';
}
return 1;
}
static int parse_conf(char *file, int is_rcsd)
@@ -690,8 +821,13 @@ static int parse_conf(char *file, int is_rcsd)
if (MATCH_CMD(line, "#", x))
continue;
parse_static(line, is_rcsd);
parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file);
if (!parse_static(line, is_rcsd))
continue;
if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file))
continue;
/* Not static or dynamic conf, check if it is a global env. */
parse_env(line);
}
fclose(fp);
@@ -715,6 +851,7 @@ int conf_reload(void)
/* Mark and sweep */
cgroup_mark_all();
svc_mark_dynamic();
conf_reset_env();
/*
* Reset global rlimit to bootstrap values from conf_init().
+2 -1
View File
@@ -1,6 +1,6 @@
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
@@ -42,6 +42,7 @@ int conf_any_change (void);
int conf_changed (char *file);
int conf_monitor (void);
void conf_reset_env (void);
void conf_parse_cmdline (int argc, char *argv[]);
int conf_parse_runlevels (char *runlevels);
void conf_parse_cond (svc_t *svc, char *cond);
+89 -49
View File
@@ -1,7 +1,7 @@
/* Functions for exec'ing processes
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -28,6 +28,7 @@
#include <dirent.h>
#include <err.h>
#include <stdarg.h>
#include <sysexits.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
@@ -70,89 +71,128 @@ int complete(char *cmd, int pid)
return status;
}
int run(char *cmd)
static int do_redirect(void)
{
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
return fclose(fp);
}
return -1;
}
/*
* Run 'cmd' and wait for completion. If 'log' is not NULL any output
* from the command is logged with the given string as prefix, use "" to
* skip. If 'log' is NULL all output is redirected to /dev/null, which
* is what this function originally did.
*/
int run(char *cmd, char *log)
{
char *args[NUM_ARGS + 1] = { 0 };
int status, result, i = 0;
char *args[NUM_ARGS + 1], *arg, *backup;
char *backup = NULL;
pid_t pid;
/* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd);
if (!arg)
return 1; /* Failed allocating a string to be modified. */
if (!log) {
char *arg;
/* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t ");
while (arg && i < NUM_ARGS) {
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
if (*arg == '\'' || *arg == '"') {
char *p, delim[2] = " ";
/* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd);
if (!arg)
return 1;
delim[0] = arg[0];
args[i++] = arg++;
strsep(&arg, delim);
p = arg - 1;
*p = *delim;
*arg++ = 0;
} else {
args[i++] = strsep(&arg, "\t ");
/* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t ");
while (arg && i < NUM_ARGS) {
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
if (*arg == '\'' || *arg == '"') {
char *p, delim[2] = " ";
delim[0] = arg[0];
args[i++] = arg++;
strsep(&arg, delim);
p = arg - 1;
*p = *delim;
*arg++ = 0;
} else {
args[i++] = strsep(&arg, "\t ");
}
}
}
args[i] = NULL;
args[i] = NULL;
if (i == NUM_ARGS && arg) {
_e("Command too long: %s", cmd);
free(backup);
errno = EOVERFLOW;
return 1;
if (i == NUM_ARGS && arg) {
_e("Command too long: %s", cmd);
free(backup);
errno = EOVERFLOW;
return 1;
}
}
pid = fork();
if (0 == pid) {
FILE *fp;
int rc = EX_OSERR;
setsid();
sig_unblock();
if (!log) {
do_redirect();
execvp(args[0], args);
} else {
char *pfx = *log ? ": " : "";
FILE *pp;
/* Always redirect stdio for run() */
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
pp = popen(cmd, "r");
if (pp) {
char buf[256];
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
while (fgets(buf, sizeof(buf), pp)) {
chomp(buf);
logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
}
rc = pclose(pp);
}
}
sig_unblock();
execvp(args[0], args);
_exit(1); /* Only if execv() fails. */
_exit(rc);
} else if (-1 == pid) {
_pe("%s", args[0]);
free(backup);
_pe("%s", !log ? args[0] : cmd);
if (backup)
free(backup);
return -1;
}
status = complete(args[0], pid);
if (-1 == status) {
free(backup);
if (backup)
free(backup);
return 1;
}
result = WEXITSTATUS(status);
if (WIFEXITED(status)) {
_d("Started %s and ended OK: %d", args[0], result);
_d("Started '%s' and exit without signal, status: %d", cmd, result);
} else if (WIFSIGNALED(status)) {
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
_d("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
if (!result)
result = 1; /* Must alert callee that the command did complete successfully.
* This is necessary since not all programs trap signals and
* change their return code accordingly. --Jocke */
}
free(backup);
if (backup)
free(backup);
return result;
}
@@ -186,7 +226,7 @@ int run_interactive(char *cmd, char *fmt, ...)
}
/* Run cmd ... */
status = run(cmd);
status = run(cmd, "");
/* Restore stderr/stdout */
if (fp && !debug) {
@@ -222,7 +262,6 @@ int run_interactive(char *cmd, char *fmt, ...)
int exec_runtask(char *cmd, char *args[])
{
size_t i;
char buf[1024] = "";
char *argv[4] = {
"sh",
@@ -230,6 +269,7 @@ int exec_runtask(char *cmd, char *args[])
buf,
NULL
};
size_t i;
strlcat(buf, cmd, sizeof(buf));
for (i = 1; args[i]; i++) {
+218 -90
View File
@@ -1,7 +1,7 @@
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -26,16 +26,18 @@
#include <ctype.h>
#include <dirent.h>
#ifdef HAVE_FSTAB_H
#include <fstab.h>
#endif
#include <getopt.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <time.h> /* tzet() */
#include <sys/klog.h>
#include <sys/mount.h>
#include <sys/stat.h> /* umask(), mkdir() */
#include <sys/wait.h>
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
@@ -60,12 +62,14 @@
int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int cmdlevel = 0; /* runlevel override from cmdline */
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) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
char *fstab = FINIT_FSTAB;
char *sdown = NULL;
char *network = NULL;
char *hostname = NULL;
@@ -106,70 +110,167 @@ static void banner(void)
#endif
}
static int sulogin(int do_reboot)
{
int rc = EX_OSFILE;
char *cmd[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(cmd); i++) {
char *path = which(cmd[i]);
if (access(path, X_OK)) {
free(path);
continue;
}
rc = systemf("%s", path);
free(path);
break;
}
if (do_reboot) {
do_shutdown(SHUT_REBOOT);
exit(rc);
}
return rc;
}
char *fs_root_dev(char *real, size_t len)
{
struct dirent *d;
struct stat st;
int found = 0;
dev_t dev;
DIR *dir;
if (stat("/", &st))
return NULL;
if (S_ISBLK(st.st_mode))
dev = st.st_rdev;
else
dev = st.st_dev;
dir = opendir("/sys/block");
if (!dir)
return NULL;
while ((d = readdir(dir))) {
char buf[10];
char *ptr;
if (fnread(buf, sizeof(buf), "/sys/block/%s/dev", d->d_name) == -1)
continue;
ptr = strchr(buf, ':');
if (!ptr)
continue;
*ptr++ = 0;
if (atoi(buf) == (int)major(dev) && atoi(ptr) == (int)minor(dev)) {
/* Guess name, assume no renaming */
snprintf(real, len, "/dev/%s", d->d_name);
found = 1;
break;
}
}
closedir(dir);
if (!found)
return NULL;
return real;
}
/*
* Check all filesystems in /etc/fstab with a fs_passno > 0
*/
static int fsck(int pass)
{
struct fstab *fs;
struct mntent mount;
struct mntent *mnt;
char real[192];
char buf[256];
int rc = 0;
FILE *fp;
if (!setfsent()) {
_pe("Failed opening fstab");
return 1;
fp = setmntent(fstab, "r");
if (!fp) {
_pe("Failed opening fstab: %s", fstab);
sulogin(1);
}
while ((fs = getfsent())) {
char cmd[80];
struct stat st;
_d("Opened %s, pass %d", fstab, pass);
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
int fsck_rc = 0;
struct stat st;
char cmd[256];
char *dev;
if (fs->fs_passno != pass)
_d("got: fsname '%s' dir '%s' type '%s' opts '%s' freq '%d' passno '%d'",
mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
mnt->mnt_freq, mnt->mnt_passno);
if (mnt->mnt_passno == 0 || mnt->mnt_passno != pass)
continue;
/* Device to maybe fsck, */
dev = mnt->mnt_fsname;
errno = 0;
if (stat(fs->fs_spec, &st) || !S_ISBLK(st.st_mode)) {
if (!string_match(fs->fs_spec, "UUID=") && !string_match(fs->fs_spec, "LABEL=")) {
_d("Cannot fsck %s, not a block device: %s", fs->fs_spec, strerror(errno));
if (stat(dev, &st) || !S_ISBLK(st.st_mode)) {
int skip = 1;
if (string_match(dev, "UUID=") || string_match(dev, "LABEL="))
skip = 0;
/*
* Kernel short form for root= device, figure out
* actual device since we cannot rely on symlinks
* https://bugs.busybox.net/show_bug.cgi?id=8891
*/
else if (string_compare(dev, "/dev/root")) {
if (fs_root_dev(real, sizeof(real))) {
dev = real;
skip = 0;
}
}
if (skip) {
_d("Cannot fsck %s, not a block device: %s", dev, strerror(errno));
continue;
}
}
if (ismnt("/proc/mounts", fs->fs_file, "rw")) {
_d("Skipping fsck of %s, already mounted rw on %s.", fs->fs_spec, fs->fs_file);
if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) {
_d("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir);
continue;
}
#ifdef FSCK_FIX
snprintf(cmd, sizeof(cmd), "fsck -yf %s", fs->fs_spec);
snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev);
#else
snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
snprintf(cmd, sizeof(cmd), "fsck -a %s", dev);
#endif
fsck_rc = run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
_d("Running pass %d fsck command %s", pass, cmd);
fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev);
/*
* "failure" is defined as exiting with a return code of
* 2 or larger. A return code of 1 indicates that filesystem
* errors were corrected but that the boot may proceed.
*/
if (fsck_rc > 1) {
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
break;
}
do_shutdown(SHUT_REBOOT);
logit(LOG_CONSOLE | LOG_ALERT, "Failed fsck %s, attempting sulogin ...", dev);
sulogin(1);
}
rc += fsck_rc;
}
endfsent();
endmntent(fp);
return rc;
}
@@ -202,38 +303,22 @@ static void fs_mount(const char *src, const char *tgt, const char *fstype,
}
#ifndef SYSROOT
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
static int fs_readonly_root(struct fstab *fs)
{
char *tok, *str;
if (!fs)
return 1;
str = fs->fs_mntops;
while ((tok = strtok(str, ","))) {
if (!strcmp(tok, "ro"))
return 1;
str = NULL;
}
return 0;
}
static void fs_remount_root(int fsckerr)
{
struct fstab *fs;
struct mntent *mnt;
FILE *fp;
if (!setfsent())
fp = setmntent(fstab, "r");
if (!fp)
return;
while ((fs = getfsent())) {
if (!strcmp(fs->fs_file, "/"))
while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, "/"))
break;
}
if (fs_readonly_root(fs))
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
if (!mnt || hasmntopt(mnt, "ro"))
goto out;
if (fsckerr)
@@ -243,7 +328,7 @@ static void fs_remount_root(int fsckerr)
"Remounting / as read-write");
out:
endfsent();
endmntent(fp);
}
#else
static void fs_remount_root(int fsckerr)
@@ -319,21 +404,69 @@ static void fs_finalize(void)
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
}
static void fs_swapon(char *cmd, size_t len)
{
struct mntent *mnt;
FILE *fp;
if (!whichp("swapon"))
return;
fp = setmntent(fstab, "r");
if (!fp)
return;
while ((mnt = getmntent(fp))) {
if (strcmp(mnt->mnt_type, MNTTYPE_SWAP))
continue;
snprintf(cmd, len, "swapon %s", mnt->mnt_fsname);
run_interactive(cmd, "Enabling swap %s", mnt->mnt_fsname);
}
endmntent(fp);
}
static void fs_mount_all(void)
{
char cmd[256] = "mount -na";
if (!fstab || !fexist(fstab)) {
logit(LOG_CONSOLE | LOG_NOTICE, "%s system fstab %s, trying fallback ...",
!fstab ? "Missing" : "Cannot find", fstab ?: "\b");
fstab = FINIT_FSTAB;
}
if (!fstab || !fexist(fstab)) {
logit(LOG_CONSOLE | LOG_EMERG, "%s system fstab %s, attempting sulogin ...",
!fstab ? "Missing" : "Cannot find", fstab ?: "\b");
sulogin(1);
}
/*
* Needed by fsck, both BusyBox and util-linux support this.
* We leave it set in the env. for the benefit of any mount
* helpers and other system tools that the user expects to
* behave even if we've booted with a different fstab.
*/
setenv("FSTAB_FILE", fstab, 1);
if (!rescue)
fs_remount_root(fsck_all());
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
if (run_interactive("mount -na", "Mounting filesystems"))
if (fstab && strcmp(fstab, "/etc/fstab"))
snprintf(cmd, sizeof(cmd), "mount -na -T %s", fstab);
if (run_interactive(cmd, "Mounting filesystems from %s", fstab))
plugin_run_hooks(HOOK_MOUNT_ERROR);
_d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
_d("Enable any swap ...");
fs_swapon(cmd, sizeof(cmd));
_d("Finalize, ensure common file systems are available ...");
fs_finalize();
@@ -437,11 +570,12 @@ static void crank_worker(void *unused)
*/
static void bootstrap_worker(void *work)
{
static int cnt = 120;
static struct wq final = {
.cb = finalize,
.delay = 10
};
static int cnt = 120;
int level = cfglevel;
_d("Step all services ...");
service_step_all(SVC_TYPE_ANY);
@@ -466,11 +600,18 @@ static void bootstrap_worker(void *work)
if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL);
/*
* Start all tasks/services in the configured runlevel
* Start all tasks/services in the configured runlevel, or jump
* into the runlevel selected from the command line.
*/
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(cfglevel);
if (cmdlevel) {
_d("Runlevel %d requested from command line, starting all services ...", cmdlevel);
level = cmdlevel;
} else
_d("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(level);
}
static int version(int rc)
@@ -569,7 +710,7 @@ int main(int argc, char *argv[])
};
uev_ctx_t loop;
/* telinit or stand-alone process monitor */
/* user calling telinit or init */
if (getpid() != 1)
return telinit(argc, argv);
@@ -580,12 +721,12 @@ int main(int argc, char *argv[])
/*
* Parse /proc/cmdline (debug, rescue, console=, etc.)
* Also calls log_init() to set correct log level
*/
conf_parse_cmdline(argc, argv);
/*
* Figure out system console(s)
* Figure out system console(s) and call log_init() to set
* correct log level, possibly finit.debug enabled.
*/
console_init();
@@ -598,10 +739,7 @@ int main(int argc, char *argv[])
/*
* Set PATH, SHELL, and PWD early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1);
conf_reset_env();
if (chdir("/"))
_pe("Failed cd /");
@@ -609,21 +747,8 @@ int main(int argc, char *argv[])
/*
* In case of emergency.
*/
if (rescue) {
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
rescue = 0;
break;
}
}
if (rescue)
rescue = sulogin(0);
/*
* Load plugins early, the first hook is in banner(), so we
@@ -652,7 +777,10 @@ int main(int argc, char *argv[])
*/
cgroup_init(&loop);
/* Check and mount filesystems. */
/*
* Check custom fstab from cmdline, including fallback, then run
* fsck before mounting all filesystems, on error call sulogin.
*/
fs_mount_all();
/* Bootstrap conditions, needed for hooks */
@@ -675,7 +803,7 @@ int main(int argc, char *argv[])
*/
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");
wdog = svc_find("watchdog", "finit");
}
/*
+5 -2
View File
@@ -1,7 +1,7 @@
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -95,6 +95,7 @@
#define INIT_CMD_SVC_QUERY 130
#define INIT_CMD_SVC_FIND 131
#define INIT_CMD_SVC_FIND_BYC 132
#define INIT_CMD_SIGNAL 133
#define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255
@@ -109,16 +110,18 @@ struct init_request {
extern int runlevel;
extern int cfglevel;
extern int cmdlevel;
extern int prevlevel;
extern int debug;
extern int rescue;
extern int single;
extern int bootstrap;
extern int kerndebug;
extern char *rcsd;
extern char *fstab;
extern char *sdown;
extern char *network;
extern char *hostname;
extern char *rcsd;
extern char *runparts;
extern char *osheading;
extern uev_ctx_t *ctx;
+2 -2
View File
@@ -1,6 +1,6 @@
/* Initialize and serve a login terminal
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -21,7 +21,7 @@
* THE SOFTWARE.
*/
#include "config.h"
#include "config.h" /* Generated by configure script */
#include <err.h>
#include <errno.h>
+82 -46
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions for finit and its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */
#include <errno.h>
#include <grp.h>
@@ -30,6 +32,7 @@
#include <net/if.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <sysexits.h>
#include <sys/ioctl.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
@@ -40,6 +43,7 @@
#include "finit.h"
#include "helpers.h"
#include "private.h"
#include "sig.h"
#include "util.h"
#include "utmp-api.h"
@@ -57,7 +61,7 @@ static pstyle_t progress_style = PROGRESS_DEFAULT;
#define STATUS_CLASS { \
CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \
CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \
CHOOSE("WARN", "WARN", "\e[1;31m"), /* Red */ \
CHOOSE("WARN", "WARN", "\e[1;33m"), /* Yellow */ \
CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \
}
@@ -79,7 +83,7 @@ char *console(void)
void console_init(void)
{
ttinit();
log_init();
}
ssize_t cprintf(const char *fmt, ...)
@@ -98,6 +102,17 @@ ssize_t cprintf(const char *fmt, ...)
return size;
}
void tabstospaces(char *line)
{
if (!line)
return;
for (int i = 0; line[i]; i++) {
if (line[i] == '\t')
line[i] = ' ';
}
}
char *strip_line(char *line)
{
char *ptr;
@@ -493,27 +508,52 @@ void networking(int updown)
/* Debian/Ubuntu/Busybox/RH/Suse */
if (!whichp("ifup"))
goto done;
goto fallback;
if (fexist("/etc/network/interfaces")) {
pid_t pid;
pid = fork();
if (pid == 0) {
int rc = EX_OSERR;
const char *cmd;
FILE *pp;
setsid();
sig_unblock();
if (updown)
run("ifup -a");
cmd = "ifup -a 2>&1";
else if (whichp("ifquery"))
/* Regular ifodwn supports --force but not -f */
cmd = "ifdown -a --force 2>&1";
else
run("ifdown -a");
_exit(0);
/* Busybox ifdown support -f, but not --force */
cmd = "ifdown -a -f 2>&1";
pp = popen(cmd, "r");
if (pp) {
char buf[256];
while (fgets(buf, sizeof(buf), pp))
logit(LOG_NOTICE, "network: %s", buf);
rc = pclose(pp);
}
_exit(rc);
}
cgroup_service("network", pid, NULL);
print(pid > 0 ? 0 : 1, "%s network interfaces ...",
updown ? "Bringing up" : "Taking down");
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping");
goto done;
}
done:
fallback:
/* Fall back to bring up at least loopback */
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
done:
/* Hooks that rely on loopback, or basic networking being up. */
if (updown) {
@@ -522,48 +562,44 @@ done:
}
}
#ifndef HAVE_GETFSENT
static lfile_t *fstab = NULL;
int setfsent(void)
/*
* Very basic, and incomplete, check if we're running in a container.
*/
int in_container(void)
{
if (fstab)
lfclose(fstab);
const char *containers[] = {
"lxc",
"docker",
"kubepod",
"unshare"
};
const char *files[] = {
"/run/.containerenv",
"/.dockerenv"
};
static int incont = -1;
size_t i;
char *c;
fstab = lfopen("/etc/fstab", " \t\n");
if (!fstab)
return 0;
if (incont > 0)
return incont;
return 1;
c = getenv("container");
if (c) {
for (i = 0; i < NELEMS(containers); i++) {
if (!strcmp(containers[i], c))
return incont = 1;
}
}
for (i = 0; i < NELEMS(files); i++) {
if (!access(files[i], F_OK))
return incont = 1;
}
return 0;
}
struct fstab *getfsent(void)
{
static struct fstab fs;
fs.fs_spec = lftok(fstab);
if (fs.fs_spec == NULL)
return NULL;
fs.fs_file = lftok(fstab);
fs.fs_vfstype = lftok(fstab);
fs.fs_mntops = lftok(fstab);
fs.fs_type = "rw";
fs.fs_freq = atoi(lftok(fstab) ?: "0");
fs.fs_passno = atoi(lftok(fstab) ?: "0");
return &fs;
}
void endfsent(void)
{
if (fstab)
lfclose(fstab);
fstab = NULL;
}
#endif /* HAVE_GETFSENT */
/**
* Local Variables:
* indent-tabs-mode: t
+7 -23
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions and C-library extensions for finit and its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -25,8 +25,6 @@
#ifndef FINIT_HELPERS_H_
#define FINIT_HELPERS_H_
#include <mntent.h>
#include <sched.h> /* sched_yield() */
#include <stdarg.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -47,26 +45,11 @@ typedef enum {
PROGRESS_MODERN,
} pstyle_t;
#ifndef HAVE_GETFSENT
struct fstab {
char *fs_spec; /* block device name */
char *fs_file; /* mount point */
char *fs_vfstype; /* file-system type */
char *fs_mntops; /* mount options */
const char *fs_type; /* rw/rq/ro/sw/xx option */
int fs_freq; /* dump frequency, in days */
int fs_passno; /* pass number on parallel dump */
};
int setfsent (void);
void endfsent (void);
struct fstab *getfsent (void);
#endif /* HAVE_GETFSENT */
char *console (void);
void console_init (void);
ssize_t cprintf (const char *fmt, ...);
ssize_t cprintf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
void tabstospaces (char *line);
char *strip_line (char *line);
void enable_progress (int onoff);
@@ -79,7 +62,7 @@ void stty (int fd, speed_t speed);
void print_banner (const char *heading);
void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...);
void print (int action, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void print_desc (char *action, char *desc);
int print_result (int fail);
@@ -93,10 +76,11 @@ int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname);
void networking (int updown);
int in_container (void);
int complete (char *cmd, int pid);
int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...);
int run (char *cmd, char *log);
int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
+153 -52
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2015-2022 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
@@ -52,6 +52,7 @@ struct cmd {
struct cmd *ctx;
int (*cb)(char *arg);
int *cond;
int (*cb_multiarg)(int argc, char **argv);
};
int icreate = 0;
@@ -160,8 +161,11 @@ static int do_log(char *svc)
if (!svc || !svc[0])
svc = "finit";
if (!fexist(logfile))
if (!fexist(logfile)) {
logfile = "/var/log/messages";
if (!fexist(logfile))
return 0; /* bail out, maybe in container */
}
return systemf("cat %s | grep %s | tail -10", logfile, svc);
}
@@ -275,6 +279,61 @@ static int do_restart(char *arg)
return do_startstop(INIT_CMD_RESTART_SVC, arg);
}
/**
* do_signal - Ask finit to send a signal to a service.
* @argv: must point to an array of strings, containing a service
* and signal name, in that order.
* @argc: must be 2.
*
* A signal can be a complete signal name such as "SIGHUP", or
* it can be the shortest unique name, such as "HUP" (no SIG prefix).
* It can also be a raw signal number, such as "9" (SIGKILL).
*/
int do_signal(int argc, char *argv[])
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_QUERY
};
int signo;
if (argc != 2)
errx(1, "invalid number of arguments to signal");
/* Validate service name */
strlcpy(rq.data, argv[0], sizeof(rq.data));
if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such task or service(s): %s\n\n", argv[0]);
goto show_usage;
}
/* Validate signal name (or number) */
signo = str2sig(argv[1]);
if (signo == -1) {
/* Not a signal name, is it an actual signal number? */
errno = 0;
signo = (int)strtol(argv[1], NULL, 10);
/* Was it a number? Was it a signo that finit recognizes? */
if (errno || !*(sig2str(signo))) {
fprintf(stderr, "Not a valid signal (or signo): %s\n", argv[1]);
goto show_usage;
}
}
/* Reuse runlevel for signal number. */
rq.magic = INIT_MAGIC;
rq.cmd = INIT_CMD_SIGNAL;
rq.runlevel = signo;
strlcpy(rq.data, argv[0], sizeof(rq.data));
return client_send(&rq, sizeof(rq));
show_usage:
fprintf(stderr, "Usage: initctl signal <NAME>[:ID] <S>\n");
return 1;
}
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
const char *cond, *asserted;
@@ -292,7 +351,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
if (strncmp("pid/", cond, 4) == 0) {
svc_t *svc;
svc= client_svc_find_by_cond(cond);
svc = client_svc_find_by_cond(cond);
if (!svc) {
nm = "unknown";
pid = 0;
@@ -332,7 +391,7 @@ static int do_cond_dump(char *arg)
return 0;
}
static int do_cond_act(char *arg, int creat)
static int do_cond_act(char *arg, int op)
{
char oneshot[256];
size_t off;
@@ -350,17 +409,29 @@ static int do_cond_act(char *arg, int creat)
snprintf(oneshot, sizeof(oneshot), _PATH_CONDUSR "%s", arg);
off = strlen(_PATH_COND);
if (creat) {
switch (op) {
case 2:
off = !fexist(oneshot);
if (verbose)
puts(off ? "off" : "on");
return off;
case 1:
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
err(1, "Failed asserting condition <%s>", &oneshot[off]);
} else {
break;
case 0:
if (erase(oneshot) && errno != ENOENT)
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
break;
default:
errx(1, "Unsupported operation (%d)", op);
break;
}
return 0;
}
static int do_cond_get(char *arg) { return do_cond_act(arg, 2); }
static int do_cond_set(char *arg) { return do_cond_act(arg, 1); }
static int do_cond_clr(char *arg) { return do_cond_act(arg, 0); }
@@ -605,6 +676,13 @@ static char *svc_command(svc_t *svc, char *buf, size_t len)
strlcat(buf, svc->args[i], len);
}
if (svc_is_sysv(svc)) {
char *cmd = svc->state == SVC_HALTED_STATE ? "stop" : "start";
strlcat(buf, " ", len);
strlcat(buf, cmd, len);
}
strlcat(buf, bold ? "\e[0m" : "", len);
return buf;
@@ -722,7 +800,7 @@ static int show_status(char *arg)
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
svc_ident(svc, ident, sizeof(ident));
if (string_match(ident, arg))
if (string_compare(ident, arg))
num++;
if (string_case_compare(ident, arg))
exact++;
@@ -735,8 +813,15 @@ static int show_status(char *arg)
if (!svc)
return 255;
if (quiet)
return svc->state != SVC_RUNNING_STATE;
if (quiet) {
if (svc_is_runtask(svc))
if (svc->started)
return 0;
else
return 1;
else
return svc->state != SVC_RUNNING_STATE;
}
pidfn = svc->pidfile;
if (pidfn[0] == '!')
@@ -765,6 +850,8 @@ static int show_status(char *arg)
char *group;
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf));
if (!group)
goto no_cgroup; /* ... or PID doesn't exist (anymore) */
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
cg = cg_conf(path);
@@ -774,6 +861,7 @@ static int show_status(char *arg)
cg->cg_mem.min, cg->cg_mem.max);
show_cgroup_tree(group, " ");
}
no_cgroup:
printf("\n");
return do_log(svc->cmd);
@@ -797,7 +885,7 @@ static int show_status(char *arg)
char *lvls;
svc_ident(svc, ident, sizeof(ident));
if (num && !string_match(ident, arg))
if (num && !string_compare(ident, arg))
continue;
printf("%-*d ", pw, svc->pid);
@@ -891,12 +979,13 @@ static int usage(int rc)
" -q, --quiet Silent, only return status of command\n"
" -t, --no-heading Skip table headings\n"
" -v, --verbose Verbose output\n"
" -V, --version Show program version\n"
" -h, --help This help text\n"
"\n"
"Commands:\n"
" debug Toggle Finit (daemon) debug\n"
" help This help text\n"
" version Show Finit version\n"
" version Show program version\n"
"\n", prognm);
if (has_rcsd)
@@ -920,15 +1009,16 @@ static int usage(int rc)
" disable <CONF> Disable .conf in %s\n", ena);
if (has_rcsd)
fprintf(stderr,
" reload Reload *.conf in " FINIT_RCSD " (activate changes)\n");
" reload Reload *.conf in " FINIT_RCSD " (activate changes)\n");
else
fprintf(stderr,
" reload Reload " FINIT_CONF " (activate changes)\n");
" reload Reload " FINIT_CONF " (activate changes)\n");
fprintf(stderr,
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
"\n"
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
" cond get <COND> Get status of user-defined condition, see $? and -v\n"
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
" cond status Show condition status, default cond command\n"
" cond dump Dump all conditions and their status\n"
@@ -938,6 +1028,7 @@ static int usage(int rc)
" stop <NAME>[:ID] Stop/Pause a running service by name\n"
" reload <NAME>[:ID] Reload service by name (SIGHUP or restart)\n"
" restart <NAME>[:ID] Restart (stop/start) service by name\n"
" signal <NAME>[:ID] <S> Send signal S to service by name, with optional ID\n"
" ident [NAME] Show matching identities for NAME, or all\n"
" status <NAME>[:ID] Show service status, by name\n"
" status Show status of services, default command\n");
@@ -994,12 +1085,15 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
if (!cmd_cond(&command[i]))
continue;
if (!string_match(command[i].cmd, argv[0]))
if (!string_compare(command[i].cmd, argv[0]))
continue;
if (command[i].ctx)
return cmd_parse(argc - 1, &argv[1], command[i].ctx);
if (command[i].cb_multiarg)
return command[i].cb_multiarg(argc - 1, argv + 1);
if (command[i].cb) {
int rc = 0;
@@ -1018,6 +1112,7 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
return command[0].cb(NULL); /* default cmd */
}
int main(int argc, char *argv[])
{
struct option long_options[] = {
@@ -1031,55 +1126,58 @@ int main(int argc, char *argv[])
{ "quiet", 0, NULL, 'q' },
{ "no-heading", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
struct cmd cond[] = {
{ "status", NULL, do_cond_show, NULL }, /* default cmd */
{ "dump", NULL, do_cond_dump, NULL },
{ "set", NULL, do_cond_set, NULL },
{ "clr", NULL, do_cond_clr, NULL },
{ "clear", NULL, do_cond_clr, NULL },
{ NULL, NULL, NULL, NULL }
{ "status", NULL, do_cond_show, NULL, NULL }, /* default cmd */
{ "dump", NULL, do_cond_dump, NULL, NULL },
{ "set", NULL, do_cond_set, NULL, NULL },
{ "get", NULL, do_cond_get, NULL, NULL },
{ "clr", NULL, do_cond_clr, NULL, NULL },
{ "clear", NULL, do_cond_clr, NULL, NULL },
{ NULL, NULL, NULL, NULL, NULL }
};
struct cmd command[] = {
{ "status", NULL, show_status, NULL }, /* default cmd */
{ "ident", NULL, show_ident, NULL },
{ "status", NULL, show_status, NULL, NULL }, /* default cmd */
{ "ident", NULL, show_ident, NULL, NULL },
{ "debug", NULL, toggle_debug, NULL },
{ "devel", NULL, do_devel, NULL },
{ "help", NULL, do_help, NULL },
{ "version", NULL, show_version, NULL },
{ "debug", NULL, toggle_debug, NULL, NULL },
{ "devel", NULL, do_devel, NULL, NULL },
{ "help", NULL, do_help, NULL, NULL },
{ "version", NULL, show_version, NULL, NULL },
{ "list", NULL, serv_list, NULL },
{ "ls", NULL, serv_list, NULL },
{ "enable", NULL, serv_enable, NULL },
{ "disable", NULL, serv_disable, NULL },
{ "touch", NULL, serv_touch, NULL },
{ "show", NULL, serv_show, NULL },
{ "edit", NULL, serv_edit, NULL },
{ "create", NULL, serv_creat, NULL },
{ "delete", NULL, serv_delete, NULL },
{ "reload", NULL, do_reload, NULL },
{ "list", NULL, serv_list, NULL, NULL },
{ "ls", NULL, serv_list, NULL, NULL },
{ "enable", NULL, serv_enable, NULL, NULL },
{ "disable", NULL, serv_disable, NULL, NULL },
{ "touch", NULL, serv_touch, NULL, NULL },
{ "show", NULL, serv_show, NULL, NULL },
{ "edit", NULL, serv_edit, NULL, NULL },
{ "create", NULL, serv_creat, NULL, NULL },
{ "delete", NULL, serv_delete, NULL, NULL },
{ "reload", NULL, do_reload, NULL, NULL },
{ "cond", cond, NULL, NULL },
{ "cond", cond, NULL, NULL, NULL },
{ "log", NULL, do_log, NULL },
{ "start", NULL, do_start, NULL },
{ "stop", NULL, do_stop, NULL },
{ "restart", NULL, do_restart, NULL },
{ "log", NULL, do_log, NULL, NULL },
{ "start", NULL, do_start, NULL, NULL },
{ "stop", NULL, do_stop, NULL, NULL },
{ "restart", NULL, do_restart, NULL, NULL },
{ "signal", NULL, NULL, NULL, do_signal },
{ "cgroup", NULL, show_cgroup, &cgrp },
{ "ps", NULL, show_cgps, &cgrp },
{ "top", NULL, show_cgtop, &cgrp },
{ "cgroup", NULL, show_cgroup, &cgrp, NULL },
{ "ps", NULL, show_cgps, &cgrp, NULL },
{ "top", NULL, show_cgtop, &cgrp, NULL },
{ "runlevel", NULL, do_runlevel, NULL },
{ "reboot", NULL, do_reboot, NULL },
{ "halt", NULL, do_halt, NULL },
{ "poweroff", NULL, do_poweroff, NULL },
{ "suspend", NULL, do_suspend, NULL },
{ "runlevel", NULL, do_runlevel, NULL, NULL },
{ "reboot", NULL, do_reboot, NULL, NULL },
{ "halt", NULL, do_halt, NULL, NULL },
{ "poweroff", NULL, do_poweroff, NULL, NULL },
{ "suspend", NULL, do_suspend, NULL, NULL },
{ "utmp", NULL, do_utmp, &utmp },
{ NULL, NULL, NULL, NULL }
{ "utmp", NULL, do_utmp, &utmp, NULL },
{ NULL, NULL, NULL, NULL, NULL }
};
int interactive = 1, c;
@@ -1090,7 +1188,7 @@ int main(int argc, char *argv[])
cgrp = cgroup_avail();
utmp = has_utmp();
while ((c = getopt_long(argc, argv, "1bcdfh?pqtv", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "1bcdfh?pqtvV", long_options, NULL)) != EOF) {
switch(c) {
case '1':
ionce = 1;
@@ -1131,6 +1229,9 @@ int main(int argc, char *argv[])
case 'v':
verbose = 1;
break;
case 'V':
return show_version(NULL);
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2015-2022 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
@@ -34,7 +34,7 @@ extern int heading; /* initctl -t */
extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */
extern void print_header(const char *fmt, ...);
extern void print_header(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
#endif /* FINIT_INITCTL_H_ */
+17 -1
View File
@@ -1,7 +1,7 @@
/* inotify watcher for files or directories
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -23,6 +23,7 @@
*/
#include <errno.h>
#include <sys/socket.h>
#include "finit.h"
#include "iwatch.h"
@@ -39,6 +40,9 @@ static int initialized = 0;
int iwatch_init(struct iwatch *iw)
{
socklen_t len;
int sz;
if (!iw) {
errno = EINVAL;
return -1;
@@ -51,6 +55,18 @@ int iwatch_init(struct iwatch *iw)
_pe("Failed creating inotify descriptor");
return -1;
}
/*
* Double the size of the socket's receive buffer to address
* issues with lost events in some cases, e.g. bootstrap and
* reconf of systems with lots of services.
*/
len = sizeof(sz);
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) {
if (setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)))
_pe("Failed increasing size of inotify socket");
}
initialized = 1;
return iw->fd;
+1 -1
View File
@@ -1,7 +1,7 @@
/* inotify watcher for files or directories
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
+42 -6
View File
@@ -1,6 +1,6 @@
/* Listens to kernel events like AC power status and manages sys/ conditions
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2021-2022 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
@@ -24,6 +24,7 @@
#include <errno.h>
#include <dirent.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -52,6 +53,10 @@
static int num_ac_online;
static int num_ac;
static int running = 1;
static int level;
static int logon;
int debug; /* debug in other modules as well */
void logit(int prio, const char *fmt, ...)
@@ -59,9 +64,9 @@ void logit(int prio, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
if (!debug)
if (logon)
vsyslog(prio, fmt, ap);
else {
else if (prio <= level) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
@@ -179,6 +184,26 @@ static void init(void)
sys_cond("pwr/ac", 1);
}
static void set_logging(int prio)
{
setlogmask(LOG_UPTO(prio));
level = prio;
}
static void toggle_debug(int signo)
{
(void)signo;
debug ^= 1;
set_logging(debug ? LOG_DEBUG : LOG_NOTICE);
}
static void shut_down(int signo)
{
(void)signo;
running = 0;
}
/*
* Started by Finit as soon as possible when base filesystem is up,
* modules have been probed, or insmodded from /etc/finit.conf, so by
@@ -198,10 +223,13 @@ int main(int argc, char *argv[])
}
if (!debug) {
openlog("keventd", LOG_CONS | LOG_PID, LOG_DAEMON);
setlogmask(LOG_UPTO(LOG_NOTICE));
openlog("keventd", LOG_PID, LOG_DAEMON);
set_logging(LOG_NOTICE);
logon = 1;
}
signal(SIGUSR1, toggle_debug);
signal(SIGTERM, shut_down);
init();
pfd.events = POLLIN;
@@ -216,11 +244,18 @@ int main(int argc, char *argv[])
panic("bind failed");
logit(LOG_DEBUG, "Waiting for events ...");
while (-1 != poll(&pfd, 1, -1)) {
while (running) {
char *path = NULL;
int ac = 0;
int i, len;
if (-1 == poll(&pfd, 1, -1)) {
if (errno == EINTR)
continue;
break;
}
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
if (len == -1) {
switch (errno) {
@@ -235,6 +270,7 @@ int main(int argc, char *argv[])
}
}
buf[len] = 0;
logit(LOG_DEBUG, "%s", buf);
/* skip libusb events, focus on kernel events/changes */
if (strncmp(buf, "change@", 7))
+28 -20
View File
@@ -1,6 +1,6 @@
/* Finit daemon log functions
*
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -21,7 +21,8 @@
* THE SOFTWARE.
*/
#include "config.h"
#include "config.h" /* Generated by configure script */
#include <stdio.h>
#include <stdarg.h>
#ifdef _LIBITE_LITE
@@ -40,6 +41,8 @@ static int loglevel = LOG_INFO;
void log_init(void)
{
ttinit();
if (debug)
loglevel = LOG_DEBUG;
else
@@ -59,18 +62,29 @@ void log_exit(void)
enable_progress(1);
}
static void log_open(void)
static int log_open(void)
{
int opts = LOG_PID;
int opts;
closelog();
if (up)
return 1;
if (access("/dev/log", W_OK))
return 0;
opts = LOG_CONS | LOG_PID;
if (debug)
opts |= LOG_PERROR; /* LOG_CONS | */
opts |= LOG_PERROR;
openlog("finit", opts, LOG_DAEMON);
setlogmask(LOG_UPTO(loglevel));
up = 1;
return up = 1;
}
static void log_close(void)
{
closelog();
up = 0;
}
/* Toggle debug mode */
@@ -78,12 +92,8 @@ void log_debug(void)
{
debug = !debug;
if (debug) {
loglevel = LOG_DEBUG;
} else {
loglevel = LOG_NOTICE;
ttinit();
}
log_close();
log_init();
log_open();
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
@@ -95,15 +105,12 @@ void log_debug(void)
*/
void logit(int prio, const char *fmt, ...)
{
FILE *fp;
va_list ap;
FILE *fp;
va_start(ap, fmt);
if (up || fexist("/dev/log")) {
if (!up)
log_open();
if (up || log_open()) {
vsyslog(prio, fmt, ap);
goto done;
}
@@ -111,9 +118,9 @@ void logit(int prio, const char *fmt, ...)
if (LOG_PRI(prio) > loglevel)
goto done;
fp = fopen("/dev/kmsg", "w");
if (!fp) {
if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
goto done;
}
@@ -125,6 +132,7 @@ void logit(int prio, const char *fmt, ...)
va_end(ap);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
}
done:
+7 -7
View File
@@ -1,6 +1,6 @@
/* Finit daemon log functions
*
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -40,17 +40,17 @@
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
* use `initctl debug` or add `debug` to the kernel cmdline.
*/
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args)
#define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt "\n", __func__, ##args)
#define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s\n", __func__, ##args, strerror(errno))
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
#define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
#define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
void log_init (void);
void log_exit (void);
void log_debug (void);
void logit (int prio, const char *fmt, ...);
void flog (char *file, const char *fmt, ...);
void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif /* FINIT_LOG_H_ */
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2018-2022 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
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2018-2022 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
+3 -1
View File
@@ -1,6 +1,6 @@
/* Handle MD arrays
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -21,6 +21,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <glob.h>
#include <stdio.h>
#include <string.h>
+28 -6
View File
@@ -1,6 +1,6 @@
/* Mount and unmount helpers
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -21,10 +21,17 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <errno.h>
#include <string.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <sys/mount.h>
#include "helpers.h"
/*
* SysV init on Debian/Ubuntu skips these protected mount points
*
@@ -65,12 +72,12 @@ static void iterator_end(FILE **fp)
*fp = NULL;
}
static struct mntent *iterator(char *fstab, FILE **fp)
static struct mntent *iterator(char *filename, FILE **fp)
{
static struct mntent *mnt;
if (!*fp && fstab) {
*fp = setmntent(fstab, "r");
if (!*fp && filename) {
*fp = setmntent(filename, "r");
if (!*fp)
return NULL;
}
@@ -86,13 +93,28 @@ static struct mntent *iterator(char *fstab, FILE **fp)
return NULL;
}
static int unmount(const char *target)
{
int rc;
rc = umount(target);
if (rc) {
if (errno != EBUSY)
print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno));
return rc;
}
print(0, "Unmounting %s", target);
return 0;
}
void unmount_tmpfs(void)
{
struct mntent *mnt;
FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts", &fp))) {
if (!strcmp("tmpfs", mnt->mnt_fsname) && !umount(mnt->mnt_dir))
if (!strcmp("tmpfs", mnt->mnt_fsname) && !unmount(mnt->mnt_dir))
iterator_end(&fp); /* Restart iteration */
}
}
@@ -103,7 +125,7 @@ void unmount_regular(void)
FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts", &fp))) {
if (!umount(mnt->mnt_dir))
if (!unmount(mnt->mnt_dir))
iterator_end(&fp); /* Restart iteration */
}
}
+13 -5
View File
@@ -1,7 +1,7 @@
/* PID and PID file helpers
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <errno.h>
#include <paths.h>
#include <stdio.h>
@@ -69,10 +71,11 @@ int pid_alive(pid_t pid)
*/
char *pid_get_name(pid_t pid, char *name, size_t len)
{
static char line[64];
char *pname = NULL;
char path[32];
int ret = 1;
FILE *fp;
char *pname, path[32];
static char line[64];
snprintf(path, sizeof(path), "/proc/%d/status", pid);
fp = fopen(path, "r");
@@ -113,8 +116,8 @@ char *pid_file(svc_t *svc)
pid_t pid_file_read(const char *fn)
{
pid_t pid = 0;
FILE *fp;
char buf[42];
FILE *fp;
fp = fopen(fn, "r");
if (!fp)
@@ -157,6 +160,11 @@ int pid_file_set(svc_t *svc, char *file, int not)
file++;
}
if (svc_is_forking(svc) && !not) {
logit(LOG_WARNING, "Service %s is forking, adjusting to pid:!%s", svc->name, file);
not = 1;
}
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
if (not)
svc->pidfile[0] = '!';
@@ -199,8 +207,8 @@ int pid_file_parse(svc_t *svc, char *arg)
/* 'pid:' implies argument following*/
if (!strncmp(arg, "pid:", 4)) {
int len, not = 0;
char path[MAX_ARG_LEN];
int len, not = 0;
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
+1 -1
View File
@@ -1,7 +1,7 @@
/* PID and PID file helpers
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
+3 -2
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
@@ -21,6 +21,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <errno.h>
#include <dlfcn.h> /* dlopen() et al */
#include <dirent.h> /* readdir() et al */
@@ -34,7 +36,6 @@
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include "config.h"
#include "cond.h"
#include "finit.h"
#include "helpers.h"
+1 -1
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit
*
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2012-2022 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
+1 -1
View File
@@ -1,7 +1,7 @@
/* Private header file for main finit daemon, not for plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
+9 -10
View File
@@ -1,6 +1,6 @@
/* reboot, halt, poweroff, shutdown, suspend for finit
*
* Copyright (c) 2009-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2009-2022 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
@@ -26,6 +26,7 @@
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
@@ -98,11 +99,6 @@ static void do_kill(int signo, char *msg)
do_sleep(5);
}
static void sig(int signo)
{
(void)signo; /* NOP */
}
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
@@ -164,10 +160,13 @@ int reboot_main(int argc, char *argv[])
/* Check for any overrides */
transform(NULL);
/* Catch and ignore SIGTERM on halt/poweroff */
signal(SIGTERM, sig);
/* Check if we're in sulogin shell */
if (getenv("SULOGIN"))
force = 1;
if (force) {
sync();
switch (cmd) {
case CMD_REBOOT:
c = reboot(RB_AUTOBOOT);
@@ -182,9 +181,9 @@ int reboot_main(int argc, char *argv[])
break;
case CMD_SUSPEND:
c = reboot(RB_SW_SUSPEND);
c = suspend();
if (c && errno == EINVAL)
errx(1, "Kernel does not support suspend.");
errx(1, "Kernel does not support suspend to RAM.");
break;
case CMD_UNKNOWN:
+1 -1
View File
@@ -1,6 +1,6 @@
/* Work queue helper functions
*
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2018-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* Work queue helper functions
*
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2018-2022 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
+32 -2
View File
@@ -1,6 +1,6 @@
/* List and enable/disable service configurations
*
* Copyright (c) 2017-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2017-2022 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
@@ -34,9 +34,24 @@
# include <lite/lite.h>
#endif
#include "client.h"
#include "initctl.h"
static int is_builtin(char *arg)
{
svc_t *svc;
svc = client_svc_find(arg);
if (!svc)
return 0;
if (svc->file[0])
return 0;
return 1;
}
static int calc_width(char *arr[], size_t len)
{
int width = 0;
@@ -292,6 +307,8 @@ int serv_touch(char *arg)
if (!fexist(fn)) {
if (!strstr(arg, "finit.conf"))
errx(1, "%s not available.", arg);
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
strlcpy(path, FINIT_CONF, sizeof(path));
fn = path;
@@ -311,6 +328,9 @@ int serv_show(char *arg)
fn = conf(path, sizeof(path), arg, 0);
if (!fexist(fn)) {
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
warnx("Cannot find %s", arg);
return 1;
}
@@ -345,6 +365,9 @@ static int do_edit(char *arg, int creat)
fn = conf(path, sizeof(path), arg, creat);
if (!fexist(fn)) {
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
if (!creat) {
warnx("Cannot find %s, use -c flag, create command, or select one of:", arg);
return serv_list(NULL);
@@ -371,6 +394,7 @@ int serv_edit(char *arg)
if (!arg || !arg[0]) {
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF))
return 0;
arg = "";
}
return do_edit(arg, icreate);
@@ -385,6 +409,9 @@ int serv_creat(char *arg)
if (!arg || !arg[0])
errx(1, "missing argument to create");
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
/* Input from a pipe or a proper TTY? */
if (isatty(STDIN_FILENO))
return do_edit(arg, 1);
@@ -420,8 +447,11 @@ int serv_delete(char *arg)
}
fn = conf(buf, sizeof(buf), arg, 0);
if (!fn)
if (!fn) {
if (is_builtin(arg))
errx(1, "%s is a built-in service.", arg);
errx(1, FINIT_RCSD " missing on system.");
}
if (!fexist(fn))
warnx("cannot find %s", fn);
+1 -1
View File
@@ -1,6 +1,6 @@
/* List and enable/disable service configurations
*
* Copyright (c) 2017-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2017-2022 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
+260 -88
View File
@@ -1,7 +1,7 @@
/* Finit service monitor, task starter and generic API for managing svc_t
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -25,6 +25,7 @@
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */
#include <sched.h> /* sched_yield() */
#include <string.h>
#include <sys/reboot.h>
#include <sys/prctl.h>
@@ -89,7 +90,7 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
* Returns:
* POSIX OK(0) on success, non-zero on error.
*/
static int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc))
int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc))
{
if (svc->timer_cb)
return -EBUSY;
@@ -107,7 +108,7 @@ static int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc)
* Returns:
* POSIX OK(0) on success, non-zero on error.
*/
static int service_timeout_cancel(svc_t *svc)
int service_timeout_cancel(svc_t *svc)
{
int err;
@@ -381,6 +382,20 @@ static char *group_name(svc_t *svc, char *buf, size_t len)
return buf;
}
static void compose_cmdline(svc_t *svc, char *buf, size_t len)
{
size_t i;
strlcpy(buf, svc->cmd, len);
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
if (!strlen(svc->args[i]))
break;
strlcat(buf, " ", len);
strlcat(buf, svc->args[i], len);
}
}
static pid_t service_fork(svc_t *svc)
{
pid_t pid;
@@ -445,6 +460,7 @@ static pid_t service_fork(svc_t *svc)
static int service_start(svc_t *svc)
{
int result = 0, do_progress = 1;
char cmdline[CMD_SIZE] = "";
sigset_t nmask, omask;
char grnam[80];
pid_t pid;
@@ -481,8 +497,9 @@ static int service_start(svc_t *svc)
}
}
compose_cmdline(svc, cmdline, sizeof(cmdline));
if (svc_is_sysv(svc))
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", cmdline);
if (!svc->desc[0])
do_progress = 0;
@@ -558,7 +575,7 @@ static int service_start(svc_t *svc)
for (i = 0; i < we.we_wordc; i++) {
if (strlen(we.we_wordv[i]) >= sizeof(svc->args[i])) {
logit(LOG_ERR, "%s: expanded arg. '%s' too long", we.we_wordv[i]);
logit(LOG_ERR, "%s: expanded arg. '%s' too long", svc->cmd, we.we_wordv[i]);
rc = WRDE_NOSPACE;
goto nomem;
}
@@ -569,8 +586,16 @@ static int service_start(svc_t *svc)
}
wordfree(&we);
} else {
size_t j;
i = 0;
args[i++] = svc->cmd;
/* this handles, e.g., bridge-stop br0 start */
for (j = 0; j < MAX_NUM_SVC_ARGS; j++) {
if (!strlen(svc->args[j]))
break;
args[i++] = svc->args[j];
}
args[i++] = "start";
}
args[i] = NULL;
@@ -603,16 +628,7 @@ static int service_start(svc_t *svc)
_exit(status);
} else if (debug) {
char buf[CMD_SIZE] = "";
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
if (strlen(svc->args[i]) == 0)
break;
if (buf[0])
strlcat(buf, " ", sizeof(buf));
strlcat(buf, svc->args[i], sizeof(buf));
}
_d("Starting %s %s", svc->cmd, buf);
_d("Starting %s", cmdline);
}
if (svc_is_tty(svc))
@@ -620,7 +636,8 @@ static int service_start(svc_t *svc)
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);
if (!svc_is_sysv(svc))
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
svc->pid = pid;
svc->start_time = jiffies();
@@ -628,10 +645,13 @@ static int service_start(svc_t *svc)
switch (svc->type) {
case SVC_TYPE_RUN:
svc->status = complete(svc->cmd, pid);
if (WIFEXITED(svc->status) && !WEXITSTATUS(svc->status))
if (WIFEXITED(svc->status) && !WEXITSTATUS(svc->status)) {
svc->started = 1;
result = 0;
else
} else {
svc->started = 0;
result = 1;
}
svc->start_time = svc->pid = 0;
svc->once++;
svc_set_state(svc, SVC_STOPPING_STATE);
@@ -701,7 +721,7 @@ static void service_cleanup(svc_t *svc)
utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */
svc->oldpid = svc->pid;
svc->start_time = svc->pid = 0;
svc->starting = svc->start_time = svc->pid = 0;
}
/**
@@ -713,6 +733,7 @@ static void service_cleanup(svc_t *svc)
*/
static int service_stop(svc_t *svc)
{
char cmdline[CMD_SIZE] = "";
int do_progress = 1;
int rc = 0;
@@ -724,6 +745,7 @@ static int service_stop(svc_t *svc)
service_timeout_cancel(svc);
compose_cmdline(svc, cmdline, sizeof(cmdline));
if (!svc_is_sysv(svc)) {
if (svc->pid <= 1)
return 1;
@@ -733,7 +755,7 @@ static int service_stop(svc_t *svc)
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s[%d], sending %s ...",
svc_ident(svc, NULL, 0), svc->pid, sig_name(svc->sighalt));
} else {
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", svc->cmd);
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", cmdline);
}
/*
@@ -743,7 +765,13 @@ static int service_stop(svc_t *svc)
*/
svc_started(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
/*
* Verify there's still something there before we send the reaper.
*/
if (svc->pid > 1 && !pid_alive(svc->pid)) {
svc->pid = 0;
return 0;
}
if (!svc->desc[0])
do_progress = 0;
@@ -755,30 +783,46 @@ 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);
_d("kill(-%d, %d) => rc %d, errno %d", svc->pid, svc->sighalt, rc, errno);
/* PID lost or forking process never really started */
if (rc == -1 && ESRCH == errno)
service_cleanup(svc);
} else
if (rc == -1 && (errno == ESRCH || errno == ENOENT)) {
service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE);
} else
svc_set_state(svc, SVC_STOPPING_STATE);
} else {
service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE);
}
} else {
char *args[] = { svc->cmd, "stop", NULL };
char *args[MAX_NUM_SVC_ARGS + 1];
size_t i = 0, j;
pid_t pid;
args[i++] = svc->cmd;
/* this handles, e.g., bridge-stop br0 stop */
for (j = 0; j < MAX_NUM_SVC_ARGS; j++) {
if (!strlen(svc->args[j]))
break;
args[i++] = svc->args[j];
}
args[i++] = "stop";
args[i] = NULL;
pid = fork();
switch (pid) {
case 0:
setsid();
redirect(svc);
exec_runtask(svc->cmd, args);
exec_runtask(args[0], &args[1]);
_exit(0);
break;
case -1:
_pe("Failed fork() to call sysv script '%s stop'", svc->cmd);
_pe("Failed fork() to call sysv script '%s stop'", cmdline);
rc = 1;
break;
default:
rc = WEXITSTATUS(complete(svc->cmd, pid));
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
}
@@ -829,7 +873,7 @@ static int service_restart(svc_t *svc)
logit(LOG_CONSOLE | LOG_NOTICE, "Restarting %s[%d], sending SIGHUP ...",
svc_ident(svc, NULL, 0), svc->pid);
rc = kill(svc->pid, SIGHUP);
if (rc == -1 && errno == ESRCH) {
if (rc == -1 && (errno == ESRCH || errno == ENOENT)) {
/* nobody home, reset internal state machine */
lost = svc->pid;
} else {
@@ -880,7 +924,7 @@ void service_runlevel(int newlevel)
sm_set_runlevel(&sm, newlevel);
sm_step(&sm);
if (!rescue && runlevel <= 1)
if (!rescue && (runlevel <= 1 || runlevel == 6))
networking(0);
}
@@ -916,7 +960,7 @@ static void parse_env(svc_t *svc, char *env)
return;
if (strlen(env) >= sizeof(svc->env)) {
_e("%s: env file is too long (>%d chars)", svc->cmd, sizeof(svc->env));
_e("%s: env file is too long (>%zu chars)", svc->cmd, sizeof(svc->env));
return;
}
@@ -940,7 +984,7 @@ static void parse_cgroup(svc_t *svc, char *cgroup)
}
if (strlen(ptr) >= sizeof(svc->cgroup)) {
_e("%s: cgroup settings too long (>%d chars)", svc->cmd, sizeof(svc->cgroup));
_e("%s: cgroup settings too long (>%zu chars)", svc->cmd, sizeof(svc->cgroup));
return;
}
@@ -976,26 +1020,39 @@ static void parse_killdelay(svc_t *svc, char *delay)
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);
logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.", type, script);
else
strlcpy(buf, script, len);
}
/*
* name:<name>
* 'name:<name>' or derived from '/path/to/cmd args'
*/
static void parse_name(svc_t *svc, char *arg)
static char *parse_name(char *cmd, char *arg)
{
char *name = NULL;
if (arg && !strncasecmp(arg, "name:", 5)) {
name = arg + 5;
} else {
name = strrchr(svc->cmd, '/');
name = name ? name + 1 : svc->cmd;
char *ptr;
ptr = strchr(cmd, ' ');
if (!ptr)
ptr = cmd + strlen(cmd);
while (ptr > cmd) {
if (*ptr == '/') {
ptr++;
break;
}
ptr--;
}
name = ptr;
}
strlcpy(svc->name, name, sizeof(svc->name));
return name;
}
/*
@@ -1147,7 +1204,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *dev = NULL;
int respawn = 0;
int levels = 0;
int manual = 0;
int forking = 0, manual = 0;
int restart_max = SVC_RESPAWN_MAX;
int restart_tmo = 0;
unsigned oncrash_action = SVC_ONCRASH_IGNORE;
@@ -1206,6 +1263,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
pid = cmd;
else if (!strncasecmp(cmd, "name:", 5))
name = cmd;
else if (!strncasecmp(cmd, "type:forking", 10))
forking = 1;
else if (!strncasecmp(cmd, "manual:yes", 10))
manual = 1;
else if (!strncasecmp(cmd, "restart:", 8))
@@ -1251,6 +1310,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
return 0;
}
name = parse_name(cmd, name);
if (!id)
id = "";
@@ -1292,11 +1352,11 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
next:
svc = svc_find_by_tty(dev);
} else
svc = svc_find(cmd, id);
svc = svc_find(name, id);
if (!svc) {
_d("Creating new svc for %s id #%s type %d", cmd, id, type);
svc = svc_new(cmd, id, type);
_d("Creating new svc for %s name %s id %s type %d", cmd, name, id, type);
svc = svc_new(cmd, name, id, type);
if (!svc) {
_e("Out of memory, cannot register service %s", cmd);
return errno = ENOMEM;
@@ -1309,6 +1369,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
svc->restart_tmo = restart_tmo;
svc->oncrash_action = oncrash_action;
} else {
_d("Found existing svc for %s name %s id %s type %d", cmd, name, id, type);
/* update type, may have changed from service -> task */
svc->type = type;
@@ -1317,10 +1379,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
svc_unblock(svc);
}
/* Decode any optional pid:/optional/path/to/file.pid */
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid))
_e("Invalid 'pid' argument to service: %s", pid);
if (username) {
char *ptr = strchr(username, ':');
@@ -1335,7 +1393,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
svc->runlevels = levels;
_d("Service %s runlevel 0x%02x", svc->cmd, svc->runlevels);
_d("Service %s runlevel 0x%02x", svc_ident(svc, NULL, 0), svc->runlevels);
conf_parse_cond(svc, cond);
@@ -1369,8 +1427,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
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);
@@ -1398,8 +1455,26 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
parse_env(svc, env);
if (file)
strlcpy(svc->file, file, sizeof(svc->file));
if (respawn)
svc->respawn = 1;
svc->respawn = respawn;
svc->forking = forking;
/* Decode any (optional) pid:/optional/path/to/file.pid */
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
char tmp[sizeof(svc->name) + 6]; /* pid:! + svc->name */
/* no pid: set, figure out a default to track this svc */
if (!pid && forking) {
snprintf(tmp, sizeof(tmp), "pid:!%s", svc->name);
pid = tmp;
logit(LOG_INFO, "%s: forking but no pid:!file set, guessing -> %s", svc->name, tmp);
}
if (pid && pid_file_parse(svc, pid))
logit(LOG_WARNING, "%s: service has invalid 'pid:' config: %s", svc->name, pid);
if (svc->pidfile[0] == '!')
svc->forking = 1;
}
/* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
@@ -1472,14 +1547,22 @@ void service_monitor(pid_t lost, int status)
default:
_d("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->cmd, lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
svc_ident(svc, NULL, 0), 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;
if (svc_is_forking(svc)) {
/* Likely start script exiting */
if (svc_is_starting(svc)) {
svc->pid = 0; /* Expect no more activity from this one */
goto cont;
}
logit(LOG_CONSOLE | LOG_NOTICE, "Stopped %s[%d]", svc_ident(svc, NULL, 0), lost);
}
/* Terminate any children in the same proess group, e.g. logit */
kill(-svc->pid, SIGKILL);
@@ -1498,11 +1581,11 @@ void service_monitor(pid_t lost, int status)
done:
/* No longer running, update books. */
svc->start_time = svc->pid = 0;
cont:
if (!service_step(svc)) {
/* Clean out any bootstrap tasks, they've had their time in the sun. */
if (svc_clean_bootstrap(svc))
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
_d("collected bootstrap task %s(%d), removing.", svc_ident(svc, NULL, 0), lost);
}
sm_step(&sm);
@@ -1548,6 +1631,24 @@ static void service_kill_script(svc_t *svc)
kill(-svc->pid, SIGKILL);
}
/*
* Shared env vars for both pre: and post: scripts
*/
static void set_pre_post_envs(svc_t *svc, const char *type)
{
char buf[25 + 256] = "/etc/default:/etc/conf.d";
setenv("SERVICE_TYPE", svc_typestr(svc), 1);
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
setenv("SERVICE_SCRIPT_TYPE", type, 1);
#ifdef FINIT_SYSCONFIG
strlcat(buf, ":", sizeof(buf));
strlcat(buf, FINIT_SYSCONFIG, sizeof(buf));
#endif
setenv("SERVICE_CONF_DIR", buf, 1);
}
static void service_pre_script(svc_t *svc)
{
svc->pid = service_fork(svc);
@@ -1557,14 +1658,22 @@ static void service_pre_script(svc_t *svc)
}
if (svc->pid == 0) {
char buf[CMD_SIZE];
char *argv[4] = {
"sh",
"-c",
svc->pre_script,
"-ac",
buf,
NULL
};
char *env_file;
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
env_file = svc_getenv(svc);
if (env_file)
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->pre_script);
else
strlcpy(buf, svc->pre_script, sizeof(buf));
set_pre_post_envs(svc, "pre");
execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR);
}
@@ -1582,18 +1691,25 @@ static void service_post_script(svc_t *svc)
}
if (svc->pid == 0) {
char buf[CMD_SIZE];
char *argv[4] = {
"sh",
"-c",
svc->post_script,
"-ac",
buf,
NULL
};
char *env_file;
int rc, sig;
rc = WEXITSTATUS(svc->status);
sig = WTERMSIG(svc->status);
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
env_file = svc_getenv(svc);
if (env_file)
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->post_script);
else
strlcpy(buf, svc->post_script, sizeof(buf));
set_pre_post_envs(svc, "post");
if (WIFEXITED(svc->status)) {
char val[4];
@@ -1621,7 +1737,7 @@ static void service_retry(svc_t *svc)
service_timeout_cancel(svc);
if (svc->respawn) {
_d("%s crashed, respawning ...", svc->cmd);
_d("%s crashed/exited, respawning ...", svc_ident(svc, NULL, 0));
svc_unblock(svc);
service_step(svc);
return;
@@ -1629,6 +1745,7 @@ static void service_retry(svc_t *svc)
if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) {
svc_set_state(svc, SVC_RUNNING_STATE);
logit(LOG_CONSOLE | LOG_NOTICE, "Successfully restarted crashing service %s.",
svc_ident(svc, NULL, 0));
return;
@@ -1641,7 +1758,7 @@ static void service_retry(svc_t *svc)
svc_crashing(svc);
*restart_cnt = 0;
if (svc->oncrash_action == SVC_ONCRASH_REBOOT) {
logit(LOG_ERR, "%s issuing reboot", svc->cmd);
logit(LOG_ERR, "%s issuing reboot", svc_ident(svc, NULL, 0));
sync();
kill(1, SIGTERM);
}
@@ -1651,7 +1768,7 @@ static void service_retry(svc_t *svc)
(*restart_cnt)++;
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
_d("%s crashed, trying to start it again, attempt %d", svc_ident(svc, NULL, 0), *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, svc->restart_max);
svc_unblock(svc);
@@ -1673,10 +1790,64 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if (*state == SVC_STOPPING_STATE) {
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
svc->cmd, svc->killdelay / 1000);
svc_ident(svc, NULL, 0), svc->killdelay / 1000);
service_timeout_cancel(svc);
service_timeout_after(svc, svc->killdelay, service_kill);
}
if (svc_is_runtask(svc)) {
char success[MAX_COND_LEN], failure[MAX_COND_LEN];
snprintf(success, sizeof(success), "%s/%s/success", svc_typestr(svc), svc_ident(svc, NULL, 0));
snprintf(failure, sizeof(failure), "%s/%s/failure", svc_typestr(svc), svc_ident(svc, NULL, 0));
/* create success/failure condition when entering SVC_DONE_STATE. */
if (*state == SVC_DONE_STATE) {
if (svc->started)
cond_set_oneshot(success);
else
cond_set_oneshot(failure);
}
/* clear all conditions when entering SVC_HALTED_STATE. */
if (*state == SVC_HALTED_STATE) {
cond_clear(success);
cond_clear(failure);
}
}
if (svc_is_daemon(svc)) {
char cond[MAX_COND_LEN];
snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0));
cond_clear(cond);
switch (svc->state) {
case SVC_HALTED_STATE:
case SVC_RUNNING_STATE:
strlcat(cond, svc_status(svc), sizeof(cond));
cond_set_oneshot(cond);
break;
default:
break;
}
}
}
/*
* Called by pidfile plugin for forking services that have successfully started.
* We get here when the service_monitor() collects the PID, sets svc->pid to 0,
* and the state machine prepares for the worst bu setting state to HALTED.
*/
void service_forked(svc_t *svc)
{
/* Stop forking_retry() timer */
service_timeout_cancel(svc);
/* Not crashing, restore RUNNING state */
svc_unblock(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
}
/*
@@ -1697,7 +1868,7 @@ restart:
old_state = svc->state;
enabled = svc_enabled(svc);
_d("%20s(%4d): %8s %3sabled/%-7s cond:%-4s", svc->cmd, svc->pid,
_d("%20s(%4d): %8s %3sabled/%-7s cond:%-4s", svc_ident(svc, NULL, 0), svc->pid,
svc_status(svc), enabled ? "en" : "dis", svc_dirtystr(svc),
condstr(cond_get_agg(svc->cond)));
@@ -1719,14 +1890,15 @@ restart:
case SVC_STOPPING_STATE:
if (!svc->pid) {
char cond[MAX_COND_LEN];
char condstr[MAX_COND_LEN];
_d("%s: stopped, cleaning up timers and conditions ...", svc->cmd);
_d("%s: stopped, cleaning up timers and conditions ...", svc_ident(svc, NULL, 0));
service_timeout_cancel(svc);
cond_clear(mkcond(svc, cond, sizeof(cond)));
cond_clear(mkcond(svc, condstr, sizeof(condstr)));
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_SYSV:
case SVC_TYPE_TTY:
if (svc_has_post(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE);
@@ -1737,7 +1909,6 @@ restart:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
svc_set_state(svc, SVC_DONE_STATE);
break;
@@ -1789,30 +1960,31 @@ restart:
}
if (!svc->pid) {
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
if (svc_is_daemon(svc) || svc_is_sysv(svc) || svc_is_tty(svc)) {
svc_restarting(svc);
svc_set_state(svc, SVC_HALTED_STATE);
/*
* Restart directly after the first crash,
* then retry after 2 sec
* Restart directly after the first crash, except for forking services
* which we need to wait for the forked-off child to create its pid
* file. In both cases, after that, retry after 2 sec
*/
_d("delayed restart of %s", svc->cmd);
service_timeout_after(svc, 1, service_retry);
if (!svc->respawn)
_d("delayed restart of %s", svc_ident(svc, NULL, 0));
if (svc_is_forking(svc))
service_timeout_after(svc, 2000, service_retry);
else
service_timeout_after(svc, 1, service_retry);
break;
}
if (svc_is_runtask(svc)) {
if (svc_is_sysv(svc)) {
if (!svc->started)
svc_set_state(svc, SVC_STOPPING_STATE);
} else {
svc_set_state(svc, SVC_STOPPING_STATE);
}
if (svc_is_runtask(svc)) { /* only run/task, not sysv here */
svc_set_state(svc, SVC_STOPPING_STATE);
svc->once++;
break;
}
}
service_timeout_cancel(svc);
cond = cond_get_agg(svc->cond);
switch (cond) {
@@ -1886,7 +2058,7 @@ restart:
}
if (svc->state != old_state) {
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
_d("%20s(%4d): -> %8s", svc_ident(svc, NULL, 0), svc->pid, svc_status(svc));
changed++;
goto restart;
}
@@ -1957,15 +2129,15 @@ int service_completed(void)
if (strstr(svc->cond, plugin_hook_str(HOOK_SVC_UP)) ||
strstr(svc->cond, plugin_hook_str(HOOK_SYSTEM_UP))) {
_d("Skipping %s(%s), post-strap hook", svc->desc, svc->cmd);
_d("Skipping %s(%s), post-strap hook", svc->desc, svc_ident(svc, NULL, 0));
continue;
}
if (!svc->once) {
_d("%s has not yet completed ...", svc->cmd);
_d("%s has not yet completed ...", svc_ident(svc, NULL, 0));
return 0;
}
_d("%s has completed ...", svc->cmd);
_d("%s has completed ...", svc_ident(svc, NULL, 0));
}
return 1;
+6 -1
View File
@@ -1,7 +1,7 @@
/* Finit service monitor, task starter and generic API for managing svc_t
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -35,6 +35,11 @@ void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_update_rdeps (void);
int service_timeout_after (svc_t *svc, int timeout, void (*cb)(svc_t *svc));
int service_timeout_cancel (svc_t *svc);
void service_forked (svc_t *svc);
int service_step (svc_t *svc);
void service_step_all (int types);
void service_worker (void *unused);
+117 -15
View File
@@ -1,7 +1,7 @@
/* Signal management - Be conservative with what finit responds to!
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -69,8 +69,14 @@
* Finit defaults to nothing but sets sys/pwr/fail condition.
*/
#include "config.h" /* Generated by configure script */
#include <dirent.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <string.h> /* strerror() */
#include <sched.h>
#include <sys/reboot.h>
#include <sys/wait.h>
#ifdef _LIBITE_LITE
@@ -165,6 +171,30 @@ void mdadm_wait(void);
void unmount_tmpfs(void);
void unmount_regular(void);
static void fs_swapoff(void)
{
struct mntent *mnt;
char cmd[256];
FILE *fp;
if (!whichp("swapoff"))
return;
fp = setmntent(fstab, "r");
if (!fp)
return;
while ((mnt = getmntent(fp))) {
if (strcmp(mnt->mnt_type, MNTTYPE_SWAP))
continue;
snprintf(cmd, sizeof(cmd), "swapoff %s", mnt->mnt_fsname);
run_interactive(cmd, "Disabling swap %s", mnt->mnt_fsname);
}
endmntent(fp);
}
/*
* Kernel threads have no cmdline so fgets() returns NULL for them. We
* also skip "special" processes, e.g. mdadm/mdmon or watchdogd that must
@@ -172,7 +202,7 @@ void unmount_regular(void);
*
* https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
*/
void do_kill(int signo)
void do_iterate_proc(int (*cb)(int, void *), void *data)
{
DIR *dirp;
@@ -191,6 +221,8 @@ void do_kill(int signo)
pid = atoi(d->d_name);
if (!pid)
continue;
if (pid == 1)
continue;
snprintf(file, sizeof(file), "/proc/%s/cmdline", d->d_name);
fp = fopen(file, "r");
@@ -203,7 +235,11 @@ void do_kill(int signo)
else if (file[0] == '@')
_d("Skipping %s ...", &file[1]);
else
kill(pid, signo);
if (cb(pid, data)) {
_d("PID %d is still alive (%s)", pid, file);
fclose(fp);
break;
}
}
fclose(fp);
}
@@ -211,8 +247,61 @@ void do_kill(int signo)
}
}
static int kill_cb(int pid, void *data)
{
int signo = *(int *)data;
kill(pid, signo);
return 0;
}
static int status_cb(int pid, void *data)
{
int *has_proc = (int *)data;
*has_proc = 1;
return 1;
}
/* return value:
* 1 - at least one process remaining
* 0 - no processes remaining
*/
static int do_wait(int secs)
{
const int delay = 250000;
int iterations = secs * 1000 * 1000 / delay;
int has_proc;
do {
do_usleep(delay);
while (waitpid(-1, NULL, WNOHANG) > 0)
;
has_proc = 0;
iterations--;
do_iterate_proc(status_cb, &has_proc);
}
while (has_proc && iterations > 0);
return has_proc;
}
void do_shutdown(shutop_t op)
{
struct sched_param sched_param = { .sched_priority = 99 };
int in_cont = in_container();
int signo = SIGTERM;
if (!in_cont) {
/*
* On a PREEMPT-RT system, Finit must run as the highest prioritized
* RT process to ensure it completes the shutdown sequence.
*/
sched_setscheduler(1, SCHED_RR, &sched_param);
}
if (sdown)
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
@@ -223,9 +312,11 @@ void do_shutdown(shutop_t op)
* 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(1);
do_kill(SIGKILL);
do_iterate_proc(kill_cb, &signo);
if (do_wait(1)) {
signo = SIGKILL;
do_iterate_proc(kill_cb, &signo);
}
/* Exit plugins and API gracefully */
plugin_exit();
@@ -235,6 +326,12 @@ void do_shutdown(shutop_t op)
while (waitpid(-1, NULL, WNOHANG) > 0)
;
if (in_cont) {
if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading);
_exit(0);
}
/* Close all local non-console descriptors */
for (int fd = 3; fd < 128; fd++)
close(fd);
@@ -255,16 +352,21 @@ void do_shutdown(shutop_t op)
/* Unmount any tmpfs before unmounting swap ... */
unmount_tmpfs();
run("swapoff -e -a");
fs_swapoff();
/* ... unmount remaining regular file systems. */
unmount_regular();
/* We sit on / so we must remount it ro, try all the things! */
/*
* We sit on / so we must remount it ro, try all the things!
* See the following links for details:
* - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=339023
* - https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/29187
*/
sync();
run("mount -n -o remount,ro -t dummytype dummydev /");
run("mount -n -o remount,ro dummydev /");
run("mount -n -o remount,ro /");
run("mount -n -o remount,ro -t dummytype dummydev /", NULL);
run("mount -n -o remount,ro dummydev /", NULL);
run("mount -n -o remount,ro /", "mount");
/* Call mdadm to mark any RAID array(s) as clean before halting. */
mdadm_wait();
@@ -394,8 +496,8 @@ static void sigterm_cb(uev_t *w, void *arg, int events)
*/
static void sigchld_cb(uev_t *w, void *arg, int events)
{
pid_t pid;
int status;
pid_t pid;
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
@@ -458,8 +560,8 @@ static void chld_handler(int sig, siginfo_t *info, void *ctx)
void sig_init(void)
{
int i;
struct sigaction sa;
int i;
for (i = 1; i < NSIG; i++)
IGNSIG(sa, i, SA_RESTART);
@@ -472,9 +574,9 @@ void sig_init(void)
*/
void sig_unblock(void)
{
int i;
sigset_t nmask;
struct sigaction sa;
sigset_t nmask;
int i;
sigemptyset(&nmask);
sigaddset(&nmask, SIGHUP);
+1 -1
View File
@@ -1,7 +1,7 @@
/* Signal management - Be conservative with what finit responds to!
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
+5 -3
View File
@@ -1,6 +1,6 @@
/* Reset terminal line settings (stty sane)
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -21,10 +21,12 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <errno.h>
#include <stdlib.h>
#include <unistd.h> /* _POSIX_VDISABLE */
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <unistd.h> /* _POSIX_VDISABLE */
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
#include "helpers.h"
+4 -1
View File
@@ -1,6 +1,6 @@
/* poor man's sulogin, may be statically linked
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2021-2022 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
@@ -193,6 +193,9 @@ int main(void)
nopass:
freepw(&pw);
/* tell initctl/reboot to trigger force mode */
setenv("SULOGIN", "finit", 1);
return sh();
}
}
+36 -55
View File
@@ -1,7 +1,7 @@
/* Low-level service primitives and generic API for managing svc_t structures
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <err.h>
#include <ctype.h> /* isdigit() */
#include <time.h>
@@ -105,16 +107,17 @@ static void svc_gc(void *arg)
/**
* svc_new - Create a new service
* @cmd: External program to call
* @name: Default or custom svc name
* @id: Instance id
* @type: Service type, one of service, task, run
*
* Returns:
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
*/
svc_t *svc_new(char *cmd, char *id, int type)
svc_t *svc_new(char *cmd, char *name, char *id, int type)
{
int job = -1;
svc_t *svc, *iter = NULL;
int job = -1;
/* Find first job n:o if registering multiple instances */
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
@@ -132,6 +135,8 @@ svc_t *svc_new(char *cmd, char *id, int type)
svc->type = type;
svc->job = job;
if (name)
strlcpy(svc->name, name, sizeof(svc->name));
if (id && id[0])
strlcpy(svc->id, id, sizeof(svc->id));
if (cmd)
@@ -256,7 +261,7 @@ svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
char *name = svc->name;
if (!strncmp(name, cmd, strlen(name)))
if (string_compare(name, cmd))
return svc;
}
@@ -346,13 +351,13 @@ svc_t *svc_stop_completed(void)
/**
* svc_find - Find a service object by its full path name
* @cmd: Full path name, e.g., /sbin/syslogd
* @id: Optional instance id
* @name: Full path name, e.g., /sbin/syslogd
* @id: Optional instance id
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find(char *cmd, char *id)
svc_t *svc_find(char *name, char *id)
{
svc_t *svc, *iter = NULL;
@@ -360,7 +365,7 @@ svc_t *svc_find(char *cmd, char *id)
id = "";
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd) && !strcmp(svc->id, id))
if (!strcmp(svc->name, name) && !strcmp(svc->id, id))
return svc;
}
@@ -409,30 +414,6 @@ svc_t *svc_find_by_jobid(int job, char *id)
return NULL;
}
/**
* svc_find_by_nameid - Find an service object by its basename:ID
* @name: Process name to match
* @id: Optional instance id
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_nameid(char *name, char *id)
{
svc_t *svc, *iter = NULL;
if (!id)
id = "";
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->id, id) && !strcmp(name, svc->name))
return svc;
}
return NULL;
}
svc_t *svc_find_by_tty(char *dev)
{
svc_t *svc, *iter = NULL;
@@ -473,23 +454,23 @@ svc_t *svc_find_by_pidfile(char *fn)
pid_t pid;
pid = pid_file_read(fn);
if (pid <= 0) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
int v = 0;
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue;
if (pid > 1) {
svc = svc_find_by_pid(pid);
if (svc)
return svc;
}
return NULL;
}
/*
* No PID in file, or a sysv service. So it has probably forked
* off a chiled that we don't know about. See if we can match
* the PID file to a service instead.
*/
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid != pid)
int v = 0;
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue;
return svc;
@@ -650,7 +631,7 @@ static char *tokstr(char *str, size_t len)
/*
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
*/
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *))
int svc_parse_jobstr(char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *))
{
char *input, *token;
int result = 0;
@@ -686,11 +667,11 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
if (!ptr) {
svc = svc_job_iterator(&iter, 1, job);
if (!svc && not_found)
result += not_found(NULL, token);
result += not_found(NULL, token, user_data);
while (svc) {
if (found)
result += found(svc);
result += found(svc, user_data);
svc = svc_job_iterator(&iter, 0, job);
}
} else {
@@ -699,30 +680,30 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
svc = svc_find_by_jobid(job, id);
if (!svc && not_found)
result += not_found(token, id);
result += not_found(token, id, user_data);
else if (found)
result += found(svc);
result += found(svc, user_data);
}
} else {
if (!ptr) {
svc = svc_named_iterator(&iter, 1, token);
if (!svc && not_found)
result += not_found(token, id);
result += not_found(token, id, user_data);
while (svc) {
if (found)
result += found(svc);
result += found(svc, user_data);
svc = svc_named_iterator(&iter, 0, token);
}
} else {
*ptr++ = 0;
id = ptr;
svc = svc_find_by_nameid(token, id);
svc = svc_find(token, id);
if (!svc && not_found)
result += not_found(token, id);
result += not_found(token, id, user_data);
else if (found)
result += found(svc);
result += found(svc, user_data);
}
}
+35 -7
View File
@@ -1,7 +1,7 @@
/* Low-level service primitives and generic API for managing svc_t structures
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2008-2022 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
@@ -127,6 +127,7 @@ typedef struct svc {
int starting; /* ... waiting for pidfile to be re-asserted */
int runlevels;
int sighup; /* This service supports SIGHUP :) */
int forking; /* This is a service/sysv daemon that forks, wait for it ... */
svc_block_t block; /* Reason that this service is currently stopped */
char cond[MAX_COND_LEN];
@@ -192,14 +193,13 @@ typedef struct svc {
struct timespec gc;
} svc_t;
svc_t *svc_new (char *cmd, char *id, int type);
svc_t *svc_new (char *cmd, char *name, char *id, int type);
int svc_del (svc_t *svc);
void svc_validate (svc_t *svc);
svc_t *svc_find (char *cmd, char *id);
svc_t *svc_find (char *name, char *id);
svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_by_jobid (int job, char *id);
svc_t *svc_find_by_nameid (char *name, char *id);
svc_t *svc_find_by_tty (char *dev);
svc_t *svc_find_by_pidfile (char *fn);
@@ -222,13 +222,13 @@ void svc_prune_bootstrap (void);
void svc_enable (svc_t *svc);
int svc_enabled (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);}
static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; }
static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
@@ -278,6 +278,31 @@ static inline int svc_has_cond(svc_t *svc)
return 0;
}
static inline const char *svc_typestr(svc_t *svc)
{
switch (svc->type) {
case SVC_TYPE_FREE:
return "free";
case SVC_TYPE_SERVICE:
return "service";
case SVC_TYPE_TASK:
return "task";
case SVC_TYPE_RUN:
return "run";
case SVC_TYPE_TTY:
return "tty";
case SVC_TYPE_SYSV:
return "sysv";
}
return "unknown";
}
static inline char *svc_status(svc_t *svc)
{
if (!svc)
@@ -307,7 +332,10 @@ static inline char *svc_status(svc_t *svc)
return "unknown";
case SVC_DONE_STATE:
return "done";
if (svc->started)
return "done";
else
return "failed";
case SVC_STOPPING_STATE:
switch (svc->type) {
+4 -2
View File
@@ -1,7 +1,7 @@
/* Finit TTY handling
*
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2013-2022 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
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isdigit() */
#include <signal.h>
#include <termios.h>
@@ -170,7 +172,7 @@ int tty_parse_args(char *cmd, struct tty *tty)
else
tty->args[tty->num++] = cmd;
cmd = strtok(NULL, " \t");
cmd = strtok(NULL, " ");
} while (cmd && tty->num < NELEMS(tty->args));
/* rescue shells are always notty */
+1 -1
View File
@@ -1,7 +1,7 @@
/* Finit TTY handling
*
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2013-2022 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
+82 -7
View File
@@ -1,6 +1,6 @@
/* Misc. shared utility functions for initctl, reboot and finit
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -21,10 +21,13 @@
* THE SOFTWARE.
*/
#include "config.h"
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isprint() */
#include <errno.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <regex.h>
#include <string.h>
#include <stdlib.h>
@@ -224,7 +227,7 @@ int fngetint(char *path, int *val)
{
char buf[64];
if (fnread(buf, sizeof(buf), path) <= 0)
if (fnread(buf, sizeof(buf), "%s", path) <= 0)
return -1;
errno = 0;
@@ -235,6 +238,40 @@ int fngetint(char *path, int *val)
return 0;
}
/*
* This is a replacement for the non-working reboot(RB_SW_SUSPEND). It
* checks for suspend to RAM support, assuming `mem_sleep` has a sane
* default, e.g. 'deep' or 's2idle'. The latter should be possible to
* select from finit.conf, but for now we go with the kernel default.
* For more information on kernel sleep states, see:
* https://www.kernel.org/doc/html/latest/admin-guide/pm/sleep-states.html
*/
int suspend(void)
{
char buf[80];
char *ptr;
if (fnread(buf, sizeof(buf), "/sys/power/state") <= 0) {
opnotsup:
errno = EINVAL;
return 1;
}
chomp(buf);
ptr = strtok(buf, " ");
while (ptr) {
if (!strcmp(ptr, "mem"))
break;
ptr = strtok(NULL, " ");
}
if (!ptr)
goto opnotsup;
return fnwrite("mem", "/sys/power/state");
}
int strtobytes(char *arg)
{
int mod = 0, bytes;
@@ -264,17 +301,39 @@ int strtobytes(char *arg)
return bytes;
}
char *sig2str(int sig)
char *sig2str(int signo)
{
static char signame[8];
if (sig < 1 || sig >= (int)NELEMS(signames))
if (signo < 1 || signo >= (int)NELEMS(signames))
return "";
snprintf(signame, sizeof(signame), "/%s", signames[sig]);
snprintf(signame, sizeof(signame), "/%s", signames[signo]);
return signame;
}
/**
* str2sig - Translate signal name to the corresponding signal number.
* @sig: The name of the signal
*
* A signal can be a complete signal name such as "SIGHUP", or
* it can be the shortest unique name, such as "HUP" (no SIG prefix).
*/
int str2sig(char *sig)
{
size_t i;
if (!strncasecmp(sig, "SIG", 3))
sig += 3;
for (i = 1; i < NELEMS(signames); ++i) {
if (strcasecmp(sig, signames[i]) == 0)
return i;
}
return -1;
}
char *code2str(int code)
{
static char codename[20];
@@ -297,6 +356,20 @@ void do_sleep(unsigned int sec)
;
}
void do_usleep(unsigned int usec)
{
struct timespec deadline;
clock_gettime(CLOCK_MONOTONIC, &deadline);
deadline.tv_nsec += usec * 1000;
deadline.tv_sec += deadline.tv_nsec / 1000000000;
deadline.tv_nsec = deadline.tv_nsec % 1000000000;
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0 && errno == EINTR)
;
}
/* Seconds since boot, from sysinfo() */
long jiffies(void)
{
@@ -418,7 +491,9 @@ static int hasopt(char *opts, char *opt)
int ismnt(char *file, char *dir, char *mode)
{
struct mntent mount;
struct mntent *mnt;
char buf[256];
int found = 0;
FILE *fp;
@@ -426,7 +501,7 @@ int ismnt(char *file, char *dir, char *mode)
if (!fp)
return 0; /* Dunno, maybe not */
while ((mnt = getmntent(fp))) {
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
if (!strcmp(mnt->mnt_dir, dir)) {
if (mode) {
if (hasopt(mnt->mnt_opts, mode))
+9 -5
View File
@@ -1,6 +1,6 @@
/* Misc. shared utility functions for initctl, reboot and finit
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -54,16 +54,20 @@ extern char *prognm;
char *progname (char *arg0);
char *str (char *fmt, ...);
int fnread (char *buf, size_t len, char *fmt, ...);
int fnwrite (char *value, char *fmt, ...);
char *str (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
int fnwrite (char *value, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
int fngetint (char *path, int *val);
int suspend (void);
int strtobytes (char *arg);
char *sig2str (int sig);
int str2sig (char *sig);
char *code2str (int code);
void do_sleep (unsigned int sec);
void do_usleep (unsigned int usec);
long jiffies (void);
char *uptime (long secs, char *buf, size_t len);
char *memsz (uint64_t sz, char *buf, size_t len);
@@ -83,7 +87,7 @@ int ttcooked (void);
#define ttcooked() 0
#endif
static inline void dbg(char *fmt, ...)
static __attribute__ ((format (printf, 1, 2))) inline void dbg(char *fmt, ...)
{
va_list ap;
+2 -2
View File
@@ -1,6 +1,6 @@
/* UTMP API
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
@@ -21,7 +21,7 @@
* THE SOFTWARE.
*/
#include "config.h"
#include "config.h" /* Generated by configure script */
#include <time.h>
#include <arpa/inet.h>
+1 -1
View File
@@ -1,6 +1,6 @@
/* UTMP API
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* Built-in watchdog daemon
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
+1 -1
View File
@@ -1,6 +1,6 @@
/* Built-in watchdog daemon
*
* Copyright (c) 2016-2021 Joachim Wiberg <troglobit@gmail.com>
* Copyright (c) 2016-2022 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
+37 -4
View File
@@ -1,12 +1,36 @@
EXTRA_DIST = tenv/lib.sh tenv/exec.sh tenv/start.sh
EXTRA_DIST += tenv/root.mk tenv/busybox-x86_64.md5
EXTRA_DIST += tenv/chrootsetup.sh
SUBDIRS = common tenv
EXTRA_DIST = skel/bin/busybox-x86_64.md5 skel/sbin/service.sh skel/bin/slay \
skel/etc/env skel/bin/pre.sh skel/bin/post.sh skel/sbin/task.sh \
skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \
skel/etc/inittab skel/etc/hostname skel/etc/fstab \
skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \
skel/etc/finit.d/.empty skel/etc/init.d/S01-service.sh \
skel/etc/init.d/S02-serv.sh \
skel/proc/.empty skel/root/.empty skel/run/.empty \
skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \
skel/usr/bin/.empty skel/usr/lib/.empty skel/usr/sbin/.empty \
skel/home/.empty skel/usr/games/.empty skel/usr/src/.empty \
skel/usr/share/.empty skel/usr/include/.empty \
skel/usr/local/games/.empty skel/usr/local/sbin/.empty \
skel/usr/local/share/.empty skel/usr/local/src/.empty \
skel/usr/local/lib/.empty skel/usr/local/bin/.empty \
skel/usr/local/etc/.empty skel/var/.empty
EXTRA_DIST += setup-root.sh
EXTRA_DIST += common/service.conf common/service.sh
EXTRA_DIST += add-remove-dynamic-service.sh
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
EXTRA_DIST += cond-start-task.sh
EXTRA_DIST += failing-sysv.sh
EXTRA_DIST += global-envs.sh
EXTRA_DIST += pre-post-serv.sh
EXTRA_DIST += runlevel.sh
EXTRA_DIST += start-stop-service.sh
EXTRA_DIST += start-stop-service-sub-config.sh
EXTRA_DIST += start-kill-service.sh
EXTRA_DIST += start-stop-sysv.sh
EXTRA_DIST += start-stop-serv.sh
EXTRA_DIST += signal-service.sh
AM_TESTS_ENVIRONMENT = TENV_ROOT='$(abs_builddir)/tenv-root/';
AM_TESTS_ENVIRONMENT += export TENV_ROOT;
@@ -24,8 +48,17 @@ TESTS = checkself.sh
TESTS += setup-root.sh
TESTS += add-remove-dynamic-service.sh
TESTS += add-remove-dynamic-service-sub-config.sh
TESTS += cond-start-task.sh
TESTS += failing-sysv.sh
TESTS += global-envs.sh
TESTS += pre-post-serv.sh
TESTS += runlevel.sh
TESTS += start-stop-service.sh
TESTS += start-stop-service-sub-config.sh
TESTS += start-kill-service.sh
TESTS += start-stop-sysv.sh
TESTS += start-stop-serv.sh
TESTS += signal-service.sh
clean-local:
-rm -rf $(builddir)/tenv-root/
+14 -3
View File
@@ -1,5 +1,5 @@
Tests
=====
Finit Test Suite
================
Finit comes with a set of tests and a small framework for running them.
Contributors are encouraged to write new tests when implementing features, or
@@ -10,10 +10,16 @@ be able to be launched as PID 1. Since it's also running with it's own root
directory it will be able to function properly without having any super user
privileges in the host environment.
Running tests
-------------
To run the test suite, first [build](../doc/build.md) Finit - then simply run:
To run the test suite, first [build](../doc/build.md) Finit, e.g:
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
make -j9 clean all
Then run (parallel does not work atm):
make check
@@ -24,3 +30,8 @@ test suite, which is handy when developing new tests or debugging existing
test. To execute an individual test, simply invoke the script containing it:
./test/name-of-the-test.sh
Another way to run a single (or more) test(s) is to define the `TESTS`
environment variable:
TESTS="start-kill-service" make check
@@ -7,20 +7,18 @@ TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_teardown() {
say "Test done $(date)"
say "Running test teardown."
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_RCSD/service.conf"
texec rm -f /test_assets/service.sh
texec rm -f "$FINIT_RCSD/service.conf"
}
say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say "Add a dynamic service in $FINIT_RCSD/service.conf"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf"
texec sh -c "echo 'service [2345] kill:20 log service.sh -- Dyn serv' > $FINIT_RCSD/service.conf"
say 'Reload Finit'
texec sh -c "initctl reload"
+6 -8
View File
@@ -7,20 +7,18 @@ TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_teardown() {
say "Test done $(date)"
say "Running test teardown."
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF"
texec rm -f /test_assets/service.sh
texec rm -f "$FINIT_CONF"
}
say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say "Add a dynamic service in $FINIT_CONF"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF"
texec sh -c "echo 'service [2345] kill:20 log service.sh -- Dyn service' > $FINIT_CONF"
say 'Reload Finit'
texec sh -c "initctl reload"
+5
View File
@@ -0,0 +1,5 @@
/Makefile
/Makefile.in
/.deps/
/serv
+2
View File
@@ -0,0 +1,2 @@
noinst_PROGRAMS = serv
serv_SOURCES = serv.c
+150
View File
@@ -0,0 +1,150 @@
/*
* Basic UNIX daemon
*
* Options to run in foreground and to create a PID file. When running
* in foregrund it does not create a PID file by default.
*/
#include <err.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PROGNM "serv"
static void verify_env(char *arg)
{
char *key, *value;
char *replica;
char *env;
key = replica = strdup(arg);
if (!replica)
err(1, "Failed duplicating arg %s", arg);
value = strchr(replica, ':');
if (!value)
errx(1, "Invalid format of KEY:VALUE arg, missing ':' in %s", replica);
*(value++) = 0;
env = getenv(key);
if (!env)
errx(1, "No '%s' in environment", key);
if (strcmp(env, value))
errx(1, "Mismatch, environment '%s' vs expected value '%s'", env, value);
}
static void verify_noenv(char *key)
{
char *val;
val = getenv(key);
if (val)
errx(1, "Error, key %s is set in environment to '%s'", key, val);
}
static void sig(int signo)
{
warnx("We got signal %d ...", signo);
exit(0);
}
static void pidfile(char *pidfn)
{
char fn[80];
pid_t pid;
FILE *fp;
if (!pidfn) {
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, PROGNM);
pidfn = fn;
}
pid = getpid();
warnx("Creating PID file %s with %d", pidfn, pid);
fp = fopen(pidfn, "w");
if (!fp)
exit(1);
fprintf(fp, "%d\n", pid);
fclose(fp);
}
static int usage(int rc)
{
FILE *fp = rc ? stderr : stdout;
fprintf(fp,
"%s [-nhp] [-P FILE]\n"
"\n"
" -h Show help text (this)\n"
" -e K:V Verify K environment variable is V value\n"
" -E K Verify K is not set in the environment\n"
" -n Run in foreground\n"
" -p Create PID file despite running in foreground\n"
" -P FILE Create PID file using FILE\n"
"\n"
"By default this program daemonizes itself to the background, and,\n"
"when it's done setting up its signal handler(s), creates a PID file\n"
"to let the rest of the system know it's done. When the program runs\n"
"in the foreground it does not create a PID file by default.\n",
PROGNM);
return rc;
}
int main(int argc, char *argv[])
{
int do_background = 1;
int do_pidfile = 1;
char *pidfn = NULL;
int c;
while ((c = getopt(argc, argv, "e:E:hnpP:")) != EOF) {
switch (c) {
case 'h':
return usage(0);
case 'e':
verify_env(optarg);
break;
case 'E':
verify_noenv(optarg);
break;
case 'n':
do_background = 0;
do_pidfile--;
break;
case 'p':
do_pidfile++;
break;
case 'P':
pidfn = optarg;
break;
default:
return usage(1);
}
}
/* Daemonize, fork to background etc. */
if (do_background) {
if (daemon(0, 1))
return 1;
}
/* Signal handlers first *then* PID file */
signal(SIGTERM, sig);
/* Tell world where we are, but not if bg w/o pid file */
if (do_pidfile == 1)
pidfile(pidfn);
warnx("Entering while(1) loop");
while (1)
sleep(1);
warnx("Leaving ...");
return 0;
}
-1
View File
@@ -1 +0,0 @@
service [2345] kill:20 log /test_assets/service.sh
-7
View File
@@ -1,7 +0,0 @@
#!/bin/sh
set -eu
while true; do
sleep 5
done
+91
View File
@@ -0,0 +1,91 @@
#!/bin/sh
# A run/task runs once per runlevel, unless its .conf is touched
# then it will run again on initctl reload. This test verifies
# this behavior, with the added twist of starting the task by
# asserting a condition.
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
TEST_CONF=$FINIT_RCSD/cond.conf
test_setup()
{
say "Test start $(date)"
}
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$TEST_CONF"
}
test_one()
{
cond=$1
stanza=$2
sleep 5
say 'Enable finit debug, try to figure out what is going on ...'
texec sh -c "initctl debug"
say "Add stanza '$stanza' to $TEST_CONF ..."
texec sh -c "echo '$stanza' > $TEST_CONF"
texec sh -c "ls -l /run/ /run/finit/"
say 'Reload Finit'
texec sh -c "initctl ls"
texec sh -c "initctl reload"
texec sh -c "initctl status"
texec sh -c "ps"
date
sleep 2
date
texec sh -c "ls -l /run/ /run/finit/"
say 'Asserting condition'
texec sh -c "initctl cond set $cond"
date
sleep 2
date
texec sh -c "ls -l /run/ /run/finit/"
texec sh -c "initctl status"
texec sh -c "ps"
texec sh -c "initctl status task.sh"
sleep 1
texec sh -c "echo Hej; cat /run/task.state"
assert_file_contains /run/task.state 1
say 'Reload Finit'
texec sh -c "initctl reload"
sleep 1
say 'Ensure task has not run again ...'
assert_file_contains /run/task.state 1
say 'Switch to another runlevel ...'
texec sh -c "initctl runlevel 3"
sleep 1
say 'Ensure task has run again ...'
assert_file_contains /run/task.state 2
say 'Touch task.sh .conf file and reload Finit ...'
texec sh -c "touch $TEST_CONF"
texec sh -c "initctl reload"
sleep 1
# texec sh -c "initctl status task.sh"
assert_file_contains /run/task.state 3
say "Done, drop stanza from $TEST_CONF ..."
texec sh -c "rm $TEST_CONF"
texec sh -c "initctl reload"
}
test_one "hello" "task <usr/hello> task.sh -- Hello task"

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