mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffd01b0276 | ||
|
|
2c95f091ee | ||
|
|
b75b107d6d | ||
|
|
1f2c1d2109 | ||
|
|
f328d24d01 | ||
|
|
211293a83d | ||
|
|
446f5bc8b8 | ||
|
|
0c6f748f2c | ||
|
|
a677b94ad9 | ||
|
|
6552cda605 | ||
|
|
3add682be3 | ||
|
|
abac31682d | ||
|
|
cfe915e6d5 | ||
|
|
ecf65ce44b | ||
|
|
0d742d6ae8 | ||
|
|
cf201a43df | ||
|
|
9245869e18 | ||
|
|
0f4a487328 | ||
|
|
f1b5b42919 | ||
|
|
f546129baf | ||
|
|
27451ac3f2 | ||
|
|
0a8f3a6250 | ||
|
|
70c4e7d774 | ||
|
|
5265eee25d | ||
|
|
4b06e1a49f | ||
|
|
6b9aa21509 | ||
|
|
990307fbc9 | ||
|
|
9d8a9b7fba | ||
|
|
bbc83eaa64 | ||
|
|
23d034f521 | ||
|
|
9a3148a49d | ||
|
|
8560103a24 | ||
|
|
7a75e34808 | ||
|
|
4d4fc10d57 | ||
|
|
003faae9e9 | ||
|
|
01a01d65c0 | ||
|
|
048302f06a | ||
|
|
74f0fcfccd | ||
|
|
3cfe0a33b8 | ||
|
|
465bc17ca4 | ||
|
|
d4889b435d | ||
|
|
49c0557ced | ||
|
|
6be16f2f6d | ||
|
|
bc8118d515 | ||
|
|
0c0e880f3f | ||
|
|
119e66a7e9 | ||
|
|
46ffa81f5c | ||
|
|
54701422a5 | ||
|
|
9d463e3823 | ||
|
|
56e558c960 | ||
|
|
dfaf351da1 | ||
|
|
a8433fcb7f | ||
|
|
2daa953bee | ||
|
|
3e3e9aadf5 | ||
|
|
dc873c6548 | ||
|
|
29bd7a504e | ||
|
|
a0685219cf | ||
|
|
13b107b7b1 | ||
|
|
409d68d05b | ||
|
|
ab62b5282b | ||
|
|
d02d65feeb | ||
|
|
9a0dea6aef | ||
|
|
7481505b1a | ||
|
|
8251f1a422 | ||
|
|
e269176a79 | ||
|
|
612f5a9385 | ||
|
|
4a2381eb6f | ||
|
|
0cae7d44dd | ||
|
|
340cae4afd | ||
|
|
6a89e60fc8 | ||
|
|
840d612fc2 | ||
|
|
60c676ab2d | ||
|
|
deada0fa89 | ||
|
|
7b501f255b | ||
|
|
052136cb99 | ||
|
|
742b8b72cc | ||
|
|
c37037c015 | ||
|
|
c32b5a749a | ||
|
|
7701718d7b | ||
|
|
eb4eca9607 | ||
|
|
cab94b81e8 | ||
|
|
5913217808 | ||
|
|
b49f55ab3d | ||
|
|
bf4a584e2e | ||
|
|
90a4df8d12 | ||
|
|
146bf55122 | ||
|
|
c1ed373398 | ||
|
|
5998342b3e | ||
|
|
9dc7b8a6b0 | ||
|
|
c221823654 | ||
|
|
248f7d3110 | ||
|
|
fe90ba5781 | ||
|
|
71c87734b7 | ||
|
|
cd060e8af3 | ||
|
|
33a7f123d7 |
@@ -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)
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
|
||||
-O coverity-latest.tar.gz.md5
|
||||
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: coverity-latest.tar.gz
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;}')
|
||||
|
||||
+107
-1
@@ -4,6 +4,109 @@ 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
|
||||
--------------------
|
||||
|
||||
### 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
|
||||
--------------------
|
||||
|
||||
@@ -24,6 +127,7 @@ All relevant changes are documented in this file.
|
||||
Found and fixed by Ryan Rorison
|
||||
- Fix #389: must libc requires `libgen.h` for `basename()` function.
|
||||
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
|
||||
@@ -1655,7 +1759,9 @@ Major bug fix 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.6]: https://github.com/troglobit/finit/compare/4.5...4.6
|
||||
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
|
||||
|
||||
@@ -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,
|
||||
with a few hooks and blocking call outs to external scripts.
|
||||
|
||||
However, not all `initctl` commands are prohibited, supported commands:
|
||||
are:
|
||||
However, not all `initctl` commands are prohibited. Supported commands:
|
||||
|
||||
- `inictl cond`: only operate of files in `/run/finit/cond`
|
||||
- `initctl enable/disable`: enabled run/task/service is activated on
|
||||
|
||||
+39
-10
@@ -1,4 +1,4 @@
|
||||
AC_INIT([Finit], [4.7], [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])
|
||||
@@ -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]),,[
|
||||
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,
|
||||
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
||||
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,
|
||||
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp]),,[
|
||||
enable_logrotate=yes])
|
||||
@@ -169,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
|
||||
@@ -181,6 +193,12 @@ AS_IF([test "x$enable_auto_reload" = "xyes"], [
|
||||
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
|
||||
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"], [
|
||||
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")
|
||||
|
||||
AS_IF([test "x$with_rc_local" != "xno"], [
|
||||
AS_IF([test "x$rclocal" = "xyes"], [
|
||||
rclocal=$sysconfdir/rc.local])])
|
||||
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.])
|
||||
AS_IF([test "x$rclocal" = "xyes"], [rclocal=$sysconfdir/rc.local])
|
||||
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.])])
|
||||
|
||||
AS_IF([test "x$with_sysconfig" != "xno"], [
|
||||
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.])])
|
||||
|
||||
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"], [
|
||||
@@ -355,15 +379,16 @@ cat <<EOF
|
||||
|
||||
------------------ Summary ------------------
|
||||
$PACKAGE_NAME version $PACKAGE_VERSION
|
||||
Prefix................: $prefix
|
||||
Exec prefix...........: $eprefix
|
||||
Prefix................: $(eval echo $prefix)
|
||||
Exec prefix...........: $(eval echo $exec_prefix)
|
||||
Libdir................: $(eval echo $libdir)
|
||||
Sysconfdir............: `eval echo $sysconfdir`
|
||||
Localstatedir.........: `eval echo $localstatedir`
|
||||
Default fstab.........: `eval echo $fstab`
|
||||
Finit config file.....: $conf_path
|
||||
Finit config.d path...: $rcsd_path
|
||||
Finit plugin path.....: $plugin_path
|
||||
Compat rc.local path..: $rclocal_path
|
||||
Compat rc.local path..: `eval echo $rclocal`
|
||||
System environment....: ${sysconfig_path:-${sysconfig}}
|
||||
Random seed path......: $random_path
|
||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||
@@ -373,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
|
||||
@@ -382,6 +408,9 @@ Optional features:
|
||||
Built-in sulogin......: $with_sulogin $sulogin
|
||||
Built-in watchdogd....: $with_watchdog $watchdog
|
||||
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
|
||||
Run fsck fix mode.....: $enable_fsckfix
|
||||
Redirect output.......: $enable_redirect
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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
|
||||
the user has already installed make, a compiler, C library header files,
|
||||
and other tools needed to build a GNU configure based project.
|
||||
|
||||
+40
-21
@@ -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
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
@@ -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
|
||||
|
||||
+11
-2
@@ -52,7 +52,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
|
||||
ptr = slash++;
|
||||
slash = strchr(ptr, '/');
|
||||
if (slash) {
|
||||
dbg("Path too deep, skipping.");
|
||||
dbg("Path too deep, skipping %s", path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
char cond[MAX_COND_LEN];
|
||||
char fn[PATH_MAX];
|
||||
svc_t *svc;
|
||||
char *nm;
|
||||
|
||||
paste(fn, sizeof(fn), dir, name);
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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)) {
|
||||
/*
|
||||
* 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)
|
||||
service_ready(svc, 1);
|
||||
} else if (mask & IN_DELETE) {
|
||||
sneaky_zebra:
|
||||
cond_clear(cond);
|
||||
if (svc->notify == SVC_NOTIFY_PID)
|
||||
service_ready(svc, 0);
|
||||
|
||||
+5
-1
@@ -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);
|
||||
|
||||
+177
-60
@@ -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,65 +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);
|
||||
} else {
|
||||
print_desc(NULL, "Restoring system clock (UTC) from RTC");
|
||||
rc = time_set(&tm);
|
||||
}
|
||||
|
||||
invalid:
|
||||
logit(LOG_ERR, "Failed restoring system clock from RTC.");
|
||||
if (rc) {
|
||||
logit(LOG_WARNING, "Failed restoring system clock from RTC.");
|
||||
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)
|
||||
logit(LOG_ERR, "RTC has no previously saved (valid) time.");
|
||||
logit(LOG_WARNING, "RTC has no previously saved (valid) time.");
|
||||
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? */
|
||||
if (rc)
|
||||
goto out;
|
||||
print(2, NULL);
|
||||
|
||||
/* Attempt to set RTC to a sane value ... */
|
||||
tv.tv_sec = rtc_date_fallback;
|
||||
if (!gmtime_r(&tv.tv_sec, &tm))
|
||||
goto out;
|
||||
/* Try restoring from last save game */
|
||||
if (rtc_file)
|
||||
file_restore(arg);
|
||||
} 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);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -211,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);
|
||||
}
|
||||
|
||||
+49
-10
@@ -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
|
||||
@@ -42,11 +43,43 @@
|
||||
#define RANDOM_BYTES (32*1024)
|
||||
#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)
|
||||
{
|
||||
#ifdef RANDOMSEED
|
||||
struct rand_pool_info *rpi;
|
||||
ssize_t len = 0;
|
||||
struct stat st;
|
||||
int rc = -1;
|
||||
int fd, err;
|
||||
|
||||
@@ -55,7 +88,7 @@ static void setup(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fexist(RANDOMSEED)) {
|
||||
if (stat(RANDOMSEED, &st) || st.st_size < 512) {
|
||||
int ret = 1;
|
||||
mode_t prev;
|
||||
FILE *fp;
|
||||
@@ -64,16 +97,22 @@ static void setup(void *arg)
|
||||
prev = umask(077);
|
||||
fp = fopen(RANDOMSEED, "w");
|
||||
if (fp) {
|
||||
int iter = 128;
|
||||
struct timeval tv;
|
||||
const char *hwrng = "/dev/hwrng";
|
||||
FILE *hw;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_sec % 3600);
|
||||
while (iter--) {
|
||||
uint32_t i, prng = random();
|
||||
hw = fopen(hwrng, "r");
|
||||
if (hw) {
|
||||
char buf[512];
|
||||
size_t len;
|
||||
|
||||
for (i = 0; i < sizeof(prng); i++)
|
||||
fputc((prng >> (i * CHAR_BIT)) & UCHAR_MAX, fp);
|
||||
len = fread(buf, sizeof(buf[0]), sizeof(buf), hw);
|
||||
if (len == 0)
|
||||
fallback(fp);
|
||||
else
|
||||
len = fwrite(buf, sizeof(buf[0]), len, fp);
|
||||
fclose(hw);
|
||||
} else {
|
||||
fallback(fp);
|
||||
}
|
||||
ret = fclose(fp);
|
||||
}
|
||||
@@ -130,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:
|
||||
|
||||
@@ -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");
|
||||
@@ -377,7 +382,9 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
case INIT_CMD_POWEROFF:
|
||||
case INIT_CMD_SUSPEND:
|
||||
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;
|
||||
}
|
||||
default:
|
||||
@@ -576,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();
|
||||
|
||||
@@ -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");
|
||||
@@ -463,6 +468,11 @@ void cgroup_init(uev_ctx_t *ctx)
|
||||
FILE *fp;
|
||||
int fd;
|
||||
|
||||
#ifndef CGROUP2_ENABLED
|
||||
avail = 0;
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
|
||||
if (errno == ENOENT)
|
||||
logit(LOG_INFO, "Kernel does not support cgroups v2, disabling.");
|
||||
|
||||
+4
-2
@@ -29,6 +29,8 @@
|
||||
#include "client.h"
|
||||
#include "log.h"
|
||||
|
||||
#define REQUEST_TIMEOUT 15000
|
||||
|
||||
static int sd = -1;
|
||||
|
||||
int client_connect(void)
|
||||
@@ -85,7 +87,7 @@ int client_request(struct init_request *rq, ssize_t len)
|
||||
|
||||
pfd.fd = sd;
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
@@ -107,7 +109,7 @@ int client_request(struct init_request *rq, ssize_t len)
|
||||
|
||||
pfd.fd = sd;
|
||||
pfd.events = POLLIN | POLLERR | POLLHUP;
|
||||
if ((rc = poll(&pfd, 1, 2000)) <= 0) {
|
||||
if ((rc = poll(&pfd, 1, REQUEST_TIMEOUT)) <= 0) {
|
||||
if (rc) {
|
||||
if (errno == EINTR) /* shutdown/reboot */
|
||||
return -1;
|
||||
|
||||
+8
-4
@@ -35,6 +35,7 @@
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
#include "sm.h"
|
||||
|
||||
struct cond_boot {
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -284,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;
|
||||
@@ -317,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;
|
||||
|
||||
@@ -368,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;
|
||||
|
||||
|
||||
+36
-17
@@ -348,8 +348,11 @@ static void parse_kernel_loglevel(void)
|
||||
*/
|
||||
void conf_parse_cmdline(int argc, char *argv[])
|
||||
{
|
||||
const char *ptr;
|
||||
|
||||
/* Set up defaults */
|
||||
fstab = strdup(FINIT_FSTAB);
|
||||
if ((ptr = FINIT_FSTAB))
|
||||
fstab = strdup(ptr);
|
||||
finit_conf = strdup(FINIT_CONF);
|
||||
finit_rcsd = strdup(FINIT_RCSD);
|
||||
path = getenv("PATH");
|
||||
@@ -1429,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);
|
||||
@@ -1477,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;
|
||||
|
||||
@@ -1529,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;
|
||||
@@ -1561,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+21
-20
@@ -138,31 +138,32 @@ int run(char *cmd, char *log)
|
||||
|
||||
pid = fork();
|
||||
if (0 == pid) {
|
||||
int rc = EX_OSERR;
|
||||
char *pfx, buf[256];
|
||||
FILE *pp;
|
||||
int rc;
|
||||
|
||||
setsid();
|
||||
sig_unblock();
|
||||
if (!log) {
|
||||
do_redirect();
|
||||
execvp(args[0], args);
|
||||
} else {
|
||||
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(EX_OSERR);
|
||||
}
|
||||
|
||||
_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) {
|
||||
err(1, "%s", !log ? args[0] : cmd);
|
||||
if (backup)
|
||||
@@ -198,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) {
|
||||
@@ -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] */
|
||||
if (fp && !debug) {
|
||||
char line[LINE_SIZE];
|
||||
size_t len, written;
|
||||
|
||||
rewind(fp);
|
||||
@@ -280,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;
|
||||
@@ -350,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)
|
||||
|
||||
+21
-8
@@ -87,14 +87,22 @@ svc_t *wdog = NULL; /* No watchdog by default */
|
||||
*/
|
||||
static void banner(void)
|
||||
{
|
||||
#ifndef KERNEL_LOGGING
|
||||
/*
|
||||
* Silence kernel logs, assuming users have sysklogd or
|
||||
* similar enabled to start emptying /dev/kmsg, but for
|
||||
* our progress we want to own the console.
|
||||
* similar enabled to start emptying /dev/kmsg.
|
||||
*
|
||||
* 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)
|
||||
klogctl(6, NULL, 0);
|
||||
|
||||
#endif
|
||||
/*
|
||||
* First level hooks, if you want to run here, you're
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
@@ -401,7 +414,7 @@ static void fs_finalize(void)
|
||||
* To override any of this behavior, add entries to /etc/fstab
|
||||
* 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%");
|
||||
|
||||
/* 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 */
|
||||
if (!fismnt("/tmp"))
|
||||
if (!fismnt("/tmp") && !fistmpfs("/tmp"))
|
||||
fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,9 @@ static int osrel(struct osrel *rel)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+15
-10
@@ -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();
|
||||
}
|
||||
|
||||
@@ -522,9 +525,10 @@ void networking(int updown)
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int rc = EX_OSERR;
|
||||
const char *cmd;
|
||||
char buf[256];
|
||||
FILE *pp;
|
||||
int rc;
|
||||
|
||||
setsid();
|
||||
sig_unblock();
|
||||
@@ -539,16 +543,16 @@ void networking(int updown)
|
||||
cmd = "ifdown -a -f 2>&1";
|
||||
|
||||
pp = popen(cmd, "r");
|
||||
if (pp) {
|
||||
char buf[256];
|
||||
if (!pp)
|
||||
_exit(EX_OSERR);
|
||||
|
||||
while (fgets(buf, sizeof(buf), pp))
|
||||
logit(LOG_NOTICE, "network: %s", chomp(buf));
|
||||
while (fgets(buf, sizeof(buf), pp))
|
||||
logit(LOG_NOTICE, "network: %s", chomp(buf));
|
||||
|
||||
rc = pclose(pp);
|
||||
}
|
||||
|
||||
_exit(rc);
|
||||
rc = pclose(pp);
|
||||
if (rc == -1)
|
||||
_exit(EX_OSERR);
|
||||
_exit(WEXITSTATUS(rc));
|
||||
}
|
||||
cgroup_service("network", pid, NULL);
|
||||
print(pid > 0 ? 0 : 1, "%s network interfaces ...",
|
||||
@@ -581,7 +585,8 @@ int in_container(void)
|
||||
"lxc",
|
||||
"docker",
|
||||
"kubepod",
|
||||
"unshare"
|
||||
"unshare",
|
||||
"systemd-nspawn"
|
||||
};
|
||||
const char *files[] = {
|
||||
"/run/.containerenv",
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,8 +42,6 @@ static int loglevel = LOG_INFO;
|
||||
|
||||
void log_init(void)
|
||||
{
|
||||
ttinit();
|
||||
|
||||
if (debug)
|
||||
loglevel = LOG_DEBUG;
|
||||
else
|
||||
@@ -53,13 +51,6 @@ void log_init(void)
|
||||
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+18
-6
@@ -26,18 +26,27 @@
|
||||
#else
|
||||
#include <sys/prctl.h>
|
||||
|
||||
const char *active_msg = NULL;
|
||||
int interactive = 1;
|
||||
int debug;
|
||||
|
||||
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)
|
||||
{
|
||||
if (rc)
|
||||
fputs("\r\e[1m[\e[0m\e[1;31mFAIL\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);
|
||||
if (interactive) {
|
||||
if (rc)
|
||||
fputs("\r\e[1m[\e[0m\e[1;31mFAIL\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)
|
||||
@@ -193,8 +202,11 @@ int main(int argc, char *argv[])
|
||||
int rc, c, progress = 0, sysv = 0;
|
||||
char *dir;
|
||||
|
||||
while ((c = getopt(argc, argv, "dh?ps")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "bdh?ps")) != EOF) {
|
||||
switch(c) {
|
||||
case 'b': /* batch mode */
|
||||
interactive = 0;
|
||||
break;
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+46
-27
@@ -222,20 +222,13 @@ int serv_list(char *arg)
|
||||
* If the resulting file doesn't exist, and creat is not set, *or*
|
||||
* 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")) {
|
||||
strlcpy(path, finit_conf, len);
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!strstr(name, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", name);
|
||||
name = corr;
|
||||
}
|
||||
|
||||
if (!fisdir(finit_rcsd))
|
||||
return NULL;
|
||||
|
||||
@@ -243,14 +236,25 @@ static char *conf(char *path, size_t len, char *name, int creat)
|
||||
if (!fisdir(path)) {
|
||||
if (creat && mkdir(path, 0755) && errno != EEXIST)
|
||||
return NULL;
|
||||
else
|
||||
paste(path, len, finit_rcsd, name);
|
||||
paste(path, len, finit_rcsd, name);
|
||||
} else
|
||||
strlcat(path, name, len);
|
||||
|
||||
if (suffix(path, len, ".conf"))
|
||||
return NULL;
|
||||
|
||||
/* fall back to static service unless edit/create */
|
||||
if (!creat && !fexist(path))
|
||||
paste(path, len, finit_rcsd, name);
|
||||
if (!creat && !fexist(path)) {
|
||||
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;
|
||||
}
|
||||
@@ -336,14 +340,14 @@ int do_disable(char *arg, int check)
|
||||
return serv_list("available");
|
||||
}
|
||||
|
||||
len = strlen(arg);
|
||||
serv = alloca(len + 6);
|
||||
len = strlen(arg) + 7;
|
||||
serv = alloca(len);
|
||||
if (!serv)
|
||||
ERR(71, "failed allocating stack");
|
||||
|
||||
strlcpy(serv, arg, len + 6);
|
||||
if (len < 6 || strcmp(&serv[len - 5], ".conf"))
|
||||
strlcat(serv, ".conf", len + 6);
|
||||
strlcpy(serv, arg, len);
|
||||
if (suffix(serv, len, ".conf"))
|
||||
ERR(71, "failed composing service name (%s)", serv);
|
||||
|
||||
if (chdir(finit_rcsd))
|
||||
ERR(72, "failed cd %s", finit_rcsd);
|
||||
@@ -374,20 +378,30 @@ int serv_touch(char *arg)
|
||||
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 (!strstr(arg, "finit.conf"))
|
||||
ERRX(72, "%s not available.", arg);
|
||||
ERRX(noerr ? 0 : 72, "%s not available.", 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));
|
||||
fn = path;
|
||||
}
|
||||
|
||||
touchit:
|
||||
/* libite:touch() follows symlinks */
|
||||
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;
|
||||
}
|
||||
@@ -397,13 +411,18 @@ int serv_show(char *arg)
|
||||
char path[256];
|
||||
char *fn;
|
||||
|
||||
fn = conf(path, sizeof(path), arg, 0);
|
||||
fn = conf(path, sizeof(path), arg, 0, 0);
|
||||
if (!fexist(fn)) {
|
||||
if (is_builtin(arg))
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
|
||||
WARNX("Cannot find %s", arg);
|
||||
return 1;
|
||||
fn = conf(path, sizeof(path), arg, 0, 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);
|
||||
@@ -434,7 +453,7 @@ static int do_edit(char *arg, int creat)
|
||||
char path[256];
|
||||
char *fn;
|
||||
|
||||
fn = conf(path, sizeof(path), arg, creat);
|
||||
fn = conf(path, sizeof(path), arg, creat, 0);
|
||||
if (!fexist(fn)) {
|
||||
if (is_builtin(arg))
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
@@ -488,7 +507,7 @@ int serv_creat(char *arg)
|
||||
return do_edit(arg, 1);
|
||||
|
||||
/* Open fn for writing from pipe */
|
||||
fn = conf(buf, sizeof(buf), arg, 1);
|
||||
fn = conf(buf, sizeof(buf), arg, 1, 0);
|
||||
if (!fn)
|
||||
ERR(73, "failed creating conf %s", arg);
|
||||
|
||||
@@ -517,7 +536,7 @@ int serv_delete(char *arg)
|
||||
return serv_list("available");
|
||||
}
|
||||
|
||||
fn = conf(buf, sizeof(buf), arg, 0);
|
||||
fn = conf(buf, sizeof(buf), arg, 0, 0);
|
||||
if (!fn) {
|
||||
if (is_builtin(arg))
|
||||
ERRX(4, "%s is a built-in service.", arg);
|
||||
|
||||
+231
-82
@@ -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() */
|
||||
@@ -1632,11 +1669,18 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
|
||||
if (type == SVC_TYPE_TTY) {
|
||||
size_t i, len = 0;
|
||||
size_t i, len;
|
||||
char *ptr;
|
||||
|
||||
if (tty_parse_args(&tty, cmd, &args))
|
||||
return errno;
|
||||
|
||||
if (tty_isatcon(tty.dev))
|
||||
dev = tty_atcon();
|
||||
else
|
||||
dev = tty.dev;
|
||||
next:
|
||||
len = 0;
|
||||
if (tty.cmd)
|
||||
len += strlen(tty.cmd);
|
||||
else
|
||||
@@ -1659,14 +1703,22 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
if (!cmd)
|
||||
return errno;
|
||||
|
||||
if (tty_isatcon(tty.dev))
|
||||
dev = tty_atcon();
|
||||
else
|
||||
dev = tty.dev;
|
||||
|
||||
/* tty's always respawn, never incr. restart_cnt */
|
||||
respawn = 1;
|
||||
next:
|
||||
|
||||
/* Create name:id tuple for identity, e.g., tty:S0 */
|
||||
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);
|
||||
} else
|
||||
svc = svc_find(name, id);
|
||||
@@ -1722,8 +1774,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
conf_parse_cond(svc, cond);
|
||||
|
||||
if (type == SVC_TYPE_TTY) {
|
||||
char *ptr;
|
||||
|
||||
if (dev)
|
||||
strlcpy(svc->dev, dev, sizeof(svc->dev));
|
||||
if (tty.baud)
|
||||
@@ -1738,19 +1788,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
|
||||
/* TTYs cannot be redirected */
|
||||
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);
|
||||
@@ -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);
|
||||
@@ -1870,16 +1912,18 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
/* continue expanding any 'tty @console ...' */
|
||||
if (tty_isatcon(tty.dev)) {
|
||||
dev = tty_atcon();
|
||||
if (dev)
|
||||
if (dev) {
|
||||
id = NULL; /* reset for next tty:ID */
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -1902,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);
|
||||
@@ -1927,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;
|
||||
}
|
||||
@@ -1948,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:
|
||||
@@ -1963,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;
|
||||
@@ -1999,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)
|
||||
{
|
||||
@@ -2010,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)));
|
||||
}
|
||||
}
|
||||
@@ -2038,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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2049,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);
|
||||
|
||||
@@ -2077,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);
|
||||
@@ -2089,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)
|
||||
@@ -2142,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;
|
||||
@@ -2182,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)
|
||||
@@ -2261,7 +2364,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 (new_state == SVC_STOPPING_STATE) {
|
||||
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_after(svc, svc->killdelay, service_kill);
|
||||
}
|
||||
@@ -2291,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));
|
||||
@@ -2299,6 +2402,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) ||
|
||||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
|
||||
; /* only paused during reload, don't clear conds. */
|
||||
else if (sm_is_in_teardown(&sm))
|
||||
cond_clear_noupdate(cond);
|
||||
else
|
||||
cond_clear(cond);
|
||||
|
||||
@@ -2375,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 ...",
|
||||
@@ -2387,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);
|
||||
@@ -2409,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;
|
||||
@@ -2429,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:
|
||||
@@ -2517,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++;
|
||||
@@ -2739,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;
|
||||
}
|
||||
@@ -2797,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;
|
||||
}
|
||||
|
||||
@@ -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,12 +327,30 @@ 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);
|
||||
|
||||
#ifdef FINIT_RC_LOCAL
|
||||
/* Compat SysV init */
|
||||
if (prevlevel == INIT_LEVEL && !rescue)
|
||||
run_bg(FINIT_RC_LOCAL, NULL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "I've seen things you people wouldn't believe. Attack ships on fire off
|
||||
@@ -364,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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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_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; }
|
||||
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_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_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
@@ -385,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";
|
||||
|
||||
@@ -403,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";
|
||||
}
|
||||
|
||||
+5
-1
@@ -90,7 +90,7 @@ static int rmrf(const char *path)
|
||||
if (!fisdir(path))
|
||||
return 0;
|
||||
|
||||
nftw(path, do_delete, 20, FTW_DEPTH);
|
||||
nftw(path, do_delete, 20, FTW_DEPTH | FTW_PHYS);
|
||||
if (remove(path) && errno != ENOENT)
|
||||
warn("Failed removing path %s", path);
|
||||
|
||||
@@ -434,6 +434,10 @@ static void tmpfiles(char *line)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'X':
|
||||
case 'x':
|
||||
dbg("Unsupported x/X command, ignoring %s, no support for clean at runtime.", path);
|
||||
break;
|
||||
case 'Z':
|
||||
opts = "-R";
|
||||
/* fallthrough */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+16
@@ -42,6 +42,8 @@
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <sys/vfs.h> /* statfs */
|
||||
#include <linux/magic.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
#else
|
||||
@@ -572,6 +574,20 @@ int fismnt(char *dir)
|
||||
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
|
||||
/*
|
||||
* Called by initctl, and by finit at boot and shutdown, to
|
||||
|
||||
+21
@@ -80,6 +80,7 @@ void de_dotdot (char *file);
|
||||
|
||||
int ismnt (char *file, char *dir, char *mode);
|
||||
int fismnt (char *dir);
|
||||
int fistmpfs (char *dir);
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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_ */
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
# change the contents any way you like, it can even be empty.
|
||||
|
||||
# Check for systemd-udevd and eudev, if we find both, we opt for the latter.
|
||||
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log /lib/systemd/systemd-udevd -- Device event daemon (udev)
|
||||
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log udevd -D -- Device event daemon (udev)
|
||||
service nowarn cgroup.system name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
||||
[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.
|
||||
# The last 'settle' call waits for it to finalize processing all uevents.
|
||||
|
||||
+34
-26
@@ -1,26 +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/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
|
||||
@@ -38,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
|
||||
@@ -63,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
|
||||
@@ -84,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
|
||||
@@ -107,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
@@ -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 ..."
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+28
@@ -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
@@ -1,4 +1,10 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Verify pre:script and post:script runs as intended when starting,
|
||||
# stopping and disabling services in various configurations and
|
||||
# conditions.
|
||||
#
|
||||
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
@@ -16,6 +22,65 @@ test_teardown()
|
||||
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()
|
||||
{
|
||||
pre=$1
|
||||
@@ -23,31 +88,15 @@ test_one()
|
||||
service=$3
|
||||
post=$4
|
||||
postcont=$5
|
||||
avail="$FINIT_RCSD/available/serv.conf"
|
||||
|
||||
say "Add service stanza '$service' to $FINIT_CONF ..."
|
||||
run "echo '$service' > $FINIT_CONF"
|
||||
sep "Stage 1/2"
|
||||
startit "$service" "$FINIT_CONF" "$pre" "$precont"
|
||||
stopit "$FINIT_CONF" "$post" "$postcont"
|
||||
|
||||
say 'Reload Finit'
|
||||
run "initctl reload"
|
||||
|
||||
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"
|
||||
sep "Stage 2/2"
|
||||
startit "$service" "$avail" "$pre" "$precont"
|
||||
stopit "$avail" "$post" "$postcont"
|
||||
}
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
# Regression test for basic /etc/rc.local functionality
|
||||
# shellcheck disable=SC2317
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
# 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.
|
||||
set -eu
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
sleep 2
|
||||
exit 42
|
||||
+2
-1
@@ -10,7 +10,8 @@
|
||||
#
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
#DEBUG=1
|
||||
# shellcheck disable=2034
|
||||
DEBUG=1
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
BOOTSTRAP="runparts sysv /etc/rcS.d"
|
||||
|
||||
@@ -2,10 +2,14 @@ finitdir = $(finit_tmpfiles)
|
||||
dist_finit_DATA = finit.conf
|
||||
|
||||
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 \
|
||||
quagga.conf sshd.conf uuidd.conf
|
||||
|
||||
if BUILD_TESTSERV_PLUGIN
|
||||
dist_tmpfiles_DATA += testserv.conf
|
||||
endif
|
||||
|
||||
if BUILD_X11_COMMON_PLUGIN
|
||||
dist_tmpfiles_DATA += x11.conf
|
||||
endif
|
||||
|
||||
Reference in New Issue
Block a user