Compare commits

...
101 Commits
Author SHA1 Message Date
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
Joachim Wiberg 54701422a5 Bump version for v4.8 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-13 19:42:44 +02:00
Joachim Wiberg 9d463e3823 Update ChangeLog, added fistmpfs() check and zebra stop fix
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-13 19:42:44 +02:00
Joachim Wiberg 56e558c960 initctl: add support for showing template@foo.conf
Fixes #411

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-13 19:40:21 +02:00
Joachim Wiberg dfaf351da1 Fix #414: zebra immediately restarts if manually stopped
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-13 19:40:21 +02:00
Joachim Wiberg a8433fcb7f test: ignore unreachable shellcheck warning
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-10-12 08:39:30 +02:00
Joachim Wiberg 2daa953bee Merge pull request #409 from liuming50/prevent-nftw-follow-symbolic-links 2024-08-24 14:56:28 +02:00
Ming Liu 3e3e9aadf5 tmpfiles.c: prevent nftw follow symbolic links
When dealing with "L+" and "R", the function call 'nftw' should not
follow symbolic links, otherwise, it would also delete the targets
which is wrong.

For instance, if there is already a symbolic link:
```
/path/to/the/link -> /path/to/some/folder
```

if we set the following in a tmpfile conf:

```
L+ /path/to/the/link -    -    -     - /path/to/the/target
```

the result would be /path/to/some/folder also get deleted, which it
should not.

it could be even worse, when the symbolic link already is pointing to:
/path/to/the/target, the whole /path/to/the/target would be deleted on
next system boot.

Signed-off-by: Mathias Thore <mathias.thore@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2024-08-24 13:29:10 +02:00
Joachim Wiberg dc873c6548 Show expanded prefix and libdir in summary
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-08-19 08:02:12 +02:00
Joachim Wiberg 29bd7a504e Merge pull request #408 from mandelmassa/master
Avoid remounting already mounted /run and /tmp directories

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-08-06 13:13:49 +02:00
Mathias Thore a0685219cf Avoid remounting already mounted /run and /tmp directories
Adds the function fistmpfs to determine if a new tmpfs mount should be
performed on /run and /tmp. The function supports cases where more
complex mount hierarchies are in use, including overlayfs backed mounts.

Signed-off-by: Mathias Thore <mathias.thore@atlascopco.com>
2024-08-05 14:33:43 +02:00
Joachim Wiberg 13b107b7b1 Fix #407: extend initctl poll timeout
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-06-22 09:23:14 +02:00
Joachim Wiberg 409d68d05b Update ChangeLog
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-06-22 09:20:28 +02:00
Joachim Wiberg ab62b5282b runparts: add -b (batch) mode for syslog output
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-06-19 09:20:03 +02:00
Joachim Wiberg d02d65feeb .github: minor, bump action version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-06-19 09:20:03 +02:00
Ming 9a0dea6aef configure.ac: make cgroup2 configurable (#406) 2024-05-11 14:06:01 +02:00
Joachim Wiberg 7481505b1a plugins: minor refactor
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-28 19:38:03 +02:00
Joachim Wiberg 8251f1a422 Fix derefernce before NULL check
Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-28 14:40:05 +02:00
Joachim Wiberg e269176a79 plugins: fix possible use of unitialized variable
Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-28 14:39:30 +02:00
Joachim Wiberg 612f5a9385 Allow building Finit --without-rc-local support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-28 14:25:53 +02:00
Joachim Wiberg 4a2381eb6f Fix #404: possible undefined behavior --without-fstab
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-28 14:22:57 +02:00
Joachim Wiberg 0cae7d44dd Follow-up to 6a89e60, len may be used unitialized
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-28 14:21:22 +02:00
Joachim Wiberg 340cae4afd Change default behavior, allow kernel logs to console
A Linux system booted with the kernel command line option 'quiet' only
logs error (and above) severity messages to the console.  For embedded
systems, which is the primary target for Finit, this is what you want
to see.

Hence, and after careful consideration, this patch changes the default
behavior of Finit to allow kernel logs to the console.  A build-time
configure flags, --disable-kernel-logging, has been added to restore
legacy behavior.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-24 12:46:59 +02:00
Joachim Wiberg 6a89e60fc8 Fix #405: parsing >1 active consoles in tty @console setups
Systems that have the following tty setup and multiple consoles listed
in /sys/class/tty/console/active misbehave:

    tty [12345789] @console 0 xterm noclear passenv

Only the first listed console is started properly, the remaining ones
were registered using the wrong :ID and no arguments to getty.

This patch fixes the parsing and re-use of the base paramenters for
all consoles listed in the active file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-24 11:33:35 +02:00
Joachim Wiberg 840d612fc2 plugins: minor, show skipped path in debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-03 08:11:50 +02:00
Joachim Wiberg 60c676ab2d test: minor, spelling in comment
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-03 08:11:41 +02:00
Joachim Wiberg deada0fa89 initctl: refactor to use new suffix() function
Reduce code duplication and use suffix() function to check and append a
suffix to a path.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-03 08:10:35 +02:00
Joachim Wiberg 7b501f255b initctl: touch does not support template services
Fixes #403

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-03 07:47:23 +02:00
Joachim Wiberg 052136cb99 initctl: touch does not respect -n switch
Fixes #402

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-04-02 16:07:01 +02:00
Joachim Wiberg 742b8b72cc Update ChangeLog for v4.8-beta1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-30 12:39:06 +01:00
Joachim Wiberg c37037c015 system: allow udevd to read its arguments from /etc/default/udevd
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-30 12:38:50 +01:00
Joachim Wiberg c32b5a749a Refactor popen()/pclose() logic in ifupdown calls
Currently no code checks the return code of ifupdown at runlevel
changes, but for future reference this code has been refactored
to match the changes made in cbdb949 to fix issue #400.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-30 12:38:50 +01:00
Joachim Wiberg 7701718d7b Fix #400: resolve exit code from pclose() by calling WEXITSTATUS()
This problem affects all calls to run_interactive() that check the
return value of the command.  Causing HOOK_MOUNT_ERROR to *not* run
on mount failure, and sulogin() to *not* be started on fsck error.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-30 12:38:50 +01:00
Joachim Wiberg eb4eca9607 test: verify pre:/post:scripts also for configlets
For a background, see issue #399

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-23 08:55:42 +01:00
Joachim Wiberg cab94b81e8 Bump version for start of 4.8 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-23 08:31:53 +01:00
Joachim Wiberg 5913217808 test: add finit.d/available and finit.d/enabled/ dirs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-23 08:17:03 +01:00
Joachim Wiberg b49f55ab3d tmpfiles.d: ignore x/X command, no cleanup at runtime with Finit
Silence log warnings for command x/X (ignore clean for path), because
Finit does not do tmpfiles cleaning at runtime.

x /tmp/podman-run-*
x /tmp/containers-user-*
x /tmp/run-*/libpod
D! /var/lib/containers/storage/tmp 0700 root root
D! /run/podman 0700 root root
D! /var/lib/cni/networks

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-13 12:06:39 +01:00
Joachim Wiberg bf4a584e2e plugins: add support for hwrng and check if seed file is empty
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-13 12:06:32 +01:00
Joachim Wiberg 90a4df8d12 plugins: on error-retry, don't print "Restoring RTC" twice
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-11 18:53:22 +01:00
Joachim Wiberg 146bf55122 Fix #398: display unsupported initctl command (number) in log
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-11 16:01:30 +01:00
Joachim Wiberg c1ed373398 Fix #397: drop ttinit() for PID 1
After reports from the field, see issue #397, of lockups at reboot,
we've decided to drop this code from PID 1.  It was added before the 4.x
series, when the current progress output was introduced.  For the older
style progress it served a purpose since the placement of [OK]/[FAIL]
was on the right hand side.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-11 15:46:38 +01:00
Joachim Wiberg 5998342b3e system: drop debug mode from udevd
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-02-26 08:41:14 +01:00
Joachim Wiberg 9dc7b8a6b0 tmpfiles.d: skip x11.conf unless --enable-x11-common-plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-02-04 15:27:27 +01:00
Joachim Wiberg c221823654 shutdown: use cond_clear_noupdate() to prevent nested service_stop()
When the system shuts down, or user changes runlevels, we don't have to
call cond_clear_update(), because this can lead to nested service_stop()
calls, which in turn lead to out of sync progress updates:

[ .. ] Stopping Foo
[ OK ] Stopping Bar
[ OK ]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-02-03 17:31:37 +01:00
Joachim Wiberg 248f7d3110 Merge pull request #393 from solt87/patch-1
Improve wording.
2024-01-16 08:04:36 +01:00
Solt Budavári fe90ba5781 Improve wording. 2024-01-15 20:56:16 +01:00
Joachim Wiberg 71c87734b7 Add missing ChangeLog for critical issue triggering v4.7 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-01-08 16:46:56 +01:00
Joachim Wiberg cd060e8af3 getty: trigger /etc/issue compat mode for Alpine Linux
The /etc/issue file on Alpine Linux says "Kernel \r on an \m (\l)", so
\r needs to return the uts release rather than os-release VERSION.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-01-07 21:18:58 +01:00
Joachim Wiberg 33a7f123d7 contrib/alpine: link to blog post that goes more in-depth
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-01-07 21:07:48 +01:00
54 changed files with 1253 additions and 401 deletions
+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)
+2 -1
View File
@@ -4,6 +4,7 @@ on:
push: push:
branches: branches:
- 'dev' - 'dev'
workflow_dispatch:
env: env:
PROJECT_NAME: finit PROJECT_NAME: finit
@@ -25,7 +26,7 @@ jobs:
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \ --post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
-O coverity-latest.tar.gz.md5 -O coverity-latest.tar.gz.md5
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
- uses: actions/cache@v3 - uses: actions/cache@v4
id: cache id: cache
with: with:
path: coverity-latest.tar.gz path: coverity-latest.tar.gz
+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;}')
+108 -1
View File
@@ -4,6 +4,110 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.10][] - 2025-02-28
---------------------
### 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
[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
--------------------
### Changes
- Avoid remounting already mounted `/run` and `/tmp` directories. This
extends the existing support for detecting mounted directories to
include complex mount hierarchies are in use, overlayfs and tmpfs
mounts. Feature by Mathias Thore, Atlas Copco
- getty: trigger /etc/issue compat mode for Alpine Linux
- tmpfiles.d: skip `x11.conf` unless X11 common plugin is enabled
- tmpfiles.d: ignore x/X command, nos support for cleanup at runtime
- Drop debug mode `-D` from `udevd` in `hotplug.conf.in`, allow the
user to set this in `/etc/default/udevd` instead
- Certain initctl APIs at bootstrap are not supported, update warning
log to include command (number) for troubleshooting, issue #398
- Add support for hwrng to urandom plugin and check for empty seed
- Add `runparts -b` (batch mode) support, disables escape sequences
- New configure `--without-rc-local`, disables `/etc/rc.local` support
- New configure `--disable-cgroup` option, disables cgroup v2 detection
- `initctl show template@foo.conf` now shows how an enabled template
service has been evaluated by Finit, issue #411
- Extend `initctl` timeout connecting and waiting for Finit reply. The
previous 2 + 2 second poll timeout has proved to be too short on more
complex systems. Now a 15 + 15 second timeout is applied which should
be more resistant to temporary overload scenarios, issue #407
### Fixes
- Fix #397: system shutdown/reboot can block on console input if action
is started remotely (ssh). Caused by legact TTY screen size probing,
removed from both bootstrap and shutdown/reboot
- Fix #400: both `HOOK_MOUNT_ERROR` and `sulogin()` fail to trigger on
either mount or `fsck` errors. Problem caused by unresolved status
from pipe, calling `pclose()` without extracting exit status
- Fix #402: `initctl touch` does not respect `-n` (no error) flag
- Fix #403: `initctl touch` does not support template services
- Fix #404: possible undefined behavior when `--with-fstab=no` is set
- Fix #405: `@console` getty does not work with `tty0 ttyS0`
- Fix #409: prevent tmpfiles from following symlinks for `L+` and `R`,
otherwise symlink targets would also be removed. Found and fixed by
Mathias Thore and Ming Liu, Atlas Copco
- Fix #414: Frr Zebra immediately restarts on `initctl stop zebra`.
The fix likely works for all Frr/Quagga services due to the way they
create and delete their pid file
- Cosmetic issue with `[ OK ]` messages being printed out of order at
shutdown/reboot. Caused by nested calls to `service_stop()`
- Cosmetic issue with duplicate "Restoring RTC" message at bootstrap
[4.7][] - 2024-01-07 [4.7][] - 2024-01-07
-------------------- --------------------
@@ -24,6 +128,7 @@ All relevant changes are documented in this file.
Found and fixed by Ryan Rorison Found and fixed by Ryan Rorison
- Fix #389: must libc requires `libgen.h` for `basename()` function. - Fix #389: must libc requires `libgen.h` for `basename()` function.
Reported and worked around with new `basenm()` function by Stargirl Reported and worked around with new `basenm()` function by Stargirl
- Fix #392: `service/foo/ready` condition reasserted on pidfile removal
[4.6][] - 2023-11-13 [4.6][] - 2023-11-13
@@ -1655,7 +1760,9 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.7...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.9...HEAD
[4.9]: https://github.com/troglobit/finit/compare/4.8...4.9
[4.8]: https://github.com/troglobit/finit/compare/4.7...4.8
[4.7]: https://github.com/troglobit/finit/compare/4.6...4.7 [4.7]: https://github.com/troglobit/finit/compare/4.6...4.7
[4.6]: https://github.com/troglobit/finit/compare/4.5...4.6 [4.6]: https://github.com/troglobit/finit/compare/4.5...4.6
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5 [4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
+1 -2
View File
@@ -405,8 +405,7 @@ The event loop is the whole thing which Finit is built around, except
for runlevel S, which remains a slow procession through a lot of set up, for runlevel S, which remains a slow procession through a lot of set up,
with a few hooks and blocking call outs to external scripts. with a few hooks and blocking call outs to external scripts.
However, not all `initctl` commands are prohibited, supported commands: However, not all `initctl` commands are prohibited. Supported commands:
are:
- `inictl cond`: only operate of files in `/run/finit/cond` - `inictl cond`: only operate of files in `/run/finit/cond`
- `initctl enable/disable`: enabled run/task/service is activated on - `initctl enable/disable`: enabled run/task/service is activated on
+39 -10
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.7], [https://github.com/troglobit/finit/issues], AC_INIT([Finit], [4.10-rc2], [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])
@@ -67,10 +67,18 @@ AC_ARG_ENABLE(fsckfix,
AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[ AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[
enable_fsckfix=no]) enable_fsckfix=no])
AC_ARG_ENABLE(cgroup,
AS_HELP_STRING([--disable-cgroup], [Disable cgroup v2 support, default: autodetect from /sys/fs/cgroup]),,[
enable_cgroup=yes])
AC_ARG_ENABLE(redirect, AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[ AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
enable_redirect=yes]) enable_redirect=yes])
AC_ARG_ENABLE(kernel_logging,
AS_HELP_STRING([--disable-kernel-logging], [Disable kernel logging to console (use 'quiet' instead!]),,[
enable_kernel_logging=yes])
AC_ARG_ENABLE(logrotate, AC_ARG_ENABLE(logrotate,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp]),,[ AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp]),,[
enable_logrotate=yes]) enable_logrotate=yes])
@@ -169,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
@@ -181,6 +193,12 @@ AS_IF([test "x$enable_auto_reload" = "xyes"], [
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [ AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])]) AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])])
AS_IF([test "x$enable_kernel_logging" = "xyes"], [
AC_DEFINE(KERNEL_LOGGING, 1, [Keep kernel warn/err logs to console])])
AS_IF([test "x$enable_cgroup" = "xyes"], [
AC_DEFINE(CGROUP2_ENABLED, 1, [Autodetect cgroup v2 support from /sys/fs/cgroup])])
AS_IF([test "x$enable_fastboot" = "xyes"], [ AS_IF([test "x$enable_fastboot" = "xyes"], [
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])]) AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
@@ -227,10 +245,9 @@ AC_DEFINE_UNQUOTED(FINIT_RCSD, "$rcsd_path", [Finit rcS.d/, default /etc/finit.d
AC_SUBST(FINIT_RCSD, "$rcsd_path") AC_SUBST(FINIT_RCSD, "$rcsd_path")
AS_IF([test "x$with_rc_local" != "xno"], [ AS_IF([test "x$with_rc_local" != "xno"], [
AS_IF([test "x$rclocal" = "xyes"], [ AS_IF([test "x$rclocal" = "xyes"], [rclocal=$sysconfdir/rc.local])
rclocal=$sysconfdir/rc.local])]) AC_EXPAND_DIR(rclocal_path, "$rclocal")
AC_EXPAND_DIR(rclocal_path, "$rclocal") AC_DEFINE_UNQUOTED(FINIT_RC_LOCAL, "$rclocal_path", [Compat SysV /etc/rc.local file to run last at boot.])])
AC_DEFINE_UNQUOTED(FINIT_RC_LOCAL, "$rclocal_path", [Compat SysV /etc/rc.local file to run last at boot.])
AS_IF([test "x$with_sysconfig" != "xno"], [ AS_IF([test "x$with_sysconfig" != "xno"], [
AS_IF([test "x$sysconfig" != "xdefault"], [ AS_IF([test "x$sysconfig" != "xdefault"], [
@@ -265,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"], [
@@ -355,15 +379,16 @@ cat <<EOF
------------------ Summary ------------------ ------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION $PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $prefix Prefix................: $(eval echo $prefix)
Exec prefix...........: $eprefix Exec prefix...........: $(eval echo $exec_prefix)
Libdir................: $(eval echo $libdir)
Sysconfdir............: `eval echo $sysconfdir` Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir` Localstatedir.........: `eval echo $localstatedir`
Default fstab.........: `eval echo $fstab` Default fstab.........: `eval echo $fstab`
Finit config file.....: $conf_path Finit config file.....: $conf_path
Finit config.d path...: $rcsd_path Finit config.d path...: $rcsd_path
Finit plugin path.....: $plugin_path Finit plugin path.....: $plugin_path
Compat rc.local path..: $rclocal_path Compat rc.local path..: `eval echo $rclocal`
System environment....: ${sysconfig_path:-${sysconfig}} System environment....: ${sysconfig_path:-${sysconfig}}
Random seed path......: $random_path Random seed path......: $random_path
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
@@ -373,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
@@ -382,6 +408,9 @@ Optional features:
Built-in sulogin......: $with_sulogin $sulogin Built-in sulogin......: $with_sulogin $sulogin
Built-in watchdogd....: $with_watchdog $watchdog Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Use cgroup v2.........: $enable_cgroup
Parse kernel cmdline..: $enable_kernel_cmdline
Keep kernel logging...: $enable_kernel_logging
Skip fsck check.......: $enable_fastboot Skip fsck check.......: $enable_fastboot
Run fsck fix mode.....: $enable_fsckfix Run fsck fix mode.....: $enable_fsckfix
Redirect output.......: $enable_redirect Redirect output.......: $enable_redirect
+2
View File
@@ -1,6 +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/
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,
and other tools needed to build a GNU configure based project. and other tools needed to build a GNU configure based project.
+40 -21
View File
@@ -890,33 +890,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
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

+39 -25
View File
@@ -175,13 +175,13 @@ 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 ,
when entering a disallowed runlevel, Finit calls when entering a disallowed runlevel, Finit calls
@@ -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
+11 -2
View File
@@ -52,7 +52,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
ptr = slash++; ptr = slash++;
slash = strchr(ptr, '/'); slash = strchr(ptr, '/');
if (slash) { if (slash) {
dbg("Path too deep, skipping."); dbg("Path too deep, skipping %s", path);
return -1; return -1;
} }
} }
@@ -66,6 +66,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
char fn[PATH_MAX]; char fn[PATH_MAX];
svc_t *svc; svc_t *svc;
char *nm;
paste(fn, sizeof(fn), dir, name); paste(fn, sizeof(fn), dir, name);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0)) if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
@@ -79,9 +80,16 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
return; return;
} }
dbg("Found svc %s for %s with pid %d", svc_ident(svc, NULL, 0), fn, svc->pid); /* Service condistion to set/clear */
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
nm = svc_ident(svc, NULL, 0);
dbg("Found svc %s for %s with pid %d", nm, fn, svc->pid);
if (svc_is_stopping(svc)) {
dbg("Ignoring pidfile death rattles while service %s is %s", nm, svc_status(svc));
goto sneaky_zebra;
}
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
/* /*
* only mark native services as started, the s6 and * only mark native services as started, the s6 and
@@ -125,6 +133,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE) if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE)
service_ready(svc, 1); service_ready(svc, 1);
} else if (mask & IN_DELETE) { } else if (mask & IN_DELETE) {
sneaky_zebra:
cond_clear(cond); cond_clear(cond);
if (svc->notify == SVC_NOTIFY_PID) if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc, 0); service_ready(svc, 0);
+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);
+178 -60
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,65 +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 {
print_desc(NULL, "Restoring system clock (UTC) from RTC");
rc = time_set(&tm);
}
invalid: if (rc) {
logit(LOG_ERR, "Failed restoring system clock from RTC."); logit(LOG_WARNING, "Failed restoring system clock from RTC.");
if (EINVAL == errno) if (EINVAL == errno)
logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp); logit(LOG_WARNING, "RTC time is too old (before %s)", rtc_timestamp);
else if (ENOENT == errno) else if (ENOENT == errno)
logit(LOG_ERR, "RTC has no previously saved (valid) time."); logit(LOG_WARNING, "RTC has no previously saved (valid) time.");
else else
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno)); logit(LOG_WARNING, "RTC error code %d: %s", errno, strerror(errno));
/* Been here already? */ print(2, NULL);
if (rc)
goto out;
/* Attempt to set RTC to a sane value ... */ /* Try restoring from last save game */
tv.tv_sec = rtc_date_fallback; if (rtc_file)
if (!gmtime_r(&tv.tv_sec, &tm)) file_restore(arg);
goto out; } else
print(0, NULL);
logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp);
rc = 2;
}
print_desc(NULL, "Restoring system clock (UTC) from RTC");
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;
goto invalid;
}
if (settimeofday(&tv, NULL) == -1)
rc = 1;
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
} }
}; };
@@ -211,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);
} }
+49 -10
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
@@ -42,11 +43,43 @@
#define RANDOM_BYTES (32*1024) #define RANDOM_BYTES (32*1024)
#endif #endif
#ifdef RANDOMSEED
/*
* This is the fallback seed function, please make sure you have drivers
* enabling /dev/hwrng instead.
*/
static void fallback(FILE *fp)
{
unsigned long seed;
struct timeval tv;
struct rusage ru;
int iter = 128;
gettimeofday(&tv, NULL);
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--) {
uint32_t i, prng = random();
for (i = 0; i < sizeof(prng); i++)
fputc((prng >> (i * CHAR_BIT)) & UCHAR_MAX, fp);
}
}
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
struct rand_pool_info *rpi; struct rand_pool_info *rpi;
ssize_t len = 0; ssize_t len = 0;
struct stat st;
int rc = -1; int rc = -1;
int fd, err; int fd, err;
@@ -55,7 +88,7 @@ static void setup(void *arg)
return; return;
} }
if (!fexist(RANDOMSEED)) { if (stat(RANDOMSEED, &st) || st.st_size < 512) {
int ret = 1; int ret = 1;
mode_t prev; mode_t prev;
FILE *fp; FILE *fp;
@@ -64,16 +97,22 @@ static void setup(void *arg)
prev = umask(077); prev = umask(077);
fp = fopen(RANDOMSEED, "w"); fp = fopen(RANDOMSEED, "w");
if (fp) { if (fp) {
int iter = 128; const char *hwrng = "/dev/hwrng";
struct timeval tv; FILE *hw;
gettimeofday(&tv, NULL); hw = fopen(hwrng, "r");
srandom(tv.tv_sec % 3600); if (hw) {
while (iter--) { char buf[512];
uint32_t i, prng = random(); size_t len;
for (i = 0; i < sizeof(prng); i++) len = fread(buf, sizeof(buf[0]), sizeof(buf), hw);
fputc((prng >> (i * CHAR_BIT)) & UCHAR_MAX, fp); if (len == 0)
fallback(fp);
else
len = fwrite(buf, sizeof(buf[0]), len, fp);
fclose(hw);
} else {
fallback(fp);
} }
ret = fclose(fp); ret = fclose(fp);
} }
@@ -130,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:
+8 -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");
@@ -377,7 +382,9 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
if (IS_RESERVED_RUNLEVEL(runlevel)) { if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("Unsupported command in runlevel S and 6/0."); strterm(rq.data, sizeof(rq.data));
warnx("Unsupported command (cmd: %d, data: %s) in runlevel S and 6/0.",
rq.cmd, rq.data);
goto leave; goto leave;
} }
default: default:
@@ -576,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();
+10
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");
@@ -463,6 +468,11 @@ void cgroup_init(uev_ctx_t *ctx)
FILE *fp; FILE *fp;
int fd; int fd;
#ifndef CGROUP2_ENABLED
avail = 0;
return;
#endif
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.");
+4 -2
View File
@@ -29,6 +29,8 @@
#include "client.h" #include "client.h"
#include "log.h" #include "log.h"
#define REQUEST_TIMEOUT 15000
static int sd = -1; static int sd = -1;
int client_connect(void) int client_connect(void)
@@ -85,7 +87,7 @@ int client_request(struct init_request *rq, ssize_t len)
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLOUT; pfd.events = POLLOUT;
if (poll(&pfd, 1, 2000) <= 0) { if (poll(&pfd, 1, REQUEST_TIMEOUT) <= 0) {
warn("Timed out waiting for Finit, errno %d", errno); warn("Timed out waiting for Finit, errno %d", errno);
return -1; return -1;
} }
@@ -107,7 +109,7 @@ int client_request(struct init_request *rq, ssize_t len)
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLIN | POLLERR | POLLHUP; pfd.events = POLLIN | POLLERR | POLLHUP;
if ((rc = poll(&pfd, 1, 2000)) <= 0) { if ((rc = poll(&pfd, 1, REQUEST_TIMEOUT)) <= 0) {
if (rc) { if (rc) {
if (errno == EINTR) /* shutdown/reboot */ if (errno == EINTR) /* shutdown/reboot */
return -1; return -1;
+8 -4
View File
@@ -35,6 +35,7 @@
#include "cond.h" #include "cond.h"
#include "pid.h" #include "pid.h"
#include "service.h" #include "service.h"
#include "sm.h"
struct cond_boot { struct cond_boot {
TAILQ_ENTRY(cond_boot) link; TAILQ_ENTRY(cond_boot) link;
@@ -214,7 +215,10 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct
err(1, "Failed removing condition %s", fpath); err(1, "Failed removing condition %s", fpath);
cond = ptr + strlen(COND_BASE) + 1; cond = ptr + strlen(COND_BASE) + 1;
cond_update(cond); if (sm_is_in_teardown(&sm))
cond_clear_noupdate(cond);
else
cond_update(cond);
return 0; return 0;
} }
@@ -284,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;
@@ -317,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;
@@ -368,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;
+36 -17
View File
@@ -348,8 +348,11 @@ static void parse_kernel_loglevel(void)
*/ */
void conf_parse_cmdline(int argc, char *argv[]) void conf_parse_cmdline(int argc, char *argv[])
{ {
const char *ptr;
/* Set up defaults */ /* Set up defaults */
fstab = strdup(FINIT_FSTAB); if ((ptr = FINIT_FSTAB))
fstab = strdup(ptr);
finit_conf = strdup(FINIT_CONF); finit_conf = strdup(FINIT_CONF);
finit_rcsd = strdup(FINIT_RCSD); finit_rcsd = strdup(FINIT_RCSD);
path = getenv("PATH"); path = getenv("PATH");
@@ -1429,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);
@@ -1477,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;
@@ -1529,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;
@@ -1561,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;
} }
/* /*
+21 -20
View File
@@ -138,31 +138,32 @@ int run(char *cmd, char *log)
pid = fork(); pid = fork();
if (0 == pid) { if (0 == pid) {
int rc = EX_OSERR; char *pfx, buf[256];
FILE *pp;
int rc;
setsid(); setsid();
sig_unblock(); sig_unblock();
if (!log) { if (!log) {
do_redirect(); do_redirect();
execvp(args[0], args); execvp(args[0], args);
} else { _exit(EX_OSERR);
char *pfx = *log ? ": " : "";
FILE *pp;
pp = popen(cmd, "r");
if (pp) {
char buf[256];
while (fgets(buf, sizeof(buf), pp)) {
chomp(buf);
logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
}
rc = pclose(pp);
}
} }
_exit(rc); pp = popen(cmd, "r");
if (!pp)
_exit(EX_OSERR);
pfx = *log ? ": " : "";
while (fgets(buf, sizeof(buf), pp)) {
chomp(buf);
logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
}
rc = pclose(pp);
if (rc == -1)
_exit(EX_OSERR);
_exit(WEXITSTATUS(rc));
} else if (-1 == pid) { } else if (-1 == pid) {
err(1, "%s", !log ? args[0] : cmd); err(1, "%s", !log ? args[0] : cmd);
if (backup) if (backup)
@@ -198,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) {
@@ -243,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);
@@ -280,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;
@@ -350,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)
+21 -8
View File
@@ -87,14 +87,22 @@ svc_t *wdog = NULL; /* No watchdog by default */
*/ */
static void banner(void) static void banner(void)
{ {
#ifndef KERNEL_LOGGING
/* /*
* Silence kernel logs, assuming users have sysklogd or * Silence kernel logs, assuming users have sysklogd or
* similar enabled to start emptying /dev/kmsg, but for * similar enabled to start emptying /dev/kmsg.
* our progress we want to own the console. *
* Instead of using `configure --disable-kernel-logging`, we
* recommend adjusting the kernel log level in the kernel's
* menuconfig, or using sysctl kernel.printk, or setting the
* desired log level, on the kernel cmdline, e.g. 'quiet'.
*
* By default KERNEL_LOGGING is enabled so you can see any
* warnings/errors or higher on your system console.
*/ */
if (!debug && !kerndebug) if (!debug && !kerndebug)
klogctl(6, NULL, 0); klogctl(6, NULL, 0);
#endif
/* /*
* First level hooks, if you want to run here, you're * First level hooks, if you want to run here, you're
* pretty much on your own. Nothing's up yet ... * pretty much on your own. Nothing's up yet ...
@@ -264,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);
@@ -401,7 +414,7 @@ static void fs_finalize(void)
* To override any of this behavior, add entries to /etc/fstab * To override any of this behavior, add entries to /etc/fstab
* for /run (and optionally /run/lock). * for /run (and optionally /run/lock).
*/ */
if (fisdir("/run") && !fismnt("/run")) { if (fisdir("/run") && !fismnt("/run") && !fistmpfs("/run")) {
fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%"); fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */ /* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
@@ -410,7 +423,7 @@ static void fs_finalize(void)
} }
/* Modern systems use tmpfs for /tmp */ /* Modern systems use tmpfs for /tmp */
if (!fismnt("/tmp")) if (!fismnt("/tmp") && !fistmpfs("/tmp"))
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777"); fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
} }
+3
View File
@@ -121,6 +121,9 @@ static int osrel(struct osrel *rel)
snprintf(rel->version, sizeof(rel->version), "(%s)", codename); snprintf(rel->version, sizeof(rel->version), "(%s)", codename);
} }
if (rel->id[0] && !strcmp(rel->id, "alpine"))
return 1; /* compat mode for \r et al. */
return 0; return 0;
} }
+15 -10
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 atttributes, background and foreground color */
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
log_init(); log_init();
} }
@@ -522,9 +525,10 @@ void networking(int updown)
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
int rc = EX_OSERR;
const char *cmd; const char *cmd;
char buf[256];
FILE *pp; FILE *pp;
int rc;
setsid(); setsid();
sig_unblock(); sig_unblock();
@@ -539,16 +543,16 @@ void networking(int updown)
cmd = "ifdown -a -f 2>&1"; cmd = "ifdown -a -f 2>&1";
pp = popen(cmd, "r"); pp = popen(cmd, "r");
if (pp) { if (!pp)
char buf[256]; _exit(EX_OSERR);
while (fgets(buf, sizeof(buf), pp)) while (fgets(buf, sizeof(buf), pp))
logit(LOG_NOTICE, "network: %s", chomp(buf)); logit(LOG_NOTICE, "network: %s", chomp(buf));
rc = pclose(pp); rc = pclose(pp);
} if (rc == -1)
_exit(EX_OSERR);
_exit(rc); _exit(WEXITSTATUS(rc));
} }
cgroup_service("network", pid, NULL); cgroup_service("network", pid, NULL);
print(pid > 0 ? 0 : 1, "%s network interfaces ...", print(pid > 0 ? 0 : 1, "%s network interfaces ...",
@@ -581,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;
+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;
-9
View File
@@ -42,8 +42,6 @@ static int loglevel = LOG_INFO;
void log_init(void) void log_init(void)
{ {
ttinit();
if (debug) if (debug)
loglevel = LOG_DEBUG; loglevel = LOG_DEBUG;
else else
@@ -53,13 +51,6 @@ void log_init(void)
/* If we enabled terse mode at boot, restore to previous setting at shutdown */ /* If we enabled terse mode at boot, restore to previous setting at shutdown */
void log_exit(void) void log_exit(void)
{ {
/*
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (!debug)
ttinit();
enable_progress(1); enable_progress(1);
} }
+18 -6
View File
@@ -26,18 +26,27 @@
#else #else
#include <sys/prctl.h> #include <sys/prctl.h>
const char *active_msg = NULL;
int interactive = 1;
int debug; int debug;
static void print_desc(const char *prefix, const char *msg) static void print_desc(const char *prefix, const char *msg)
{ {
fprintf(stderr, "\e[1m[\e[0m\e[1;33m ⋯ \e[0m\e[1m]\e[0m %s %s", prefix, msg); if (interactive)
fprintf(stderr, "\e[1m[\e[0m\e[1;33m ⋯ \e[0m\e[1m]\e[0m %s %s", prefix, msg);
else
fprintf(stderr, "%s %s ...\n", prefix, msg);
active_msg = msg;
} }
static void print_result(int rc) static void print_result(int rc)
{ {
if (rc) if (interactive) {
fputs("\r\e[1m[\e[0m\e[1;31mFAIL\e[0m\e[1m]\e[0m\n", stderr); if (rc)
else fputs("\r\e[1m[\e[0m\e[1;31mFAIL\e[0m\e[1m]\e[0m\n", stderr);
fputs("\r\e[1m[\e[0m\e[1;32m OK \e[0m\e[1m]\e[0m\n", stderr); else
fputs("\r\e[1m[\e[0m\e[1;32m OK \e[0m\e[1m]\e[0m\n", stderr);
} else
fprintf(stderr, "[%s] %s\n", rc ? "FAIL" : " OK ", active_msg ?: "");
} }
void sig_unblock(void) void sig_unblock(void)
@@ -193,8 +202,11 @@ int main(int argc, char *argv[])
int rc, c, progress = 0, sysv = 0; int rc, c, progress = 0, sysv = 0;
char *dir; char *dir;
while ((c = getopt(argc, argv, "dh?ps")) != EOF) { while ((c = getopt(argc, argv, "bdh?ps")) != EOF) {
switch(c) { switch(c) {
case 'b': /* batch mode */
interactive = 0;
break;
case 'd': case 'd':
debug = 1; debug = 1;
break; break;
+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;
} }
+46 -27
View File
@@ -222,20 +222,13 @@ int serv_list(char *arg)
* If the resulting file doesn't exist, and creat is not set, *or* * If the resulting file doesn't exist, and creat is not set, *or*
* the base directory doesn't exist, we return NULL. * the base directory doesn't exist, we return NULL.
.*/ .*/
static char *conf(char *path, size_t len, char *name, int creat) static char *conf(char *path, size_t len, char *name, int creat, int template)
{ {
char corr[40];
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) { if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
strlcpy(path, finit_conf, len); strlcpy(path, finit_conf, len);
return path; return path;
} }
if (!strstr(name, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", name);
name = corr;
}
if (!fisdir(finit_rcsd)) if (!fisdir(finit_rcsd))
return NULL; return NULL;
@@ -243,14 +236,25 @@ static char *conf(char *path, size_t len, char *name, int creat)
if (!fisdir(path)) { if (!fisdir(path)) {
if (creat && mkdir(path, 0755) && errno != EEXIST) if (creat && mkdir(path, 0755) && errno != EEXIST)
return NULL; return NULL;
else paste(path, len, finit_rcsd, name);
paste(path, len, finit_rcsd, name);
} else } else
strlcat(path, name, len); strlcat(path, name, len);
if (suffix(path, len, ".conf"))
return NULL;
/* fall back to static service unless edit/create */ /* fall back to static service unless edit/create */
if (!creat && !fexist(path)) if (!creat && !fexist(path)) {
paste(path, len, finit_rcsd, name); char *at = strchr(path, '@');
if (template && at)
*++at = 0;
else
paste(path, len, finit_rcsd, name);
if (suffix(path, len, ".conf"))
return NULL;
}
return path; return path;
} }
@@ -336,14 +340,14 @@ int do_disable(char *arg, int check)
return serv_list("available"); return serv_list("available");
} }
len = strlen(arg); len = strlen(arg) + 7;
serv = alloca(len + 6); serv = alloca(len);
if (!serv) if (!serv)
ERR(71, "failed allocating stack"); ERR(71, "failed allocating stack");
strlcpy(serv, arg, len + 6); strlcpy(serv, arg, len);
if (len < 6 || strcmp(&serv[len - 5], ".conf")) if (suffix(serv, len, ".conf"))
strlcat(serv, ".conf", len + 6); ERR(71, "failed composing service name (%s)", serv);
if (chdir(finit_rcsd)) if (chdir(finit_rcsd))
ERR(72, "failed cd %s", finit_rcsd); ERR(72, "failed cd %s", finit_rcsd);
@@ -374,20 +378,30 @@ int serv_touch(char *arg)
return serv_list("enabled"); return serv_list("enabled");
} }
fn = conf(path, sizeof(path), arg, 0); /* 1. Try /etc/finit.d/enabled/$arg.conf to handle template@.conf */
paste(path, sizeof(path), finit_rcsd, "enabled/");
strlcat(path, arg, sizeof(path));
if (!suffix(path, sizeof(path), ".conf") && fexist(path)) {
fn = path;
goto touchit;
}
/* 2. Try /etc/finit.d/available/$arg.conf and other combos (legacy) */
fn = conf(path, sizeof(path), arg, 0, 0);
if (!fexist(fn)) { if (!fexist(fn)) {
if (!strstr(arg, "finit.conf")) if (!strstr(arg, "finit.conf"))
ERRX(72, "%s not available.", arg); ERRX(noerr ? 0 : 72, "%s not available.", arg);
if (is_builtin(arg)) if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg); ERRX(noerr ? 0 : 4, "%s is a built-in service.", arg);
strlcpy(path, finit_conf, sizeof(path)); strlcpy(path, finit_conf, sizeof(path));
fn = path; fn = path;
} }
touchit:
/* libite:touch() follows symlinks */ /* libite:touch() follows symlinks */
if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW)) if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW))
ERR(71, "failed marking %s for reload", fn); ERR(noerr ? 0 : 71, "failed marking %s for reload", fn);
return 0; return 0;
} }
@@ -397,13 +411,18 @@ int serv_show(char *arg)
char path[256]; char path[256];
char *fn; char *fn;
fn = conf(path, sizeof(path), arg, 0); fn = conf(path, sizeof(path), arg, 0, 0);
if (!fexist(fn)) { if (!fexist(fn)) {
if (is_builtin(arg)) if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg); ERRX(4, "%s is a built-in service.", arg);
WARNX("Cannot find %s", arg); fn = conf(path, sizeof(path), arg, 0, 1);
return 1; if (!fn || !fexist(fn)) {
WARNX("Cannot find %s", arg);
return 1;
}
return systemf("cat %s | sed 's/%%i/system/g'", fn);
} }
return systemf("cat %s", fn); return systemf("cat %s", fn);
@@ -434,7 +453,7 @@ static int do_edit(char *arg, int creat)
char path[256]; char path[256];
char *fn; char *fn;
fn = conf(path, sizeof(path), arg, creat); fn = conf(path, sizeof(path), arg, creat, 0);
if (!fexist(fn)) { if (!fexist(fn)) {
if (is_builtin(arg)) if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg); ERRX(4, "%s is a built-in service.", arg);
@@ -488,7 +507,7 @@ int serv_creat(char *arg)
return do_edit(arg, 1); return do_edit(arg, 1);
/* Open fn for writing from pipe */ /* Open fn for writing from pipe */
fn = conf(buf, sizeof(buf), arg, 1); fn = conf(buf, sizeof(buf), arg, 1, 0);
if (!fn) if (!fn)
ERR(73, "failed creating conf %s", arg); ERR(73, "failed creating conf %s", arg);
@@ -517,7 +536,7 @@ int serv_delete(char *arg)
return serv_list("available"); return serv_list("available");
} }
fn = conf(buf, sizeof(buf), arg, 0); fn = conf(buf, sizeof(buf), arg, 0, 0);
if (!fn) { if (!fn) {
if (is_builtin(arg)) if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg); ERRX(4, "%s is a built-in service.", arg);
+234 -82
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);
@@ -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() */
@@ -1632,11 +1669,19 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
} }
if (type == SVC_TYPE_TTY) { if (type == SVC_TYPE_TTY) {
size_t i, len = 0; size_t i, len;
char *ptr;
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))
dev = tty_atcon();
else
dev = tty.dev;
next:
len = 0;
if (tty.cmd) if (tty.cmd)
len += strlen(tty.cmd); len += strlen(tty.cmd);
else else
@@ -1659,14 +1704,24 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (!cmd) if (!cmd)
return errno; return errno;
if (tty_isatcon(tty.dev))
dev = tty_atcon();
else
dev = tty.dev;
/* tty's always respawn, never incr. restart_cnt */ /* tty's always respawn, never incr. restart_cnt */
respawn = 1; respawn = 1;
next:
/* Create name:id tuple for identity, e.g., tty:S0 */
if (dev) {
ptr = strrchr(dev, '/');
if (ptr)
ptr++;
else
ptr = dev;
if (!strncmp(ptr, "tty", 3))
ptr += 3;
name = "tty";
if (!id || id[0] == 0)
id = ptr;
}
svc = svc_find_by_tty(dev); svc = svc_find_by_tty(dev);
} else } else
svc = svc_find(name, id); svc = svc_find(name, id);
@@ -1722,8 +1777,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
conf_parse_cond(svc, cond); conf_parse_cond(svc, cond);
if (type == SVC_TYPE_TTY) { if (type == SVC_TYPE_TTY) {
char *ptr;
if (dev) if (dev)
strlcpy(svc->dev, dev, sizeof(svc->dev)); strlcpy(svc->dev, dev, sizeof(svc->dev));
if (tty.baud) if (tty.baud)
@@ -1738,19 +1791,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
/* TTYs cannot be redirected */ /* TTYs cannot be redirected */
log = NULL; log = NULL;
/* Create name:id tuple for identity, e.g., tty:S0 */
ptr = strrchr(svc->dev, '/');
if (ptr)
ptr++;
else
ptr = svc->dev;
if (!strncmp(ptr, "tty", 3))
ptr += 3;
if (!id || id[0] == 0)
id = ptr;
strlcpy(svc->name, "tty", sizeof(svc->name));
strlcpy(svc->id, id, sizeof(svc->id));
} }
parse_cmdline_args(svc, cmd, &args); parse_cmdline_args(svc, cmd, &args);
@@ -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);
@@ -1870,16 +1915,18 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
/* continue expanding any 'tty @console ...' */ /* continue expanding any 'tty @console ...' */
if (tty_isatcon(tty.dev)) { if (tty_isatcon(tty.dev)) {
dev = tty_atcon(); dev = tty_atcon();
if (dev) if (dev) {
id = NULL; /* reset for next tty:ID */
goto next; goto next;
}
} }
return 0; return 0;
} }
/* /*
* 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
@@ -1902,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);
@@ -1927,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;
} }
@@ -1948,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:
@@ -1963,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;
@@ -1999,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)
{ {
@@ -2010,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)));
} }
} }
@@ -2038,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);
} }
/* /*
@@ -2049,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);
@@ -2077,6 +2145,8 @@ static void service_pre_script(svc_t *svc)
}; };
char *env_file; char *env_file;
redirect(svc);
env_file = svc_getenv(svc); env_file = svc_getenv(svc);
if (env_file) if (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);
@@ -2089,9 +2159,7 @@ static void service_pre_script(svc_t *svc)
} }
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)
@@ -2142,11 +2210,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 re-use
* 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;
@@ -2182,9 +2255,42 @@ void service_ready_script(svc_t *svc)
} }
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);
env_file = svc_getenv(svc);
if (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");
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)
@@ -2261,7 +2367,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */ /* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if (new_state == SVC_STOPPING_STATE) { if (new_state == SVC_STOPPING_STATE) {
dbg("%s is stopping, wait %d sec before sending SIGKILL ...", dbg("%s is stopping, wait %d sec before sending SIGKILL ...",
svc_ident(svc, NULL, 0), svc->killdelay / 1000); svc_ident(svc, NULL, 0), svc->killdelay / 1000);
service_timeout_cancel(svc); service_timeout_cancel(svc);
service_timeout_after(svc, svc->killdelay, service_kill); service_timeout_after(svc, svc->killdelay, service_kill);
} }
@@ -2291,7 +2397,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));
@@ -2299,6 +2405,8 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) || if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) ||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE)) (old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
; /* only paused during reload, don't clear conds. */ ; /* only paused during reload, don't clear conds. */
else if (sm_is_in_teardown(&sm))
cond_clear_noupdate(cond);
else else
cond_clear(cond); cond_clear(cond);
@@ -2375,7 +2483,9 @@ restart:
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 ...",
@@ -2387,6 +2497,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);
@@ -2409,8 +2543,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;
@@ -2429,14 +2566,28 @@ 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 (!svc->pid) {
svc_set_state(svc, SVC_STARTING_STATE); 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:
@@ -2517,7 +2668,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++;
@@ -2739,7 +2890,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;
} }
@@ -2797,6 +2948,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);
+51 -3
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;
@@ -313,12 +327,30 @@ 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);
#ifdef FINIT_RC_LOCAL
/* Compat SysV init */ /* Compat SysV init */
if (prevlevel == INIT_LEVEL && !rescue) if (prevlevel == INIT_LEVEL && !rescue)
run_bg(FINIT_RC_LOCAL, NULL); run_bg(FINIT_RC_LOCAL, NULL);
#endif
/* /*
* "I've seen things you people wouldn't believe. Attack ships on fire off * "I've seen things you people wouldn't believe. Attack ships on fire off
@@ -364,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);
} }
} }
+26 -5
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 */
@@ -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,11 +279,13 @@ 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_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; }
@@ -385,12 +400,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";
@@ -403,6 +421,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";
} }
+5 -1
View File
@@ -90,7 +90,7 @@ static int rmrf(const char *path)
if (!fisdir(path)) if (!fisdir(path))
return 0; return 0;
nftw(path, do_delete, 20, FTW_DEPTH); nftw(path, do_delete, 20, FTW_DEPTH | FTW_PHYS);
if (remove(path) && errno != ENOENT) if (remove(path) && errno != ENOENT)
warn("Failed removing path %s", path); warn("Failed removing path %s", path);
@@ -434,6 +434,10 @@ static void tmpfiles(char *line)
} }
} }
break; break;
case 'X':
case 'x':
dbg("Unsupported x/X command, ignoring %s, no support for clean at runtime.", path);
break;
case 'Z': case 'Z':
opts = "-R"; opts = "-R";
/* fallthrough */ /* fallthrough */
+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) {
+16
View File
@@ -42,6 +42,8 @@
# include <sys/ioctl.h> # include <sys/ioctl.h>
#endif #endif
#include <sys/sysinfo.h> /* sysinfo() */ #include <sys/sysinfo.h> /* sysinfo() */
#include <sys/vfs.h> /* statfs */
#include <linux/magic.h>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
#else #else
@@ -572,6 +574,20 @@ int fismnt(char *dir)
return ismnt("/proc/mounts", dir, NULL); return ismnt("/proc/mounts", dir, NULL);
} }
/* Return 1 if dir is a backed by tmpfs or overlayfs */
int fistmpfs(char *dir)
{
struct statfs info = {0};
if (statfs(dir, &info))
return 0;
if (info.f_type == TMPFS_MAGIC || info.f_type == OVERLAYFS_SUPER_MAGIC)
return 1;
return 0;
}
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
/* /*
* Called by initctl, and by finit at boot and shutdown, to * Called by initctl, and by finit at boot and shutdown, to
+21
View File
@@ -80,6 +80,7 @@ void de_dotdot (char *file);
int ismnt (char *file, char *dir, char *mode); int ismnt (char *file, char *dir, char *mode);
int fismnt (char *dir); int fismnt (char *dir);
int fistmpfs (char *dir);
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
int ttinit (void); int ttinit (void);
@@ -109,6 +110,26 @@ static inline int paste(char *buf, size_t len, const char *dir, const char *file
fisslashdir(dir) ? "" : file[0] == '/' ? "" : "/", file); fisslashdir(dir) ? "" : file[0] == '/' ? "" : "/", file);
} }
/* ensure path has suffix */
static inline int suffix(char *path, size_t len, const char *sfx)
{
size_t slen = strlen(sfx);
size_t plen = strlen(path);
if (plen < slen)
slen = strlcat(path, sfx, len);
else {
plen -= slen;
if (strcmp(&path[plen], sfx))
slen = strlcat(path, sfx, len);
}
if (slen >= len)
return -1;
return 0;
}
#endif /* FINIT_UTIL_H_ */ #endif /* FINIT_UTIL_H_ */
/** /**
+4 -2
View File
@@ -18,8 +18,10 @@
# change the contents any way you like, it can even be empty. # change the contents any way you like, it can even be empty.
# Check for systemd-udevd and eudev, if we find both, we opt for the latter. # Check for systemd-udevd and eudev, if we find both, we opt for the latter.
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log /lib/systemd/systemd-udevd -- Device event daemon (udev) service nowarn cgroup.system name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log udevd -D -- Device event daemon (udev) [S12345789] /lib/systemd/systemd-udevd $UDEVD_ARGS -- Device event daemon (udev)
service nowarn cgroup.system name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
[S12345789] udevd $UDEVD_ARGS -- Device event daemon (udev)
# Wait for udevd to start, then trigger coldplug events and module loading. # Wait for udevd to start, then trigger coldplug events and module loading.
# The last 'settle' call waits for it to finalize processing all uevents. # The last 'settle' call waits for it to finalize processing all uevents.
+34 -26
View File
@@ -1,26 +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/sbin/task.sh skel/bin/crasher.sh \ skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \
skel/etc/init.d/S01-service.sh skel/etc/init.d/S02-serv.sh \ skel/etc/finit.d/enabled/.empty \
skel/proc/.empty skel/root/.empty skel/run/.empty \ skel/sbin/task.sh skel/bin/crasher.sh skel/bin/fail.sh \
skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \ skel/etc/init.d/S01-service.sh skel/etc/init.d/S02-serv.sh \
skel/usr/bin/.empty skel/usr/lib/.empty skel/usr/sbin/.empty \ skel/proc/.empty skel/root/.empty skel/run/.empty \
skel/home/.empty skel/usr/games/.empty skel/usr/src/.empty \ skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \
skel/usr/share/.empty skel/usr/include/.empty \ skel/usr/bin/.empty skel/usr/lib/.empty skel/usr/sbin/.empty \
skel/usr/local/games/.empty skel/usr/local/sbin/.empty \ skel/home/.empty skel/usr/games/.empty skel/usr/src/.empty \
skel/usr/local/share/.empty skel/usr/local/src/.empty \ skel/usr/share/.empty skel/usr/include/.empty \
skel/usr/local/lib/.empty skel/usr/local/bin/.empty \ skel/usr/local/games/.empty skel/usr/local/sbin/.empty \
skel/usr/local/share/.empty skel/usr/local/src/.empty \
skel/usr/local/lib/.empty skel/usr/local/bin/.empty \
skel/usr/local/etc/.empty skel/var/.empty skel/usr/local/etc/.empty skel/var/.empty
EXTRA_DIST += setup-sysroot.sh EXTRA_DIST += setup-sysroot.sh
@@ -38,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
@@ -63,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
@@ -84,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
@@ -107,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
+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
+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)"
+72 -23
View File
@@ -1,4 +1,10 @@
#!/bin/sh #!/bin/sh
#
# Verify pre:script and post:script runs as intended when starting,
# stopping and disabling services in various configurations and
# conditions.
#
set -eu set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
@@ -16,6 +22,65 @@ test_teardown()
run "rm -f $FINIT_CONF" run "rm -f $FINIT_CONF"
} }
startit()
{
service=$1
conf=$2
pre=$3
precont=$4
say "Add stanza '$service' to $conf ..."
run "echo '$service' > $conf"
if echo "$conf" |grep -q available; then
# configlets need must be enabled
run "initctl enable serv"
fi
say 'Reload Finit'
run "initctl reload"
# Ensure service has started before checking pre condition
retry 'assert_num_children 1 serv'
# Verify pre:script has run
if [ -n "$pre" ]; then
assert_file_contains "$pre" "$precont"
# Drop pre condition indicator for next test
run "rm -f $pre"
fi
}
stopit()
{
conf=$1
post=$2
postcont=$3
if echo "$conf" |grep -q available; then
say 'Disable service & reload'
run "initctl disable serv"
run "initctl reload"
else
say 'Stop the service'
run "initctl stop serv"
fi
# Ensure service has stopped before checking for post condition
retry 'assert_num_children 0 serv'
# Verify post:script has run
if [ -n "$post" ]; then
assert_file_contains "$post" "$postcont"
# Drop post condition indicator for next test
run "rm -f $post"
fi
say "Done, drop $conf ..."
run "rm $conf"
run "initctl reload"
}
test_one() test_one()
{ {
pre=$1 pre=$1
@@ -23,31 +88,15 @@ test_one()
service=$3 service=$3
post=$4 post=$4
postcont=$5 postcont=$5
avail="$FINIT_RCSD/available/serv.conf"
say "Add service stanza '$service' to $FINIT_CONF ..." sep "Stage 1/2"
run "echo '$service' > $FINIT_CONF" startit "$service" "$FINIT_CONF" "$pre" "$precont"
stopit "$FINIT_CONF" "$post" "$postcont"
say 'Reload Finit' sep "Stage 2/2"
run "initctl reload" startit "$service" "$avail" "$pre" "$precont"
stopit "$avail" "$post" "$postcont"
retry 'assert_num_children 1 serv'
if [ -n "$pre" ]; then
assert_file_contains "$pre" "$precont"
run "rm -f $pre"
fi
say 'Stop the service'
run "initctl stop serv"
retry 'assert_num_children 0 serv'
if [ -n "$post" ]; then
assert_file_contains "$post" "$postcont"
run "rm -f $post"
fi
say "Done, drop service from $FINIT_CONF ..."
run "rm $FINIT_CONF"
run "initctl reload"
} }
# shellcheck source=/dev/null # shellcheck source=/dev/null
+1
View File
@@ -1,5 +1,6 @@
#!/bin/sh #!/bin/sh
# Regression test for basic /etc/rc.local functionality # Regression test for basic /etc/rc.local functionality
# shellcheck disable=SC2317
set -eu set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
+3 -3
View File
@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# Regression test for issue #351 - verify that a dirty run/task in # Regression test for issue #351 - verify that a dirty run/task in
# runlevel S is not restarted forever jus because it exits with a # runlevel S is not restarted forever just because it exits with a
# non-zero error code. # non-zero error code.
set -eu set -eu
@@ -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
+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"
+5 -1
View File
@@ -2,10 +2,14 @@ finitdir = $(finit_tmpfiles)
dist_finit_DATA = finit.conf dist_finit_DATA = finit.conf
tmpfilesdir = $(tmpfiles_path) tmpfilesdir = $(tmpfiles_path)
dist_tmpfiles_DATA = etc.conf legacy.conf var.conf x11.conf dist_tmpfiles_DATA = etc.conf legacy.conf var.conf
dist_tmpfiles_DATA += dnsmasq.conf frr.conf lldpd.conf openswan.conf \ dist_tmpfiles_DATA += dnsmasq.conf frr.conf lldpd.conf openswan.conf \
quagga.conf sshd.conf uuidd.conf quagga.conf sshd.conf uuidd.conf
if BUILD_TESTSERV_PLUGIN if BUILD_TESTSERV_PLUGIN
dist_tmpfiles_DATA += testserv.conf dist_tmpfiles_DATA += testserv.conf
endif endif
if BUILD_X11_COMMON_PLUGIN
dist_tmpfiles_DATA += x11.conf
endif