Compare commits

..
47 Commits
Author SHA1 Message Date
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
35 changed files with 842 additions and 257 deletions
+6 -4
View File
@@ -21,7 +21,6 @@ jobs:
compiler: [gcc, clang]
fail-fast: false
env:
MAKEFLAGS: -j3
CC: ${{ matrix.compiler }}
steps:
- name: Install dependencies
@@ -33,14 +32,14 @@ jobs:
run: |
./autogen.sh
./configure --prefix= --enable-static
make V=1
make -j3 V=1
- name: Regular Finit
run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd
make clean
make V=1
make -j3 clean
make -j3 V=1
- name: Install to /tmp
run: |
DESTDIR=/tmp make install-strip
@@ -57,6 +56,9 @@ jobs:
run: |
sudo /tmp/sbin/finit -h
sudo /tmp/sbin/initctl -h
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Run Unit Tests
run: |
make check || (cat test/test-suite.log; false)
+3
View File
@@ -39,6 +39,9 @@ jobs:
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Build release ...
run: |
make release || (cat test/test-suite.log; false)
+3
View File
@@ -41,6 +41,9 @@ jobs:
- name: Install to /tmp
run: |
DESTDIR=/tmp make install-strip
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Check dist tarball
run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
+57 -1
View File
@@ -4,6 +4,61 @@ Change Log
All relevant changes are documented in this file.
[4.10][] - 2025-02-23
---------------------
### 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 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
--------------------
@@ -1704,7 +1759,8 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.8...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.6]: https://github.com/troglobit/finit/compare/4.5...4.6
+15 -3
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.8], [https://github.com/troglobit/finit/issues],
AC_INIT([Finit], [4.10-rc1], [https://github.com/troglobit/finit/issues],
[finit], [https://troglobit.com/projects/finit/])
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
@@ -177,9 +177,13 @@ AC_ARG_WITH(plugin-path,
[plugin_path=$withval], [plugin_path=yes])
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])
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 ###########################################################################
# Create config.h from selected features and fallback defaults
@@ -278,9 +282,16 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
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=""])
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_sulogin" != "xno"], [
@@ -387,6 +398,7 @@ Behavior:
Boot heading..........: $heading
Plugins...............: $plugins
RTC restore date......: $RTC_DATE
RTC fallback file.....: $rtc_file
Optional features:
Install doc/..........: $enable_doc
+40 -21
View File
@@ -890,33 +890,52 @@ configurable with the following options:
* `oncrash:script` -- similarly, but instead of rebooting, call the
`post:script` action if set, see below.
When stopping a service (run/task/sysv/service), either manually or
when moving to another runlevel, Finit starts by sending `SIGTERM`, to
allow the process to shut down gracefully. If the process has not
been collected within 3 seconds, Finit sends `SIGKILL`. To halt the
process using a different signal, use the option `halt:SIGNAL`, e.g.,
`halt:SIGPWR`. To change the delay between your halt signal and KILL,
use the option `kill:SEC`, e.g., `kill:10` to wait 10 seconds before
sending `SIGKILL`.
When stopping a service (run/task/sysv/service), either manually or when
moving to another runlevel, Finit starts by sending `SIGTERM`, to allow
the process to shut down gracefully. However, if the process has not
been collected within 3 seconds, Finit will send `SIGKILL`. To stop the
process using a different signal than `SIGTERM`, use `halt:SIGNAL`,
e.g., `halt:SIGPWR`. To change the delay between the stop signal and
KILL, use the option `kill:<1-60>`, e.g., `kill:10` to wait 10 seconds
before sending `SIGKILL`.
Services support `pre:script` and `post:script` actions as well. These
run as the same `@USER:GROUP` as the service itself, with any `env:file`
sourced. The scripts must use an absolute path, but are executed from
the `$HOME` of the given user. The scripts are not called with any
argument (currently), but both get the `SERVICE_IDENT=foo` environment
variable set. Here `foo` denotes the identity of the service, which if
there are multiple services named `foo`, may be `foo:1`, or any unique
identifier specified in the .conf file. The `post:script` is called
with an additional set of environment variables:
Services, including the `sysv` variant, support pre/post/ready and
cleanup scripts:
- `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
the program, if it exited normally, or the signal name (`HUP`,
`TERM`, etc.) if it exited due to signal
These script actions *must terminate*, so they have a default execution
time of 3 seconds before they are SIGKILLed, this can be adjusted using
the above `kill:SEC` syntax.
When a run/task/sys/service is removed (disable + reload) it is first
stopped and then removed from the runlevel. The `post:script` always
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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 410 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
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
Similar to
.Cm task
.Cm service
is the
.Cm sysv
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
existing setup and init scripts in Linux distributions.
.Pp
primary intention for this command is to be able to re-use traditional
SysV init scripts in Linux distributions.
.Pp
When entering an allowed runlevel, Finit calls
.Cm init-script start ,
when entering a disallowed runlevel, Finit calls
@@ -434,30 +434,42 @@ e.g.,
.Cm kill:10
to wait 10 seconds before sending SIGKILL.
.Pp
Services support the
.Cm pre:script
and
.Cm post:script
command actions as well. These run as the same
Services, including the
.Cm sysv
variant, support pre/post/ready and cleanup scripts:
.Bl -tag -width 1n
.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
as the service itself, with any
.Cm env:file
sourced. The scripts must use an absolute path, but are executed from
the
.Cm $HOME
of the given user. The scripts are not called with any argument
(currently), but both get the
.Cm SERVICE_IDENT=foo
environment variable set. Here
.Cm foo
denotes the identity of the service, which if there are multiple
services named
.Cm foo ,
may be
.Cm foo:1 ,
or any unique identifier specified in the .conf file. The
of the given user. The scripts are not called with any argument, but
both get a set of environment variables:
.Pp
.Bl -tag -width 1n -compact
.It Cm SERVICE_IDENT=foo:1
.It Cm SERVICE_NAME=foo
.It Cm SERVICE_ID=1
.El
.Pp
The
.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
.It Cm EXIT_CODE=[exited,signal,crashed]
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
.El
.Pp
The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the
.Cm kill:SEC
modifier syntax.
When a run/task/sys/service is removed (disable + reload) it is first
stopped and then removed from the runlevel. The
.Cm post:script
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
Call
.Xr run-parts 8
+5 -1
View File
@@ -32,6 +32,10 @@
#include "helpers.h"
#include "plugin.h"
#ifndef SYSCTL_PATH
#define SYSCTL_PATH "/sbin/sysctl"
#endif
static void setup(void *arg)
{
glob_t gl;
@@ -54,7 +58,7 @@ static void setup(void *arg)
for (i = 0; i < gl.gl_pathc; i++) {
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");
}
globfree(&gl);
+180 -64
View File
@@ -36,8 +36,15 @@
#include "helpers.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_FMT "%Y-%m-%d %H:%M:%S"
#define RTC_PERIOD (11 * 60 * 1000)
#ifdef RTC_TIMESTAMP_CUSTOM
static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM;
#else
@@ -45,6 +52,10 @@ static char *rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
#endif
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)
{
char *ptr;
@@ -68,6 +79,125 @@ static void tz_restore(char *tz)
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)
{
char *alt[] = {
@@ -91,10 +221,8 @@ static int rtc_open(void)
static void rtc_save(void *arg)
{
struct timeval tv = { 0 };
struct tm tm = { 0 };
int fd, rc = 0;
char tz[128];
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
@@ -105,38 +233,26 @@ static void rtc_save(void *arg)
if (fd < 0)
return;
tz_set(tz, sizeof(tz));
rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < rtc_date_fallback) {
if ((rc = time_get(&tm))) {
print_desc(NULL, "System clock invalid, not saving to RTC");
invalid:
logit(LOG_ERR, "System clock invalid, before %s, not saving to RTC", rtc_timestamp);
} else {
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;
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);
close(fd);
}
static void rtc_restore(void *arg)
{
struct timeval tv = { 0 };
struct tm tm = { 0 };
int fd, rc = 0;
char tz[128];
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
@@ -145,66 +261,62 @@ static void rtc_restore(void *arg)
fd = rtc_open();
if (fd < 0) {
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
file_restore(arg);
return;
}
tz_set(tz, sizeof(tz));
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
if ((rc = ioctl(fd, RTC_RD_TIME, &tm)) < 0) {
char msg[120];
snprintf(msg, sizeof(msg), "Failed restoring system clock, %s",
EINVAL == errno ? "RTC time is too old" :
ENOENT == errno ? "RTC has no saved time" : "see log for details");
print_desc(NULL, msg);
invalid:
logit(LOG_ERR, "Failed restoring system clock from RTC.");
if (EINVAL == errno)
logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp);
else if (ENOENT == errno)
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno));
/* Been here already? */
if (rc)
goto out;
/* Attempt to set RTC to a sane value ... */
tv.tv_sec = rtc_date_fallback;
if (!gmtime_r(&tv.tv_sec, &tm))
goto out;
logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp);
rc = 2;
}
if (!rc)
} else {
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;
rc = time_set(&tm);
}
if (settimeofday(&tv, NULL) == -1)
rc = 1;
if (rc) {
logit(LOG_WARNING, "Failed restoring system clock from RTC.");
if (EINVAL == errno)
logit(LOG_WARNING, "RTC time is too old (before %s)", rtc_timestamp);
else if (ENOENT == errno)
logit(LOG_WARNING, "RTC has no previously saved (valid) time.");
else
logit(LOG_WARNING, "RTC error code %d: %s", errno, strerror(errno));
print(2, NULL);
/* Try restoring from last save game */
if (rtc_file)
file_restore(arg);
} else
print(0, NULL);
out:
tz_restore(tz);
print(rc, NULL);
close(fd);
}
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 = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = rtc_restore
},
.hook[HOOK_SHUTDOWN] = {
.cb = rtc_save
.cb = save
}
};
@@ -212,10 +324,14 @@ PLUGIN_INIT(plugin_init)
{
struct tm tm = { 0 };
if (!strptime(rtc_timestamp, "%Y-%m-%d %H:%M", &tm))
rtc_date_fallback = mktime(&tm);
else
if (!strptime(rtc_timestamp, RTC_FMT, &tm)) {
logit(LOG_WARNING, "Invalid restore date '%s', reverting to '%s'",
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);
}
+17 -2
View File
@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */
#include <sys/types.h>
#include <sys/resource.h> /* getrusage() */
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
@@ -43,13 +44,27 @@
#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);
srandom(tv.tv_sec % 3600);
getrusage(RUSAGE_SELF, &ru);
/* Mix multiple sources of "randomness" */
seed = tv.tv_sec ^ (tv.tv_usec << 16);
seed ^= ru.ru_utime.tv_sec ^ (ru.ru_utime.tv_usec << 16);
seed ^= ru.ru_stime.tv_sec ^ (ru.ru_stime.tv_usec << 16);
seed ^= getpid() << 8;
srandom(seed);
while (iter--) {
uint32_t i, prng = random();
@@ -154,7 +169,7 @@ static void setup(void *arg)
close(fd);
free(rpi);
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);
return;
fallback:
+5 -2
View File
@@ -332,6 +332,11 @@ static void api_cb(uev_t *w, void *arg, int events)
int sd, lvl;
svc_t *svc;
if (UEV_ERROR == events) {
dbg("%s(): api socket %d invalid.", __func__, w->fd);
goto error;
}
sd = accept(w->fd, NULL, NULL);
if (sd < 0) {
err(1, "Failed serving API request");
@@ -578,8 +583,6 @@ static void api_cb(uev_t *w, void *arg, int events)
leave:
close(sd);
if (UEV_ERROR == events)
goto error;
return;
error:
api_exit();
+5
View File
@@ -259,6 +259,11 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
ssize_t sz;
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);
if (sz <= 0) {
err(1, "invalid inotify event");
+3 -3
View File
@@ -288,7 +288,7 @@ int cond_update(const char *name)
svc_t *svc, *iter = NULL;
int affects = 0;
dbg("%s", name);
// dbg("%s", name);
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue;
@@ -321,7 +321,7 @@ int cond_set_noupdate(const char *name)
void cond_set(const char *name)
{
dbg("%s", name);
// dbg("%s", name);
if (cond_set_noupdate(name))
return;
@@ -372,7 +372,7 @@ int cond_clear_noupdate(const char *name)
void cond_clear(const char *name)
{
dbg("%s", name);
// dbg("%s", name);
if (cond_clear_noupdate(name))
return;
+32 -16
View File
@@ -1432,16 +1432,22 @@ static int conf_change_act(char *dir, char *name, uint32_t mask)
strlcpy(fn, dir, sizeof(fn));
dbg("path: %s mask: %08x", fn, mask);
/* Handle disabling/removal of service */
rp = realpath(fn, NULL);
if (!rp) {
if (errno != ENOENT)
goto fail;
if (strchr(name, '@')) {
/* Skip realpath for templates */
rp = strdup(fn);
if (!rp)
goto fail;
} else {
/* 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);
if (node) {
dbg("event already registered for %s ...", name);
@@ -1480,7 +1486,11 @@ int conf_changed(char *file)
if (!file)
return 0;
rp = realpath(file, NULL);
if (strchr(file, '@'))
rp = strdup(file);
else
rp = realpath(file, NULL);
if (!rp)
return 0;
@@ -1532,6 +1542,11 @@ static int conf_iwatch_read(int fd)
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)) {
err(1, "invalid inotify event");
return;
@@ -1564,25 +1579,26 @@ int conf_monitor(void)
* have or not have symlinks in place. We need to monitor for
* 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);
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);
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
rc += iwatch_add(&iw_conf, finit_conf, 0);
rc |= iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
rc |= iwatch_add(&iw_conf, finit_conf, 0);
/*
* Systems with /etc/default, /etc/conf.d, or similar, can also
* monitor changes in env files sourced by .conf files (above)
* define your own with --with-sysconfig=/path/to/envfiles
*/
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/default/", IN_ONLYDIR);
rc |= iwatch_add(&iw_conf, "/etc/conf.d/", IN_ONLYDIR);
#ifdef FINIT_SYSCONFIG
rc += iwatch_add(&iw_conf, FINIT_SYSCONFIG, IN_ONLYDIR);
rc |= iwatch_add(&iw_conf, FINIT_SYSCONFIG, IN_ONLYDIR);
#endif
rc |= conf_reload();
return rc + conf_reload();
return rc;
}
/*
+3 -3
View File
@@ -199,7 +199,6 @@ int run(char *cmd, char *log)
int run_interactive(char *cmd, char *fmt, ...)
{
int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
FILE *fp;
if (!cmd) {
@@ -244,6 +243,7 @@ int run_interactive(char *cmd, char *fmt, ...)
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
if (fp && !debug) {
char line[LINE_SIZE];
size_t len, written;
rewind(fp);
@@ -281,7 +281,7 @@ int exec_runtask(char *cmd, char *args[])
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];
int fd, dummy;
@@ -351,7 +351,7 @@ static int activate_console(int noclear, int nowait)
dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg));
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
continue;
;
/* On any error (likely EINTR), we avoid starting getty */
if (rc == -1)
+8 -3
View File
@@ -272,10 +272,15 @@ static int fsck(int pass)
* errors were corrected but that the boot may proceed.
*/
if (fsck_rc > 1) {
logit(LOG_CONSOLE | LOG_ALERT, "Failed fsck %s, attempting sulogin ...", dev);
sulogin(1);
if (hasmntopt(mnt,"nofail")) {
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);
+5 -1
View File
@@ -87,6 +87,9 @@ void console_init(void)
/* Enable line wrap, if disabled previously, e.g., qemu */
dprint(STDOUT_FILENO, "\033[?7h", 5);
/* Reset atttributes, background and foreground color */
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
log_init();
}
@@ -582,7 +585,8 @@ int in_container(void)
"lxc",
"docker",
"kubepod",
"unshare"
"unshare",
"systemd-nspawn"
};
const char *files[] = {
"/run/.containerenv",
+2 -1
View File
@@ -141,6 +141,7 @@ static inline int ln(const char *target, const char *linkpath)
return 0;
}
static inline int dprint(int fd, const char *s, size_t len)
{
size_t loop = 3;
@@ -159,7 +160,7 @@ static inline int dprint(int fd, const char *s, size_t len)
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;
size_t len;
+6
View File
@@ -96,6 +96,12 @@ int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
if (!initialized)
return -1;
iwp = iwatch_find_by_path(iw, file);
if (iwp) {
dbg("skipping, %s already registered", file);
return 0;
}
if (!fexist(file)) {
dbg("skipping %s: no such file or directory", file);
return 0;
+2
View File
@@ -23,6 +23,7 @@
#include "config.h"
#include "finit.h"
#include "log.h"
#include "schedule.h"
#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;
if (UEV_ERROR == events) {
dbg("%s(): spurious problem with schedule work timer, restarting.", __func__);
uev_timer_start(w);
return;
}
+203 -58
View File
@@ -81,7 +81,12 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
{
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)
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))
{
if (timeout == 0)
return 0; /* OK, not starting timer. */
if (svc->timer_cb)
return -EBUSY;
@@ -147,16 +155,17 @@ static void service_script_kill(svc_t *svc)
continue;
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);
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) {
err(1, "Failed starting service script timer");
return 1;
@@ -166,7 +175,7 @@ static int service_script_add(svc_t *svc, pid_t pid)
ptr->pid = pid;
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;
}
@@ -484,28 +493,21 @@ static void source_env(svc_t *svc)
while (*end && !isspace(*end))
end++;
if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val);
warnx("'%s=%s': not a valid identifier", key, value);
continue; /* invalid key */
}
switch (wordexp(value, &we, 0)) {
case 0:
if (wordexp(value, &we, 0)) {
setenv(key, value, 1);
} else {
for (i = 0, *val = 0; i < we.we_wordc; i++) {
if (i > 0)
strlcat(val, " ", LINE_SIZE);
strlcat(val, we.we_wordv[i], LINE_SIZE);
}
setenv(key, val, 1);
wordfree(&we);
break;
case WRDE_NOSPACE:
wordfree(&we);
/* fallthrough */
default:
setenv(key, value, 1);
break;
}
wordfree(&we);
}
fclose(fp);
@@ -1297,12 +1299,44 @@ static void parse_killdelay(svc_t *svc, char *delay)
svc->killdelay = (int)(sec * 1000);
}
static void parse_script(char *type, char *script, char *buf, size_t len)
/*
* 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))
logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.", type, script);
else
strlcpy(buf, script, len);
char *found, *path;
path = strchr(script, ',');
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 *pre_script = NULL, *post_script = NULL;
char *ready_script = NULL, *conflict = NULL;
char *cleanup_script = NULL;
char ident[MAX_IDENT_LEN];
char *ifstmt = NULL;
char *notify = NULL;
@@ -1594,6 +1629,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
post_script = arg;
else if (MATCH_CMD(cmd, "ready:", arg))
ready_script = arg;
else if (MATCH_CMD(cmd, "cleanup:", arg))
cleanup_script = arg;
else if (MATCH_CMD(cmd, "env:", arg))
env = arg;
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
@@ -1770,17 +1807,22 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else
svc->killdelay = SVC_TERM_TIMEOUT;
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
memset(svc->pre_script, 0, sizeof(svc->pre_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
memset(svc->post_script, 0, sizeof(svc->post_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
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 (log)
parse_log(svc, log);
@@ -1880,8 +1922,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
}
/*
* This function is called when cleaning up lingering (stopped) services
* after a .conf reload.
* This function is called at the end of a runlevel change or reload
* command to delete all remnants of removed services.
*
* We need to ensure we properly stop the service before removing it,
* including stopping any pending restart or SIGKILL timers before we
@@ -1904,24 +1946,36 @@ void service_unregister(svc_t *svc)
void service_monitor(pid_t lost, int status)
{
int sig = WIFSIGNALED(status);
int rc = WEXITSTATUS(status);
int ok = WIFEXITED(status);
svc_t *svc;
if (lost <= 1)
return;
/* main process as well as pre: and post: scripts use svc->pid */
svc = svc_find_by_pid(lost);
if (!svc) {
/* Check if ready: script in assoc list */
if (service_script_del(lost))
dbg("collected unknown PID %d", lost);
return;
}
switch (svc->state) {
case SVC_CLEANUP_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",
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script,
lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
(svc->state == SVC_TEARDOWN_STATE
? 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 */
dbg("Killing lingering children in same process group ...");
kill(-svc->pid, SIGKILL);
@@ -1929,7 +1983,7 @@ void service_monitor(pid_t lost, int status)
default:
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;
break;
}
@@ -1950,14 +2004,11 @@ void service_monitor(pid_t lost, int status)
kill(-svc->pid, SIGKILL);
/* 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);
} else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */
if (WIFEXITED(status))
svc->started = 1;
else
svc->started = 0;
svc->started = ok;
}
done:
@@ -1965,7 +2016,7 @@ done:
svc->start_time = svc->pid = 0;
cont:
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 */
run_block_pid = 0;
@@ -2001,6 +2052,10 @@ static void svc_mark_affected(char *cond)
* Called on conf_reload() to update service reverse dependencies.
* E.g., if ospfd depends on zebra and the zebra Finit conf has
* changed, we need to mark the ospfd Finit conf as changed too.
*
* 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)
{
@@ -2012,6 +2067,10 @@ void service_update_rdeps(void)
if (!svc_is_changed(svc))
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)));
}
}
@@ -2040,7 +2099,8 @@ static void service_kill_script(svc_t *svc)
if (svc->pid <= 1)
return;
kill(-svc->pid, SIGKILL);
dbg("Timeout, killing service %s script PID %d", svc_ident(svc, NULL, 0), svc->pid);
kill(svc->pid, SIGKILL);
}
/*
@@ -2051,6 +2111,9 @@ static void set_pre_post_envs(svc_t *svc, const char *type)
char buf[25 + 256] = "/etc/default:/etc/conf.d";
setenv("SERVICE_TYPE", svc_typestr(svc), 1);
setenv("SERVICE_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_SCRIPT_TYPE", type, 1);
@@ -2079,6 +2142,8 @@ static void service_pre_script(svc_t *svc)
};
char *env_file;
redirect(svc);
env_file = svc_getenv(svc);
if (env_file)
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->pre_script);
@@ -2091,9 +2156,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);
/* Short hard-coded timeout to prevent locking up Finit */
service_timeout_after(svc, svc->killdelay, service_kill_script);
service_timeout_after(svc, svc->pre_tmo, service_kill_script);
}
static void service_post_script(svc_t *svc)
@@ -2144,11 +2207,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);
/* Short hard-coded timeout to prevent locking up Finit */
service_timeout_after(svc, svc->killdelay, service_kill_script);
service_timeout_after(svc, svc->post_tmo, 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)
{
pid_t pid;
@@ -2184,9 +2252,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);
service_script_add(svc, pid, svc->ready_tmo);
}
/* Short hard-coded timeout to prevent locking up Finit */
service_script_add(svc, pid);
static void service_cleanup_script(svc_t *svc)
{
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)
@@ -2293,7 +2394,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];
snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0));
@@ -2379,7 +2480,9 @@ restart:
if (enabled)
svc_set_state(svc, SVC_WAITING_STATE);
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
logit(svc->nowarn ? LOG_DEBUG : LOG_INFO,
"%s in conflict with %s, checking again ...",
@@ -2391,6 +2494,30 @@ restart:
}
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:
if (svc_is_changed(svc))
svc_set_state(svc, SVC_HALTED_STATE);
@@ -2413,8 +2540,11 @@ restart:
case SVC_TYPE_SYSV:
case SVC_TYPE_TTY:
if (svc_has_post(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE);
svc_set_state(svc, SVC_TEARDOWN_STATE);
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
svc_set_state(svc, SVC_HALTED_STATE);
break;
@@ -2433,14 +2563,28 @@ restart:
}
break;
case SVC_CLEANUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_HALTED_STATE);
break;
case SVC_SETUP_STATE:
if (!svc->pid)
svc_set_state(svc, SVC_STARTING_STATE);
if (!svc->pid) {
int rc = WEXITSTATUS(svc->status);
int ok = WIFEXITED(svc->status);
if (!ok || rc) {
const char *sig;
if (WIFSIGNALED(svc->status))
sig = sig_name(WTERMSIG(svc->status));
else
sig = "none";
logit(LOG_WARNING, "Service %s pre:%s failed, rc: %d sig:%s",
svc_ident(svc, NULL, 0), svc->pre_script, rc, sig);
svc_set_state(svc, SVC_STOPPING_STATE);
svc_crashing(svc);
} else {
svc_set_state(svc, SVC_STARTING_STATE);
}
}
break;
case SVC_WAITING_STATE:
@@ -2521,7 +2665,7 @@ restart:
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->restart_tot++;
svc->once++;
@@ -2743,7 +2887,7 @@ void service_notify_cb(uev_t *w, void *arg, int events)
ssize_t len;
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);
return;
}
@@ -2801,6 +2945,7 @@ static void service_interval_cb(uev_t *w, void *arg, int events)
(void)arg;
if (UEV_ERROR == events) {
dbg("%s(): spurious problem, restarting.", __func__);
uev_timer_start(w);
return;
}
+49 -3
View File
@@ -107,6 +107,9 @@ static char *sm_status(sm_state_t state)
case SM_BOOTSTRAP_STATE:
return "bootstrap";
case SM_BOOTSTRAP_WAIT_STATE:
return "bootstrap/wait";
case SM_RUNNING_STATE:
return "running";
@@ -116,15 +119,20 @@ static char *sm_status(sm_state_t state)
case SM_RUNLEVEL_WAIT_STATE:
return "runlevel/wait";
case SM_RUNLEVEL_CLEAN_STATE:
return "runlevel/clean";
case SM_RELOAD_CHANGE_STATE:
return "reload/change";
case SM_RELOAD_WAIT_STATE:
return "reload/wait";
default:
return "unknown";
case SM_RELOAD_CLEAN_STATE:
return "reload/clean";
}
return "unknown";
}
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)
{
sm->newlevel = newlevel;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
}
void sm_set_reload(sm_t *sm)
{
sm->reload = 1;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
}
int sm_is_in_teardown(sm_t *sm)
@@ -195,7 +209,7 @@ restart:
switch (sm->state) {
case SM_BOOTSTRAP_STATE:
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;
break;
@@ -313,6 +327,22 @@ restart:
sm->in_teardown = 0;
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 */
svc_clean_dynamic(service_unregister);
@@ -366,6 +396,22 @@ restart:
dbg("Starting services after reconf ...");
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 */
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_RUNNING_STATE, /* Normal state, services running */
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_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;
typedef struct sm {
+38 -1
View File
@@ -348,6 +348,43 @@ svc_t *svc_stop_completed(void)
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
* @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;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->removed && cb)
if (svc_is_removed(svc) && cb)
cb(svc);
}
}
+25 -5
View File
@@ -52,14 +52,16 @@ typedef enum {
} svc_type_t;
#define SVC_TYPE_ANY (-1)
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_TTY)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV)
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_SYSV | SVC_TYPE_TTY)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK)
typedef enum {
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
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_CLEANUP_STATE, /* Running post: script */
SVC_SETUP_STATE, /* Running pre: script */
SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */
SVC_WAITING_STATE, /* Enabled but condition not satisfied */
@@ -196,9 +198,18 @@ typedef struct svc {
char conflict[MAX_ARG_LEN];
char desc[MAX_STR_LEN];
char env[MAX_CMD_LEN];
char pre_script[MAX_CMD_LEN];
int pre_tmo;
char post_script[MAX_CMD_LEN];
int post_tmo;
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
@@ -237,6 +248,7 @@ void svc_foreach (int (*cb)(svc_t *));
void svc_foreach_type (int types, int (*cb)(svc_t *));
svc_t *svc_stop_completed (void);
svc_t *svc_clean_completed (void);
void svc_mark (svc_t *svc);
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_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_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_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
@@ -266,6 +279,7 @@ 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_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_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_started (svc_t *svc) { if (svc) svc->starting = 0; }
@@ -386,12 +400,15 @@ static inline char *svc_status(svc_t *svc)
return "stopping";
}
case SVC_CLEANUP_STATE:
return "cleanup";
case SVC_TEARDOWN_STATE:
return "teardown";
case SVC_SETUP_STATE:
return "setup";
case SVC_CLEANUP_STATE:
return "cleanup";
case SVC_PAUSED_STATE:
return "paused";
@@ -404,6 +421,9 @@ static inline char *svc_status(svc_t *svc)
case SVC_RUNNING_STATE:
return "running";
case SVC_DEAD_STATE:
return "dead";
default:
return "UNKNOWN";
}
+4 -3
View File
@@ -172,7 +172,7 @@ int tty_parse_args(struct tty *tty, char *cmd, char **args)
passenv = 1;
else if (!strcmp(cmd, "rescue"))
tty->rescue = 1; /* for rescue shells */
else if (whichp(cmd)) /* in $PATH? */
else if (!tty->cmd && whichp(cmd))
tty->cmd = cmd;
else
tty->args[tty->num++] = cmd;
@@ -229,8 +229,9 @@ int tty_parse_args(struct tty *tty, char *cmd, char **args)
return errno = EINVAL;
}
dbg("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
dbg("Registering %s getty on TTY %s at %s baud with term %s",
tty->cmd ? "external" : "built-in",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
/* Built-in getty now comes as standalone program */
if (!tty->cmd) {
+34 -28
View File
@@ -1,28 +1,28 @@
SUBDIRS = lib src
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/sbin/fail.sh skel/usr/share/runparts/ref.log \
skel/usr/share/runparts/sysv.log \
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/rcS.d/foo.sh skel/etc/rcS.d/test.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/etc/inittab skel/etc/hostname skel/etc/fstab \
skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \
skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \
skel/etc/finit.d/enabled/.empty \
skel/sbin/task.sh skel/bin/crasher.sh \
skel/etc/init.d/S01-service.sh skel/etc/init.d/S02-serv.sh \
skel/proc/.empty skel/root/.empty skel/run/.empty \
skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \
skel/usr/bin/.empty skel/usr/lib/.empty skel/usr/sbin/.empty \
skel/home/.empty skel/usr/games/.empty skel/usr/src/.empty \
skel/usr/share/.empty skel/usr/include/.empty \
skel/usr/local/games/.empty skel/usr/local/sbin/.empty \
skel/usr/local/share/.empty skel/usr/local/src/.empty \
skel/usr/local/lib/.empty skel/usr/local/bin/.empty \
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/sbin/fail.sh skel/usr/share/runparts/ref.log \
skel/usr/share/runparts/sysv.log \
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/rcS.d/foo.sh skel/etc/rcS.d/test.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/etc/inittab skel/etc/hostname skel/etc/fstab \
skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \
skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \
skel/etc/finit.d/enabled/.empty \
skel/sbin/task.sh skel/bin/crasher.sh skel/bin/fail.sh \
skel/etc/init.d/S01-service.sh skel/etc/init.d/S02-serv.sh \
skel/proc/.empty skel/root/.empty skel/run/.empty \
skel/sbin/chrootsetup.sh skel/srv/.empty skel/sys/.empty \
skel/usr/bin/.empty skel/usr/lib/.empty skel/usr/sbin/.empty \
skel/home/.empty skel/usr/games/.empty skel/usr/src/.empty \
skel/usr/share/.empty skel/usr/include/.empty \
skel/usr/local/games/.empty skel/usr/local/sbin/.empty \
skel/usr/local/share/.empty skel/usr/local/src/.empty \
skel/usr/local/lib/.empty skel/usr/local/bin/.empty \
skel/usr/local/etc/.empty skel/var/.empty
EXTRA_DIST += setup-sysroot.sh
@@ -40,6 +40,7 @@ EXTRA_DIST += initctl-status-subset.sh
EXTRA_DIST += notify.sh
EXTRA_DIST += pidfile.sh
EXTRA_DIST += pre-post-serv.sh
EXTRA_DIST += pre-fail.sh
EXTRA_DIST += process-depends.sh
EXTRA_DIST += rclocal.sh
EXTRA_DIST += ready-serv.sh
@@ -65,11 +66,6 @@ AM_TESTS_ENVIRONMENT += export LD_LIBRARY_PATH;
AM_TESTS_ENVIRONMENT += top_builddir=$(top_builddir);
AM_TESTS_ENVIRONMENT += export top_builddir;
checkself.sh:
@echo '#!/bin/sh' > $@
@echo shellcheck $(addprefix '"$$srcdir"'/,$(filter %.sh,$(filter-out $@, $(TESTS)))) >> $@
@chmod +x $@
TEST_EXTENSIONS = .sh
TESTS = checkself.sh
TESTS += add-remove-dynamic-service.sh
@@ -86,6 +82,7 @@ TESTS += initctl-status-subset.sh
TESTS += notify.sh
TESTS += pidfile.sh
TESTS += pre-post-serv.sh
TESTS += pre-fail.sh
TESTS += process-depends.sh
TESTS += rclocal.sh
TESTS += ready-serv.sh
@@ -109,6 +106,15 @@ TESTS += unexpected-restart.sh
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
setup-chroot:
@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"
sleep 2
#run "initctl debug"
run "initctl debug"
say "Add service stanza to $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'
run "initctl reload"
sleep 1
run "initctl"
retry 'assert_num_children 1 serv'
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"
}
# 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()
{
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)"
+2 -2
View File
@@ -42,8 +42,8 @@ test_one()
sep
say "Add stanza to $FINIT_CONF"
run "echo 'run fail.sh -- Failure' > $FINIT_RCSD/fail.conf"
run "echo 'run initctl touch fail' > $FINIT_RCSD/touch.conf"
run "echo 'run /sbin/fail.sh -- Failure' > $FINIT_RCSD/fail.conf"
run "echo 'run initctl touch fail' > $FINIT_RCSD/touch.conf"
test_one 2
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")
#DEBUG=1
# shellcheck disable=2034
DEBUG=1
# shellcheck disable=SC2034
BOOTSTRAP="runparts sysv /etc/rcS.d"