Compare commits

..
52 Commits
Author SHA1 Message Date
Joachim Wiberg fca5ce57ea Bump version for v4.6 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 07:03:30 +01:00
Joachim Wiberg 53aa195ad5 test: update, pid/B is no longer assert by Finit if notify != pid
As of Finit v4.6 we no longer assert the PID condition for services
declaring themselves as notify != pid.  We replace D with a forking
service to catch any future regressions in the pidfile plugin.

No need to check reload PID of D, it is enough to check PID of C.

Also, reduce the number of retries at startup.  If we haven't gone
up within 10 sec with this tiny config something is really wrong.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 06:09:35 +01:00
Joachim Wiberg e60211168e Fix type:forking regression in 66020f4, caught by tests
Premise, a service declaring itself 'notify:none' should never assert a
pid condition.  However, forking services still need to be supported and
the only way to do that is if they create a pid file.  Hence, instead of
skipping pidfile_update_conds() completely we need to filter the type.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 06:06:58 +01:00
Joachim Wiberg dc3ee9e467 Handle rare warning when registering configuration changes
2023-11-13T04:02:23 [WRN]: conf_change_act():failed registering /etc/finit.conf/ event mask 00000004: Not a directory

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 05:05:07 +01:00
Joachim Wiberg 6918f3b711 Update ChangeLog for v4.6
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:39:08 +01:00
Joachim Wiberg 66020f48ea Do not create PID conditions for services that do not support it
Follow-up to issue #386

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:37:03 +01:00
Joachim Wiberg 47b0d4cccf Reassert both PID and ready conditions when leaving paused state
Follow-up to issue #386

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:35:31 +01:00
Joachim Wiberg 92c2efa655 Minor, use svc_ident() instead of incomplete svc->name in debug logs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:32:30 +01:00
Joachim Wiberg b69aa8a8bd Bump version for 4.6 bug fix release, rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 19:08:40 +01:00
Joachim Wiberg ca90bbfb04 Fix #387: sanity check environment variables before sourcing
This fixes an issue when finding a global environment variable with
spaces in the variable name:

    set COLORTERM=yes

Literally, 'set COLORTERM' was the name of the variable.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 19:07:54 +01:00
Joachim Wiberg 0b5c555c7f Add 'notify:pid' style readiness notifaction and 'readiness none'
This change expands the readiness notification system in Finit with the
native 'pid' style, which will remain the default readiness in Finit 4.x

For systems that want to transition to Finit 5.x early, a global option
to set 'readiness none' in /etc/finit.conf, has been added.  This change
the service default notification mode to 'notify:none', which can also
be set by Finit 4.x ('readiness pid') for select services.

Fixes #386.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 19:07:12 +01:00
Joachim Wiberg 6443995fc9 Fix #385: internal conditions are type oneshot, always active
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 09:04:35 +01:00
Joachim Wiberg 180faf8a40 test: minor, disable debug as final step in function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 09:04:15 +01:00
Joachim Wiberg 0ebdd32f6b Fix #384: reassemble value fragments after wordexp() before setenv()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 08:50:12 +01:00
Joachim Wiberg 60bf858302 test: extend svc-env.sh with more common use-case
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 08:37:05 +01:00
Joachim Wiberg 7c86b40190 Fix #383: configure must expand FINIT_RUNPATH_ before define
This fixes an annying buglet that creeped in just before the v4.5 release
causing none of the internal services to be registered properly:

    conf_save_service():Failed creating ${localstatedir}/run/finit/system/dbus.conf: No such file or directory
    plugin_run_hook():Calling modules-load hook n:o 5 (arg: 0x55c4c8133621) ...
    load():Scanning /etc/modules-load.d for config files ...
    cond_set_oneshot():hook/svc/plugin
    cond_set_oneshot_noupdate():hook/svc/plugin => /run/finit/cond/hook/svc/plugin
    cond_update():hook/svc/plugin
    parse_conf():*** Parsing /etc/finit.conf
    conf_save_service():Failed creating ${localstatedir}/run/finit/system/runparts.conf: No such file or directory

The fix, like most, is simple when you find it.  We must expand $runstatedir
before creating the #define in config.h

For convenience, here's the patch for the generated configure script:

&<---------------------------[cut here]---------------------------
--- a/configure	2023-11-05 23:39:51.907334321 +0100
+++ b/configure	2023-11-05 23:39:58.339299795 +0100
@@ -15352,7 +15352,15 @@
 printf "%s\n" "#define FINIT_EXECPATH_ \"$pkglibexecdir\"" >>confdefs.h

-printf "%s\n" "#define FINIT_RUNPATH_ \"$runstatedir/finit/system\"" >>confdefs.h
+	finit_runpath="$runstatedir/finit/system"
+	finit_runpath=`(
+	    test "x$prefix" = xNONE && prefix="$ac_default_prefix"
+	    test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
+	    eval echo \""$finit_runpath"\"
+	    )`
+
+
+printf "%s\n" "#define FINIT_RUNPATH_ \"$finit_runpath\"" >>confdefs.h

&<---------------------------[cut here]---------------------------
Ensure you include the three empty lines for context at the end!

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-05 23:46:26 +01:00
Joachim Wiberg 4f3f140a09 Disable Bash completion in 'make distcheck'
We cannot rely on the auto-detection of Bash completion dir during 'make
distcheck' because autotools does not use DESTDIR, only --prefix for the
install check, and pkg-config returns a system path.

Also, show detected path in configure summary for debug.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 22:54:42 +01:00
Joachim Wiberg 10c24b1674 Minor, reorder alphabetically
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 22:53:20 +01:00
Joachim Wiberg 2b4f89ecd1 test: cannot use absolute path to initctl (distcheck)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 22:09:54 +01:00
Joachim Wiberg c42a6f2869 Update ChangeLog and bump version for v4.5 GA
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 21:05:10 +01:00
Joachim Wiberg 89bb609a7e Update Filesystem Layout section slightly, tie in with bootmisc.so
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 21:04:02 +01:00
Joachim Wiberg da6298aecd test: wait for runparts to finish
In Finit v4.5 we've moved the start of rc.local and runparts to the
transtion from bootstrap to multi-user, so we must give it time to
finish.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 21:04:02 +01:00
Joachim Wiberg 576318cc29 Install Bash completion by default, auto-detect install path
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 20:47:11 +01:00
Joachim Wiberg 0d7d331ad4 Fix minor resource leak at boot, found by Coverity Scan
Introduced in release cycle, no need to document.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 20:11:48 +01:00
Joachim Wiberg c886ecb5cc Add support for multiple if:!foo,!bar to skip loading of service
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 19:25:10 +01:00
Joachim Wiberg 304a096877 test: wait for runparts to finish
In Finit v4.5 we've moved the start of rc.local and runparts to the
transtion from bootstrap to multi-user, so we must give it time to
finish.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 18:16:24 +01:00
Joachim Wiberg 064c7682fc Minor, do not append errno message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 18:15:34 +01:00
Joachim Wiberg 58239b897b Silence Finit progress output for run/tasks stopping
Sync with Starting message in service_start(), should only be shown for
daemons and sysv services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 18:01:13 +01:00
Joachim Wiberg 5281d82e80 Fix #378: add run/task support for <!> to allow transition from bootstrap
This odd little feature makes it possible to declare run/tasks with a
condition that does not block Finit transitioning from bootstrap to the
next runlevel.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 17:55:39 +01:00
Joachim Wiberg 5ff28c4dc6 system: minor, ignore generated file
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 16:18:11 +01:00
Joachim Wiberg f2218836af Fix #378: warn on console if run/task times out during bootstrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 14:52:26 +01:00
Joachim Wiberg fbb3779d17 Fix bootstrap timeout (120 sec), off by a factor 10 (1200 sec)
Regression in v4.5 release cycle, no changelog notice needed.

Reported as part of issue #378 by Alexander Zangerl.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 13:50:43 +01:00
Joachim Wiberg 68f44f7a97 Fix #377: expand service env:file variables
This change allows us to support constructs like this:

    RUNDIR=/var/run/somesvc
    DAEMON_ARGS=--workdir $RUNDIR --other-args...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 13:32:09 +01:00
Joachim Wiberg 50e587f447 test: new test, verify env:file variable expansion
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 13:31:36 +01:00
Joachim Wiberg fb4c560196 test: shellcheck fixes to new test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 13:31:31 +01:00
Joachim Wiberg 8b5fa5eeaa test: add optional argument to sep() helper function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 13:31:01 +01:00
Joachim Wiberg 88ec21e9cc plugins: change wording, we don't recreate machine-id every boot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 10:14:32 +01:00
Joachim Wiberg 84adec4006 Fix #382: do not clear service conditions if only paused
Introduced back in v4.3-rc2, 82cc10be8, the support for automatic
service conditions have had a weird and unintended behavior.  Any
change in state (see doc/svc-machine.png) caused Finit to clear
out *all* previously acquired service conditions.

However, when moving between RUNNING and PAUSED states, a service
should not have its conditions cleared.  The PAUSED state, seen
also by all conditions moving to FLUX, is only temporary while an
`initctl reload` is processed.  If a service has no changes to be
applied it will move back to RUNNING.

Also, we cannot clear the service conditions because other run/task
or services may depend on it and clearing them would cause Finit to
SIGTERM these processes (since they are no longer eligible to run).

This patch not only adds this pre-condition to `cond_clearn()`, it
also clarifies which state (before or after) the particular code
is interested in.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 01:05:25 +01:00
Joachim Wiberg 19b2b2a279 test: follow-up to 2235fb83
Renamed .conf file for udev/mdev etc. caused 'mdev -df' to start in
tests.  This fix closes that again since none of that is needed in
our small namespaced world.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 01:02:33 +01:00
Joachim Wiberg e4163b7221 test: new test, issue #382
Alexander Zangerl reports that <service/foo/STATE> conditions seem to be
removed when calling `initctl reload`, even though no .conf changes have
been made.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-30 00:58:28 +01:00
Joachim Wiberg eaef302420 Drop doc/bootstrap.md, inaccurate and confusing to users
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-24 23:43:28 +02:00
Joachim Wiberg d5738da307 system: add support for mdevd, preferred over plain mdev
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 13:37:13 +02:00
Joachim Wiberg c9f930c7ad Skip registering service when if:!name matches a named svc_t
The delayed matcehd works when all run/task/services provide their own
unique identity, but for replacements this is sometimes not the case.

Example from system/10-hotplug.conf.in:

    run nowarn conflict:udevd,mdev cgroup.init name:coldplug <service/mdevd/ready> \
	[S] mdevd-coldplug -- Cold plugging system

vs

    run nowarn conflict:udevd,mdevd cgroup.init name:coldplug if:!mdevd <service/mdev/running> \
	[S] @pkglibexecdir@/coldplug -- Cold plugging system

Here they both provide the <run/coldplug/*> conditions, and if the first
is loaded, because we found mdevd, then we should not attempt to load
this one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 13:37:13 +02:00
Joachim Wiberg e2ee372b5d Fix #374: describe automount of /run, /tmp and tmpfiles.d support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 12:18:42 +02:00
Joachim Wiberg 7ff7f93364 Fix #376: describe shell limitations with run/task stanzas
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 12:17:07 +02:00
Joachim Wiberg d5a95760ef Replace hardcoded /run/finit/system with configured path
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 11:49:37 +02:00
Joachim Wiberg 24d1425b74 Disable modprobe plugin by default, udevd and mdev loads modules
The updated 10-hotplug.conf now ensures modules are loaded, so we no
longer need the modprobe.so plugin to be enabled by default.

Also, update the hotplug "plugin" description, it is kept entirely
for backwards compatibility reasons after the plugin was converted
to 10-hotplug.conf.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 11:49:37 +02:00
Joachim Wiberg 43d9e25ba7 Fix #367: add support for mdev's netlink daemon mode
This commit replaces the `mdev -s` call to populate the device tree with
the "new" `mdev -df` daemon mode, introduced in BusyBox 1.31.0, 2019.

In daemon mode mdev listens to kernel uevents, replacing the hotplug use
of mdev.  After creating the netlink socket, 'mdev -df' performs the same
initial scan as 'mdev -s' did.

To perform device (re)discovery, module loading and setup, including any
firmware loading, a coldplug operation is typically required.  This is
done by the new /libexec/finit/coldplug script, by Alexander Zangerl.

Unlike 'mdevadm settle', mdev does not offer any mechanism to detect when
the discovery operation is done: on slower systems the triggering side,
the coldplug script, often completes quite a bit earlier than mdev's
uevent processing.  I.e., depending on <run/coldplug/success> is not an
indicator of all devices having been (re)discovered and fully set up.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-22 11:49:29 +02:00
Joachim Wiberg f27998ae4f Assert <int/container> condition if we detect running in container
Useful both for troubleshooting and for triggering if:<int/container> tasks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-14 08:37:15 +02:00
Joachim Wiberg c8e0b6b9ef Redirect 'log*' output to console when finit.debug is enabled
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-14 08:34:06 +02:00
Joachim Wiberg f7e8fccb2f system: extend final udevadm settle timeout 5 -> 30 sec
On some systems udev may very well take more than five seconds to complete.
Probing modules, loading firmware, etc.  Note, the timeout only sets the
maximum wait time.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-14 08:29:08 +02:00
Joachim Wiberg 7bbccb1213 Silence non-critical failure to remove /run/finit/cond/ at reboot
The directory may hold various custom generated conditions that are not
managed by Finit.  Not being able to remove the directory is not a
critical error.

finit[1]: cond_delpath():Failed removing condition path /var/run/finit/cond/: Directory not empty

Therefore, ignore ENOENT and ENOTEMPTY and log everything/anything else.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-14 08:25:56 +02:00
37 changed files with 680 additions and 259 deletions
+59 -3
View File
@@ -4,7 +4,27 @@ Change Log
All relevant changes are documented in this file.
[4.5][] - 2023-10-04
[4.6][] - 2023-11-13
--------------------
### Changes
- Add support for service `notify:pid` and `readiness none` global
option to change how Finit expects readiness notification, issue #386
### Fixes
- Fix #383: dbus and runparts regression in Finit v4.5. The configure
script must expand `FINIT_RUNPATH_` before defining it in `config.h`
- Fix #384: service environment variables drop everything but the first
argument, e.g., `VAR="foo bar qux"` drops everything but `foo`
- Fix #385: internal conditions, e.g., `<int/bootstrap>` turn into flux
when leaving bootstrap, causing depending services to stop
- Fix #387: global environment variables declared with `set VAR=NAME` do
not drop leading `set `, causing `'set VAR'='NAME'` in env.
- Sanity check environment variables, for services and globally. Ensure
the variable name (key) does not contain spaces, or a leading `set `
[4.5][] - 2023-10-30
--------------------
### Changes
@@ -22,7 +42,7 @@ All relevant changes are documented in this file.
- Improved logging on failure to `execvp()` a run/task/service, now
with `errno`, e.g., "No such file or directory" when the command
is missing from `$PATH`
- Add support for Bash completion to `initctl`, issue #360
- Add Bash completion support for `initctl`, configurable, issue #360
- Handle absolute path to `initctl [enable|disable]`, not supported
- Update `finit.conf(5)` man page with the recommended directory
hierarchy in `/etc/finit.d/`
@@ -43,6 +63,18 @@ All relevant changes are documented in this file.
- Plugins and bundled services: dbus, keventd, watchdogd, and runparts,
are now loaded *after* all services in `/lib/finit/system/`. A new
runtime-only path (for inspection) in `/run/finit/system/` is used
- Redirect `log*` output to console when `finit.debug` is enabled
- Assert `<int/container>` condition if we detect running in container
- Add support for mdev's netlink daemon mode, issue #367
- Add support for mdevd in `10-hotplug.conf`, preferred over plain mdev
- Disable modprobe plugin by default, udevd and mdev/mdevd loads modules
- Update documentation for run/task shell limitations, issue #376
- Update documentation regarding automount of `/run` and `/tmp`
- Update plugin documentation, add section about limited tmpfiles.d(5) support
- Skip registering service when `if:!name` matches a known service.
This allows conditional loading of alternative services, e.g. if udevd
is already loaded we do not need mdevd
- Drop `doc/bootstrap.md`, inaccurate and confusing to users
### Fixes
- Fix #227: believed to have been fixed in v4.3, the root cause was
@@ -64,12 +96,35 @@ All relevant changes are documented in this file.
at bootstrap is now saved in `/run/finit/conf.order` for inspection,
`/run/finit/exec.order` shows the start order of each process
- Fix #372: lost `udevadm` calls due to overloading
- Adjust final `udevadm settle` timeout: 5 -> 30 sec
- Fixed dbus plugin, the function that located `<pidfile> ...` in the
`dbus/system.conf` caused spurious line breaks which led to the
service not being loaded properly
- The `runparts` executor now skips backup files (`foo~`)
- The `runparts` stanza now properly appends ` start` to scripts that
start with `S[0-9]+`. This has been broken for a very long time.
- Fix #377: expand service `env:file` variables, allow constructs like:
RUNDIR=/var/run/somesvc
DAEMON_ARGS=--workdir $RUNDIR --other-args...
- Fix #378: warn on console if run/task times out during bootstrap
- Fix #378: add run/task support for `<!>` to allow transition from
bootstrap to multi-user runlevel even though task has not run yet.
- Fix #382: do not clear `<service/foo/STATE>` conditions on reload.
Introduced back in v4.3-rc2, 82cc10be8, the support for automatic
service conditions have had a weird and unintended behavior. Any
change in state (see `doc/svc-machine.png`) caused Finit to clear
out *all* previously acquired service conditions.
However, when moving between RUNNING and PAUSED states, a service
should not have its conditions cleared. The PAUSED state, seen
also by all conditions moving to FLUX, is only temporary while an
`initctl reload` is processed. If a service has no changes to be
applied it will move back to RUNNING.
Also, we cannot clear the service conditions because other run/task
or services may depend on it and clearing them would cause Finit to
`SIGTERM` these processes (since they are no longer eligible to run).
[4.4][] - 2023-05-15
@@ -1578,7 +1633,8 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.5...HEAD
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.6...HEAD
[4.6]: https://github.com/troglobit/finit/compare/4.5...4.6
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
+8 -3
View File
@@ -10,6 +10,10 @@ if DOC
SUBDIRS += doc
endif
if BASH
bashdir = $(BASH_DIR)
dist_bash_DATA = initctl
endif
# Compat hook
install-dev:
@make -C src install-pkgincludeHEADERS
@@ -30,6 +34,7 @@ release: distcheck
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
--enable-testserv-plugin --enable-x11-common-plugin \
--with-watchdog --with-keventd --with-fstab=/etc/fstab
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
--enable-testserv-plugin --enable-x11-common-plugin \
--with-watchdog --with-keventd --with-fstab=/etc/fstab \
--with-bash-completion-dir=no
+1 -3
View File
@@ -3,7 +3,6 @@
* [Introduction](#introduction)
* [Features](#features)
* [Bootstrap](doc/bootstrap.md#bootstrap)
* [Runlevels](#runlevels)
* [Syntax](doc/config.md#syntax)
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
@@ -391,8 +390,7 @@ Right after the runlevel change when all services have started properly,
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
in [doc/bootstrap.md](doc/bootstrap.md).
in [doc/plugins.md](doc/plugins.md#hooks).
### Limitations
+30 -9
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.5-rc5], [https://github.com/troglobit/finit/issues],
AC_INIT([Finit], [4.6], [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])
@@ -15,10 +15,10 @@ AC_CONFIG_FILES([Makefile
man/Makefile
plugins/Makefile
src/Makefile
system/Makefile
tmpfiles.d/Makefile
system/Makefile system/10-hotplug.conf
test/test.env test/Makefile
test/lib/Makefile test/src/Makefile])
test/lib/Makefile test/src/Makefile
tmpfiles.d/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4])
@@ -94,18 +94,22 @@ AC_ARG_ENABLE(all_plugins,
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([modprobe], [no], [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([hotplug], [yes], [Start udevd or mdev kernel event datamon])
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])
AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon])
# Check for extra arguments or packages
AC_ARG_WITH([bash-completion-dir],
AS_HELP_STRING([--with-bash-completion-dir[=PATH]], [Directory to install Bash completion, default: auto]),
[bash_dir=$withval], [bash_dir=yes])
AC_ARG_WITH(fstab,
AS_HELP_STRING([--with-fstab=FILE], [System default fstab, default $sysconfdir/fstab]),
[fstab=$withval], [fstab=yes])
@@ -196,6 +200,12 @@ AS_IF([test "x$enable_rescue" != "xno"], [
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ##############################################################################
AS_IF([test "x$bash_dir" = "xyes"], [
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
[BASH_DIR="$(pkg-config --variable=completionsdir bash-completion)"],
[BASH_DIR="$datadir/bash-completion/completions"])], [
BASH_DIR="$bash_dir"])
AS_IF([test "x$with_fstab" != "xno"], [
AS_IF([test "x$fstab" = "xyes"], [fstab=$sysconfdir/fstab])
AC_EXPAND_DIR(fstab_path, "$fstab")
@@ -285,20 +295,27 @@ AS_IF([test "x$with_plugin_path" != "xno"], [
AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])])
# Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(BASH, [test "x$bash_dir" != "xno"])
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(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
# Workaround for as-of-yet unreleased runstatedir support, planned for
# autoconf 2.70, which some major distros have backported.
AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run")
AC_SUBST(runstatedir)
# Override default libdir, used for plugins and rescue.conf
pkglibdir=$libdir/finit
AC_SUBST(pkglibexecdir)
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
AC_DEFINE_UNQUOTED(FINIT_EXECPATH_, "$pkglibexecdir", [Finit libexec path])
AC_DEFINE_UNQUOTED(FINIT_RUNPATH_, "/run/finit/system", [Finit runtime dynamic system services])
AC_EXPAND_DIR(finit_runpath, "$runstatedir/finit/system")
AC_DEFINE_UNQUOTED(FINIT_RUNPATH_, "$finit_runpath", [Finit runtime dynamic system services])
AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins")
AC_SUBST(plugin_path)
@@ -321,6 +338,9 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$pkglibdir/sample.conf")
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
AC_EXPAND_DIR(bash_completion_dir, "$BASH_DIR")
AC_SUBST(BASH_DIR, "$bash_completion_dir")
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
AS_IF([test "$heading" = "no"], [heading="<NONE>"])
@@ -357,6 +377,7 @@ Behavior:
Optional features:
Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib
Bash completion.......: $bash_completion_dir
Built-in keventd......: $with_keventd
Built-in sulogin......: $with_sulogin $sulogin
Built-in watchdogd....: $with_watchdog $watchdog
-59
View File
@@ -1,59 +0,0 @@
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
4. Mount `/proc` and `/sys`
5. Check kernel command line for `debug` to figure out log level
6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type
9. Parse `/etc/finit.conf`
10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
means any WDT that requires a kernel module need to be either
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
11. Load all `/etc/finit.d/*.conf` files and set hostname
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
13. Call 1st level hooks, `HOOK_ROOTFS_UP`
14. Mount all file systems listed in `/etc/fstab` and swap, if available.
On mount error `HOOK_MOUNT_ERROR` is called. After mount, regardless
of error, `HOOK_MOUNT_POST` is called
15. Enable SysV init signals
16. Call 2nd level hooks, `HOOK_BASEFS_UP`
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
18. Load kernel params from `/etc/sysctl.d/*.conf`, `/etc/sysctl.conf`
et al. (Supports all locations that SysV init does.), handled by
`procps` plugin
19. Start all 'S' runlevel tasks and services
20. Bring up loopback interface and all `/etc/network/interfaces`, if
the `.conf` setting `network <SCRIPT>` is set, it is called instead
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
(re)loaded and new services, tasks, and blocking run commands are
started. Provided they are allowed in the new runlevel and all of
their conditions, if any, are set.
24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs
In (19) and (23) tasks and services defined in `/etc/finit.conf` and
`/etc/finit.d/*.conf` are started.
Notice the seven hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any
way they please. There are other hook points available, for more on
this, see [plugins.md](plugins.md).
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
USB stick, convert/copy its contents to the system's `/etc/` directory,
well before all 'S' runlevel tasks are started. This could be used with
system images that are created read-only and all configuration is stored
on external media.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
+25
View File
@@ -27,6 +27,16 @@ specified separated by comma. Multiple conditions are logically AND'ed
during evaluation, i.e. all conditions must be satisfied in order for a
service to run.
A special syntax, using a leading `!` in run/task/service conditions,
denote if a:
- service does not support `SIGHUP`
- run/task should not block runlevel changes (i.e., bootstrap)
Finit guarantees by default that all run/tasks run (at least) once
per runlevel. For most tasks this is a good default, for example
checking SSH host keys or loading keymap. However, for conditions
that are unlikely to happen it is not. (See example below.)
### Example
@@ -41,6 +51,19 @@ being created, i.e., the service's default readiness notification.
asserted. I.e., if the Zebra process above stops or restarts, netd
will also stop or restart.
Another example is `dropbear`, it does not support `SIGHUP`, but we can
also see optional sourcing of arguments from an environment file:
service [2345789] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
Finally, the weird "block runlevel changes" example. Here we see what
happens when Finit receives `SIGPWR`, sent from a power daemon like
[powstatd(8)][]. A condition is asserted and a user can set up their
own task to act on it. We do not want this task to block Finit from
moving to the next runlevel after bootstrap, so we set `<!>`:
task [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
Triggering
----------
@@ -260,3 +283,5 @@ Therefore, any plugin that supplies Finit with conditions must ensure
that their state is updated after each reconfiguration. This can be
done by binding to the `HOOK_SVC_RECONF` hook. For an example of how
to do this, see `plugins/pidfile.c`.
[powstatd(8)]: https://manpages.ubuntu.com/manpages/trusty/en/man8/powstatd.8.html
+59 -42
View File
@@ -174,18 +174,22 @@ layout, as specified in the [FHS][]:
|- spool/
`- tmp/
Finit starts (very early) by mounting `/dev`, `/proc/`, and `/sys`,
unless they (i.e., `/dev`) are already mounted. When all plugins and
other, core Finit functions, have been set up, all relevant filesystems
(`PASS > 0`) are checked and mounted from the selected `fstab`, either
Finit starts by mounting the critical file systems `/dev`, `/proc/`, and
`/sys`, unless they are already mounted. When all plugins and other,
core Finit functions, have been set up, all relevant filesystems (where
`PASS > 0`) are checked and mounted from the selected `fstab`, either
the default `/etc/fstab`, or any custom one selected from the command
line, or at build time.
Any file system not listed `fstab` are automatically mounted by Finit,
as listed above, provided their respective mount point exists.
To provide a smooth ride, file system not listed in the given `fstab`,
e.g. `/tmp` and `/run`, are automatically mounted by Finit, as listed
above, provided their respective mount point exists.
With all filesystems mounted, Finit calls `swapon`.
> **Tip:** to see what happens when all filesystems are mounted, have a
> look at the [`bootmisc.so` plugin](plugins.md).
At shutdown, and after having stopped all services and other lingering
processes have been killed, filesystems are unmounted in the reverse
order, and `swapoff` is called.
@@ -202,12 +206,13 @@ order, and `swapoff` is called.
- ` 0`: shutdown
Runlevels are declared per service/run/task/sysv command. Starting in
runlevel S ([bootStrap](bootstrap.md)), usually only for tasks supposed
to run once at boot, and services like `syslogd`, which you need to
start and run throughout the whole time your system is up. Before `S`
is started, however, Finit performs a lot of housekeeping tasks like
mounting all filesystems, calling `fsck` if needed, and making sure the
everything is OK.
runlevel S (bootStrap), usually only for tasks supposed to run once at
boot, and services like `syslogd`, which you need to start and run
throughout the whole time your system is up.
Before `S` is started, however, Finit performs a lot of housekeeping
tasks like mounting all filesystems, calling `fsck` if needed, and
making sure the everything is OK.
task [S] /lib/console-setup/console-setup.sh
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
@@ -351,17 +356,31 @@ most notably systemd and s6, other methods for signaling "readiness"
arrived and daemons were adapted to these new schemes to a larger
extent.
As of Finit v4.4 partial support for systemd and s6 is available. The
native PID file mode of operation is still default, the other's can be
defined per service, like this:
As of Finit v4.4 partial support for systemd and s6 style readiness
notification is available, and the native PID file mode of operation is,
as of Finit v4.6 optional, by default it is still enabled, but this can
be changed in `finit.conf`:
readiness none
This will be made the default in Finit 5.0. In this mode of operation,
every service needs to explicitly declare their readiness notification,
like this:
service notify:pid watchdogd
service notify:systemd foo
service notify:s6 bar
service notify:none qux
The `notify:none` syntax is for completeness in systems which run in
`readiness pid` mode (default). Services declared with `notify:none`
will assert ready condition as soon as Finit has started them, e.g.,
`service/qux/ready`.
To synchronize two services the following condition can be used:
service notify:systemd A
service <service/A/ready> B
service notify:pid watchdogd
service <service/watchdogd/ready> stress-ng --cpu 8
For details on the syntax and options, see below.
@@ -520,7 +539,7 @@ following directives:
> **Note:** not all directives are available in `/etc/finit.d/*.conf`
> and some directives are only available at [bootstrap][], runlevel `S`,
> and some directives are only available at bootstrap, runlevel `S`,
> see [Limitations](#limitations) below for details.
@@ -533,7 +552,7 @@ the contents of that file is used.
Deprecated. We recommend using `/etc/hostname` instead.
> **Note:** only read and executed in runlevel S ([bootstrap][]).
> **Note:** only read and executed in runlevel S (bootstrap).
### Kernel Modules
@@ -551,7 +570,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
> **Note:** only read and executed in runlevel S ([bootstrap][]).
> **Note:** only read and executed in runlevel S (bootstrap).
### Networking
@@ -564,7 +583,7 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
or `/etc/network/interfaces` if you have a system with `ifupdown`, like
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
> **Note:** only read and executed in runlevel S ([bootstrap][]).
> **Note:** only read and executed in runlevel S (bootstrap).
### Alternate finit.d/
@@ -619,7 +638,7 @@ stanzas can share the same rlimits if they are in the same .conf.
**Syntax:** `runlevel <N>`
The system runlevel to go to after [bootstrap][] (S) has completed. `N`
The system runlevel to go to after bootstrap (S) has completed. `N`
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt.
@@ -628,7 +647,7 @@ Finit disables networking in this mode.
*Default:* 2
> **Note:** only read and executed in runlevel S ([bootstrap][]).
> **Note:** only read and executed in runlevel S (bootstrap).
### One-shot Commands (sequence)
@@ -668,11 +687,14 @@ also the `--quiet` and `--batch` options.
One-shot like 'run', but starts in parallel with the next command.
Both `run` and `task` commands are run in a shell, so pipes and
redirects can be freely used:
Both `run` and `task` commands are run in a shell, so basic pipes and
redirects can be used:
task [s] echo "foo" | cat >/tmp/bar
Please note, `;`, `&&`, `||`, and similar are *not supported*. Any
non-trivial constructs are better placed in a separate shell script.
### SysV Init Scripts
@@ -963,9 +985,8 @@ previous stanza can also be written as:
**Syntax:** `runparts [progress] [sysv] <DIR>`
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel
`S`, [bootstrap][].
files in the directory are called, in alphabetic order. The scripts in
this directory are executed at the very end of runlevel `S`.
A common use-case for runparts scripts is to create and enable/disable
services, which Finit will then apply when changing runlevel from S to
@@ -990,10 +1011,9 @@ before continuing. None of them can issue commands to start, stop, or
restart other services. Also, ensure all your services and programs
either terminate or start in the background or you will block Finit.
> **Note:** `runparts` scrips are only read and executed in runlevel S
> ([bootstrap][]). See [hook scripts](plugins.md#hooks) for other ways
> to run scripts at certain points during the complete lifetime of the
> system.
> **Note:** `runparts` scrips are only read and executed in runlevel S.
> See [hook scripts](plugins.md#hooks) for other ways to run scripts at
> certain points during the complete lifetime of the system.
**Recommendations:**
@@ -1106,8 +1126,8 @@ can be omitted to keep the kernel default.
tty [12345] @console noclear vt220
On really bare bones systems, or for board bringup, Finit can give you a
shell prompt as soon as [bootstrap][] is done, without opening any
device node:
shell prompt as soon as bootstrap is done, without opening any device
node:
tty [12345789] notty
@@ -1342,12 +1362,11 @@ Watchdog
When built `--with-watchdog` a separate service is built and installed
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
device node exists, Finit will start it and treat it as the elected
watchdog service to delegate its reboot to. See [bootstrap][] for
details. This delegation is to ensure that the system is rebooted by a
hardware watchdog timer -- on many embedded systems this is crucial to
ensure all circuits on the board are properly reset for the next boot,
in effect ensuring the system works the same after both a power-on and
reboot event.
watchdog service to delegate its reboot to. This delegation is to
ensure that the system is rebooted by a hardware watchdog timer -- on
many embedded systems this is crucial to ensure all circuits on the
board are properly reset for the next boot, in effect ensuring the
system works the same after both a power-on and reboot event.
The delegation is performed at the very last steps of system shutdown,
if reboot has been selected and an elected watchdog is known, first a
@@ -1369,8 +1388,6 @@ device descriptor. If the kernel driver has been built without this,
the only option is to remove `/libexec/finit/watchdogd` or build without
it at configure time.
[bootstrap]: bootstrap.md
keventd
-------
+17 -5
View File
@@ -28,9 +28,16 @@ For your convenience a set of *optional* plugins are available:
system and runs at `HOOK_BASEFS_UP`. The `/var`, `/run`, and `/dev`
file systems must be writable for this plugin to work.
Note: On an embedded system both `/var` and `/run` can be `tmpfs` RAM
disks and `/dev` is usually a `devtmpfs`. This must be defined in the
`/etc/fstab` file and in the Linux kernel config.
This plugin is a wrapper for the [tmpfiles.d(5)][] implementation that
Finit has. Capable but limited: no aging, attributes, or subvolumes.
By default, `/lib/finit/tmpfiles.d` carries all the default .conf
files distributed with Finit. It is read first but can be overridden
by any of the standard tmpfiles.d directories, e.g. `/etc/tmpfiles.d`.
> **Note:** On an embedded system both `/var` and `/run` can be `tmpfs`
> RAM disks and `/dev` is usually a `devtmpfs`. This must be defined
> in the `/etc/fstab` file and in the Linux kernel config.
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
_Optional plugin._
@@ -50,8 +57,11 @@ For your convenience a set of *optional* plugins are available:
customized at build-time using the `--with-hook-scripts-path=PATH`
argument to `configure`.
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
available. Enabled by default.
* *hotplug.so*: Replaced with `/lib/finit/system/10-hotplug.conf`, which
checks for `udevd` and `mdev` at boot. This file can be overridden by a
file in `/etc/finit.d/10-hotplug.conf`.
Enabled by default.
> See the [Services](config.md#services) section in the configuration
> guide for an example how to run `mdevd`, alternative to plain mdev.
@@ -217,3 +227,5 @@ Plugins like `tty.so` extend finit by acting on events, they are called
I/O plugins and are called from the finit main loop when `poll()`
detects an event. See the source code for `plugins/*.c` for more help
and ideas.
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
View File
+3 -3
View File
@@ -143,18 +143,18 @@ static void setup(void *arg)
/* Generate machine id for dbus */
if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
run_interactive("dbus-uuidgen --ensure", "Verifying D-Bus machine UUID");
/*
* Register service with Finit
* Note: dbus drops privs after starting up.
*/
if (pidfn) {
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus pid:!%s %s %s -- %s",
snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus pid:!%s %s %s -- %s",
pidfn, cmd, DBUS_ARGS, DBUS_DESC);
free(pidfn);
} else
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus %s %s -- %s",
snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus %s %s -- %s",
cmd, DBUS_ARGS, DBUS_DESC);
conf_save_service(SVC_TYPE_SERVICE, line, "dbus.conf");
+7 -6
View File
@@ -79,7 +79,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
return;
}
dbg("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
dbg("Found svc %s for %s with pid %d", svc_ident(svc, NULL, 0), fn, svc->pid);
mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
@@ -88,7 +88,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
* systemd style services rely on their respective
* readiness notification. Issue #343
*/
if (!svc->notify)
if (svc->notify == SVC_NOTIFY_PID)
svc_started(svc);
if (!svc_has_pidfile(svc)) {
@@ -120,12 +120,13 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
}
}
cond_set(cond);
if (!svc->notify)
if (svc->notify == SVC_NOTIFY_PID)
cond_set(cond);
if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE)
service_ready(svc);
} else if (mask & IN_DELETE) {
cond_clear(cond);
if (!svc->notify)
if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc);
}
}
@@ -240,7 +241,7 @@ static void pidfile_reconf(void *arg)
if (svc_is_changed(svc) || svc_is_starting(svc))
continue;
if (!svc->notify)
if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc);
mkcond(svc, cond, sizeof(cond));
+2
View File
@@ -19,6 +19,8 @@ if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd
endif
dist_pkglibexec_SCRIPTS = coldplug
finit_pkglibdir = $(pkglibdir)
finit_pkglib_SCRIPTS = rescue.conf sample.conf
Executable
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
# With the daemon mode ('mdev -df') we no longer need the hotplug helper
echo "" > /proc/sys/kernel/hotplug
# Wait for 'mdev -df' to set up its event queue.
sleep 1
# Trigger 'add' event for all devices and subsystems to load all modules
# and any firmware needed by the hardware.
find /sys/devices -name uevent -print | while IFS='' read X; do echo add > $X; done
+10 -2
View File
@@ -222,8 +222,16 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct
static void cond_delpath(const char *path)
{
nftw(path, do_delete, 20, FTW_DEPTH);
if (remove(path) && errno != ENOENT)
err(1, "Failed removing condition path %s", path);
if (remove(path)) {
switch (errno) {
case ENOENT: /* Ignore */
case ENOTEMPTY: /* Only at shutdown and for directories */
break;
default:
err(1, "Failed removing condition path %s", path);
break;
}
}
}
int cond_set_path(const char *path, enum cond_state next)
+50 -9
View File
@@ -445,7 +445,7 @@ void conf_save_exec_order(svc_t *svc, char *cmdline, int result)
free(prepared);
prepared = malloc(++len);
if (!prepared)
return;
goto done;
snprintf(prepared, len, " %-7s %-64s (%s)", svc_typestr(svc), cmdline, svc->desc);
}
} else {
@@ -458,7 +458,7 @@ void conf_save_exec_order(svc_t *svc, char *cmdline, int result)
fprintf(fp, "%s %-7s %-64s (%s)\n", ststr, svc_typestr(svc), cmdline, svc->desc);
}
}
done:
fclose(fp);
}
@@ -474,7 +474,7 @@ void conf_save_service(int type, char *cfg, char *file)
mkpath(FINIT_RUNPATH_, 0755);
snprintf(fn, sizeof(fn), "%s/%s", FINIT_RUNPATH_, file);
if (fexist(fn))
warn("File %s already exists, overwriting.", fn);
warnx("File %s already exists, overwriting.", fn);
fp = fopen(fn, "w");
if (!fp) {
err(1, "Failed creating %s", fn);
@@ -545,6 +545,23 @@ static void parse_env(char *line)
*end-- = 0;
}
/* strip any leading 'set ' */
end = key;
if (!strncmp(key, "set", 3))
end += 3;
/* check key, no spaces allowed */
while (*end && isspace(*end))
end++;
key = end;
while (*end && !isspace(*end))
end++;
if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val);
return; /* invalid key */
}
dbg("Global env '%s'='%s'", key, val);
setenv(key, val, 1);
node = malloc(sizeof(*node));
@@ -656,11 +673,22 @@ void conf_parse_cond(svc_t *svc, char *cond)
return;
}
/* First character must be '!' if SIGHUP is not supported. */
/*
* First character must be '!' if:
* - service: SIGHUP is not supported
* - run/task: Do not block bootstrap
*/
ptr = cond;
if (ptr[i] == '!') {
svc->sighup = 0;
ptr++;
if (svc_is_runtask(svc)) {
/* see service_runtask_clean() */
svc->sighup = 1;
svc->once = 1;
} else {
svc->sighup = 0;
}
}
while (ptr[i] != '>' && ptr[i] != 0)
@@ -947,6 +975,18 @@ static int parse_static(char *line, int is_rcsd)
return 0;
}
/*
* Before Finit 5 the default readiness notification is PID.
* This default can be changed by setting 'readiness none' in
* the file /etc/finit.conf, only read once at bootstrap.
*/
if (BOOTSTRAP && MATCH_CMD(line, "readiness ", x)) {
char *token = strip_line(x);
if (!strcmp(token, "none"))
readiness = SVC_NOTIFY_NONE;
}
if (MATCH_CMD(line, "reboot-delay ", x)) {
syncsec = strtonum(strip_line(x), 0, 60, NULL);
return 0;
@@ -1382,7 +1422,8 @@ static int conf_change_act(char *dir, char *name, uint32_t mask)
struct conf_change *node;
char *rp = NULL;
if (name[0])
/* Check for actual printable characters, sometimes STX */
if (name[0] && name[0] >= ' ')
paste(fn, sizeof(fn), dir, name);
else
strlcpy(fn, dir, sizeof(fn));
@@ -1568,7 +1609,7 @@ int conf_init(uev_ctx_t *ctx)
*/
#ifdef WDT_DEVNODE
if (whichp(FINIT_EXECPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init name:watchdog :finit "
conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init notify:none name:watchdog :finit "
FINIT_EXECPATH_ "/watchdogd -- Finit watchdog daemon", "watchdogd.conf");
}
#endif
@@ -1576,7 +1617,7 @@ int conf_init(uev_ctx_t *ctx)
* Start kernel event daemon as soon as possible, if enabled
*/
if (whichp(FINIT_EXECPATH_ "/keventd"))
conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init "
conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init notify:none "
FINIT_EXECPATH_ "/keventd -- Finit kernel event daemon", "keventd.conf");
dbg("Allow plugins to register early runlevel 1 run/task/services ...");
@@ -1610,7 +1651,7 @@ int conf_init(uev_ctx_t *ctx)
if (runparts_sysv)
strlcat(args, "-s ", sizeof(args));
snprintf(conf, sizeof(conf), "[S] <int/bootstrap> log:console %s %s %s"
snprintf(conf, sizeof(conf), "[S] <int/bootstrap> notify:none log:console %s %s %s"
" -- Calling runparts %s in the background",
_PATH_RUNPARTS, args, runparts, runparts);
conf_save_service(SVC_TYPE_TASK, conf, "runparts.conf");
+5
View File
@@ -69,6 +69,7 @@ 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 */
int syncsec = 0; /* reboot delay */
int readiness = SVC_NOTIFY_PID;
char *finit_conf= NULL;
char *finit_rcsd= NULL;
char *fstab = NULL;
@@ -695,6 +696,10 @@ int main(int argc, char *argv[])
cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
/* Some bootstrap tasks may need to know if we're in a container. */
if (in_container())
cond_set_oneshot("int/container");
/*
* Initialize .conf system and load static /etc/finit.conf then
* tell the world what we used.
+1
View File
@@ -121,6 +121,7 @@ extern int single;
extern int bootstrap;
extern int kerndebug;
extern int syncsec;
extern int readiness;
extern char *fstab;
extern char *sdown;
extern char *network;
+136 -58
View File
@@ -330,14 +330,14 @@ static int lredirect(svc_t *svc)
snprintf(rot, sizeof(rot), "%d:%d", logfile_size_max, logfile_count_max);
snprintf(pid, sizeof(pid), "%d", svc_pid);
execlp("logger", "logger", "-f", svc->log.file, "-b", "-t", tag, "-p", prio, "-I", pid, "-r", rot, NULL);
execlp("logger", "logger", "-f", svc->log.file, "-b", "-t", tag, "-p", prio, debug ? "-s" : "", "-I", pid, "-r", rot, NULL);
} else {
char sz[20], num[3];
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp(_PATH_LOGIT, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
execlp(_PATH_LOGIT, "logit", "-f", svc->log.file, "-n", sz, "-r", num, debug ? "-s" : "", NULL);
}
_exit(1);
}
@@ -351,9 +351,9 @@ static int lredirect(svc_t *svc)
char pid[16];
snprintf(pid, sizeof(pid), "%d", svc_pid);
execlp("logger", "logger", "-t", tag, "-p", prio, "-I", pid, NULL);
execlp("logger", "logger", "-t", tag, "-p", prio, debug ? "-s" : "", "-I", pid, NULL);
} else {
execlp(_PATH_LOGIT, "logit", "-t", tag, "-p", prio, NULL);
execlp(_PATH_LOGIT, "logit", "-t", tag, "-p", prio, debug ? "-s" : "", NULL);
}
_exit(1);
}
@@ -394,11 +394,8 @@ static int redirect(svc_t *svc)
*/
static void source_env(svc_t *svc)
{
char buf[LINE_SIZE];
char *line;
size_t len;
char *buf, *val, *line, *fn;
FILE *fp;
char *fn;
fn = svc_getenv(svc);
if (!fn)
@@ -409,11 +406,19 @@ static void source_env(svc_t *svc)
if (!fp)
return;
buf = alloca(LINE_SIZE);
val = alloca(LINE_SIZE);
if (!buf || !val) {
warn("Failed allocating temporary env buffer");
return;
}
line = buf;
len = sizeof(buf);
while (fgets(line, len, fp)) {
while (fgets(line, LINE_SIZE, fp)) {
char *key = chomp(line);
wordexp_t we = { 0 };
char *value, *end;
size_t i;
/* skip any leading whitespace */
while (isspace(*key))
@@ -466,7 +471,40 @@ static void source_env(svc_t *svc)
*end-- = 0;
}
setenv(key, value, 1);
/* strip any leading 'set ' */
end = key;
if (!strncmp(key, "set", 3))
end += 3;
/* check key, no spaces allowed */
while (*end && isspace(*end))
end++;
key = end;
while (*end && !isspace(*end))
end++;
if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val);
continue; /* invalid key */
}
switch (wordexp(value, &we, 0)) {
case 0:
for (i = 0, *val = 0; i < we.we_wordc; i++) {
if (i > 0)
strlcat(val, " ", LINE_SIZE);
strlcat(val, we.we_wordv[i], LINE_SIZE);
}
setenv(key, val, 1);
wordfree(&we);
break;
case WRDE_NOSPACE:
wordfree(&we);
/* fallthrough */
default:
setenv(key, value, 1);
break;
}
}
fclose(fp);
@@ -679,11 +717,12 @@ static int service_start(svc_t *svc)
wordexp_t we = { 0 };
int rc;
if (svc->notify) {
if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) {
if (client_command(INIT_CMD_NOTIFY_SOCKET)) {
err(1, "%s: failed creating notify socket", svc_ident(svc, NULL, 0));
client_disconnect();
svc->notify = 0; /* does not change parent, restarting may work. */
/* does not change parent, restarting may work. */
svc->notify = SVC_NOTIFY_NONE;
} else {
char val[20];
@@ -708,7 +747,7 @@ static int service_start(svc_t *svc)
if (len == 0)
break;
if (svc->notify) {
if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) {
char *ptr = strstr(arg, "%n");
if (ptr) {
@@ -885,6 +924,19 @@ static void service_kill(svc_t *svc)
print(2, NULL);
}
/* Ensure we don't have any notify socket lingering */
static void service_notify_stop(svc_t *svc)
{
if (svc->notify != SVC_NOTIFY_SYSTEMD && svc->notify != SVC_NOTIFY_S6)
return;
uev_io_stop(&svc->notify_watcher);
if (svc->notify_watcher.fd > 0) {
close(svc->notify_watcher.fd);
svc->notify_watcher.fd = 0;
}
}
/*
* Clean up any lingering state from dead/killed services
*/
@@ -900,12 +952,7 @@ static void service_cleanup(svc_t *svc)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
svc_ident(svc, NULL, 0), fn);
/* Ensure we don't have any notify socket lingering */
if (svc->notify && svc->notify_watcher.fd > 0) {
uev_io_stop(&svc->notify_watcher);
close(svc->notify_watcher.fd);
svc->notify_watcher.fd = 0;
}
service_notify_stop(svc);
/* No longer running, update books. */
if (svc_is_tty(svc) && svc->pid > 1)
@@ -979,7 +1026,11 @@ int service_stop(svc_t *svc)
if (!svc->desc[0])
do_progress = 0;
if (runlevel != 1 && do_progress)
/*
* Skip run/tasks in progress, would otherwise print silly stuff
* like: "Stopping Shutting down" ...
*/
if (runlevel != 1 && do_progress && (svc_is_daemon(svc) || svc_is_sysv(svc)))
print_desc("Stopping ", svc->desc);
if (!svc_is_sysv(svc)) {
@@ -1035,7 +1086,7 @@ int service_stop(svc_t *svc)
}
}
if (runlevel != 1 && do_progress)
if (runlevel != 1 && do_progress && (svc_is_daemon(svc) || svc_is_sysv(svc)))
print_result(rc);
return rc;
@@ -1162,13 +1213,15 @@ static void parse_log(svc_t *svc, char *arg)
}
}
static int parse_notify(char *arg)
static svc_notify_t parse_notify(char *arg)
{
if (!strcmp(arg, "systemd"))
return 1;
return SVC_NOTIFY_SYSTEMD;
if (!strcmp(arg, "s6"))
return 2;
return 0; /* unsupported/disabled */
return SVC_NOTIFY_S6;
if (!strcmp(arg, "pid"))
return SVC_NOTIFY_PID;
return SVC_NOTIFY_NONE; /* unsupported/none */
}
static void parse_env(svc_t *svc, char *env)
@@ -1401,7 +1454,11 @@ static void parse_cmdline_args(svc_t *svc, char *cmd, char **args)
* before they are started. Or restarted, or even SIGHUP'ed, when the
* gateway changes or interfaces come and go. The special case when a
* service is declared with <!> means it does not support SIGHUP but
* must be STOP/START'ed at system reconfiguration.
* must be STOP/START'ed at system reconfiguration. For run/task a <!>
* means Finit can relax its promise to run at least once per runlevel.
* I.e., for run/task conditions that would otherwise block bootstrap:
*
* task [S0123456789] <!sys/pwr/fail> name:pwrfail initctl poweroff -- Power failure, shutting down
*
* Conditions can for example be: pid/NAME:ID for process dependencies,
* net/<IFNAME>/up or net/<IFNAME>/exists. The condition handling is
@@ -1430,6 +1487,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *id = NULL, *env = NULL, *cgroup = NULL;
char *pre_script = NULL, *post_script = NULL;
char *ready_script = NULL, *conflict = NULL;
char ident[MAX_IDENT_LEN];
char *ifstmt = NULL;
char *notify = NULL;
struct tty tty = { 0 };
@@ -1554,8 +1612,16 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
name = parse_name(cmd, name);
if (!id)
strlcpy(ident, name, sizeof(ident));
if (!id) {
id = "";
} else {
strlcat(ident, ":", sizeof(ident));
strlcat(ident, id, sizeof(ident));
}
if (ifstmt && !svc_ifthen(1, ident, ifstmt))
return 0;
levels = conf_parse_runlevels(runlevels);
if (runlevel != INIT_LEVEL && !ISOTHER(levels, INIT_LEVEL)) {
@@ -1720,17 +1786,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else
svc->log.enabled = 0;
}
if (notify) {
int type = parse_notify(notify);
if (type <= 0)
goto disable_notify;
svc->notify = type;
} else if (svc->notify) {
disable_notify:
/* XXX: close existing socket */
svc->notify = 0;
}
if (notify)
svc->notify = parse_notify(notify);
else
svc->notify = readiness;
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
else if (type == SVC_TYPE_TTY)
@@ -2194,6 +2255,7 @@ static void service_retry(svc_t *svc)
static void svc_set_state(svc_t *svc, svc_state_t new_state)
{
svc_state_t *state = (svc_state_t *)&svc->state;
const svc_state_t old_state = svc->state;
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if (new_state == SVC_STOPPING_STATE) {
@@ -2214,7 +2276,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
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 (new_state == SVC_DONE_STATE) {
if (svc->started && !WEXITSTATUS(svc->status))
cond_set_oneshot(success);
else
@@ -2222,7 +2284,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
}
/* clear all conditions when entering SVC_HALTED_STATE. */
if (*state == SVC_HALTED_STATE) {
if (new_state == SVC_HALTED_STATE) {
cond_clear(success);
cond_clear(failure);
}
@@ -2232,9 +2294,14 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
char cond[MAX_COND_LEN];
snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0));
cond_clear(cond);
switch (svc->state) {
if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) ||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
; /* only paused during reload, don't clear conds. */
else
cond_clear(cond);
switch (new_state) {
case SVC_HALTED_STATE:
case SVC_RUNNING_STATE:
strlcat(cond, svc_status(svc), sizeof(cond));
@@ -2328,11 +2395,7 @@ restart:
char condstr[MAX_COND_LEN];
dbg("%s: stopped, cleaning up timers and conditions ...", svc_ident(svc, NULL, 0));
if (svc->notify && svc->notify_watcher.fd > 0) {
uev_io_stop(&svc->notify_watcher);
close(svc->notify_watcher.fd);
svc->notify_watcher.fd = 0;
}
service_notify_stop(svc);
service_timeout_cancel(svc);
cond_clear(mkcond(svc, condstr, sizeof(condstr)));
@@ -2472,7 +2535,7 @@ restart:
case COND_ON:
if (svc_is_changed(svc)) {
if (svc_nohup(svc))
if (svc_nohup(svc) || !svc_is_daemon(svc))
service_stop(svc);
else {
/*
@@ -2486,6 +2549,8 @@ restart:
svc_mark_clean(svc);
}
if (svc->notify == SVC_NOTIFY_NONE)
service_ready(svc);
break;
}
break;
@@ -2510,16 +2575,21 @@ restart:
svc_set_state(svc, SVC_RUNNING_STATE);
/* Reassert condition if we go from waiting and no change */
if (!svc_is_changed(svc)) {
char name[MAX_COND_LEN];
if (svc->notify == SVC_NOTIFY_PID) {
char name[MAX_COND_LEN];
mkcond(svc, name, sizeof(name));
dbg("Reassert condition %s", name);
cond_set_path(cond_path(name), COND_ON);
mkcond(svc, name, sizeof(name));
dbg("Reassert condition %s", name);
cond_set_path(cond_path(name), COND_ON);
}
dbg("Reassert %s ready condition", svc_ident(svc, NULL, 0));
service_ready(svc);
}
break;
case COND_OFF:
dbg("Condition for %s is off, sending SIGCONT + SIGTERM", svc->name);
dbg("Condition for %s is off, sending SIGCONT + SIGTERM", svc_ident(svc, NULL, 0));
kill(svc->pid, SIGCONT);
service_stop(svc);
break;
@@ -2573,7 +2643,12 @@ void service_runtask_clean(void)
if (!svc_is_runtask(svc))
continue;
svc->once = 0;
/* run/task declared with <!> */
if (svc->sighup)
svc->once = 1;
else
svc->once = 0;
if (svc->state == SVC_DONE_STATE)
svc_set_state(svc, SVC_HALTED_STATE);
}
@@ -2581,6 +2656,7 @@ void service_runtask_clean(void)
/**
* service_completed - Have run/task completed in current runlevel
* @svcp: On %FALSE return, this points to the first incomplete svc
*
* This function checks if all run/task have run once in the current
* runlevel. E.g., at bootstrap we must wait for these scripts or
@@ -2593,7 +2669,7 @@ void service_runtask_clean(void)
* Returns:
* %TRUE(1) or %FALSE(0)
*/
int service_completed(void)
int service_completed(svc_t **svcp)
{
svc_t *svc, *iter = NULL;
@@ -2615,6 +2691,8 @@ int service_completed(void)
if (!svc->once) {
dbg("%s has not yet completed ...", svc_ident(svc, NULL, 0));
if (svcp)
*svcp = svc;
return 0;
}
dbg("%s has completed ...", svc_ident(svc, NULL, 0));
@@ -2633,13 +2711,13 @@ void service_notify_reconf(void)
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc->notify)
if (svc->notify == SVC_NOTIFY_PID)
continue; /* managed by pidfile plugin */
if (svc->state != SVC_RUNNING_STATE)
continue;
if (svc_is_changed(svc) || svc_is_starting(svc))
if (svc->notify != SVC_NOTIFY_NONE && (svc_is_changed(svc) || svc_is_starting(svc)))
continue;
service_ready(svc);
@@ -2686,7 +2764,7 @@ void service_notify_cb(uev_t *w, void *arg, int events)
service_ready(svc);
/* s6 applications close their socket after notification */
if (svc->notify == 2) {
if (svc->notify == SVC_NOTIFY_S6) {
uev_io_stop(w);
close(w->fd);
w->fd = 0;
+1 -1
View File
@@ -49,7 +49,7 @@ int service_step (svc_t *svc);
void service_step_all (int types);
void service_worker (void *unused);
int service_completed (void);
int service_completed (svc_t **svc);
void service_notify_reconf (void);
void service_init (uev_ctx_t *ctx);
+15 -7
View File
@@ -52,22 +52,30 @@ sm_t sm;
*/
static void sm_check_bootstrap(void *work)
{
static int cnt = 120 * 10; /* We run with 100ms period */
int level = cfglevel;
static int timeout = 120;
int bootstrap_done;
int level = cfglevel;
svc_t *svc = NULL;
dbg("Step all services ...");
service_step_all(SVC_TYPE_ANY);
if (cnt-- > 0 && !service_completed()) {
dbg("Not all bootstrap run/tasks have completed yet ... %d", cnt);
bootstrap_done = service_completed(&svc);
if (timeout-- > 0 && !bootstrap_done) {
dbg("Not all bootstrap run/tasks have completed yet ... %d", timeout);
schedule_work(work);
return;
}
if (cnt > 0)
if (timeout > 0) {
dbg("All run/task have completed, resuming bootstrap.");
else
} else {
dbg("Timeout, resuming bootstrap.");
if (svc)
print(2, "Timeout waiting for %s to run, resuming bootstrap", svc_ident(svc, NULL, 0));
else
print(2, "Timeout waiting for unknown run/task, resuming bootstrap");
}
dbg("Flushing pending .conf file events ...");
conf_flush_events();
@@ -208,7 +216,7 @@ restart:
service_step_all(SVC_TYPE_ANY);
/* Allow runparts to start */
cond_set("int/bootstrap");
cond_set_oneshot("int/bootstrap");
if (sm->newlevel == -1)
break;
+30 -22
View File
@@ -710,6 +710,7 @@ int svc_conflicts(svc_t *svc)
*/
int svc_ifthen(int is_conf, const char *ident, char *stmt)
{
char stmts[MAX_IDENT_LEN];
int not = 0;
svc_t *svc;
@@ -724,39 +725,46 @@ int svc_ifthen(int is_conf, const char *ident, char *stmt)
if (is_conf)
return 1;
if (stmt[0] == '!') {
stmt++;
not++;
}
while (ptr[i] != '>' && ptr[i] != 0)
i++;
ptr[i] = 0;
cond = cond_get(stmt);
if (not && cond == COND_ON)
return 0;
if (!not && cond == COND_OFF)
return 0;
strlcpy(stmts, stmt, sizeof(stmts));
for (stmt = strtok(stmts, ","); stmt; stmt = strtok(NULL, ","), not = 0) {
if (stmt[0] == '!') {
stmt++;
not++;
}
cond = cond_get(stmt);
if (not && cond == COND_ON)
return 0;
if (!not && cond == COND_OFF)
return 0;
}
return 1;
}
if (!is_conf)
return 1;
if (stmt[0] == '!') {
stmt++;
not++;
}
strlcpy(stmts, stmt, sizeof(stmts));
for (stmt = strtok(stmts, ","); stmt; stmt = strtok(NULL, ","), not = 0) {
if (stmt[0] == '!') {
stmt++;
not++;
}
svc = svc_find_by_str(stmt);
if (not && svc) {
logit(LOG_NOTICE, "skipping %s, %s already loaded.", ident, svc_ident(svc, NULL, 0));
return 0;
}
if (!not && !svc) {
logit(LOG_NOTICE, "skipping %s, %s not available.", ident, stmt);
return 0;
svc = svc_find_by_str(stmt);
if (not && svc) {
logit(LOG_NOTICE, "skipping %s, %s already loaded.", ident, svc_ident(svc, NULL, 0));
return 0;
}
if (!not && !svc) {
logit(LOG_NOTICE, "skipping %s, %s not available.", ident, stmt);
return 0;
}
}
return 1;
+9 -1
View File
@@ -83,6 +83,14 @@ typedef enum {
SVC_ONCRASH_SCRIPT,
} svc_oncrash_action_t;
/* 0: none, 1: systemd, 2: s6 */
typedef enum {
SVC_NOTIFY_NONE = 0,
SVC_NOTIFY_PID,
SVC_NOTIFY_SYSTEMD,
SVC_NOTIFY_S6,
} svc_notify_t;
#define MAX_ID_LEN 16
#define MAX_ARG_LEN 64
#define MAX_CMD_LEN 256
@@ -202,7 +210,7 @@ typedef struct svc {
/*
* Readiness notification socket: systemd, s6
*/
int notify; /* 0: none, 1: systemd, 2: s6 */
svc_notify_t notify;
uev_t notify_watcher; /* i/o watcher */
/* time at svc_del(), used by gc timer */
+2
View File
@@ -1,2 +1,4 @@
Makefile
Makefile.in
# generated
10-hotplug.conf
-17
View File
@@ -1,17 +0,0 @@
# System hotplug can be handled by udev or mdev
# Either we have systemd-udevd or eudev, on the rare occasion we find
# both, we opt for the latter. Remember, you can override this by a
# custom hotplug.conf in /etc/finit.d/ or /etc/finit.d/enabled/.
service nowarn [S12345789] cgroup.system name:udevd pid:udevd log /lib/systemd/systemd-udevd -- Device event managing daemon
service nowarn [S12345789] cgroup.system name:udevd pid:udevd log udevd -- Device event managing daemon
# Wait for udevd to start, then wait a couple of seconds for it to become ready
run nowarn if:udevd cgroup.init :1 [S] <pid/udevd> log udevadm settle -t 0 --
run nowarn if:udevd cgroup.init :2 [S] <pid/udevd> log udevadm control --reload --
run nowarn if:udevd cgroup.init :3 [S] <pid/udevd> log udevadm trigger -c add -t devices -- Requesting device events
run nowarn if:udevd cgroup.init :4 [S] <pid/udevd> log udevadm trigger -c add -t subsystems -- Requesting subsystem events
run nowarn if:udevd cgroup.init :5 [S] <pid/udevd> log udevadm settle -t 5 -- Waiting for udev to finish
# If udevd is not available, try mdev
run nowarn conflict:udevd [S] mdev -s -- Populating device tree
+53
View File
@@ -0,0 +1,53 @@
# System hotplug handled by udev, mdevd, or mdev (preferred order)
#
# Your system usually has either udevd, mdev, or mdevd. They probe the system
# for devices, loading modules and any firmware needed. Since the bootstrap
# often needs to be synchronized carefully, we suggest using your system log
# daemon as the barrier for all other services. E.g.:
#
# service if:udevd <run/udevadm:5/success> [S0123456789] syslogd -F -- System log daemon
# service if:mdev <run/coldplug/success> [S0123456789] syslogd -F -- System log daemon
# service if:mdevd <run/coldplug/success> [S0123456789] syslogd -F -- System log daemon
#
# This provdes a <pid/syslogd> condition that can act as a barrier for all
# other services. Notice the `if:` statement and the condition for starting
# syslogd is only considered if either udevd or mdev (service) is loaded and
# is guaranteed to run after each respective run stanza have completed.
#
# Override this file by copying it to /etc/finit.d/, using the same name, then
# change the contents any way you like, it can even be empty.
# Check for systemd-udevd and eudev, if we find both, we opt for the latter.
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log /lib/systemd/systemd-udevd -- Device event daemon (udev)
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log udevd -D -- Device event daemon (udev)
# Wait for udevd to start, then trigger coldplug events and module loading.
# The last 'settle' call waits for it to finalize processing all uevents.
run nowarn if:udevd cgroup.init :1 [S] <service/udevd/ready> log udevadm settle -t 0 --
run nowarn if:udevd cgroup.init :2 [S] <service/udevd/ready> log udevadm control --reload --
run nowarn if:udevd cgroup.init :3 [S] <service/udevd/ready> log udevadm trigger -c add -t devices -- Requesting device events
run nowarn if:udevd cgroup.init :4 [S] <service/udevd/ready> log udevadm trigger -c add -t subsystems -- Requesting subsystem events
run nowarn if:udevd cgroup.init :5 [S] <service/udevd/ready> log udevadm settle -t 30 -- Waiting for udev to finish
# If udevd is not available, check for mdevd. Unlike plain 'mdev -df', the
# mdevd-coldplug process exits when mdevd has completed. So the condition
# <run/coldplug/success> (same as for plain mdev below) can be relied on as a
# barrier for services that, e.g., need to probe for switchcore ports or PHYs
# that require firmware loading before they show up.
service nowarn conflict:udevd,mdev cgroup.init notify:none if:!udevd \
[S12345789] notify:s6 mdevd -O 4 -D %n -- Device event daemon (mdevd)
run nowarn conflict:udevd,mdev cgroup.init name:coldplug <service/mdevd/ready> \
[S] mdevd-coldplug -- Cold plugging system
# If neither udevd or mdevd are available, fall back to mdev. The new daemon
# mode calls `mdev -s` internally but does not create a PID file when that is
# done, so synchronization with coldplug is fragile.
service nowarn conflict:udevd,mdevd cgroup.init notify:none if:!udevd,!mdevd \
[S12345789] mdev -df -S -- Device event daemon (mdev)
# Trigger a replay of all uevents from boot, before userspace was up and the
# filesystem mounted. Compared to udevd, we don't know when 'mdev -df' has
# finished processing all events, i.e., depending on <run/coldplug/success> is
# not an indicator of all devices having been (re)discovered and fully set up.
run nowarn conflict:udevd,mdevd cgroup.init name:coldplug if:mdev <service/mdev/running> \
[S] @pkglibexecdir@/coldplug -- Cold plugging system
+1 -1
View File
@@ -1,4 +1,4 @@
# Test service, only used for `make check`, not for public use
service [S123456789] name:testserv pid:!/run/testserv.pid \
<boot/testserv> cgroup.system \
<boot/testserv> cgroup.system notify:none \
/sbin/serv -n -p -i testserv -- Test serv daemon
+3 -1
View File
@@ -1,8 +1,10 @@
EXTRA_DIST = 10-hotplug.conf.in
systemdir = $(system_path)
system_DATA =
dist_system_DATA =
if BUILD_HOTPLUG_PLUGIN
dist_system_DATA += 10-hotplug.conf
system_DATA += 10-hotplug.conf
endif
if BUILD_TESTSERV_PLUGIN
+4
View File
@@ -32,6 +32,7 @@ EXTRA_DIST += crashing.sh
EXTRA_DIST += depserv.sh
EXTRA_DIST += devmon.sh
EXTRA_DIST += failing-sysv.sh
EXTRA_DIST += svc-env.sh
EXTRA_DIST += global-envs.sh
EXTRA_DIST += initctl-status-subset.sh
EXTRA_DIST += notify.sh
@@ -53,6 +54,7 @@ EXTRA_DIST += start-stop-sysv.sh
EXTRA_DIST += start-stop-serv.sh
EXTRA_DIST += signal-service.sh
EXTRA_DIST += testserv.sh
EXTRA_DIST += unexpected-restart.sh
AM_TESTS_ENVIRONMENT = SYSROOT='$(abs_builddir)/sysroot/';
AM_TESTS_ENVIRONMENT += export SYSROOT;
@@ -75,6 +77,7 @@ TESTS += crashing.sh
TESTS += depserv.sh
TESTS += devmon.sh
TESTS += failing-sysv.sh
TESTS += svc-env.sh
TESTS += global-envs.sh
TESTS += initctl-status-subset.sh
TESTS += notify.sh
@@ -98,6 +101,7 @@ TESTS += signal-service.sh
if TESTSERV
TESTS += testserv.sh
endif
TESTS += unexpected-restart.sh
check-recursive: setup-chroot
+3
View File
@@ -80,6 +80,9 @@ test_one()
say "Done, drop stanza from $TEST_CONF ..."
run "rm $TEST_CONF"
run "initctl reload"
# Done, disable debug
run "initctl debug"
}
+9 -3
View File
@@ -2,6 +2,11 @@
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck disable=SC2034
BOOTSTRAP="foo=bar\n\
set COLORTERM=yes\n\
b aq=oroszoe\n\
baz=qux"
test_setup()
{
@@ -19,13 +24,14 @@ test_teardown()
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
say "Waiting for runlevel 2"
sleep 2
#run "initctl debug"
say "Add custom envs to $FINIT_CONF ..."
run "echo 'foo=bar' > $FINIT_CONF"
run "echo 'baz=qux' >> $FINIT_CONF"
say "Add service stanza to $FINIT_CONF ..."
run "echo 'service serv -np -e foo:bar -- Verify foo=bar' >> $FINIT_CONF"
#run "cat $FINIT_CONF"
say 'Reload Finit'
run "initctl reload"
+9 -1
View File
@@ -143,7 +143,15 @@ log()
sep()
{
printf "\e[2m――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\e[0m\n"
heading=${1:-}
if [ -n "$heading" ]; then
num=$((72 - ${#heading} - 1))
printf "\e[1m%s " "$heading"
printf -- "―%.0s" $(seq 1 $num)
printf "\e[0m\n"
else
printf "\e[1m――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\e[0m\n"
fi
}
say()
+1
View File
@@ -37,5 +37,6 @@ while true; do
sleep 1
done
sleep 1
texec diff -u /usr/share/runparts/ref.log /tmp/runparts.log
texec cmp /usr/share/runparts/ref.log /tmp/runparts.log || fail "runparts in wrong order"
+3 -3
View File
@@ -12,12 +12,12 @@ cp "$top_builddir/test/src/serv" "$SYSROOT/sbin/"
FINITBIN="$(pwd)/$top_builddir/src/finit" DEST="$SYSROOT" make -f "$srcdir/lib/sysroot.mk"
# Drop plugins we don't need in test, only causes confusing FAIL in logs.
for plugin in tty.so urandom.so rtc.so modprobe.so hotplug.so; do
for plugin in tty.so urandom.so rtc.so modprobe.so; do
find "$SYSROOT" -name $plugin -delete
done
# Drop system .conf files we don't need in test, same as above
for conf in hotplug.conf; do
# shellcheck disable=SC2043
for conf in 10-hotplug.conf; do
find "$SYSROOT" -name $conf -delete
done
+4
View File
@@ -3,3 +3,7 @@
foo=bar
baz=qux
xyzzy=$foo
FOO_ARGS="-i $foo -l $xyzzy endarg"
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
# Ensure service environment variables are sourced and expanded properly.
set -e
TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
run "rm -f $FINIT_CONF"
}
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
sep "/etc/env:"
run "cat /etc/env"
run "echo 'service env:/etc/env serv -np -e xyzzy:bar -e \"FOO_ARGS:-i bar -l bar endarg\" -- serv checks xyzzy=bar' > $FINIT_CONF"
say 'Reload Finit'
run "initctl reload"
retry 'assert_num_children 1 serv'
say "Done, drop service from $FINIT_CONF ..."
run "rm $FINIT_CONF"
run "initctl reload"
+1
View File
@@ -37,6 +37,7 @@ while true; do
sleep 1
done
sleep 1
texec cat /tmp/sysv.log
texec diff -u /usr/share/runparts/sysv.log /tmp/sysv.log
texec cmp /usr/share/runparts/sysv.log /tmp/sysv.log || fail "runparts sysv in wrong order"
+74
View File
@@ -0,0 +1,74 @@
#!/bin/sh
# A service (B) going into flux, 'initctl reload' with no changes to
# activate, should do not cause ripple effects removing any service
# conditions, e.g., <service/B/running>. During reload all services
# are paused while Finit checks for any service3 changes to activate,
# which may mean stopping or restarting existing services.
#
# Issue #382
set -e
#DEBUG=true
TEST_DIR=$(dirname "$0")
test_setup()
{
run "cat >> $FINIT_CONF" <<EOF
service log:stdout notify:pid name:A serv -np -i A -- A (pid)
service log:stdout notify:systemd <pid/A> name:B serv -np -i B -N 0 -- B needs A (systemd)
service log:stdout notify:s6 <service/B/ready> name:C serv -np -i C -N 0 -- C needs B (s6)
service log:stdout notify:none <pid/A> name:D type:forking serv -i D -- D needs A (forking)
task <service/C/ready,service/D/ready> name:allup initctl cond set allup -- Everything is up
EOF
say "Test start $(date)"
}
test_teardown()
{
say "Test done $(date)"
}
pidof()
{
texec initctl -j status "$1" | jq .pid
}
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
sep "$FINIT_CONF"
run "cat $FINIT_CONF"
sep
run "initctl reload"
run "initctl status"
run "initctl cond dump"
sep
run "initctl debug"
say "waiting for primary startup to complete"
retry 'assert_status allup "done"' 10 1
assert_status C "running"
oldpid=$(pidof C)
sep "pre-reload status"
run "initctl status"
run "initctl cond dump"
sep
say "Reload Finit, who gets restarted?"
#run "initctl debug"
run "initctl reload"
sleep 2
sep "post-reload status"
run "initctl status"
run "initctl cond dump"
sep
assert_status A "running"
assert_status B "running"
assert_status C "running"
newpid=$(pidof C)
# shellcheck disable=SC2086
assert "C was not restarted" $oldpid -eq $newpid