Compare commits

...
75 Commits
Author SHA1 Message Date
Joachim Wiberg 185a72bd14 Bump version for v4.11 GA release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 19:29:48 +01:00
Joachim Wiberg cd32c623cf plugins: ignore PID file events for svc_t in setup/teardown/cleanup
An svc_t (service/sysv) that is in setup, teardown, or cleanup state
must be ignored by the pidfile plugin for any events regarding PID
files.  E.g., a setup script for a sysv service may create a PID file
to alert the system that a setup process for the service is running.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 19:17:04 +01:00
Joachim Wiberg ac4b9c2f93 Unblock signals for pre/post/ready/cleanup scripts
The service_fork() function blocks all signals for the new process, it
is up to the callee to call sig_unblock() before calling exec().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 19:10:56 +01:00
Joachim Wiberg 5e9591d0a8 Set correct -rc version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 10:46:16 +01:00
Joachim Wiberg e3bcf9bf16 Update ChangeLog and bump version for v4.11 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 10:28:39 +01:00
Joachim Wiberg 84fd3196b0 doc: clarify what is required for moving from runlevel S to 2
All run/tasks must complete before Finit moves to runlevel 2.  This
critical piece of information has mysteriously been missing from the
documentation since the start.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 10:23:11 +01:00
Joachim Wiberg 994e51e34f Convert **Note:** et al to GitHub Markdown alerts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 10:23:10 +01:00
Joachim Wiberg 0a7814b391 Merge commit from fork
Improve argument handling when launching login
2025-03-27 07:00:54 +01:00
Joachim Wiberg 73f72b39cf Merge pull request #433 from aanderse/patch-1 2025-03-22 19:27:41 +01:00
Aaron Andersen 02e0aa7383 fixup: links in config.md 2025-03-22 18:06:05 +00:00
Joachim Wiberg 6528628b5c getty: improve argument handling when launching login
Add explicit argument separator before username to ensure correct
parsing of the login command regardless of username format.  This
follows best practices for command execution and prevents unexpected
behavior when processing special characters in usernames.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-18 13:18:10 +01:00
Joachim Wiberg 1480657982 Update ChangeLog, serious issues with stopping services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-18 07:27:00 +01:00
Joachim Wiberg 33e378a345 Fix #432: allow services in 'setup' to be stopped
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-18 07:21:44 +01:00
Joachim Wiberg b695b46a9d Revert last part of 91a9c83, fallback SIGKILL when stopping services
When a service is stopped, but does not respond to the default SIGTERM,
a fallback kicks in after 3 seconds (default) to send SIGKILL.  Since
91a9c83 (v3.2-rc2) this has however not worked since the signal was
only sent to the process group.

It is the job of service_monitor(), when it reaps the process leader,
to forward the signal to the process group.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-18 07:18:16 +01:00
Joachim Wiberg 0e61ed727f Add helper function, svc_is_stopped() when stopped by user
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-18 07:17:37 +01:00
Joachim Wiberg e9d60a6c04 Minor, coding style
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-18 07:17:23 +01:00
Joachim Wiberg 5e9edf9deb Update ChangeLog and bump version for 4.11 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-17 18:43:04 +01:00
Joachim Wiberg 8ed2751ae1 Fix pre/post/ready/cleanup script execution if env:file is missing
Don't try to source an optional env:file if it is missing.  Otherwise
execution of pre/post/ready/cleanup script will fail.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-17 18:39:05 +01:00
Joachim Wiberg 7fec66dc09 Simplify, svc_checkenv() already checks if optional
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-17 18:38:31 +01:00
Joachim Wiberg 75fa868a4b Fix various typos found by codepsell
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-17 16:46:28 +01:00
Joachim Wiberg 7b321de339 Update ChangeLog and bump version for v4.10 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-03 03:46:21 +01:00
Joachim Wiberg 1691ebb292 logit(): RFC3164 conformance for /dev/kmsg fallback
RFC3164 section 5.3 states; "a colon and a space character usually
follow the TAG".  This is also the reference format in the kernel:
https://www.kernel.org/doc/Documentation/ABI/testing/dev-kmsg

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-03 03:39:55 +01:00
Joachim Wiberg c371c10455 Update ChangeLog and bump version for 4.10-rc2
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-28 12:11:06 +01:00
Joachim Wiberg a23e152f6b plugins: rtc: follow-up to bc8118d, stop timer at shutdown
Introduced to work-around systems with broken RTCs, it also introduced a
dormant bug in Finit, triggered at shutdown on some systems.  The call
to dlclose() removed the memory to the rtc_timer which libuev later then
referenced.

Fixes #429

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-28 11:17:01 +01:00
Joachim Wiberg 9210f2851e Skip killing initctl on shutdown/reboot
Initiating a shutdown/reboot from `initctl` should not cause `initctl`
to be killed in do_shutdown().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-25 08:37:27 +01:00
Joachim Wiberg 617609cbc5 .github: allow running coverity scan on workflow dispatch
[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 12:35:48 +01:00
Joachim Wiberg b022916758 doc: update svc_t state machine, drop transparent background
The transparent png is completely illegible in dark mode.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 12:34:02 +01:00
Joachim Wiberg 9a357b7aab Fix possible NULL pointer dereference
When a user sets up a TTY without a device, triggering the tty->notty
code path, the tty->dev will be NULL and tty_parse_args() still return
OK result.

Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 10:30:16 +01:00
Joachim Wiberg ffd01b0276 Update ChangeLog and bump version for v4.10-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 05:43:14 +01:00
Joachim Wiberg 2c95f091ee Sync documentation and manual page
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 05:35:00 +01:00
Joachim Wiberg b75b107d6d plugins: urandom: improve entropy gathering in fallback path
Enhance fallback entropy generation by mixing in multiple sources:
runtime statistics, PID, and high-resolution timestamps.

This provides better initialization for the PRNG when neither saved
entropy nor hardware RNG are available during early boot.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:51:04 +01:00
Joachim Wiberg 1f2c1d2109 Minor, staticify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:46:43 +01:00
Joachim Wiberg f328d24d01 Fix possible overflow in return value
Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:29:56 +01:00
Joachim Wiberg 211293a83d Fix possible overflow in return value
Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:28:26 +01:00
Joachim Wiberg 446f5bc8b8 Fix possible memory leak in sourcing of environment file
Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:26:54 +01:00
Joachim Wiberg 0c6f748f2c Fix invalid pointer use in log message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:23:22 +01:00
Joachim Wiberg a677b94ad9 Minor, coding style fixes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:05:55 +01:00
Joachim Wiberg 6552cda605 Fix #427: make sure first found binary is used as external getty
When using Finit with an external getty that supports alternative login
program argument, the tty_parse_args() function did not check if a getty
command had already been registered:

    tty [12345] /sbin/agetty -L -l /bin/login console noclear

This caused Finit to try /bin/login as the getty program.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 04:02:00 +01:00
Joachim Wiberg 3add682be3 Wait for post:script and cleanup:script before returning OK
This change introduces two new states for the big Finit state machine:
runlevel-clean and reload-clean.  Here Finit now waits for any post or
cleanup script to finish before returning OK to the initctl command.

Additionally, the service state machine has been updated to ensure a
run/task/sysv/service calls any post or cleanup script before they are
removed.  A new 'dead' state for svc_t is introduced which any removed
svc_t ends up in now instead of becing collected from 'halted' state.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>

Add cleanup:script support, runs at service removal

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 02:36:34 +01:00
Joachim Wiberg abac31682d Handle UEV_ERROR properly in all libuev callbacks
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 02:34:31 +01:00
Joachim Wiberg cfe915e6d5 Reclassify sysv, more like service than run/task
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-23 02:34:30 +01:00
Joachim Wiberg ecf65ce44b Merge pull request #428 from aanderse/path/sysctl 2025-02-21 04:27:14 +01:00
Aaron Andersen 0d742d6ae8 allow sysctl path to be configured at build time 2025-02-19 15:31:09 -05:00
Joachim Wiberg cf201a43df Add cleanup:script support, runs at service removal
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-17 05:16:28 +01:00
Joachim Wiberg 9245869e18 Update ChangeLog and bump version for v4.9 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-15 07:09:58 +01:00
Joachim Wiberg 0f4a487328 .github: tests share same sysroot, must run in sequence
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-14 10:28:16 +01:00
Joachim Wiberg f1b5b42919 test: enable debug logs for global-envs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-14 10:25:30 +01:00
Joachim Wiberg f546129baf test: debug sysvparts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-14 10:25:30 +01:00
Joachim Wiberg 27451ac3f2 Update ChangeLog and bump version for v4.9-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-14 06:26:27 +01:00
Joachim Wiberg 0a8f3a6250 Fix #425: flush .conf file events before reload and runlevel change
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-13 07:06:13 +01:00
Joachim Wiberg 70c4e7d774 iwatch: ensure adding a new watcher is idempotent
Some subsystems call iwatch_add() without first calling iwatch_del() on
the same path.  E.g., cgroup_config().

Issue #417 but unclear atm. if this is the root cause.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-13 05:53:24 +01:00
Joachim Wiberg 5265eee25d test: slightly more robust checkself.sh
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 10:17:35 +01:00
Joachim Wiberg 4b06e1a49f Silence overly verbose debug messages in cond_set/clear/update
These functions call other functions that log more detailed information.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 10:17:35 +01:00
Joachim Wiberg 6b9aa21509 Minor cleanup, code (style) and comments
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 10:17:35 +01:00
Joachim Wiberg 990307fbc9 Fix kill() on timeout of pre:/post:/ready:scripts
A long running pre/post/ready script must be killed properly, not by
targeting its process group.  Process group cleanup is handled by the
service_monitor() when reaping the script's PID.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 10:17:34 +01:00
Joachim Wiberg 9d8a9b7fba Check for pre:- and post:scripts in $PATH
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 10:17:34 +01:00
Joachim Wiberg bbc83eaa64 test: new test
New test adds /bin/fail.sh to verify that a failing pre:script (that
also takes too long to run) is detected: exit code and timeout.

Ensure existing test pass full path to /sbin/fail.sh script.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 10:17:33 +01:00
Joachim Wiberg 23d034f521 Prevent main process from starting if pre: script fails
Check exit status of `pre:` scripts, on failure drive service/sysv to
`crashed` state.  The exit code of `post:` scripts remain ignored for
now.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-11 09:39:40 +01:00
Joachim Wiberg 9a3148a49d Bump version to v4.9-beta1 and update ChangeLog
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-10 10:43:38 +01:00
Joachim Wiberg 8560103a24 Add individual timeout support for pre/post/ready scripts
Syntax:
    [pre|post|ready]:[0-3600,]/path/to/script

Description:
    Before this patch all pre/post/ready scripts used the global kill
    delay as timeout.  After this patch it is possible to disable the
    timeout as well as set a timeout >60, which is max kill delay.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-10 10:43:37 +01:00
Joachim Wiberg 7a75e34808 Fix initctl touch of template services
Follow-up to 465bc17, which addressed unintended restart of siblings.
This patch fixes a problem where template instantiated services are not
properly reloaded/restarted when marked as "dirty" with `initctl touch`.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-10 10:43:37 +01:00
Joachim Wiberg 4d4fc10d57 plugins: rtc: follow-up to bc8118d
In save() RTC, ensure the RTC_FILE option is actually enabled.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-10 10:43:29 +01:00
Joachim Wiberg 003faae9e9 .github: disable apparmarmor to allow testing in unshare
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-03 10:23:45 +01:00
Joachim Wiberg 01a01d65c0 Merge pull request #424 from aanderse/container/systemd-nspawn
Add systemd-nspawn to the list of container types

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-02-03 07:13:33 +01:00
Aaron Andersen 048302f06a add systemd-nspawn to the list of container types 2025-02-02 08:58:36 -05:00
Joachim Wiberg 74f0fcfccd Merge pull request #422 from az143/fstab-nofail 2024-12-20 02:18:09 +01:00
az 3cfe0a33b8 fstab with UUID/LABEL: nofail handling for fsck
add support for 'nofail' fstab option. if present, finit's fsck invocation ignores errors for this device.

this is useful if you have an fstab entry for a device with UUID= or LABEL= which isn't always present and which you'd like to not
bail on (so you set nofail). in this case finit leaves the presence-or-not decision to fsck, which exits nonzero.

for block devices that are directly listed in fstab this change isn't important, because for such finit looks for the blockdev's existence and skips the fsck if n/a.
2024-12-20 09:09:24 +10:00
Joachim Wiberg 465bc17ca4 Fix unintended restart of template siblings
Consider the case where container@.conf is an available template.  When
creating a container@foo.conf it will share the same base .conf as an
existing container@bar.conf, but we do not expect to restart bar just
because foo is instantiated.

Up until this change, all template siblings were considered "dirty" if a
new one was created or updated.  Skipping realpath() for all files that
have a '@' works around the problem.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-11-28 11:13:44 +01:00
Joachim Wiberg d4889b435d Merge pull request #419 from troglobit/rtc
RTC plugin fixes and extension

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-11-04 10:48:26 +01:00
Joachim Wiberg 49c0557ced plugins: reduce log level LOG_ERR -> LOG_WARNING
These plugins signal success and failure directly to the console, the
user should inspect syslog for more information.

This change is a follow-up to 340cae4, where kernel logs of LOG_ERR and
higher are allowed to log directly to the console.  Since syslogd has
not been started before these plugins, the log messages would otherwise
leak to the console.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-11-04 10:42:45 +01:00
Joachim Wiberg 6be16f2f6d Fix buggy --with-rtc-date=DATE, introduced in Finit v4.4
In 42ef3d3c, for v4.4-rc1, support for setting a custom RTC restore date
was introduced.  Unfortunately the configure script was wrong and caused
config.h to contain

    #define RTC_TIMESTAMP_CUSTOM "$rtc_date"

instead of

    #define RTC_TIMESTAMP_CUSTOM "2023-04-10 14:35:42"

Furthermore, the error handling for strptime() was wrong, so the restore
date was always reverted to the default.

This patch fixes both issues and extends the DATE of --with-rtc-date to
also include seconds.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-11-04 10:42:45 +01:00
Joachim Wiberg bc8118d515 Fix #418: support systems with a broken RTC
This patch introduces a new configure option --with-rtc-file=FILE.  When
enabled the RTC plugin detects missing RTC device and falls back to save
and restore system time from a file instead.  When --with-rtc-file is
used without an argument the default file is /var/lib/misc/rtc, but the
feature itself is disabled by default.

The usefulness of this feature may not be obvious at first, but some
systems are equipped with an RTC that resets to a random date at power
on.  This can be really bad in the case the date is far in the future,
because an NTP sync would then cause time skips backwards, which shows
up in logs and causes a whole lot of pain in alarm systems.

The solution is to disable the RTC driver or device tree node, and when
Finit starts up, the RTC plugin detects a the device node and instead
restores time from the last save game.  Meaning time will always only
move forwards.

NOTE: when Finit is built --with-rtc-file we always save to disk, but
      only restore from the "save game" if restoring from RTC fails.
      If the system has no RTC we always restore from disk.

      As an added bonus, this change also makes sure to periodically
      sync also the RTC with the system clock.  Useful for systems
      that do not run an NTP client.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-11-04 10:40:35 +01:00
Joachim Wiberg 0c0e880f3f plugins: refactor rtc.so
Factor out time_set() and time_get() for readability and reuse.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-11-03 09:45:27 +01:00
Joachim Wiberg 119e66a7e9 Reset color attributes and clear screen when starting up
Some boot loaders, like GRUB, leave background color artifacts from
their boot menu.  This patch resets the foreground and background
color attributes, and then clears the screen, without clearing the
scrollback buffer.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-28 10:58:04 +01:00
Joachim Wiberg 46ffa81f5c Only mark rdeps dirty if main service is nohup
This patch changes a behavior that's been default since Finit 4.0,
introduced in 4d05bf9 with 4.0-rc2.

If service B depends on A and A needs to be reloaded, then B may be
affected.  If A is declared as NOHUP <!>, then A will be stopped and
restarted, during which time the condition it provides is removed,
and B will also be stopped.

However, and as of this patch, if A is declared supporting HUP, then the
condition A provides will only go into flux, during which time B will be
SIGSTOPed instead of needing to be reloaded.

Fix #415

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-17 14:33:30 +02:00
56 changed files with 1204 additions and 488 deletions
+2 -1
View File
@@ -24,7 +24,8 @@ the maintainer(s) and make it easier for them to include your code.
Coding Style Coding Style
------------ ------------
> **Tip:** Always submit code that follows the style of surrounding code! > [!TIP]
> Always submit code that follows the style of surrounding code!
First of all, lines are allowed to be longer than 72 characters these First of all, lines are allowed to be longer than 72 characters these
days. In fact, there exist no enforced maximum, but keeping it around days. In fact, there exist no enforced maximum, but keeping it around
+6 -4
View File
@@ -21,7 +21,6 @@ jobs:
compiler: [gcc, clang] compiler: [gcc, clang]
fail-fast: false fail-fast: false
env: env:
MAKEFLAGS: -j3
CC: ${{ matrix.compiler }} CC: ${{ matrix.compiler }}
steps: steps:
- name: Install dependencies - name: Install dependencies
@@ -33,14 +32,14 @@ jobs:
run: | run: |
./autogen.sh ./autogen.sh
./configure --prefix= --enable-static ./configure --prefix= --enable-static
make V=1 make -j3 V=1
- name: Regular Finit - name: Regular Finit
run: | run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \ --enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-keventd
make clean make -j3 clean
make V=1 make -j3 V=1
- name: Install to /tmp - name: Install to /tmp
run: | run: |
DESTDIR=/tmp make install-strip DESTDIR=/tmp make install-strip
@@ -57,6 +56,9 @@ jobs:
run: | run: |
sudo /tmp/sbin/finit -h sudo /tmp/sbin/finit -h
sudo /tmp/sbin/initctl -h sudo /tmp/sbin/initctl -h
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Run Unit Tests - name: Run Unit Tests
run: | run: |
make check || (cat test/test-suite.log; false) make check || (cat test/test-suite.log; false)
+1
View File
@@ -4,6 +4,7 @@ on:
push: push:
branches: branches:
- 'dev' - 'dev'
workflow_dispatch:
env: env:
PROJECT_NAME: finit PROJECT_NAME: finit
+3
View File
@@ -39,6 +39,9 @@ jobs:
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \ --enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-keventd
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Build release ... - name: Build release ...
run: | run: |
make release || (cat test/test-suite.log; false) make release || (cat test/test-suite.log; false)
+3
View File
@@ -41,6 +41,9 @@ jobs:
- name: Install to /tmp - name: Install to /tmp
run: | run: |
DESTDIR=/tmp make install-strip DESTDIR=/tmp make install-strip
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Check dist tarball - name: Check dist tarball
run: | run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}') ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
+121 -38
View File
@@ -3,6 +3,86 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.11][] - 2025-03-27
---------------------
### Changes
- Update documentation regarding transition from runlevel S to 2, how
run/tasks must "complete" before Finit can move to the next runlevel
- Improve argument handling in bundled getty when launching login
### Fixes
- Fix #432: allow services in `setup` state to be stopped. This fixes
a serious blocker introduced in 4.10 that prevented systems from
being rebooted when any service was in this state
- Fix fallback `SIGKILL` when stopping a service with `SIGTERM` does
not work. This fixes a serious issue with services not responding to
the default `SIGTERM`
- Fix pre/post/ready/cleanup script execution if service has an
optional `env:file` (leading `-`)
- Fix blocked signals for pre/post/ready/cleanup scripts preventing
normal shutdown when a service/sysv is stopped in its setup state
- Fix issue where a pre:script creates a PID file in `/run` cause the
service to move from setup state to running. Any `svc_t` in setup,
teardown, or cleanup state should be ignored in the pidfile plugin
[4.10][] - 2025-03-03
---------------------
### Changes
- Add support for `cleanup:[0-3600,]script`, runs before service is
removed, unlike `post:script` that runs when service is stopped
- Improved entropy gathering in tertiary fallback of urandom plugin
- Reclassify `sysv` from run/task class to `service`
### Fixes
- Fix #427: using an external getty with custom login program cause
Finit to use login program as the getty command
- Fix #428: allow `sysctl` path to be set at build time
- Fix #429: regression in v4.9 causing Finit to segfault at shutdown
- Fix possible memory leak when sourcing a service `env:file`
- Finit now waits until all `post:scripts` have run before returning
OK to `initctl <reload|runlevel>`. This also applies to the new
`cleanup:scripts` introduced in this release
- RFC3154 conformance formatting fix for `/dev/kmsg` fallback logging
[4.9][] - 2025-02-15
--------------------
> [!CAUTION]
> This release changes how Finit reacts to the exit status of `pre:`
> scripts. Finit will now no longer start the main process if its
> `pre:` script fails for any reason. So, if you have pre scripts that,
> e.g., create directories, make sure they are idempotent.
### Changes
- Add individual timeout (optional) support for pre/post/ready scripts
- Add support for systems with broken RTC, i.e., those that at power-on
may reset the RTC to a random date instead of zero, issue #418
- Add support for detecting "in-container" when in `systemd-nspawn`
- Check exit status of `pre:` scripts, on failure drive service/sysv to
`crashed` state. (The exit code of `post:` scripts remain ignored)
- All pre/post/ready scripts are now also searched for in `$PATH`
- Reset color attributes and clear screen when starting up. This is
for boot loaders like GRUB, which leave background color artifacts
### Fixes
- Fix #415: only mark reverse-dependencies "dirty" if the main service
does not support SIGHUP. This helps avoid unnecessary restarts of
services that depend on a service that supports SIGHUP
- Fix #417: leaking inotify watchers in the cgroup subsystem
- Fix #421: add support for `nofail` mount option in `/etc/fstab`
- Fix #425: ordering bug in detecting .conf file changes to services
and `initctl reload`, or runlevel changes
- Fix `initctl touch` of template services, previously marking a
service created from a template as "dirty" did not take
- Fix unintended restart of template "siblings". I.e., `initctl touch`
of instantiated template service A affected B from same template
- Fix buggy `--with-rtc-date=DATE` configure option
- Fix kill on timeout of pre/post/ready scripts
[4.8][] - 2024-10-13 [4.8][] - 2024-10-13
-------------------- --------------------
@@ -32,7 +112,7 @@ All relevant changes are documented in this file.
### Fixes ### Fixes
- Fix #397: system shutdown/reboot can block on console input if action - Fix #397: system shutdown/reboot can block on console input if action
is started remotely (ssh). Caused by legact TTY screen size probing, is started remotely (ssh). Caused by legacy TTY screen size probing,
removed from both bootstrap and shutdown/reboot removed from both bootstrap and shutdown/reboot
- Fix #400: both `HOOK_MOUNT_ERROR` and `sulogin()` fail to trigger on - Fix #400: both `HOOK_MOUNT_ERROR` and `sulogin()` fail to trigger on
either mount or `fsck` errors. Problem caused by unresolved status either mount or `fsck` errors. Problem caused by unresolved status
@@ -290,7 +370,7 @@ All relevant changes are documented in this file.
`/etc/finit.conf`. `/etc/finit.conf`.
For the complete experience a new top-level configuration file directive For the complete experience a new top-level configuration file directive
`rcsd PATH` has aslo been added. It in turn can be used by `factory.conf` `rcsd PATH` has also been added. It in turn can be used by `factory.conf`
as follows to override `/etc/finit.d`: as follows to override `/etc/finit.d`:
rcsd /etc/factory.d rcsd /etc/factory.d
@@ -873,7 +953,7 @@ make sure to read the whole change log when upgrading.
previously hard-coded in Finit, e.g., starting the bundled watchdogd, previously hard-coded in Finit, e.g., starting the bundled watchdogd,
various distro packed udevd and other hotplugging tools various distro packed udevd and other hotplugging tools
* A proper rescue mode has been added. It is started extremely early * A proper rescue mode has been added. It is started extremely early
and is protected with a bundled `suslogin`. Exiting rescue mode now and is protected with a bundled `sulogin`. Exiting rescue mode now
brings up the system as a normal boot, as one expects brings up the system as a normal boot, as one expects
* Support for `sysv` start/stop scripts as well as monitoring forking * Support for `sysv` start/stop scripts as well as monitoring forking
services, stared using `sysv` or `service` stanza services, stared using `sysv` or `service` stanza
@@ -1704,43 +1784,46 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.8...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.11...HEAD
[4.8]: https://github.com/troglobit/finit/compare/4.7...4.8 [4.11]: https://github.com/troglobit/finit/compare/4.10...4.11
[4.7]: https://github.com/troglobit/finit/compare/4.6...4.7 [4.10]: https://github.com/troglobit/finit/compare/4.9...4.10
[4.6]: https://github.com/troglobit/finit/compare/4.5...4.6 [4.9]: https://github.com/troglobit/finit/compare/4.8...4.9
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5 [4.8]: https://github.com/troglobit/finit/compare/4.7...4.8
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4 [4.7]: https://github.com/troglobit/finit/compare/4.6...4.7
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3 [4.6]: https://github.com/troglobit/finit/compare/4.5...4.6
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2 [4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1 [4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0 [4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1 [4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0 [4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4 [4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3 [3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[2.2]: https://github.com/troglobit/finit/compare/2.1...2.2 [3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
[2.1]: https://github.com/troglobit/finit/compare/2.0...2.1 [2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
[2.0]: https://github.com/troglobit/finit/compare/1.12...2.0 [2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
[2.2]: https://github.com/troglobit/finit/compare/2.1...2.2
[2.1]: https://github.com/troglobit/finit/compare/2.0...2.1
[2.0]: https://github.com/troglobit/finit/compare/1.12...2.0
[1.12]: https://github.com/troglobit/finit/compare/1.11...1.12 [1.12]: https://github.com/troglobit/finit/compare/1.11...1.12
[1.11]: https://github.com/troglobit/finit/compare/1.10...1.11 [1.11]: https://github.com/troglobit/finit/compare/1.10...1.11
[1.10]: https://github.com/troglobit/finit/compare/1.9...1.10 [1.10]: https://github.com/troglobit/finit/compare/1.9...1.10
[1.9]: https://github.com/troglobit/finit/compare/1.8...1.9 [1.9]: https://github.com/troglobit/finit/compare/1.8...1.9
[1.8]: https://github.com/troglobit/finit/compare/1.7...1.8 [1.8]: https://github.com/troglobit/finit/compare/1.7...1.8
[1.7]: https://github.com/troglobit/finit/compare/1.6...1.7 [1.7]: https://github.com/troglobit/finit/compare/1.6...1.7
[1.6]: https://github.com/troglobit/finit/compare/1.5...1.6 [1.6]: https://github.com/troglobit/finit/compare/1.5...1.6
[1.5]: https://github.com/troglobit/finit/compare/1.4...1.5 [1.5]: https://github.com/troglobit/finit/compare/1.4...1.5
[1.4]: https://github.com/troglobit/finit/compare/1.3...1.4 [1.4]: https://github.com/troglobit/finit/compare/1.3...1.4
[1.3]: https://github.com/troglobit/finit/compare/1.2...1.3 [1.3]: https://github.com/troglobit/finit/compare/1.2...1.3
[1.2]: https://github.com/troglobit/finit/compare/1.1...1.2 [1.2]: https://github.com/troglobit/finit/compare/1.1...1.2
[1.1]: https://github.com/troglobit/finit/compare/1.0...1.1 [1.1]: https://github.com/troglobit/finit/compare/1.0...1.1
[1.0]: https://github.com/troglobit/finit/compare/0.9...1.0 [1.0]: https://github.com/troglobit/finit/compare/0.9...1.0
[0.9]: https://github.com/troglobit/finit/compare/0.8...0.9 [0.9]: https://github.com/troglobit/finit/compare/0.8...0.9
[0.8]: https://github.com/troglobit/finit/compare/0.7...0.8 [0.8]: https://github.com/troglobit/finit/compare/0.7...0.8
[0.7]: https://github.com/troglobit/finit/compare/0.6...0.7 [0.7]: https://github.com/troglobit/finit/compare/0.6...0.7
[0.6]: https://github.com/troglobit/finit/compare/0.5...0.6 [0.6]: https://github.com/troglobit/finit/compare/0.5...0.6
[0.5]: https://github.com/troglobit/finit/compare/0.4...0.5 [0.5]: https://github.com/troglobit/finit/compare/0.4...0.5
[0.4]: https://github.com/troglobit/finit/compare/0.3...0.4 [0.4]: https://github.com/troglobit/finit/compare/0.3...0.4
[0.3]: https://github.com/troglobit/finit/compare/0.2...0.3 [0.3]: https://github.com/troglobit/finit/compare/0.2...0.3
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2 [0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
[libuEv]: https://github.com/troglobit/libuev [libuEv]: https://github.com/troglobit/libuev
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8 [dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
+20 -15
View File
@@ -61,12 +61,13 @@ distributions:
* [Alpine Linux](contrib/alpine/), and * [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/), also works on Ubuntu/Linux Mint * [Debian GNU/Linux](contrib/debian/), also works on Ubuntu/Linux Mint
> **Note:** support for various Linux distributions does not mean Finit > [!NOTE]
> installs easily on all architectures. The bundled install scripts are > Support for various Linux distributions does not mean Finit installs
> examples for standard installations, tested on amd64 (x86_64) systems. > easily on all architectures. The bundled install scripts are examples
> Custom setups, e.g., for embedded systems, can be found in any of the > for standard installations, tested on amd64 (x86_64) systems. Custom
> following [Buildroot][] based examples: [myLinux][], [Infix][], or the > setups, e.g., for embedded systems, can be found in the following
> plain [br2-finit-demo](https://github.com/troglobit/br2-finit-demo). > [Buildroot][] based examples: [myLinux][], [Infix][], or the plain
> [br2-finit-demo](https://github.com/troglobit/br2-finit-demo).
Example Example
@@ -78,9 +79,10 @@ be placed in `/etc/finit.d/available` and enabled by an operator using
the [initctl](#commands--status) tool. See the above mentioned Linux the [initctl](#commands--status) tool. See the above mentioned Linux
distributions, or [myLinux][]. distributions, or [myLinux][].
> **Note:** as of Finit v4.4, .conf lines can be broken up using the > [!TIP]
> standard UNIX continuation character (`\`), also trailing comments are > As of Finit v4.4, `.conf` lines can be broken up using the standard
> now supported. The latter means you need to escape any hashes used in > UNIX continuation character (`\`), trailing comments are now also
> supported. The latter means you need to escape any hashes used in
> directives and descriptions (`\#`). For more on this and examples, > directives and descriptions (`\#`). For more on this and examples,
> see the [finit.conf(5)][] manual or [doc/config.md](doc/config.md). > see the [finit.conf(5)][] manual or [doc/config.md](doc/config.md).
@@ -285,6 +287,7 @@ run [S] /path/to/ident --
task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
``` ```
> [!TIP]
> Notice the trick with an empty description to hide the call to `ident` > Notice the trick with an empty description to hide the call to `ident`
> in the Finit progress output. > in the Finit progress output.
@@ -367,9 +370,10 @@ The `initctl` tool has three commands to help debug and optimize the
setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup` setup and monitoring of cgroups. See the `ps`, `top`, and `cgroup`
commands for details. commands for details.
> **Note:** systems that do not support cgroups, specifically version 2, > [!NOTE]
> are automatically detected. On such systems the above functionality > Systems that do not support cgroups, specifically version 2, are
> is disabled early at boot. > automatically detected. On such systems the above functionality is
> disabled early at boot.
Runparts & /etc/rc.local Runparts & /etc/rc.local
@@ -573,9 +577,10 @@ file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md), `runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
these will also affect how a service is maintained. these will also affect how a service is maintained.
> **Note:** even though it is possible to start services not belonging in > [!NOTE]
> the current runlevel these services will not be respawned automatically > Even though it is possible to start services not belonging in the
> by Finit if they exit (crash). Hence, if the runlevel is 2, the below > current runlevel these services will not be respawned automatically by
> Finit if they exit (crash). Hence, if the runlevel is 2, the below
> Dropbear SSH service will not be restarted if it is killed or exits. > Dropbear SSH service will not be restarted if it is killed or exits.
The `status` command is the default, it displays a quick overview of all The `status` command is the default, it displays a quick overview of all
+15 -3
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.8], [https://github.com/troglobit/finit/issues], AC_INIT([Finit], [4.11], [https://github.com/troglobit/finit/issues],
[finit], [https://troglobit.com/projects/finit/]) [finit], [https://troglobit.com/projects/finit/])
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
@@ -177,9 +177,13 @@ AC_ARG_WITH(plugin-path,
[plugin_path=$withval], [plugin_path=yes]) [plugin_path=$withval], [plugin_path=yes])
AC_ARG_WITH(rtc-date, AC_ARG_WITH(rtc-date,
AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM", default "2000-01-01 00:00"]), AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM:SS", default "2000-01-01 00:00:00"]),
[rtc_date=$withval], [rtc_date=no]) [rtc_date=$withval], [rtc_date=no])
AC_ARG_WITH(rtc-file,
AS_HELP_STRING([--with-rtc-file=FILE], [If RTC is missing, save and restore system clock from this file, default: no]),
[rtc_file=$withval], [rtc_file=no])
### Enable features ########################################################################### ### Enable features ###########################################################################
# Create config.h from selected features and fallback defaults # Create config.h from selected features and fallback defaults
@@ -278,9 +282,16 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])]) AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
AS_IF([test "x$rtc_date" != "xno"], [ AS_IF([test "x$rtc_date" != "xno"], [
AC_DEFINE(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [ AC_DEFINE_UNQUOTED(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [
rtc_date=""]) rtc_date=""])
AS_IF([test "x$rtc_file" != "xno"], [
AS_IF([test "x$rtc_file" = "xyes"], [
rtc_file=/var/lib/misc/rtc])
AC_EXPAND_DIR(rtcfile_path, "$rtc_file")
AC_DEFINE_UNQUOTED(RTC_FILE, "$rtcfile_path", [Save and restore system time from this file if /dev/rtc is missing.])],[
AC_DEFINE_UNQUOTED(RTC_FILE, NULL)])
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes]) AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
AS_IF([test "x$with_sulogin" != "xno"], [ AS_IF([test "x$with_sulogin" != "xno"], [
@@ -387,6 +398,7 @@ Behavior:
Boot heading..........: $heading Boot heading..........: $heading
Plugins...............: $plugins Plugins...............: $plugins
RTC restore date......: $RTC_DATE RTC restore date......: $RTC_DATE
RTC fallback file.....: $rtc_file
Optional features: Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
+2 -1
View File
@@ -1,7 +1,8 @@
HowTo: Finit on Alpine Linux 3.4...3.19 HowTo: Finit on Alpine Linux 3.4...3.19
======================================= =======================================
> **Blog** https://troglobit.com/post/2021-02-12-alpine-linux-with-finit/ > [!TIP]
> https://troglobit.com/post/2021-02-12-alpine-linux-with-finit/
HowTo use Finit to boot an [Alpine Linux][] system. It is assumed that HowTo use Finit to boot an [Alpine Linux][] system. It is assumed that
the user has already installed make, a compiler, C library header files, the user has already installed make, a compiler, C library header files,
+5 -3
View File
@@ -20,8 +20,9 @@ tools pre-systemd work as intended.
root@debian:~# apt install initscripts console-setup root@debian:~# apt install initscripts console-setup
> **Note:** as of Debian 11 (Bullseye), libuev and libite are part of > [!TIP]
> the main section of Debian. So just install the -dev packages :) > As of Debian 11 (Bullseye), libuev and libite are part of the main
> section of Debian. So just install the -dev packages :)
The following script can then be used to configure, build, install and The following script can then be used to configure, build, install and
set up your system to run Finit: set up your system to run Finit:
@@ -47,7 +48,8 @@ comands to `initctl`, followed by `reload` to activate your changes.
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
and initialization like keyboard language etc. and initialization like keyboard language etc.
> **NOTE:** X Window system, you may need to `sudo apt install elogind` > [!NOTE]
> For the X Window system, you may need to `sudo apt install elogind`
> (Bullseye and later), followed by `initctl reload` to activate it (it > (Bullseye and later), followed by `initctl reload` to activate it (it
> is enabled by default), and logout/login again. The elogind daemon > is enabled by default), and logout/login again. The elogind daemon
> ensures a regular non-root user can start and interact with an X > ensures a regular non-root user can start and interact with an X
+30 -24
View File
@@ -1,5 +1,5 @@
Building Building Finit
======== ==============
* [Introduction](#introduction) * [Introduction](#introduction)
* [Configure](#configure) * [Configure](#configure)
@@ -17,14 +17,15 @@ optional plugins to enable. It depends on two external libraries:
- [libuEv][], the event loop - [libuEv][], the event loop
- [libite][] (-lite), much needed frog DNA - [libite][] (-lite), much needed frog DNA
**NOTE:** Most free/open source software that uses `configure` default > [!IMPORTANT]
to install to `/usr/local`. However, some Linux distributions do no > Most free/open source software packages that use `configure` default
longer search that path for installed software, e.g. Fedora and Alpine > to install to `/usr/local`. However, some Linux distributions do no
Linux. To get finit's configure script to find its dependencies you > longer search that path for installed software, e.g. Fedora and Alpine
have to help the `pkg-config` tool a bit if you do not change the > Linux. To get finit's configure script to find its dependencies you
default prefix path: > have to help the `pkg-config` tool a bit if you do not change the
> default prefix path:
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure >
> PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
The configure script checks for all dependencies, including the correct The configure script checks for all dependencies, including the correct
version of the above mentioned libraries. Currently required versions: version of the above mentioned libraries. Currently required versions:
@@ -71,12 +72,13 @@ Below are a few of the main switches to configure:
For more configure flags, see <kbd>./configure --help</kbd> For more configure flags, see <kbd>./configure --help</kbd>
> **Note:** the configure script is not available in the GIT sources. It is > [!NOTE]
> however included in (officially supported) released tarballs. The > The configure script is not available in the GIT sources. It is
> idea is that you should not need GNU autotools to build, only the > however included in (officially supported) released tarballs. The
> above mentioned dependencies, a POSIX shell, a C compiler and make. > idea is that you should not need GNU autotools to build, only the
> Any contributing to Finit can generate it from `configure.ac` using > above mentioned dependencies, a POSIX shell, a C compiler and make.
> the `autogen.sh` script. > Any contributing to Finit can generate it from `configure.ac` using
> the `autogen.sh` script.
Example Example
@@ -115,10 +117,11 @@ Linux config to:
CONFIG_UEVENT_HELPER_PATH="/sbin/mdev" CONFIG_UEVENT_HELPER_PATH="/sbin/mdev"
**Note:** If you run into problems starting Finit, take a look at > [!TIP]
`finit.c`. One of the most common problems is a custom Linux kernel > If you run into problems starting Finit, take a look at `finit.c`.
build that lack `CONFIG_DEVTMPFS`. Another is too much cruft in the > One of the most common problems is a custom Linux kernel build that
system `/etc/fstab`. > lack `CONFIG_DEVTMPFS`. Another is too much cruft in the system
> `/etc/fstab`.
Running Running
@@ -157,8 +160,10 @@ enabled. The default Finit rescue mode configuration is installed into
By default the a root shell, without login, is started. By default the a root shell, without login, is started.
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to > [!IMPORTANT]
> force `reboot`, `shutdown`, or `poweroff`. > In rescue mode `initctl` will not work, the same goes for `reboot`,
> `shutdown`, and `poweroff` commands, provided they are the Finit
> versions of these commands. Use the `-f` flag to force the action.
Debugging Debugging
@@ -187,8 +192,9 @@ kernel usually reboots: `configure --enable-emergency-shell`. However,
the behavior of Finit is severely limited when this is enabled, so use the behavior of Finit is severely limited when this is enabled, so use
it only for debugging start up issues when Finit crashes. it only for debugging start up issues when Finit crashes.
**NOTE:** Neither of these options should be enabled on production > [!CAUTION]
systems since they can potentially give a user root access. > None of these options should be enabled on production systems since
> they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-4.3.tar.gz [1]: ftp://troglobit.com/finit/finit-4.3.tar.gz
+6 -4
View File
@@ -4,10 +4,11 @@ Tips & Tricks with the kernel cmdline
This document summarizes the different boot parameters that can be This document summarizes the different boot parameters that can be
passed on the Linux kernel command line. Not limited to Finit. passed on the Linux kernel command line. Not limited to Finit.
The `bool` setting is one of `on, off, true false, 1, 0`. > [!IMPORTANT]
> Remember to use `--` to separate kernel parameters from parameters to
> init. E.g., `init=/sbin/finit -- finit.debug rescue`
> **NOTE:** remember to use `--` to separate kernel parameters from 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 * `debug`: Enable kernel debug. Debug messages are printed to the
console until Finit starts up, unless `loglevel=7` (below) is used. console until Finit starts up, unless `loglevel=7` (below) is used.
@@ -30,7 +31,8 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
Very useful for selecting different boot modes, e.g. manufacturing test, Very useful for selecting different boot modes, e.g. manufacturing test,
firmware upgrade, or rescue mode. firmware upgrade, or rescue mode.
> Note: `<boot/...>` conditions cannot be cleared with `initctl`! > [!NOTE]
> `<boot/...>` conditions cannot be cleared with `initctl`!
* `finit.config=/path/to/alternative/finit.conf`: override the * `finit.config=/path/to/alternative/finit.conf`: override the
compile-time bootstrap config file, default: compile-time bootstrap config file, default:
+19 -11
View File
@@ -47,9 +47,10 @@ both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A
`pid/` condition is satisfied by the corresponding service's PID file `pid/` condition is satisfied by the corresponding service's PID file
being created, i.e., the service's default readiness notification. being created, i.e., the service's default readiness notification.
**NOTE:** Conditions also stop services when a condition is no longer > [!IMPORTANT]
asserted. I.e., if the Zebra process above stops or restarts, netd > Conditions also stop services when a condition is no longer asserted.
will also stop or restart. > 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 Another example is `dropbear`, it does not support `SIGHUP`, but we can
also see optional sourcing of arguments from an environment file: also see optional sourcing of arguments from an environment file:
@@ -106,11 +107,16 @@ on this, see [Internals](#internals).) Thus, after a reconfiguration it
is up to the "owner" of the condition to convey the new (or possibly is up to the "owner" of the condition to convey the new (or possibly
unchanged) state of it. unchanged) state of it.
> **Note:** For `pid/` conditions it is expected that services "touch"
> or recreate their PID file on `SIGHUP`.
Static (one-shot) conditions, like `usr/`, never enter the `flux` state. Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
> [!IMPORTANT]
> For `pid/` conditions it is expected that the service reassert, i.e.,
> "touch" or recreate, their PID file on `SIGHUP`. This can be done by
> calling `utimensat()` on the PID file. Provided, of course, that the
> service supports reloading on `SIGHUP`, otherwise it will be restarted
> by Finit when they instead exit on the signal. For such services, use
> `<!>` to tell Finit the service does not support `SIGHUP`.
Built-in Conditions Built-in Conditions
------------------- -------------------
@@ -171,9 +177,10 @@ Built-in conditions:
- `boot/arg` - `boot/arg`
- `dev/node` and `dev/dir/node` - `dev/node` and `dev/dir/node`
**Note:** `up` means administratively up, the interface flag `IFF_UP`. > [!NOTE]
`running` is the `IFF_RUNNING` flag, meaning operatively up. The > Here, `up` means administratively up, the interface flag `IFF_UP`.
difference is that `running` tells if the NIC has link. > `running` is the `IFF_RUNNING` flag, meaning operatively up. The
> difference is that `running` tells if the NIC has link.
Composition Composition
@@ -205,8 +212,9 @@ the service stanza for the daemon. It is far from optimal since any
synchronization of depending services may fail due to the daemon not synchronization of depending services may fail due to the daemon not
having reinitialized/created their IPC sockets, or similar. having reinitialized/created their IPC sockets, or similar.
> **Note:** in versions of Finit prior to v4, the PID conditions were > [!NOTE]
> called 'svc' conditions, and they were far more complex. > In versions of Finit prior to v4, the PID conditions were called 'svc'
> conditions, and they were far more complex.
Debugging Debugging
+153 -103
View File
@@ -18,8 +18,8 @@ Finit Configuration
* [Alternate rcS.d/](#alternate-rcs-d-) * [Alternate rcS.d/](#alternate-rcs-d-)
* [Resource Limits](#resource-limits) * [Resource Limits](#resource-limits)
* [Runlevels](#runlevels) * [Runlevels](#runlevels)
* [One-shot Commands (sequence)](#one-shot-commands--sequence-) * [One-shot Commands (sequence)](#one-shot-commands-sequence)
* [One-shot Commands (parallel)](#one-shot-commands--parallel-) * [One-shot Commands (parallel)](#one-shot-commands-parallel)
* [SysV Init Scripts](#sysv-init-scripts) * [SysV Init Scripts](#sysv-init-scripts)
* [Services](#services) * [Services](#services)
* [Run-parts Scripts](#run-parts-scripts) * [Run-parts Scripts](#run-parts-scripts)
@@ -46,8 +46,9 @@ Plugins start at [hook points](plugins.md#hooks) and can run various set
up, or install event handlers that later provide runtime services, e.g., up, or install event handlers that later provide runtime services, e.g.,
PID file monitoring, or [conditions](conditions.md). PID file monitoring, or [conditions](conditions.md).
> **Tip:** see [SysV Init Compatibility](#sysv-init-compatibility) for > [!TIP]
> help to quickly get going with an existing SysV or BusyBox init setup. > See [SysV Init Compatibility](#sysv-init-compatibility) for help to
> quickly get going with an existing SysV or BusyBox init setup.
### Configuration Files ### Configuration Files
@@ -127,8 +128,9 @@ properties of the `run` statement. For an example on the relation of
`service` and `run` statements, and dependency handling between them, `service` and `run` statements, and dependency handling between them,
see [Conditional Loading](#conditional-loading), below. see [Conditional Loading](#conditional-loading), below.
> **Note:** the `finit.conf` and `finit.d/` names are only defaults. > [!NOTE]
> They can be changed at compile-time with two `configure` options: > The names `finit.conf` and `finit.d/` are only defaults. They can be
> changed at compile-time with two `configure` options:
> `--with-config=/etc/foo.conf` and `--with-rcsd=/var/foo.d`. > `--with-config=/etc/foo.conf` and `--with-rcsd=/var/foo.d`.
> >
> They can also be overridden from the [kernel command line](cmdline.md) > They can also be overridden from the [kernel command line](cmdline.md)
@@ -187,8 +189,9 @@ above, provided their respective mount point exists.
With all filesystems mounted, Finit calls `swapon`. With all filesystems mounted, Finit calls `swapon`.
> **Tip:** to see what happens when all filesystems are mounted, have a > [!TIP]
> look at the [`bootmisc.so` plugin](plugins.md). > 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 At shutdown, and after having stopped all services and other lingering
processes have been killed, filesystems are unmounted in the reverse processes have been killed, filesystems are unmounted in the reverse
@@ -198,6 +201,18 @@ order, and `swapoff` is called.
### Runlevels ### Runlevels
Finit supports runlevels, but unlike other init systems runlevels are
declared per service/run/task/sysv command. When booting up a system
Finit pass through three phases:
1. Setting up the console, parsing any command line options, and other
housekeeping tasks like mounting all filesystems, and calling `fsck`
2. Starting all run/task/services in runlevel S, then waiting for all
services to have started, and all run/tasks to have completed
3. Go to runlevel 2, or whatever the user has set in the configuration
Available runlevels:
- ` S`: bootStrap - ` S`: bootStrap
- ` 1`: Single user mode - ` 1`: Single user mode
- `2-5`: traditional multi-user mode - `2-5`: traditional multi-user mode
@@ -205,37 +220,40 @@ order, and `swapoff` is called.
- `7-9`: multi-user mode (extra) - `7-9`: multi-user mode (extra)
- ` 0`: shutdown - ` 0`: shutdown
Runlevels are declared per service/run/task/sysv command. Starting in Runlevel S (bootStrap), is for tasks supposed to run once at boot, and
runlevel S (bootStrap), usually only for tasks supposed to run once at services like `syslogd`, which need to start early and run throughout
boot, and services like `syslogd`, which you need to start and run the lifetime of your system.
throughout the whole time your system is up.
Before `S` is started, however, Finit performs a lot of housekeeping Example:
tasks like mounting all filesystems, calling `fsck` if needed, and
making sure the everything is OK.
task [S] /lib/console-setup/console-setup.sh task [S] /lib/console-setup/console-setup.sh
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
After runlevel S, Finit proceeds to runlevel 2. This can be changed in When bootstrap has completed, Finit moves to runlevel 2. This can be
`/etc/finit.conf` using the `runlevel N` directive, or by a script changed in `/etc/finit.conf` using the `runlevel N` directive, or by a
running in runlevel S that calls, e.g., `initctl runlevel 9`. The script running in runlevel S that calls, e.g., `initctl runlevel 9`.
latter is useful if startup scripts detect problems outside of Finit's The latter is useful if startup scripts detect problems outside of
control, e.g., critical services/devices missing or hardware problems. Finit's control, e.g., critical services/devices missing or hardware
problems.
Before starting the services in runlevel 2, Finit first stops everything Each runlevel must be allowed to "complete". Meaning, all services in
that is not allowed to run in 2, and then brings up networking. runlevel S must have started and all run/tasks have been started and
Networking is expected to be available in all runlevels except: S, 1 collected (exited). Finit waits 120 seconds for all run/tasks in S to
(single user level), 6, and 0. Networking is enabled either by the complete before proceeding to 2.
`network script` directive, or if you have an `/etc/network/interfaces`
file, Finit calls `ifup -a` -- at the very least the loopback interface
is brought up.
> **Note:** when moving from runlevel S to 2, all run/task/services that Finit first stops everything that is not allowed to run in 2, and then
> were constrained to runlevel S only are dropped from bookkeeping. So brings up networking. Networking is expected to be available in all
> when reaching the prompt, `initctl` will not show these run/tasks. runlevels except: S, 1 (single user level), 6, and 0. Networking is
> This is a safety mechanism to prevent bootstrap-only tasks from enabled either by the `network script` directive, or if you have an
> accidentally being run again. E.g., `console-setup.sh` above. `/etc/network/interfaces` file, Finit calls `ifup -a` -- at the very
least the loopback interface is brought up.
> [!NOTE]
> When moving from runlevel S to 2, all run/task/services that were
> constrained to runlevel S only are dropped from bookkeeping. So when
> reaching the prompt, `initctl` will not show these run/tasks. This is
> a safety mechanism to prevent bootstrap-only tasks from accidentally
> being run again. E.g., `console-setup.sh` above.
### Managing Services ### Managing Services
@@ -322,7 +340,8 @@ the `ps` command we can see that the process is started with:
foo -n --extra-arg=bar -s -x foo -n --extra-arg=bar -s -x
> **Note:** the leading `-` determines if Finit should treat a missing > [!NOTE]
> The leading `-` in `env:` determines if Finit should treat a missing
> environment file as blocking the start of the service or not. When > environment file as blocking the start of the service or not. When
> `-` is used, a missing environment file does *not* block the start. > `-` is used, a missing environment file does *not* block the start.
@@ -384,12 +403,13 @@ To synchronize two services the following condition can be used:
For details on the syntax and options, see below. For details on the syntax and options, see below.
> **Note:** on `initctl reload` conditions are normally set in "flux", > [!NOTE]
> while figuring out which to stop, start or restart. Services that > On `initctl reload` conditions are set in "flux", while figuring out
> need to be restarted have their `ready` condition removed prior to > which to stop, start or restart. Services that need to be restarted
> Finit sending them SIGHUP (if they support that), or stop-starting > have their `ready` condition removed prior to Finit sending them
> them. A daemon is expected to reassert its readiness, e.g. systemd > SIGHUP (if they support that), or stop-starting them. A daemon is
> style daemons to write `READY=1\n`. > expected to reassert its readiness, e.g. systemd style daemons to
> write `READY=1\n`.
> >
> However, the s6 notify mode does not support this because in s6 you > However, the s6 notify mode does not support this because in s6 you
> are expected to close your notify descriptor after having written > are expected to close your notify descriptor after having written
@@ -429,9 +449,10 @@ example employs a wrapper script in `/etc/start.d`.
# Execute the program # Execute the program
exec /usr/bin/program $OPTIONS exec /usr/bin/program $OPTIONS
> **Note:** the example sets `<!>` to denote that it doesn't support > [!NOTE]
> `SIGHUP`. That way Finit will stop/start the service > The example sets `<!>` to denote that it doesn't support `SIGHUP`.
> instead of sending SIGHUP at restart/reload events. > That way Finit will stop/start the service instead of sending SIGHUP
> at restart/reload events.
Templating Templating
@@ -514,6 +535,7 @@ A daemon using `SCHED_RR` currently need to run outside the default cgroups.
service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
> [!NOTE]
> Linux cgroups and details surrounding values are not explained in the > Linux cgroups and details surrounding values are not explained in the
> Finit documentation. The Linux admin-guide cover this well: > Finit documentation. The Linux admin-guide cover this well:
> <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html> > <https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html>
@@ -539,12 +561,9 @@ service name:sysklogd [S123456789] \
``` ```
The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support the The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support the
following directives: following directives. Some directives have restrictions, e.g., only
available at bootstrap, runlevel `S`, see [Limitations](#limitations)
below for details.
> **Note:** not all directives are available in `/etc/finit.d/*.conf`
> and some directives are only available at bootstrap, runlevel `S`,
> see [Limitations](#limitations) below for details.
### Hostname ### Hostname
@@ -556,7 +575,8 @@ the contents of that file is used.
Deprecated. We recommend using `/etc/hostname` instead. 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 ### Kernel Modules
@@ -574,7 +594,8 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS" $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 ### Networking
@@ -587,7 +608,8 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
or `/etc/network/interfaces` if you have a system with `ifupdown`, like or `/etc/network/interfaces` if you have a system with `ifupdown`, like
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system. 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/ ### Alternate finit.d/
@@ -608,8 +630,9 @@ another set of .conf files, e.g.:
rcsd /etc/factory.d rcsd /etc/factory.d
> **Note:** this directive is only available from the top-level > [!NOTE]
> bootstrap .conf file. > This directive is only available from the top-level bootstrap .conf
> file, usually `/etc/finit.conf`.
### Resource Limits ### Resource Limits
@@ -642,16 +665,18 @@ stanzas can share the same rlimits if they are in the same .conf.
**Syntax:** `runlevel <N>` **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
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for the runlevel number 0-9, where 6 is reserved for reboot and 0 for halt.
halt. Completed in this context means all services have been started and all
run/tasks have been started and collected.
It is recommended to keep runlevel 1 as single-user mode, because It is recommended to keep runlevel 1 as single-user mode, because
Finit disables networking in this mode. Finit disables networking in this mode.
*Default:* 2 *Default:* 2
> **Note:** only read and executed in runlevel S (bootstrap). > [!NOTE]
> Only read and executed in runlevel S (bootstrap).
### One-shot Commands (sequence) ### One-shot Commands (sequence)
@@ -665,10 +690,11 @@ optional arguments and description. `run` commands are guaranteed to be
completed before running the next command. Useful when serialization is completed before running the next command. Useful when serialization is
required. required.
> **Warning:** try to avoid the `run` command. It blocks much of the > [!WARNING]
> functionality in Finit, like (re)starting other (perhaps crashing) > Try to avoid the `run` command. It blocks much of the functionality
> services while a `run` task is executing. Use other synchronization > in Finit, like (re)starting other (perhaps crashing) services while a
> mechanisms instead, like conditions. > `run` task is executing. Use other synchronization mechanisms
> instead, like conditions.
Incomplete list of unsupported `initctl` commands in `run` tasks: Incomplete list of unsupported `initctl` commands in `run` tasks:
@@ -708,7 +734,7 @@ non-trivial constructs are better placed in a separate shell script.
Similar to `task` is the `sysv` stanza, which can be used to call SysV Similar to `task` is the `sysv` stanza, which can be used to call SysV
style scripts. The primary intention for this command is to be able to style scripts. The primary intention for this command is to be able to
re-use much of existing setup and init scripts in Linux distributions. reuse much of existing setup and init scripts in Linux distributions.
When entering an allowed runlevel, Finit calls `init-script start`, when When entering an allowed runlevel, Finit calls `init-script start`, when
entering a disallowed runlevel, Finit calls `init-script stop`, and if entering a disallowed runlevel, Finit calls `init-script stop`, and if
@@ -723,7 +749,8 @@ 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 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`. option to run it in the foreground, more on this below in `service`.
> **Tip:** see also [SysV Init Compatibility](#sysv-init-compatibility). > [!TIP]
> See also [SysV Init Compatibility](#sysv-init-compatibility).
### Services ### Services
@@ -731,13 +758,13 @@ option to run it in the foreground, more on this below in `service`.
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description` **Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die, by exits prematurely. Finit tries to restart services that die, by default
default 10 times before giving up and marking them as *crashed*. 10 times before giving up and marking them as *crashed*. After which
After which they have to be manually restarted with `initctl restart they have to be manually restarted with `initctl restart NAME`. The
NAME`. The limits controling this are configurable, see the options limits controlling this are configurable, see the options below.
below.
> **Tip:** to allow endless restarts, see below option `respawn` > [!TIP]
> To allow endless restarts, see below option `respawn`
For daemons that support it, we recommend appending `--foreground`, For daemons that support it, we recommend appending `--foreground`,
`--no-background`, `-n`, `-F`, or similar command line argument to `--no-background`, `-n`, `-F`, or similar command line argument to
@@ -890,33 +917,52 @@ configurable with the following options:
* `oncrash:script` -- similarly, but instead of rebooting, call the * `oncrash:script` -- similarly, but instead of rebooting, call the
`post:script` action if set, see below. `post:script` action if set, see below.
When stopping a service (run/task/sysv/service), either manually or When stopping a service (run/task/sysv/service), either manually or when
when moving to another runlevel, Finit starts by sending `SIGTERM`, to moving to another runlevel, Finit starts by sending `SIGTERM`, to allow
allow the process to shut down gracefully. If the process has not the process to shut down gracefully. However, if the process has not
been collected within 3 seconds, Finit sends `SIGKILL`. To halt the been collected within 3 seconds, Finit will send `SIGKILL`. To stop the
process using a different signal, use the option `halt:SIGNAL`, e.g., process using a different signal than `SIGTERM`, use `halt:SIGNAL`,
`halt:SIGPWR`. To change the delay between your halt signal and KILL, e.g., `halt:SIGPWR`. To change the delay between the stop signal and
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before KILL, use the option `kill:<1-60>`, e.g., `kill:10` to wait 10 seconds
sending `SIGKILL`. before sending `SIGKILL`.
Services support `pre:script` and `post:script` actions as well. These Services, including the `sysv` variant, support pre/post/ready and
run as the same `@USER:GROUP` as the service itself, with any `env:file` cleanup scripts:
sourced. The scripts must use an absolute path, but are executed from
the `$HOME` of the given user. The scripts are not called with any
argument (currently), but both get the `SERVICE_IDENT=foo` environment
variable set. Here `foo` denotes the identity of the service, which if
there are multiple services named `foo`, may be `foo:1`, or any unique
identifier specified in the .conf file. The `post:script` is called
with an additional set of environment variables:
- `EXIT_CODE=[exited,signal]`: set to one of `exited` or `signal` * `pre:[0-3600,]script` -- called before the sysv/service is stated
* `post:[0-3600,]script` -- called after the sysv/service has stopped
* `ready:[0-3600,]script` -- called when the sysv/service is ready
* `cleanup:[0-3600,]script` -- called when run/task/sysv/service is removed
The optional number (0-3600) is the timeout before Finit kills the
script, it defaults to the kill delay value and can be disabled by
setting it to zero. These scripts run as the same `@USER:GROUP` as the
service itself, with any `env:file` sourced. The scripts are executed
from the `$HOME` of the given user. The scripts are not called with any
argument, but get a set of environment variables:
* `SERVICE_IDENT=foo:1`
* `SERVICE_NAME=foo`
* `SERVICE_ID=1`
The `post:script` is called with an additional set of environment
variables. Yes, the text is correct, the naming was an accident:
- `EXIT_CODE=[exited,signal,crashed]`: normal exit, signaled, or
crashed
- `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from - `EXIT_STATUS=[num,SIGNAME]`: set to one of exit status code from
the program, if it exited normally, or the signal name (`HUP`, the program, if it exited normally, or the signal name (`HUP`,
`TERM`, etc.) if it exited due to signal `TERM`, etc.) if it exited due to signal
These script actions *must terminate*, so they have a default execution When a run/task/sys/service is removed (disable + reload) it is first
time of 3 seconds before they are SIGKILLed, this can be adjusted using stopped and then removed from the runlevel. The `post:script` always
the above `kill:SEC` syntax. runs when the process has stopped, and the `cleanup:script` runs when
the the stanza has been removed from the runlevel.
> [!IMPORTANT]
> These script actions are intended for setup, cleanup, and readiness
> notification. It is up to the user to ensure the scripts terminate.
#### Conditional Loading #### Conditional Loading
@@ -1015,8 +1061,9 @@ before continuing. None of them can issue commands to start, stop, or
restart other services. Also, ensure all your services and programs restart other services. Also, ensure all your services and programs
either terminate or start in the background or you will block Finit. either terminate or start in the background or you will block Finit.
> **Note:** `runparts` scrips are only read and executed in runlevel S. > [!NOTE]
> See [hook scripts](plugins.md#hooks) for other ways to run scripts at > `runparts` scripts 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. > certain points during the complete lifetime of the system.
**Recommendations:** **Recommendations:**
@@ -1289,8 +1336,9 @@ This rescue mode can be disabled at configure time using:
The rescue mode comes in two flavors; *traditional* and *fallback*. The rescue mode comes in two flavors; *traditional* and *fallback*.
> **Note:** in this mode `initctl` will not work. Use the `-f` flag to > [!NOTE]
> force `reboot`, `shutdown`, or `poweroff`. > In this mode `initctl` will not work. Use the `-f` flag to force
> `reboot`, `shutdown`, or `poweroff`.
### Traditional ### Traditional
@@ -1302,11 +1350,12 @@ program is used instead. If a successful login is made, or if the user
exits (Ctrl-D), the rescue mode is ended and the system boots up exits (Ctrl-D), the rescue mode is ended and the system boots up
normally. normally.
> **Note:** the bundled sulogin in Finit can at configure time be given > [!WARNING]
> another user than the default (root). If the sulogin user does not > The bundled sulogin in Finit can at configure time be given another
> have a password, or __the account is locked__, the user is presented > user than the default (root). If the sulogin user does not have a
> with a password-less `"Press enter to enter maintenance mode."`, > password, or __the account is locked__, the user is presented with a
> prompt which opens up a root shell. > prompt: `"Press enter to enter maintenance mode."`, which will open
> up a root shell *without prompting for password*!
### Fallback ### Fallback
@@ -1354,10 +1403,11 @@ These can now also be set in any `.conf` file in `/etc/finit.d`.
There is, however, nothing preventing you from having all configuration There is, however, nothing preventing you from having all configuration
settings in `/etc/finit.conf`. settings in `/etc/finit.conf`.
> **Note:** The `/etc/finit.d` directory was previously the default > [!IMPORTANT]
> Finit [runparts](#run-parts-scripts) directory. Finit >=v4.0 > The default `rcsd`, i.e., `/etc/finit.d`, was previously the Finit
> no longer has a default `runparts` directory, make sure to > [runparts](#run-parts-scripts) directory. Finit >=v4.0 no longer has
> update your setup, or the finit configuration, accordingly. > a default `runparts` directory, make sure to update your setup, or the
> finit configuration, accordingly.
Watchdog Watchdog
+3 -2
View File
@@ -34,8 +34,9 @@ build time:
./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf ./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf
``` ```
> **Note:** remember `--prefix` et al as well, the default is likely > [!IMPORTANT]
> *not* what you want. See the [build docs][1] for details. > Remember `--prefix` et al as well, the default is likely *not* what
> you want. See the [build docs][1] for details.
The resulting directory structure is depicted below. Please notice how The resulting directory structure is depicted below. Please notice how
`/etc/finit.conf` now resides in the same sub-directory as a non-symlink `/etc/finit.conf` now resides in the same sub-directory as a non-symlink
+21 -16
View File
@@ -35,9 +35,10 @@ For your convenience a set of *optional* plugins are available:
files distributed with Finit. It is read first but can be overridden 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`. 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` > [!NOTE]
> RAM disks and `/dev` is usually a `devtmpfs`. This must be defined > On an embedded system both `/var` and `/run` can be `tmpfs` RAM
> in the `/etc/fstab` file and in the Linux kernel config. > 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. * *dbus.so*: Setup and start system message bus, D-Bus, at boot.
_Optional plugin._ _Optional plugin._
@@ -63,8 +64,9 @@ For your convenience a set of *optional* plugins are available:
Enabled by default. Enabled by default.
> See the [Services](config.md#services) section in the configuration > [!TIP]
> guide for an example how to run `mdevd`, alternative to plain mdev. > See the [Services](config.md#services) section in the configuration
> guide for an example how to run `mdevd`, alternative to plain mdev.
* *rtc.so*: Restore and save system clock from/to RTC on boot/halt. * *rtc.so*: Restore and save system clock from/to RTC on boot/halt.
Enabled by default. Enabled by default.
@@ -89,16 +91,17 @@ For your convenience a set of *optional* plugins are available:
set index 1234 set index 1234
**Note:** unlike the traditional .conf `module` directive, which load Since these tasks run in the background, they return `[ OK ]` at boot,
any listed module immediately, this plugin creates a background `task` unless the modprobe tool does not exist. Check syslog for warnings
which load the module(s) in the background. The program is modprobe, and the actual status of the operation using `initctl`.
`/sbin/modprobe`, which you can override per .conf file:
set modprobe /path/to/maybe-a-modprobe-wrapper > [!IMPORTANT]
> Unlike the traditional .conf `module` directive, which load any listed
Since these tasks run in the background, they usually return `[ OK ]` > module immediately, this plugin creates a background `task` which load
at boot, unless the modprobe tool does not exist. Check syslog for > the module(s) in the background. The program is modprobe,
warnings and the actual status of the operation using `initctl`. > `/sbin/modprobe`, which you can override per .conf file:
>
> set modprobe /path/to/maybe-a-modprobe-wrapper
* *netlink.so*: Listens to Linux kernel Netlink events for gateway and * *netlink.so*: Listens to Linux kernel Netlink events for gateway and
interfaces. These events are then sent to the Finit service monitor interfaces. These events are then sent to the Finit service monitor
@@ -158,8 +161,10 @@ hook points:
EOF EOF
$ chmod +x /libexec/finit/hook/sys/down/foo.sh $ chmod +x /libexec/finit/hook/sys/down/foo.sh
> **Note:** to use hook scripts, even for pre-bootstrap and pre-shutdown > [!IMPORTANT]
> tasks, you must build with `configure --enable-hook-scripts-plugin`. > To use hook scripts, even for pre-bootstrap and pre-shutdown tasks,
> you must build with `configure --enable-hook-scripts-plugin`.
### Bootstrap Hooks ### Bootstrap Hooks
+3 -2
View File
@@ -22,8 +22,9 @@ Finit can *not* start and monitor a daemon that:
| ✔ | No | No | Yes, optionally | | ✔ | No | No | Yes, optionally |
| ✘ | Yes | No | No | | ✘ | Yes | No | No |
> **Note:** PID files is one mechanism used to assert conditions to > [!NOTE]
> synchronize the start and stop of other, dependent, services. > 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 ### Forks to Background w/ PID File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 320 KiB

+2 -2
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 814 KiB

+38 -24
View File
@@ -175,12 +175,12 @@ task [s] echo "foo" | cat >/tmp/bar
.Ed .Ed
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description .It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
Similar to Similar to
.Cm task .Cm service
is the is the
.Cm sysv .Cm sysv
stanza, which can be used to call SysV style start/stop scripts. The stanza, which can be used to call SysV style start/stop scripts. The
primary intention for this command is to be able to re-use much of primary intention for this command is to be able to re-use traditional
existing setup and init scripts in Linux distributions. SysV init scripts in Linux distributions.
.Pp .Pp
When entering an allowed runlevel, Finit calls When entering an allowed runlevel, Finit calls
.Cm init-script start , .Cm init-script start ,
@@ -434,30 +434,42 @@ e.g.,
.Cm kill:10 .Cm kill:10
to wait 10 seconds before sending SIGKILL. to wait 10 seconds before sending SIGKILL.
.Pp .Pp
Services support the Services, including the
.Cm pre:script .Cm sysv
and variant, support pre/post/ready and cleanup scripts:
.Cm post:script .Bl -tag -width 1n
command actions as well. These run as the same .It Cm pre:[0-3600,]script
called before the sysv/service is stated
.It Cm post:[0-3600,]script
called after the sysv/service has stopped
.It Cm ready:[0-3600,]script
called when the sysv/service is ready
.It Cm cleanup:[0-3600,]script
called when run/task/sysv/service is removed
.El
.Pp
The optional number (0-3600) is the timeout before Finit kills the
script, it defaults to the kill delay value and can be disabled by
setting it to zero. These scripts run as the same
.Cm @USER:GROUP .Cm @USER:GROUP
as the service itself, with any as the service itself, with any
.Cm env:file .Cm env:file
sourced. The scripts must use an absolute path, but are executed from sourced. The scripts must use an absolute path, but are executed from
the the
.Cm $HOME .Cm $HOME
of the given user. The scripts are not called with any argument of the given user. The scripts are not called with any argument, but
(currently), but both get the both get a set of environment variables:
.Cm SERVICE_IDENT=foo .Pp
environment variable set. Here .Bl -tag -width 1n -compact
.Cm foo .It Cm SERVICE_IDENT=foo:1
denotes the identity of the service, which if there are multiple .It Cm SERVICE_NAME=foo
services named .It Cm SERVICE_ID=1
.Cm foo , .El
may be .Pp
.Cm foo:1 , The
or any unique identifier specified in the .conf file. The
.Cm post:script .Cm post:script
is called with an additional set of environment variables: is called with an additional set of environment variables, and yes the
naming looks like it should be swapped:
.Bl -tag -offset indent -width 1n .Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal,crashed] .It Cm EXIT_CODE=[exited,signal,crashed]
set to one of set to one of
@@ -471,10 +483,12 @@ set to one of exit status code from the program, if it exited normally,
or the signal name (HUP, TERM, etc.) if it exited due to signal or the signal name (HUP, TERM, etc.) if it exited due to signal
.El .El
.Pp .Pp
The scripts have a default execution time of 3 seconds before they are When a run/task/sys/service is removed (disable + reload) it is first
SIGKILLed, this can be adjusted using the stopped and then removed from the runlevel. The
.Cm kill:SEC .Cm post:script
modifier syntax. always runs when the process has stopped, and the
.Cm cleanup:script
runs when the the stanza has been removed from the runlevel.
.It Cm runparts Aq DIR .It Cm runparts Aq DIR
Call Call
.Xr run-parts 8 .Xr run-parts 8
+1 -1
View File
@@ -41,7 +41,7 @@
* *
* Please note, the :ID is there for your benefit, it ensures that tasks * 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 * 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! * (or no ID), the last one read replaces any preceding one!
*/ */
#include <unistd.h> #include <unistd.h>
+17
View File
@@ -80,6 +80,23 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
return; return;
} }
switch (svc->state) {
case SVC_TEARDOWN_STATE:
case SVC_CLEANUP_STATE:
case SVC_SETUP_STATE:
/*
* E.g., a setup script for a container may run in lieu
* of crun, trying to fetch a container image from the
* network. This script may want to advertise its PID
* to the system for clean and swift reboots.
*/
dbg("%s[%d]: ignoring any PID file changes in CLEANUP/TEARDOWN/SETUP",
svc_ident(svc, NULL, 0), svc->pid);
return;
default:
break;
}
/* Service condistion to set/clear */ /* Service condistion to set/clear */
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
+5 -1
View File
@@ -32,6 +32,10 @@
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#ifndef SYSCTL_PATH
#define SYSCTL_PATH "/sbin/sysctl"
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
glob_t gl; glob_t gl;
@@ -54,7 +58,7 @@ static void setup(void *arg)
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
char cmd[160]; char cmd[160];
snprintf(cmd, sizeof(cmd), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]); snprintf(cmd, sizeof(cmd), "%s -e -p %s >/dev/null", SYSCTL_PATH, gl.gl_pathv[i]);
run(cmd, "sysctl"); run(cmd, "sysctl");
} }
globfree(&gl); globfree(&gl);
+181 -64
View File
@@ -36,8 +36,15 @@
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
/* Kernel RTC driver validates against this date for sanity check */ /*
* Kernel RTC driver validates against this date for sanity check. The
* on NTP sync the driver can also update the RTC every 11 mins. We use
* the same update interval to handle manual time set and file save.
*/
#define RTC_TIMESTAMP_BEGIN_2000 "2000-01-01 00:00:00" #define RTC_TIMESTAMP_BEGIN_2000 "2000-01-01 00:00:00"
#define RTC_FMT "%Y-%m-%d %H:%M:%S"
#define RTC_PERIOD (11 * 60 * 1000)
#ifdef RTC_TIMESTAMP_CUSTOM #ifdef RTC_TIMESTAMP_CUSTOM
static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM; static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM;
#else #else
@@ -45,6 +52,10 @@ static char *rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
#endif #endif
static time_t rtc_date_fallback = 946684800LL; static time_t rtc_date_fallback = 946684800LL;
static char *rtc_file = RTC_FILE;
static uev_t rtc_timer;
static void tz_set(char *tz, size_t len) static void tz_set(char *tz, size_t len)
{ {
char *ptr; char *ptr;
@@ -68,6 +79,125 @@ static void tz_restore(char *tz)
tzset(); tzset();
} }
static int time_set(struct tm *tm)
{
struct tm fallback = { 0 };
struct timeval tv = { 0 };
char tz[128];
int rc = 0;
tz_set(tz, sizeof(tz));
if (!tm) {
logit(LOG_NOTICE, "Resetting system clock to kernel default, %s.", rtc_timestamp);
tm = &fallback;
/* Attempt to set RTC to a sane value ... */
tv.tv_sec = rtc_date_fallback;
if (!gmtime_r(&tv.tv_sec, tm)) {
rc = 1;
goto out;
}
}
tm->tm_isdst = -1; /* Use tzdata to figure it out, please. */
tv.tv_sec = mktime(tm);
if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) {
errno = EINVAL;
rc = 2;
} else {
if (settimeofday(&tv, NULL) == -1)
rc = 1;
}
out:
tz_restore(tz);
return rc;
}
static int time_get(struct tm *tm)
{
struct timeval tv = { 0 };
char tz[128];
int rc = 0;
tz_set(tz, sizeof(tz));
rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < rtc_date_fallback)
rc = 2;
else
gmtime_r(&tv.tv_sec, tm);
tz_restore(tz);
return rc;
}
static void file_save(void *arg)
{
struct tm tm = { 0 };
int rc = 0;
FILE *fp;
if (!rtc_file)
return;
fp = fopen(rtc_file, "w");
if (!fp) {
logit(LOG_WARNING, "Failed saving system clock to %s, code %d: %s",
rtc_file, errno, strerror(errno));
return;
}
if ((rc = time_get(&tm))) {
logit(LOG_WARNING, "System clock invalid, before %s, not saving", rtc_timestamp);
print_desc(NULL, "System clock invalid, skipping");
} else {
char buf[32] = { 0 };
print_desc(NULL, "Saving system clock to file");
strftime(buf, sizeof(buf), RTC_FMT, &tm);
fprintf(fp, "%s\n", buf);
}
print(rc, NULL);
fclose(fp);
}
static void file_restore(void *arg)
{
struct tm tm = { 0 };
int rc = 1;
FILE *fp;
if (!rtc_file) {
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
return;
}
print_desc(NULL, "Restoring system clock from backup");
fp = fopen(rtc_file, "r");
if (fp) {
char buf[32];
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
strptime(buf, RTC_FMT, &tm);
rc = time_set(&tm);
}
fclose(fp);
} else
logit(LOG_WARNING, "Missing %s", rtc_file);
if (rc) {
time_set(NULL);
rc = 2;
}
print(rc, NULL);
}
static int rtc_open(void) static int rtc_open(void)
{ {
char *alt[] = { char *alt[] = {
@@ -91,10 +221,8 @@ static int rtc_open(void)
static void rtc_save(void *arg) static void rtc_save(void *arg)
{ {
struct timeval tv = { 0 };
struct tm tm = { 0 }; struct tm tm = { 0 };
int fd, rc = 0; int fd, rc = 0;
char tz[128];
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
@@ -105,38 +233,26 @@ static void rtc_save(void *arg)
if (fd < 0) if (fd < 0)
return; return;
tz_set(tz, sizeof(tz)); if ((rc = time_get(&tm))) {
rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < rtc_date_fallback) {
print_desc(NULL, "System clock invalid, not saving to RTC"); print_desc(NULL, "System clock invalid, not saving to RTC");
invalid: } else {
logit(LOG_ERR, "System clock invalid, before %s, not saving to RTC", rtc_timestamp); print_desc(NULL, "Saving system clock (UTC) to RTC");
rc = ioctl(fd, RTC_SET_TIME, &tm);
}
if (rc && errno == EINVAL) {
logit(LOG_WARNING, "System clock invalid, before %s, not saving to RTC", rtc_timestamp);
rc = 2; rc = 2;
goto out;
} }
print_desc(NULL, "Saving system time (UTC) to RTC");
gmtime_r(&tv.tv_sec, &tm);
if (ioctl(fd, RTC_SET_TIME, &tm) < 0) {
if (EINVAL == errno)
goto invalid;
rc = 1;
goto out;
}
out:
tz_restore(tz);
print(rc, NULL); print(rc, NULL);
close(fd); close(fd);
} }
static void rtc_restore(void *arg) static void rtc_restore(void *arg)
{ {
struct timeval tv = { 0 };
struct tm tm = { 0 }; struct tm tm = { 0 };
int fd, rc = 0; int fd, rc = 0;
char tz[128];
if (rescue) { if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
@@ -145,66 +261,62 @@ static void rtc_restore(void *arg)
fd = rtc_open(); fd = rtc_open();
if (fd < 0) { if (fd < 0) {
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore."); file_restore(arg);
return; return;
} }
tz_set(tz, sizeof(tz)); if ((rc = ioctl(fd, RTC_RD_TIME, &tm)) < 0) {
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
char msg[120]; char msg[120];
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s", snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
EINVAL == errno ? "RTC time is too old" : EINVAL == errno ? "RTC time is too old" :
ENOENT == errno ? "RTC has no saved time" : "see log for details"); ENOENT == errno ? "RTC has no saved time" : "see log for details");
print_desc(NULL, msg); print_desc(NULL, msg);
} else {
invalid:
logit(LOG_ERR, "Failed restoring system clock from RTC.");
if (EINVAL == errno)
logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp);
else if (ENOENT == errno)
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno));
/* Been here already? */
if (rc)
goto out;
/* Attempt to set RTC to a sane value ... */
tv.tv_sec = rtc_date_fallback;
if (!gmtime_r(&tv.tv_sec, &tm))
goto out;
logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp);
rc = 2;
}
if (!rc)
print_desc(NULL, "Restoring system clock (UTC) from RTC"); print_desc(NULL, "Restoring system clock (UTC) from RTC");
tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */ rc = time_set(&tm);
tv.tv_sec = mktime(&tm);
if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) {
errno = EINVAL;
goto invalid;
} }
if (settimeofday(&tv, NULL) == -1) if (rc) {
rc = 1; logit(LOG_WARNING, "Failed restoring system clock from RTC.");
if (EINVAL == errno)
logit(LOG_WARNING, "RTC time is too old (before %s)", rtc_timestamp);
else if (ENOENT == errno)
logit(LOG_WARNING, "RTC has no previously saved (valid) time.");
else
logit(LOG_WARNING, "RTC error code %d: %s", errno, strerror(errno));
print(2, NULL);
/* Try restoring from last save game */
if (rtc_file)
file_restore(arg);
} else
print(0, NULL);
out:
tz_restore(tz);
print(rc, NULL);
close(fd); close(fd);
} }
static void save(void *arg)
{
rtc_save(arg);
file_save(arg);
}
static void update(uev_t *w, void *arg, int events)
{
save(arg);
}
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = rtc_restore .cb = rtc_restore
}, },
.hook[HOOK_SHUTDOWN] = { .hook[HOOK_SHUTDOWN] = {
.cb = rtc_save .cb = save
} }
}; };
@@ -212,16 +324,21 @@ PLUGIN_INIT(plugin_init)
{ {
struct tm tm = { 0 }; struct tm tm = { 0 };
if (!strptime(rtc_timestamp, "%Y-%m-%d %H:%M", &tm)) if (!strptime(rtc_timestamp, RTC_FMT, &tm)) {
rtc_date_fallback = mktime(&tm); logit(LOG_WARNING, "Invalid restore date '%s', reverting to '%s'",
else rtc_timestamp, RTC_TIMESTAMP_BEGIN_2000);
rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000; rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
} else
rtc_date_fallback = mktime(&tm);
uev_timer_init(ctx, &rtc_timer, update, NULL, RTC_PERIOD, RTC_PERIOD);
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(plugin_exit)
{ {
uev_timer_stop(&rtc_timer);
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+17 -2
View File
@@ -26,6 +26,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */ #include <sys/time.h> /* gettimeofday() */
#include <sys/types.h> #include <sys/types.h>
#include <sys/resource.h> /* getrusage() */
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
#else #else
@@ -43,13 +44,27 @@
#endif #endif
#ifdef RANDOMSEED #ifdef RANDOMSEED
/*
* This is the fallback seed function, please make sure you have drivers
* enabling /dev/hwrng instead.
*/
static void fallback(FILE *fp) static void fallback(FILE *fp)
{ {
unsigned long seed;
struct timeval tv; struct timeval tv;
struct rusage ru;
int iter = 128; int iter = 128;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
srandom(tv.tv_sec % 3600); getrusage(RUSAGE_SELF, &ru);
/* Mix multiple sources of "randomness" */
seed = tv.tv_sec ^ (tv.tv_usec << 16);
seed ^= ru.ru_utime.tv_sec ^ (ru.ru_utime.tv_usec << 16);
seed ^= ru.ru_stime.tv_sec ^ (ru.ru_stime.tv_usec << 16);
seed ^= getpid() << 8;
srandom(seed);
while (iter--) { while (iter--) {
uint32_t i, prng = random(); uint32_t i, prng = random();
@@ -154,7 +169,7 @@ static void setup(void *arg)
close(fd); close(fd);
free(rpi); free(rpi);
if (rc < 0) if (rc < 0)
logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err)); logit(LOG_WARNING, "Failed adding entropy to kernel random pool: %s", strerror(err));
print_result(rc < 0); print_result(rc < 0);
return; return;
fallback: fallback:
+6 -3
View File
@@ -332,6 +332,11 @@ static void api_cb(uev_t *w, void *arg, int events)
int sd, lvl; int sd, lvl;
svc_t *svc; svc_t *svc;
if (UEV_ERROR == events) {
dbg("%s(): api socket %d invalid.", __func__, w->fd);
goto error;
}
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
err(1, "Failed serving API request"); err(1, "Failed serving API request");
@@ -557,7 +562,7 @@ static void api_cb(uev_t *w, void *arg, int events)
} }
if (uev_io_init(ctx, &svc->notify_watcher, service_notify_cb, svc, sd, UEV_READ)) { if (uev_io_init(ctx, &svc->notify_watcher, service_notify_cb, svc, sd, UEV_READ)) {
err(1, "Falied initializing %s readiness notifier", svc_ident(svc, NULL, 0)); err(1, "Failed initializing %s readiness notifier", svc_ident(svc, NULL, 0));
break; break;
} }
return; /* Don't close sd, used for notify */ return; /* Don't close sd, used for notify */
@@ -578,8 +583,6 @@ static void api_cb(uev_t *w, void *arg, int events)
leave: leave:
close(sd); close(sd);
if (UEV_ERROR == events)
goto error;
return; return;
error: error:
api_exit(); api_exit();
+6 -1
View File
@@ -259,6 +259,11 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
ssize_t sz; ssize_t sz;
size_t off; size_t off;
if (UEV_ERROR == events) {
dbg("%s(): inotify socket %d invalid.", __func__, w->fd);
return;
}
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); err(1, "invalid inotify event");
@@ -471,7 +476,7 @@ void cgroup_init(uev_ctx_t *ctx)
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
if (errno == ENOENT) if (errno == ENOENT)
logit(LOG_INFO, "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 */ else if (errno == EPERM) /* Probably inside an unprivileged container */
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling."); logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
else else
err(1, "Failed mounting cgroup v2"); err(1, "Failed mounting cgroup v2");
+4 -4
View File
@@ -113,7 +113,7 @@ static void cond_boot_strap(void)
* *
* => 'pid/' + 'teamd' + '' = condition <pid/teamd> * => 'pid/' + 'teamd' + '' = condition <pid/teamd>
* *
* When you add a second aggrate you need to tell Finit it's a different * When you add a second aggregate you need to tell Finit it's a different
* instance usugin the :ID syntax: * instance usugin the :ID syntax:
* *
* service :lag2 teamd -f /etc/teamd/lag2.conf -- Aggregate lag2 * service :lag2 teamd -f /etc/teamd/lag2.conf -- Aggregate lag2
@@ -288,7 +288,7 @@ int cond_update(const char *name)
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
int affects = 0; int affects = 0;
dbg("%s", name); // dbg("%s", name);
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue; continue;
@@ -321,7 +321,7 @@ int cond_set_noupdate(const char *name)
void cond_set(const char *name) void cond_set(const char *name)
{ {
dbg("%s", name); // dbg("%s", name);
if (cond_set_noupdate(name)) if (cond_set_noupdate(name))
return; return;
@@ -372,7 +372,7 @@ int cond_clear_noupdate(const char *name)
void cond_clear(const char *name) void cond_clear(const char *name)
{ {
dbg("%s", name); // dbg("%s", name);
if (cond_clear_noupdate(name)) if (cond_clear_noupdate(name))
return; return;
+32 -16
View File
@@ -1432,16 +1432,22 @@ static int conf_change_act(char *dir, char *name, uint32_t mask)
strlcpy(fn, dir, sizeof(fn)); strlcpy(fn, dir, sizeof(fn));
dbg("path: %s mask: %08x", fn, mask); dbg("path: %s mask: %08x", fn, mask);
/* Handle disabling/removal of service */ if (strchr(name, '@')) {
rp = realpath(fn, NULL); /* Skip realpath for templates */
if (!rp) {
if (errno != ENOENT)
goto fail;
rp = strdup(fn); rp = strdup(fn);
if (!rp) } else {
goto fail; /* Handle disabling/removal of service */
rp = realpath(fn, NULL);
if (!rp) {
if (errno != ENOENT)
goto fail;
rp = strdup(fn);
}
} }
if (!rp)
goto fail;
node = conf_find(rp); node = conf_find(rp);
if (node) { if (node) {
dbg("event already registered for %s ...", name); dbg("event already registered for %s ...", name);
@@ -1480,7 +1486,11 @@ int conf_changed(char *file)
if (!file) if (!file)
return 0; return 0;
rp = realpath(file, NULL); if (strchr(file, '@'))
rp = strdup(file);
else
rp = realpath(file, NULL);
if (!rp) if (!rp)
return 0; return 0;
@@ -1532,6 +1542,11 @@ static int conf_iwatch_read(int fd)
static void conf_cb(uev_t *w, void *arg, int events) static void conf_cb(uev_t *w, void *arg, int events)
{ {
if (UEV_ERROR == events) {
dbg("%s(): iwatch socket %d invalid.", __func__, w->fd);
return;
}
if (conf_iwatch_read(w->fd)) { if (conf_iwatch_read(w->fd)) {
err(1, "invalid inotify event"); err(1, "invalid inotify event");
return; return;
@@ -1564,25 +1579,26 @@ int conf_monitor(void)
* have or not have symlinks in place. We need to monitor for * have or not have symlinks in place. We need to monitor for
* changes to either symlink or target. * changes to either symlink or target.
*/ */
rc += iwatch_add(&iw_conf, finit_rcsd, IN_ONLYDIR); rc |= iwatch_add(&iw_conf, finit_rcsd, IN_ONLYDIR);
snprintf(path, sizeof(path), "%s/available/", finit_rcsd); snprintf(path, sizeof(path), "%s/available/", finit_rcsd);
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW); rc |= iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd); snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd);
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW); rc |= iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
rc += iwatch_add(&iw_conf, finit_conf, 0); rc |= iwatch_add(&iw_conf, finit_conf, 0);
/* /*
* Systems with /etc/default, /etc/conf.d, or similar, can also * Systems with /etc/default, /etc/conf.d, or similar, can also
* monitor changes in env files sourced by .conf files (above) * monitor changes in env files sourced by .conf files (above)
* define your own with --with-sysconfig=/path/to/envfiles * define your own with --with-sysconfig=/path/to/envfiles
*/ */
rc += iwatch_add(&iw_conf, "/etc/default/", IN_ONLYDIR); rc |= iwatch_add(&iw_conf, "/etc/default/", IN_ONLYDIR);
rc += iwatch_add(&iw_conf, "/etc/conf.d/", IN_ONLYDIR); rc |= iwatch_add(&iw_conf, "/etc/conf.d/", IN_ONLYDIR);
#ifdef FINIT_SYSCONFIG #ifdef FINIT_SYSCONFIG
rc += iwatch_add(&iw_conf, FINIT_SYSCONFIG, IN_ONLYDIR); rc |= iwatch_add(&iw_conf, FINIT_SYSCONFIG, IN_ONLYDIR);
#endif #endif
rc |= conf_reload();
return rc + conf_reload(); return rc;
} }
/* /*
+3 -3
View File
@@ -199,7 +199,6 @@ int run(char *cmd, char *log)
int run_interactive(char *cmd, char *fmt, ...) int run_interactive(char *cmd, char *fmt, ...)
{ {
int status, oldout = 1, olderr = 2; int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
FILE *fp; FILE *fp;
if (!cmd) { if (!cmd) {
@@ -244,6 +243,7 @@ int run_interactive(char *cmd, char *fmt, ...)
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */ /* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
if (fp && !debug) { if (fp && !debug) {
char line[LINE_SIZE];
size_t len, written; size_t len, written;
rewind(fp); rewind(fp);
@@ -281,7 +281,7 @@ int exec_runtask(char *cmd, char *args[])
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[]) static void prepare_tty(char *tty, speed_t speed, const char *procname, struct rlimit rlimit[])
{ {
char name[80]; char name[80];
int fd, dummy; int fd, dummy;
@@ -351,7 +351,7 @@ static int activate_console(int noclear, int nowait)
dprint(STDERR_FILENO, clr, strlen(clr)); dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg)); dprint(STDERR_FILENO, msg, strlen(msg));
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n') while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
continue; ;
/* On any error (likely EINTR), we avoid starting getty */ /* On any error (likely EINTR), we avoid starting getty */
if (rc == -1) if (rc == -1)
+9 -4
View File
@@ -66,7 +66,7 @@ int prevlevel = -1;
int debug = 0; /* debug mode from kernel cmdline */ int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */ int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */ int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootrapping (for TTYs) */ int bootstrap = 1; /* set while bootstrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */ int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
int syncsec = 0; /* reboot delay */ int syncsec = 0; /* reboot delay */
int readiness = SVC_NOTIFY_PID; int readiness = SVC_NOTIFY_PID;
@@ -272,10 +272,15 @@ static int fsck(int pass)
* errors were corrected but that the boot may proceed. * errors were corrected but that the boot may proceed.
*/ */
if (fsck_rc > 1) { if (fsck_rc > 1) {
logit(LOG_CONSOLE | LOG_ALERT, "Failed fsck %s, attempting sulogin ...", dev); if (hasmntopt(mnt,"nofail")) {
sulogin(1); logit(LOG_CONSOLE | LOG_WARNING, "Ignoring failed fsck %s because of nofail option", dev);
fsck_rc = 0;
} else {
logit(LOG_CONSOLE | LOG_ALERT, "Failed fsck %s, attempting sulogin ...", dev);
sulogin(1);
}
} }
rc += fsck_rc; rc |= fsck_rc;
} }
endmntent(fp); endmntent(fp);
+2 -2
View File
@@ -312,9 +312,9 @@ static int exec_login(char *name)
struct stat st; struct stat st;
if (passenv) if (passenv)
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, "-p", "--", name, NULL);
else else
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, "--", name, NULL);
/* /*
* Failed to exec login, should not happen on normal systems. * Failed to exec login, should not happen on normal systems.
+5 -1
View File
@@ -87,6 +87,9 @@ void console_init(void)
/* Enable line wrap, if disabled previously, e.g., qemu */ /* Enable line wrap, if disabled previously, e.g., qemu */
dprint(STDOUT_FILENO, "\033[?7h", 5); dprint(STDOUT_FILENO, "\033[?7h", 5);
/* Reset attributes, background and foreground color */
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
log_init(); log_init();
} }
@@ -582,7 +585,8 @@ int in_container(void)
"lxc", "lxc",
"docker", "docker",
"kubepod", "kubepod",
"unshare" "unshare",
"systemd-nspawn"
}; };
const char *files[] = { const char *files[] = {
"/run/.containerenv", "/run/.containerenv",
+2 -1
View File
@@ -141,6 +141,7 @@ static inline int ln(const char *target, const char *linkpath)
return 0; return 0;
} }
static inline int dprint(int fd, const char *s, size_t len) static inline int dprint(int fd, const char *s, size_t len)
{ {
size_t loop = 3; size_t loop = 3;
@@ -159,7 +160,7 @@ static inline int dprint(int fd, const char *s, size_t len)
return rc; return rc;
} }
static inline char *fgetval(char *line, const char *key, char *sep) static inline char *fgetval(const char *line, const char *key, char *sep)
{ {
char *ptr, *str, *copy; char *ptr, *str, *copy;
size_t len; size_t len;
+1 -1
View File
@@ -872,7 +872,7 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len, int ansi)
{ {
int bold = missing(svc); int bold = missing(svc);
if (!ansi || svc_checkenv(svc) || svc->env[0] == '-') if (!ansi || svc_checkenv(svc))
bold = 0; bold = 0;
strlcpy(buf, bold ? "\e[1m" : "", len); strlcpy(buf, bold ? "\e[1m" : "", len);
+6
View File
@@ -96,6 +96,12 @@ int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
if (!initialized) if (!initialized)
return -1; return -1;
iwp = iwatch_find_by_path(iw, file);
if (iwp) {
dbg("skipping, %s already registered", file);
return 0;
}
if (!fexist(file)) { if (!fexist(file)) {
dbg("skipping %s: no such file or directory", file); dbg("skipping %s: no such file or directory", file);
return 0; return 0;
+1 -1
View File
@@ -139,7 +139,7 @@ void logit(int prio, const char *fmt, ...)
goto done; goto done;
} }
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio); fprintf(fp, "<%d>finit[1]: ", LOG_DAEMON | prio);
vfprintf(fp, fmt, ap); vfprintf(fp, fmt, ap);
fclose(fp); fclose(fp);
+1 -1
View File
@@ -192,7 +192,7 @@ int pid_file_set(svc_t *svc, char *file, int not)
* pid:foo.tla --> /run/foo.tla.pid * pid:foo.tla --> /run/foo.tla.pid
* pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!) * pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!)
* *
* An exclamation mark, or logial NOT, in the first character position * An exclamation mark, or logical NOT, in the first character position
* indicates that Finit should *not* manage the PID file, and that the * indicates that Finit should *not* manage the PID file, and that the
* process uses a "non-standard" location. Eg. if service is 'bar' * process uses a "non-standard" location. Eg. if service is 'bar'
* Finit would assume the PID file is /run/bar.pid and the below simply * Finit would assume the PID file is /run/bar.pid and the below simply
+2
View File
@@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
#include "log.h"
#include "schedule.h" #include "schedule.h"
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */ #define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
@@ -35,6 +36,7 @@ static void cb(uev_t *w, void *arg, int events)
struct wq *work = (struct wq *)arg; struct wq *work = (struct wq *)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("%s(): spurious problem with schedule work timer, restarting.", __func__);
uev_timer_start(w); uev_timer_start(w);
return; return;
} }
+240 -76
View File
@@ -81,7 +81,12 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
{ {
svc_t *svc = arg; svc_t *svc = arg;
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */ if (UEV_ERROR == events) {
dbg("%s(): spurious problem, svc %s.", __func__, svc_ident(svc, NULL, 0));
uev_timer_start(w);
return;
}
if (svc->timer_cb) if (svc->timer_cb)
svc->timer_cb(svc); svc->timer_cb(svc);
} }
@@ -100,6 +105,9 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
*/ */
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 (timeout == 0)
return 0; /* OK, not starting timer. */
if (svc->timer_cb) if (svc->timer_cb)
return -EBUSY; return -EBUSY;
@@ -147,16 +155,17 @@ static void service_script_kill(svc_t *svc)
continue; continue;
dbg("Killing service %s script PID %d.", svc_ident(svc, NULL, 0), ptr->pid); dbg("Killing service %s script PID %d.", svc_ident(svc, NULL, 0), ptr->pid);
kill(-ptr->pid, SIGKILL); kill(ptr->pid, SIGKILL);
TAILQ_REMOVE(&svc_assoc_list, ptr, link); TAILQ_REMOVE(&svc_assoc_list, ptr, link);
free(ptr); free(ptr);
} }
} }
static int service_script_add(svc_t *svc, pid_t pid) static int service_script_add(svc_t *svc, pid_t pid, int tmo)
{ {
struct assoc *ptr = malloc(sizeof(*ptr)); struct assoc *ptr;
ptr = malloc(sizeof(*ptr));
if (!ptr) { if (!ptr) {
err(1, "Failed starting service script timer"); err(1, "Failed starting service script timer");
return 1; return 1;
@@ -166,7 +175,7 @@ static int service_script_add(svc_t *svc, pid_t pid)
ptr->pid = pid; ptr->pid = pid;
TAILQ_INSERT_TAIL(&svc_assoc_list, ptr, link); TAILQ_INSERT_TAIL(&svc_assoc_list, ptr, link);
service_timeout_after(svc, svc->killdelay, service_script_kill); service_timeout_after(svc, tmo, service_script_kill);
return 0; return 0;
} }
@@ -484,28 +493,21 @@ static void source_env(svc_t *svc)
while (*end && !isspace(*end)) while (*end && !isspace(*end))
end++; end++;
if (*end != 0) { if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val); warnx("'%s=%s': not a valid identifier", key, value);
continue; /* invalid key */ continue; /* invalid key */
} }
switch (wordexp(value, &we, 0)) { if (wordexp(value, &we, 0)) {
case 0: setenv(key, value, 1);
} else {
for (i = 0, *val = 0; i < we.we_wordc; i++) { for (i = 0, *val = 0; i < we.we_wordc; i++) {
if (i > 0) if (i > 0)
strlcat(val, " ", LINE_SIZE); strlcat(val, " ", LINE_SIZE);
strlcat(val, we.we_wordv[i], LINE_SIZE); strlcat(val, we.we_wordv[i], LINE_SIZE);
} }
setenv(key, val, 1); setenv(key, val, 1);
wordfree(&we);
break;
case WRDE_NOSPACE:
wordfree(&we);
/* fallthrough */
default:
setenv(key, value, 1);
break;
} }
wordfree(&we);
} }
fclose(fp); fclose(fp);
@@ -913,12 +915,12 @@ static void service_kill(svc_t *svc)
return; return;
} }
dbg("%s: Sending SIGKILL to process group %d", nm, svc->pid); dbg("%s: Sending SIGKILL to process %d", nm, svc->pid);
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s[%d], sending SIGKILL ...", id, svc->pid); logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s[%d], sending SIGKILL ...", id, svc->pid);
if (runlevel != 1) if (runlevel != 1)
print_desc("Killing ", svc->desc); print_desc("Killing ", svc->desc);
kill(-svc->pid, SIGKILL); kill(svc->pid, SIGKILL);
/* Let SIGKILLs stand out, show result as [WARN] */ /* Let SIGKILLs stand out, show result as [WARN] */
if (runlevel != 1) if (runlevel != 1)
@@ -1297,12 +1299,44 @@ static void parse_killdelay(svc_t *svc, char *delay)
svc->killdelay = (int)(sec * 1000); svc->killdelay = (int)(sec * 1000);
} }
static void parse_script(char *type, char *script, char *buf, size_t len) /*
* pre:[0-3600,]/path/to/script
*/
static void parse_script(svc_t *svc, char *type, char *script, int *tmo, char *buf, size_t len)
{ {
if (access(script, X_OK)) char *found, *path;
logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.", type, script);
else path = strchr(script, ',');
strlcpy(buf, script, len); if (path) {
const char *errstr;
long long sec;
*path++ = 0;
sec = strtonum(script, 0, 3600, &errstr);
if (errstr) {
errx(1, "%s: tmo %s is %s (0-3600)", svc_ident(svc, NULL, 0),
script, errstr);
return;
}
*tmo = (int)(sec * 1000);
} else {
path = script;
*tmo = svc->killdelay;
}
found = which(path);
if (!found) {
/* Not in $PATH, check if path exists and is executable */
if (access(path, X_OK)) {
logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.",
type, path);
return;
}
} else
free(found);
strlcpy(buf, path, len);
} }
/* /*
@@ -1488,6 +1522,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *id = NULL, *env = NULL, *cgroup = NULL; char *id = NULL, *env = NULL, *cgroup = NULL;
char *pre_script = NULL, *post_script = NULL; char *pre_script = NULL, *post_script = NULL;
char *ready_script = NULL, *conflict = NULL; char *ready_script = NULL, *conflict = NULL;
char *cleanup_script = NULL;
char ident[MAX_IDENT_LEN]; char ident[MAX_IDENT_LEN];
char *ifstmt = NULL; char *ifstmt = NULL;
char *notify = NULL; char *notify = NULL;
@@ -1594,6 +1629,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
post_script = arg; post_script = arg;
else if (MATCH_CMD(cmd, "ready:", arg)) else if (MATCH_CMD(cmd, "ready:", arg))
ready_script = arg; ready_script = arg;
else if (MATCH_CMD(cmd, "cleanup:", arg))
cleanup_script = arg;
else if (MATCH_CMD(cmd, "env:", arg)) else if (MATCH_CMD(cmd, "env:", arg))
env = arg; env = arg;
/* catch both cgroup: and cgroup. handled in parse_cgroup() */ /* catch both cgroup: and cgroup. handled in parse_cgroup() */
@@ -1638,6 +1675,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (tty_parse_args(&tty, cmd, &args)) if (tty_parse_args(&tty, cmd, &args))
return errno; return errno;
/* NOTE: this may result in dev == NULL! */
if (tty_isatcon(tty.dev)) if (tty_isatcon(tty.dev))
dev = tty_atcon(); dev = tty_atcon();
else else
@@ -1670,17 +1708,19 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
respawn = 1; respawn = 1;
/* Create name:id tuple for identity, e.g., tty:S0 */ /* Create name:id tuple for identity, e.g., tty:S0 */
ptr = strrchr(dev, '/'); if (dev) {
if (ptr) ptr = strrchr(dev, '/');
ptr++; if (ptr)
else ptr++;
ptr = dev; else
if (!strncmp(ptr, "tty", 3)) ptr = dev;
ptr += 3; if (!strncmp(ptr, "tty", 3))
ptr += 3;
name = "tty"; name = "tty";
if (!id || id[0] == 0) if (!id || id[0] == 0)
id = ptr; id = ptr;
}
svc = svc_find_by_tty(dev); svc = svc_find_by_tty(dev);
} else } else
@@ -1770,17 +1810,22 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else else
svc->killdelay = SVC_TERM_TIMEOUT; svc->killdelay = SVC_TERM_TIMEOUT;
if (pre_script) if (pre_script)
parse_script("pre", pre_script, svc->pre_script, sizeof(svc->pre_script)); parse_script(svc, "pre", pre_script, &svc->pre_tmo, svc->pre_script, sizeof(svc->pre_script));
else else
memset(svc->pre_script, 0, sizeof(svc->pre_script)); memset(svc->pre_script, 0, sizeof(svc->pre_script));
if (post_script) if (post_script)
parse_script("post", post_script, svc->post_script, sizeof(svc->post_script)); parse_script(svc, "post", post_script, &svc->post_tmo, svc->post_script, sizeof(svc->post_script));
else else
memset(svc->post_script, 0, sizeof(svc->post_script)); memset(svc->post_script, 0, sizeof(svc->post_script));
if (ready_script) if (ready_script)
parse_script("ready", ready_script, svc->ready_script, sizeof(svc->ready_script)); parse_script(svc, "ready", ready_script, &svc->ready_tmo, svc->ready_script, sizeof(svc->ready_script));
else else
memset(svc->ready_script, 0, sizeof(svc->ready_script)); memset(svc->ready_script, 0, sizeof(svc->ready_script));
if (cleanup_script)
parse_script(svc, "cleanup", cleanup_script, &svc->cleanup_tmo, svc->cleanup_script, sizeof(svc->cleanup_script));
else
memset(svc->cleanup_script, 0, sizeof(svc->cleanup_script));
if (!svc_is_tty(svc)) { if (!svc_is_tty(svc)) {
if (log) if (log)
parse_log(svc, log); parse_log(svc, log);
@@ -1880,8 +1925,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
} }
/* /*
* This function is called when cleaning up lingering (stopped) services * This function is called at the end of a runlevel change or reload
* after a .conf reload. * command to delete all remnants of removed services.
* *
* We need to ensure we properly stop the service before removing it, * We need to ensure we properly stop the service before removing it,
* including stopping any pending restart or SIGKILL timers before we * including stopping any pending restart or SIGKILL timers before we
@@ -1904,24 +1949,36 @@ void service_unregister(svc_t *svc)
void service_monitor(pid_t lost, int status) void service_monitor(pid_t lost, int status)
{ {
int sig = WIFSIGNALED(status);
int rc = WEXITSTATUS(status);
int ok = WIFEXITED(status);
svc_t *svc; svc_t *svc;
if (lost <= 1) if (lost <= 1)
return; return;
/* main process as well as pre: and post: scripts use svc->pid */
svc = svc_find_by_pid(lost); svc = svc_find_by_pid(lost);
if (!svc) { if (!svc) {
/* Check if ready: script in assoc list */
if (service_script_del(lost)) if (service_script_del(lost))
dbg("collected unknown PID %d", lost); dbg("collected unknown PID %d", lost);
return; return;
} }
switch (svc->state) { switch (svc->state) {
case SVC_CLEANUP_STATE:
case SVC_SETUP_STATE: case SVC_SETUP_STATE:
/* If the setup phase fails, drive svc to crashed. */
svc->status = status;
/* fallthrough */
case SVC_TEARDOWN_STATE:
case SVC_CLEANUP_STATE:
dbg("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d", dbg("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script, (svc->state == SVC_TEARDOWN_STATE
lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status)); ? svc->post_script
: (svc->state == SVC_CLEANUP_STATE
? svc->cleanup_script
: svc->pre_script)), lost, ok, sig, rc);
/* Kill all children in the same proess group, e.g. logit */ /* Kill all children in the same proess group, e.g. logit */
dbg("Killing lingering children in same process group ..."); dbg("Killing lingering children in same process group ...");
kill(-svc->pid, SIGKILL); kill(-svc->pid, SIGKILL);
@@ -1929,7 +1986,7 @@ void service_monitor(pid_t lost, int status)
default: default:
dbg("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d", dbg("collected %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc_ident(svc, NULL, 0), lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status)); svc_ident(svc, NULL, 0), lost, ok, sig, rc);
svc->status = status; svc->status = status;
break; break;
} }
@@ -1950,14 +2007,11 @@ void service_monitor(pid_t lost, int status)
kill(-svc->pid, SIGKILL); kill(-svc->pid, SIGKILL);
/* Try removing PID file (in case service does not clean up after itself) */ /* Try removing PID file (in case service does not clean up after itself) */
if (svc_is_daemon(svc) || svc_is_tty(svc)) { if (svc_is_daemon(svc) || svc_is_sysv(svc) || svc_is_tty(svc)) {
service_cleanup(svc); service_cleanup(svc);
} else if (svc_is_runtask(svc)) { } else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */ /* run/task should run at least once per runlevel */
if (WIFEXITED(status)) svc->started = ok;
svc->started = 1;
else
svc->started = 0;
} }
done: done:
@@ -1965,7 +2019,7 @@ done:
svc->start_time = svc->pid = 0; svc->start_time = svc->pid = 0;
cont: cont:
if (lost == run_block_pid) { if (lost == run_block_pid) {
int result = WIFEXITED(status) ? WEXITSTATUS(status) : 1; int result = ok ? rc : 1;
svc_mark_clean(svc); /* done, regardless of exit status */ svc_mark_clean(svc); /* done, regardless of exit status */
run_block_pid = 0; run_block_pid = 0;
@@ -2001,6 +2055,10 @@ static void svc_mark_affected(char *cond)
* Called on conf_reload() to update service reverse dependencies. * Called on conf_reload() to update service reverse dependencies.
* E.g., if ospfd depends on zebra and the zebra Finit conf has * E.g., if ospfd depends on zebra and the zebra Finit conf has
* changed, we need to mark the ospfd Finit conf as changed too. * changed, we need to mark the ospfd Finit conf as changed too.
*
* However, a daemon that depends on syslogd (sysklogd project), need
* not be reloeaded (SIGHUP'ed or stop/started) because syslogd support
* reloading its configuration file on SIGHUP.
*/ */
void service_update_rdeps(void) void service_update_rdeps(void)
{ {
@@ -2012,6 +2070,10 @@ void service_update_rdeps(void)
if (!svc_is_changed(svc)) if (!svc_is_changed(svc))
continue; continue;
/* Service supports reloading conf without stop/start */
if (!svc_is_nohup(svc))
continue; /* Yup, no need to stop start rdeps */
svc_mark_affected(mkcond(svc, cond, sizeof(cond))); svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
} }
} }
@@ -2040,7 +2102,8 @@ static void service_kill_script(svc_t *svc)
if (svc->pid <= 1) if (svc->pid <= 1)
return; return;
kill(-svc->pid, SIGKILL); dbg("Timeout, killing service %s script PID %d", svc_ident(svc, NULL, 0), svc->pid);
kill(svc->pid, SIGKILL);
} }
/* /*
@@ -2051,6 +2114,9 @@ static void set_pre_post_envs(svc_t *svc, const char *type)
char buf[25 + 256] = "/etc/default:/etc/conf.d"; char buf[25 + 256] = "/etc/default:/etc/conf.d";
setenv("SERVICE_TYPE", svc_typestr(svc), 1); setenv("SERVICE_TYPE", svc_typestr(svc), 1);
setenv("SERVICE_NAME", svc->name, 1);
if (svc->id[0])
setenv("SERVICE_ID", svc->id, 1);
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1); setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
setenv("SERVICE_SCRIPT_TYPE", type, 1); setenv("SERVICE_SCRIPT_TYPE", type, 1);
@@ -2079,21 +2145,24 @@ static void service_pre_script(svc_t *svc)
}; };
char *env_file; char *env_file;
redirect(svc);
/* Warning in service_start() after svc_checkenv() */
env_file = svc_getenv(svc); env_file = svc_getenv(svc);
if (env_file) if (env_file && fexist(env_file))
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->pre_script); snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->pre_script);
else else
strlcpy(buf, svc->pre_script, sizeof(buf)); strlcpy(buf, svc->pre_script, sizeof(buf));
set_pre_post_envs(svc, "pre"); set_pre_post_envs(svc, "pre");
sig_unblock();
execvp(_PATH_BSHELL, argv); execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR); _exit(EX_OSERR);
} }
dbg("%s: pre:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->pre_script, svc->pid); dbg("%s: pre:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->pre_script, svc->pid);
service_timeout_after(svc, svc->pre_tmo, service_kill_script);
/* Short hard-coded timeout to prevent locking up Finit */
service_timeout_after(svc, svc->killdelay, service_kill_script);
} }
static void service_post_script(svc_t *svc) static void service_post_script(svc_t *svc)
@@ -2118,12 +2187,14 @@ static void service_post_script(svc_t *svc)
rc = WEXITSTATUS(svc->status); rc = WEXITSTATUS(svc->status);
sig = WTERMSIG(svc->status); sig = WTERMSIG(svc->status);
/* Warning in service_start() after svc_checkenv() */
env_file = svc_getenv(svc); env_file = svc_getenv(svc);
if (env_file) if (env_file && fexist(env_file))
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->post_script); snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->post_script);
else else
strlcpy(buf, svc->post_script, sizeof(buf)); strlcpy(buf, svc->post_script, sizeof(buf));
set_pre_post_envs(svc, "post"); set_pre_post_envs(svc, "post");
sig_unblock();
if (WIFEXITED(svc->status)) { if (WIFEXITED(svc->status)) {
char val[4]; char val[4];
@@ -2144,11 +2215,16 @@ static void service_post_script(svc_t *svc)
} }
dbg("%s: post:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->post_script, svc->pid); dbg("%s: post:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->post_script, svc->pid);
service_timeout_after(svc, svc->post_tmo, service_kill_script);
/* Short hard-coded timeout to prevent locking up Finit */
service_timeout_after(svc, svc->killdelay, service_kill_script);
} }
/*
* Unlike the pre: and post: scripts, the ready: script cannot reuse
* svc->pid, so instead we use an assoc. list for matching any scripts
* related to the svc, handled by service_script_add() below. When a
* ready: script is reaped the service_monitor() checks first for the
* svc, then calls service_script_del().
*/
void service_ready_script(svc_t *svc) void service_ready_script(svc_t *svc)
{ {
pid_t pid; pid_t pid;
@@ -2172,21 +2248,60 @@ void service_ready_script(svc_t *svc)
}; };
char *env_file; char *env_file;
/* Warning in service_start() after svc_checkenv() */
env_file = svc_getenv(svc); env_file = svc_getenv(svc);
if (env_file) if (env_file && fexist(env_file))
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->ready_script); snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->ready_script);
else else
strlcpy(buf, svc->ready_script, sizeof(buf)); strlcpy(buf, svc->ready_script, sizeof(buf));
set_pre_post_envs(svc, "ready"); set_pre_post_envs(svc, "ready");
sig_unblock();
execvp(_PATH_BSHELL, argv); execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR); _exit(EX_OSERR);
} }
dbg("%s: ready:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->ready_script, pid); dbg("%s: ready:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->ready_script, pid);
service_script_add(svc, pid, svc->ready_tmo);
}
/* Short hard-coded timeout to prevent locking up Finit */ static void service_cleanup_script(svc_t *svc)
service_script_add(svc, pid); {
svc->pid = service_fork(svc);
if (svc->pid < 0) {
err(1, "Failed forking off %s cleanup:script %s", svc_ident(svc, NULL, 0), svc->cleanup_script);
return;
}
if (svc->pid == 0) {
char buf[CMD_SIZE];
char *argv[4] = {
"sh",
"-ac",
buf,
NULL
};
char *env_file;
redirect(svc);
/* Warning in service_start() after svc_checkenv() */
env_file = svc_getenv(svc);
if (env_file && fexist(env_file))
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->cleanup_script);
else
strlcpy(buf, svc->cleanup_script, sizeof(buf));
set_pre_post_envs(svc, "cleanup");
sig_unblock();
execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR);
}
dbg("%s: cleanup:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->cleanup_script, svc->pid);
service_timeout_after(svc, svc->cleanup_tmo, service_kill_script);
} }
static void service_retry(svc_t *svc) static void service_retry(svc_t *svc)
@@ -2293,7 +2408,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
} }
} }
if (svc_is_daemon(svc)) { if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0)); snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0));
@@ -2376,10 +2491,12 @@ restart:
switch (svc->state) { switch (svc->state) {
case SVC_HALTED_STATE: case SVC_HALTED_STATE:
if (enabled) if (enabled) {
svc_set_state(svc, SVC_WAITING_STATE); svc_set_state(svc, SVC_WAITING_STATE);
else { } else {
if (svc_is_conflict(svc)) { if (svc_is_removed(svc)) {
svc_set_state(svc, SVC_DEAD_STATE);
} else if (svc_is_conflict(svc)) {
#if 0 #if 0
logit(svc->nowarn ? LOG_DEBUG : LOG_INFO, logit(svc->nowarn ? LOG_DEBUG : LOG_INFO,
"%s in conflict with %s, checking again ...", "%s in conflict with %s, checking again ...",
@@ -2391,6 +2508,30 @@ restart:
} }
break; break;
case SVC_TEARDOWN_STATE:
if (!svc->pid) {
dbg("%s: post script done.", svc_ident(svc, NULL, 0));
service_timeout_cancel(svc);
if (svc_is_removed(svc) && svc_has_cleanup(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE);
service_cleanup_script(svc);
} else
svc_set_state(svc, SVC_HALTED_STATE);
}
break;
case SVC_CLEANUP_STATE:
if (!svc->pid) {
dbg("%s: cleanup script done.", svc_ident(svc, NULL, 0));
svc_set_state(svc, SVC_HALTED_STATE);
}
break;
case SVC_DEAD_STATE:
/* End of the line ☠ */
break;
case SVC_DONE_STATE: case SVC_DONE_STATE:
if (svc_is_changed(svc)) if (svc_is_changed(svc))
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
@@ -2413,8 +2554,11 @@ restart:
case SVC_TYPE_SYSV: case SVC_TYPE_SYSV:
case SVC_TYPE_TTY: case SVC_TYPE_TTY:
if (svc_has_post(svc)) { if (svc_has_post(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE); svc_set_state(svc, SVC_TEARDOWN_STATE);
service_post_script(svc); service_post_script(svc);
} else if (svc_is_removed(svc) && svc_has_cleanup(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE);
service_cleanup_script(svc);
} else } else
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
break; break;
@@ -2433,14 +2577,33 @@ restart:
} }
break; break;
case SVC_CLEANUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_HALTED_STATE);
break;
case SVC_SETUP_STATE: case SVC_SETUP_STATE:
if (!svc->pid) if (!enabled) {
svc_set_state(svc, SVC_STARTING_STATE); service_stop(svc);
break;
}
if (!svc->pid) {
int rc = WEXITSTATUS(svc->status);
int ok = WIFEXITED(svc->status);
if (!ok || rc) {
const char *sig;
if (WIFSIGNALED(svc->status))
sig = sig_name(WTERMSIG(svc->status));
else
sig = "none";
logit(LOG_WARNING, "Service %s pre:%s failed, rc: %d sig:%s",
svc_ident(svc, NULL, 0), svc->pre_script, rc, sig);
svc_set_state(svc, SVC_STOPPING_STATE);
svc_crashing(svc);
} else {
svc_set_state(svc, SVC_STARTING_STATE);
}
}
break; break;
case SVC_WAITING_STATE: case SVC_WAITING_STATE:
@@ -2521,7 +2684,7 @@ restart:
break; break;
} }
if (svc_is_runtask(svc)) { /* only run/task, not sysv here */ if (svc_is_runtask(svc)) {
svc_set_state(svc, SVC_STOPPING_STATE); svc_set_state(svc, SVC_STOPPING_STATE);
svc->restart_tot++; svc->restart_tot++;
svc->once++; svc->once++;
@@ -2743,7 +2906,7 @@ void service_notify_cb(uev_t *w, void *arg, int events)
ssize_t len; ssize_t len;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
warn("Spurious problem with %s notify callback, restarting.", svc_ident(svc, NULL, 0)); dbg("Spurious problem with %s notify callback, restarting.", svc_ident(svc, NULL, 0));
uev_io_start(w); uev_io_start(w);
return; return;
} }
@@ -2793,7 +2956,7 @@ void service_notify_cb(uev_t *w, void *arg, int events)
* *
* This does not affect the restart_tot counter, which you can see in * This does not affect the restart_tot counter, which you can see in
* the output from 'initctl status foo', along with this instability * the output from 'initctl status foo', along with this instability
* "index" in parethesis: total (cnt/max) * "index" in parenthesis: total (cnt/max)
*/ */
static void service_interval_cb(uev_t *w, void *arg, int events) static void service_interval_cb(uev_t *w, void *arg, int events)
{ {
@@ -2801,6 +2964,7 @@ static void service_interval_cb(uev_t *w, void *arg, int events)
(void)arg; (void)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("%s(): spurious problem, restarting.", __func__);
uev_timer_start(w); uev_timer_start(w);
return; return;
} }
+2
View File
@@ -232,6 +232,8 @@ void do_iterate_proc(int (*cb)(int, void *), void *data)
dbg("Skipping %s ...", file); dbg("Skipping %s ...", file);
else if (file[0] == '@') else if (file[0] == '@')
dbg("Skipping %s ...", &file[1]); dbg("Skipping %s ...", &file[1]);
else if (strstr(file, "initctl"))
dbg("Skipping %s ...", file);
else else
if (cb(pid, data)) { if (cb(pid, data)) {
dbg("PID %d is still alive (%s)", pid, file); dbg("PID %d is still alive (%s)", pid, file);
+50 -4
View File
@@ -107,6 +107,9 @@ static char *sm_status(sm_state_t state)
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
return "bootstrap"; return "bootstrap";
case SM_BOOTSTRAP_WAIT_STATE:
return "bootstrap/wait";
case SM_RUNNING_STATE: case SM_RUNNING_STATE:
return "running"; return "running";
@@ -116,15 +119,20 @@ static char *sm_status(sm_state_t state)
case SM_RUNLEVEL_WAIT_STATE: case SM_RUNLEVEL_WAIT_STATE:
return "runlevel/wait"; return "runlevel/wait";
case SM_RUNLEVEL_CLEAN_STATE:
return "runlevel/clean";
case SM_RELOAD_CHANGE_STATE: case SM_RELOAD_CHANGE_STATE:
return "reload/change"; return "reload/change";
case SM_RELOAD_WAIT_STATE: case SM_RELOAD_WAIT_STATE:
return "reload/wait"; return "reload/wait";
default: case SM_RELOAD_CLEAN_STATE:
return "unknown"; return "reload/clean";
} }
return "unknown";
} }
static char sm_runlevel(int lvl) static char sm_runlevel(int lvl)
@@ -169,11 +177,17 @@ void sm_init(sm_t *sm)
void sm_set_runlevel(sm_t *sm, int newlevel) void sm_set_runlevel(sm_t *sm, int newlevel)
{ {
sm->newlevel = newlevel; sm->newlevel = newlevel;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
} }
void sm_set_reload(sm_t *sm) void sm_set_reload(sm_t *sm)
{ {
sm->reload = 1; sm->reload = 1;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
} }
int sm_is_in_teardown(sm_t *sm) int sm_is_in_teardown(sm_t *sm)
@@ -195,7 +209,7 @@ restart:
switch (sm->state) { switch (sm->state) {
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
dbg("Bootstrapping all services in runlevel S from %s", finit_conf); dbg("Bootstrapping all services in runlevel S from %s", finit_conf);
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE); service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV);
sm->state = SM_BOOTSTRAP_WAIT_STATE; sm->state = SM_BOOTSTRAP_WAIT_STATE;
break; break;
@@ -229,7 +243,7 @@ restart:
/* Disable progress output at normal runtime */ /* Disable progress output at normal runtime */
enable_progress(0); enable_progress(0);
/* System bootrapped, launch TTYs et al */ /* System bootstrapped, launch TTYs et al */
bootstrap = 0; bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN); service_step_all(SVC_TYPE_RESPAWN);
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
@@ -313,6 +327,22 @@ restart:
sm->in_teardown = 0; sm->in_teardown = 0;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_CLEAN_STATE;
break;
case SM_RUNLEVEL_CLEAN_STATE:
/*
* Wait for post:script or cleanup:script to be collected,
* which moves the svc to HALTED or DEAD state. We will
* be called by the service_monitor() on collect.
*/
svc = svc_clean_completed();
if (svc) {
dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break;
}
/* Cleanup stale services */ /* Cleanup stale services */
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
@@ -366,6 +396,22 @@ restart:
dbg("Starting services after reconf ..."); dbg("Starting services after reconf ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_CLEAN_STATE;
break;
case SM_RELOAD_CLEAN_STATE:
/*
* Wait for post:script or cleanup:script to be collected,
* which moves the svc to HALTED or DEAD state. We will
* be called by the service_monitor() on collect.
*/
svc = svc_clean_completed();
if (svc) {
dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break;
}
/* Cleanup stale services */ /* Cleanup stale services */
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
+4 -2
View File
@@ -29,9 +29,11 @@ typedef enum {
SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */ SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */
SM_RUNNING_STATE, /* Normal state, services running */ SM_RUNNING_STATE, /* Normal state, services running */
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */ SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */ SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped processes to halt */
SM_RUNLEVEL_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */ SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */ SM_RELOAD_WAIT_STATE, /* Waiting for all stopped processes to halt */
SM_RELOAD_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
} sm_state_t; } sm_state_t;
typedef struct sm { typedef struct sm {
+38 -1
View File
@@ -348,6 +348,43 @@ svc_t *svc_stop_completed(void)
return NULL; return NULL;
} }
/**
* svc_clean_completed - Have post: and cleanup: scripts finished?
*
* Called late in runlevel and reload transitions to check if all post: and
* cleanup:scripts have completed. A removed service should end up in the
* DEAD state, while a service with a post:script ends up in HALTED.
*
* Returns:
* %NULL if post: and cleanup: scripts have run, otherwise a pointer to
* the first found svc_t waiting for post: or cleanup:script.
*/
svc_t *svc_clean_completed(void)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc_enabled(svc))
continue;
if (svc_is_runtask(svc) && svc->state == SVC_DONE_STATE)
continue;
if (svc_is_removed(svc)) {
if (svc->state == SVC_DEAD_STATE)
continue; /* any cleanup:script done */
} else {
if (svc->state == SVC_HALTED_STATE)
continue; /* any post:script done */
}
return svc;
}
return NULL;
}
/** /**
* svc_find - Find a service object by its full path name * svc_find - Find a service object by its full path name
* @name: Full path name, e.g., /sbin/syslogd * @name: Full path name, e.g., /sbin/syslogd
@@ -580,7 +617,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->removed && cb) if (svc_is_removed(svc) && cb)
cb(svc); cb(svc);
} }
} }
+27 -6
View File
@@ -52,14 +52,16 @@ typedef enum {
} svc_type_t; } svc_type_t;
#define SVC_TYPE_ANY (-1) #define SVC_TYPE_ANY (-1)
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_TTY) #define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_SYSV | SVC_TYPE_TTY)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV) #define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK)
typedef enum { typedef enum {
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */ SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
SVC_DONE_STATE, /* Task/Run job has been run */ SVC_DONE_STATE, /* Task/Run job has been run */
SVC_DEAD_STATE, /* Process is dead and scheduled for removal */
SVC_CLEANUP_STATE, /* Running cleanup: script */
SVC_TEARDOWN_STATE, /* Running post: script */
SVC_STOPPING_STATE, /* Waiting to collect the child process */ SVC_STOPPING_STATE, /* Waiting to collect the child process */
SVC_CLEANUP_STATE, /* Running post: script */
SVC_SETUP_STATE, /* Running pre: script */ SVC_SETUP_STATE, /* Running pre: script */
SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */ SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */
SVC_WAITING_STATE, /* Enabled but condition not satisfied */ SVC_WAITING_STATE, /* Enabled but condition not satisfied */
@@ -146,7 +148,7 @@ typedef struct svc {
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
/* Instance specifics */ /* Instance specifics */
int job; /* For intenal use only, canonical ref is NAME:ID */ int job; /* For internal use only, canonical ref is NAME:ID */
char name[MAX_ARG_LEN]; char name[MAX_ARG_LEN];
char id[MAX_ID_LEN]; /* :ID */ char id[MAX_ID_LEN]; /* :ID */
char ifstmt[MAX_IDENT_LEN]; char ifstmt[MAX_IDENT_LEN];
@@ -196,9 +198,18 @@ typedef struct svc {
char conflict[MAX_ARG_LEN]; char conflict[MAX_ARG_LEN];
char desc[MAX_STR_LEN]; char desc[MAX_STR_LEN];
char env[MAX_CMD_LEN]; char env[MAX_CMD_LEN];
char pre_script[MAX_CMD_LEN]; char pre_script[MAX_CMD_LEN];
int pre_tmo;
char post_script[MAX_CMD_LEN]; char post_script[MAX_CMD_LEN];
int post_tmo;
char ready_script[MAX_CMD_LEN]; char ready_script[MAX_CMD_LEN];
int ready_tmo;
char cleanup_script[MAX_CMD_LEN];
int cleanup_tmo;
/* /*
* Used to forcefully kill services that won't shutdown on * Used to forcefully kill services that won't shutdown on
@@ -237,6 +248,7 @@ void svc_foreach (int (*cb)(svc_t *));
void svc_foreach_type (int types, int (*cb)(svc_t *)); void svc_foreach_type (int types, int (*cb)(svc_t *));
svc_t *svc_stop_completed (void); svc_t *svc_stop_completed (void);
svc_t *svc_clean_completed (void);
void svc_mark (svc_t *svc); void svc_mark (svc_t *svc);
void svc_mark_dynamic (void); void svc_mark_dynamic (void);
@@ -259,6 +271,7 @@ static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & 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 && svc->forking; } static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; }
static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; } static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; }
static inline int svc_is_nohup (svc_t *svc) { return svc && (0 == svc->sighup); }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } 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); } static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
@@ -266,12 +279,14 @@ static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && s
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; } static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; } static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];} static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];}
static inline int svc_has_cleanup (svc_t *svc) { return svc->cleanup_script[0]; }
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; } static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; } static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; } static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_running (svc_t *svc) { return svc && svc->state == SVC_RUNNING_STATE; } static inline int svc_is_running (svc_t *svc) { return svc && svc->state == SVC_RUNNING_STATE; }
static inline int svc_is_stopping (svc_t *svc) { return svc && svc->state == SVC_STOPPING_STATE; } static inline int svc_is_stopping (svc_t *svc) { return svc && svc->state == SVC_STOPPING_STATE; }
static inline int svc_is_stopped (svc_t *svc) { return svc && svc->block == SVC_BLOCK_USER; }
static inline int svc_is_removed (svc_t *svc) { return svc && svc->removed; } static inline int svc_is_removed (svc_t *svc) { return svc && svc->removed; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; } static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
@@ -386,12 +401,15 @@ static inline char *svc_status(svc_t *svc)
return "stopping"; return "stopping";
} }
case SVC_CLEANUP_STATE: case SVC_TEARDOWN_STATE:
return "cleanup"; return "teardown";
case SVC_SETUP_STATE: case SVC_SETUP_STATE:
return "setup"; return "setup";
case SVC_CLEANUP_STATE:
return "cleanup";
case SVC_PAUSED_STATE: case SVC_PAUSED_STATE:
return "paused"; return "paused";
@@ -404,6 +422,9 @@ static inline char *svc_status(svc_t *svc)
case SVC_RUNNING_STATE: case SVC_RUNNING_STATE:
return "running"; return "running";
case SVC_DEAD_STATE:
return "dead";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
+4 -3
View File
@@ -172,7 +172,7 @@ int tty_parse_args(struct tty *tty, char *cmd, char **args)
passenv = 1; passenv = 1;
else if (!strcmp(cmd, "rescue")) else if (!strcmp(cmd, "rescue"))
tty->rescue = 1; /* for rescue shells */ tty->rescue = 1; /* for rescue shells */
else if (whichp(cmd)) /* in $PATH? */ else if (!tty->cmd && whichp(cmd))
tty->cmd = cmd; tty->cmd = cmd;
else else
tty->args[tty->num++] = cmd; tty->args[tty->num++] = cmd;
@@ -229,8 +229,9 @@ int tty_parse_args(struct tty *tty, char *cmd, char **args)
return errno = EINVAL; return errno = EINVAL;
} }
dbg("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in", dbg("Registering %s getty on TTY %s at %s baud with term %s",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A"); tty->cmd ? "external" : "built-in",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
/* Built-in getty now comes as standalone program */ /* Built-in getty now comes as standalone program */
if (!tty->cmd) { if (!tty->cmd) {
+34 -28
View File
@@ -1,28 +1,28 @@
SUBDIRS = lib src SUBDIRS = lib src
EXTRA_DIST = skel/bin/busybox-x86_64.sha256 skel/sbin/service.sh skel/etc/env \ EXTRA_DIST = skel/bin/busybox-x86_64.sha256 skel/sbin/service.sh skel/etc/env \
skel/bin/pre.sh skel/bin/post.sh skel/bin/ready.sh skel/bin/slay \ skel/bin/pre.sh skel/bin/post.sh skel/bin/ready.sh skel/bin/slay \
skel/sbin/fail.sh skel/usr/share/runparts/ref.log \ skel/sbin/fail.sh skel/usr/share/runparts/ref.log \
skel/usr/share/runparts/sysv.log \ skel/usr/share/runparts/sysv.log \
skel/etc/rc.d/foo.sh skel/etc/rc.d/S01abc.sh \ skel/etc/rc.d/foo.sh skel/etc/rc.d/S01abc.sh \
skel/etc/rc.d/test.sh skel/etc/rc.d/S02def.sh \ skel/etc/rc.d/test.sh skel/etc/rc.d/S02def.sh \
skel/etc/rcS.d/foo.sh skel/etc/rcS.d/test.sh \ skel/etc/rcS.d/foo.sh skel/etc/rcS.d/test.sh \
skel/etc/rcS.d/S01abc.sh skel/etc/rcS.d/S02def.sh \ skel/etc/rcS.d/S01abc.sh skel/etc/rcS.d/S02def.sh \
skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \ skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \
skel/etc/inittab skel/etc/hostname skel/etc/fstab \ skel/etc/inittab skel/etc/hostname skel/etc/fstab \
skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \ skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \
skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \ skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \
skel/etc/finit.d/enabled/.empty \ skel/etc/finit.d/enabled/.empty \
skel/sbin/task.sh skel/bin/crasher.sh \ skel/sbin/task.sh skel/bin/crasher.sh skel/bin/fail.sh \
skel/etc/init.d/S01-service.sh skel/etc/init.d/S02-serv.sh \ skel/etc/init.d/S01-service.sh skel/etc/init.d/S02-serv.sh \
skel/proc/.empty skel/root/.empty skel/run/.empty \ skel/proc/.empty skel/root/.empty skel/run/.empty \
skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \ skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \
skel/usr/bin/.empty skel/usr/lib/.empty skel/usr/sbin/.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/home/.empty skel/usr/games/.empty skel/usr/src/.empty \
skel/usr/share/.empty skel/usr/include/.empty \ skel/usr/share/.empty skel/usr/include/.empty \
skel/usr/local/games/.empty skel/usr/local/sbin/.empty \ skel/usr/local/games/.empty skel/usr/local/sbin/.empty \
skel/usr/local/share/.empty skel/usr/local/src/.empty \ skel/usr/local/share/.empty skel/usr/local/src/.empty \
skel/usr/local/lib/.empty skel/usr/local/bin/.empty \ skel/usr/local/lib/.empty skel/usr/local/bin/.empty \
skel/usr/local/etc/.empty skel/var/.empty skel/usr/local/etc/.empty skel/var/.empty
EXTRA_DIST += setup-sysroot.sh EXTRA_DIST += setup-sysroot.sh
@@ -40,6 +40,7 @@ EXTRA_DIST += initctl-status-subset.sh
EXTRA_DIST += notify.sh EXTRA_DIST += notify.sh
EXTRA_DIST += pidfile.sh EXTRA_DIST += pidfile.sh
EXTRA_DIST += pre-post-serv.sh EXTRA_DIST += pre-post-serv.sh
EXTRA_DIST += pre-fail.sh
EXTRA_DIST += process-depends.sh EXTRA_DIST += process-depends.sh
EXTRA_DIST += rclocal.sh EXTRA_DIST += rclocal.sh
EXTRA_DIST += ready-serv.sh EXTRA_DIST += ready-serv.sh
@@ -65,11 +66,6 @@ AM_TESTS_ENVIRONMENT += export LD_LIBRARY_PATH;
AM_TESTS_ENVIRONMENT += top_builddir=$(top_builddir); AM_TESTS_ENVIRONMENT += top_builddir=$(top_builddir);
AM_TESTS_ENVIRONMENT += export top_builddir; AM_TESTS_ENVIRONMENT += export top_builddir;
checkself.sh:
@echo '#!/bin/sh' > $@
@echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(TESTS)))) >> $@
@chmod +x $@
TEST_EXTENSIONS = .sh TEST_EXTENSIONS = .sh
TESTS = checkself.sh TESTS = checkself.sh
TESTS += add-remove-dynamic-service.sh TESTS += add-remove-dynamic-service.sh
@@ -86,6 +82,7 @@ TESTS += initctl-status-subset.sh
TESTS += notify.sh TESTS += notify.sh
TESTS += pidfile.sh TESTS += pidfile.sh
TESTS += pre-post-serv.sh TESTS += pre-post-serv.sh
TESTS += pre-fail.sh
TESTS += process-depends.sh TESTS += process-depends.sh
TESTS += rclocal.sh TESTS += rclocal.sh
TESTS += ready-serv.sh TESTS += ready-serv.sh
@@ -109,6 +106,15 @@ TESTS += unexpected-restart.sh
check-recursive: setup-chroot check-recursive: setup-chroot
.PHONY: checkself.sh
checkself.sh:
@echo '#!/bin/sh' > $@
@echo 'set -eu' >> $@
@echo 'cd "$$srcdir" 2>/dev/null || cd "." || exit 1' >> $@
@echo 'TESTS="$(filter %.sh,$(filter-out $@, $(TESTS)))"' >> $@
@echo 'shellcheck $$TESTS' >> $@
@chmod 0755 $@
.PHONY: setup-chroot .PHONY: setup-chroot
setup-chroot: setup-chroot:
@SYSROOT='$(abs_builddir)/sysroot/' srcdir=$(srcdir) top_builddir=$(top_builddir) $(srcdir)/setup-sysroot.sh @SYSROOT='$(abs_builddir)/sysroot/' srcdir=$(srcdir) top_builddir=$(top_builddir) $(srcdir)/setup-sysroot.sh
+1 -1
View File
@@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# Verify handling of dependant services, bug #314 # Verify handling of dependent services, bug #314
# Two services: foo and bar, where bar depends on foo: # Two services: foo and bar, where bar depends on foo:
# #
# - bar must not start until foo is ready # - bar must not start until foo is ready
+3 -2
View File
@@ -27,15 +27,16 @@ test_teardown()
say "Waiting for runlevel 2" say "Waiting for runlevel 2"
sleep 2 sleep 2
#run "initctl debug" run "initctl debug"
say "Add service stanza to $FINIT_CONF ..." say "Add service stanza to $FINIT_CONF ..."
run "echo 'service serv -np -e foo:bar -- Verify foo=bar' >> $FINIT_CONF" run "echo 'service serv -np -e foo:bar -- Verify foo=bar' >> $FINIT_CONF"
#run "cat $FINIT_CONF" run "cat $FINIT_CONF"
say 'Reload Finit' say 'Reload Finit'
run "initctl reload" run "initctl reload"
sleep 1 sleep 1
run "initctl"
retry 'assert_num_children 1 serv' retry 'assert_num_children 1 serv'
say "Modify $FINIT_CONF slightly ..." say "Modify $FINIT_CONF slightly ..."
+8
View File
@@ -84,6 +84,14 @@ assert_status()
assert "Service $service $status" "$(texec initctl -p status "$service" | awk '/Status/{print $3}')" = "$status" assert "Service $service $status" "$(texec initctl -p status "$service" | awk '/Status/{print $3}')" = "$status"
} }
# Used by, e.g., pre-fail.sh
assert_status_full()
{
service=$1; shift
status="$*"
assert "Service $service status is: $status" "$(texec initctl -p status "$service" | awk '/Status/{sub("^ *Status : ", ""); print}')" = "$status"
}
assert_cond() assert_cond()
{ {
cond=$1 cond=$1
+1 -1
View File
@@ -71,7 +71,7 @@ test_one()
assert_status "serv" "running" assert_status "serv" "running"
assert_cond "service/serv/ready" assert_cond "service/serv/ready"
# Issue #343; pidfile.so inadvertedly marked systemd services as # Issue #343; pidfile.so inadvertently marked systemd services as
# 'started' when they created their PID file. The serv test daemon # 'started' when they created their PID file. The serv test daemon
# waits 3 sec between pidfile and notify to trigger this bug. # waits 3 sec between pidfile and notify to trigger this bug.
if [ "$type" != "pid" ]; then if [ "$type" != "pid" ]; then
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# Verify Finit:
# - does not start main process when pre:script fails
# - times out and kills long running pre:script
set -eu
#export DEBUG=true
TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
vrfy()
{
nm=serv
tmo=$1
msg=$2
err=$3
run "echo 'service [2345] pre:$tmo,fail.sh $nm -n -- $msg' > $FINIT_CONF"
run "initctl reload"
retry "assert_status_full $nm 'crashed $err'" 50
}
#run "initctl debug"
vrfy 5 "Failing pre: script" "(code=exited, status=42)"
vrfy 1 "Timeout pre: script" "(code=signal, status=9/KILL)"
+2 -2
View File
@@ -42,8 +42,8 @@ test_one()
sep sep
say "Add stanza to $FINIT_CONF" say "Add stanza to $FINIT_CONF"
run "echo 'run fail.sh -- Failure' > $FINIT_RCSD/fail.conf" run "echo 'run /sbin/fail.sh -- Failure' > $FINIT_RCSD/fail.conf"
run "echo 'run initctl touch fail' > $FINIT_RCSD/touch.conf" run "echo 'run initctl touch fail' > $FINIT_RCSD/touch.conf"
test_one 2 test_one 2
sep sep
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Verifies that a run task can call initctl at bootstrap and runlevel 2, # Verifies that a run task can call initctl at bootstrap and runlevel 2,
# without blocking, performing serveral tasks: # without blocking, performing several tasks:
# #
# 1. Query running services # 1. Query running services
# 2. Stop/Start a running service # 2. Stop/Start a running service
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
sleep 2
exit 42
+2 -1
View File
@@ -10,7 +10,8 @@
# #
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
#DEBUG=1 # shellcheck disable=2034
DEBUG=1
# shellcheck disable=SC2034 # shellcheck disable=SC2034
BOOTSTRAP="runparts sysv /etc/rcS.d" BOOTSTRAP="runparts sysv /etc/rcS.d"