Compare commits

...
82 Commits
Author SHA1 Message Date
Joachim Wiberg ce7d4cebab test: minor refactor, relocate "Test start ..." message to setup.sh
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 13:31:29 +02:00
Joachim Wiberg d5a47e2d8b doc: document new stop:script support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 13:22:11 +02:00
Joachim Wiberg 020eefe732 test: extend start-stop-sysv to verify stop:script support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 13:21:50 +02:00
Joachim Wiberg 14336bd407 Add stop:'script [args]' support for service/sysv stanzas
Fixes #412

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 13:20:46 +02:00
Joachim Wiberg bb841383b8 doc: document new reload:script support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 12:05:59 +02:00
Joachim Wiberg e49763d834 test: extend start-stop-sysv to verify reload:script support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 12:05:58 +02:00
Joachim Wiberg 57545e1ab0 Add reload:'script [args]' support for service/sysv stanzas
Fixes #446

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 12:05:51 +02:00
Joachim Wiberg 7ad802b092 Add quote support to pre/post/ready/cleanup scripts
- Allow scripts to take space separated arguments with quoting
 - Simplify script-exists check, which already checks for leading /
 - Unset current script on parse error
 - Allow setting no timeout

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 12:00:02 +02:00
Joachim Wiberg e319a606b8 tmpfiles: minor, fix memory leak
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 11:36:56 +02:00
Joachim Wiberg 7d434b1eaa Ignore new tmpfiles binary
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:28:42 +02:00
Joachim Wiberg 58d48b54b3 tmpfiles: add -d,--debug mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:28:02 +02:00
Joachim Wiberg 7d4603e13b Minor, simplify
clang-tidy noticed this unnecessary check.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:14:07 +02:00
Joachim Wiberg d0c5d7be5b Minor, constify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:14:05 +02:00
Joachim Wiberg db840bdc19 Follow-up to 8be7a46, simplify linking of tmpfiles program
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:14:04 +02:00
Joachim Wiberg 3df3c51dae Relocate global configuration variables from finit.c -> conf.c
These variables really belong in conf.c.  Relocate and move external
decls. from finit.h -> conf.h to simplify linking of other programs.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:14:03 +02:00
Joachim Wiberg d37d241647 Drop log.h include from helpers.h
With the relocation of several functions, including inline functions,
from helpers to util, we no longer need to include log.h.  Which is a
good thing, since any subsystem that needs logging should explicitly
include log.h

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:14:02 +02:00
Joachim Wiberg b18b21830a Relocate functions helpers.c -> util.c
The file helpers.c should strictly only be used for misc. helper
functions to the main Finit daemon.  The functions moved in this
commit are generic enough to be used by any deamon or programs,
and for that we have util.c

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:14:00 +02:00
Joachim Wiberg 06daf2e4a5 Relocate helper function, only used by tmpfiles
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:13:59 +02:00
Joachim Wiberg eb13ccc227 plugins: minor, rename local functions
Prevent name clash with upcoming refactor and any confusion with
src/plugin.c functions with the same name.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:13:58 +02:00
Joachim Wiberg 0861373d14 Minor, relocate path definition to finit.h
This is where the other path definitions live.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:13:57 +02:00
Joachim Wiberg 1b9e69414e Refactor state machine control functions
This refactor should have been done years ago when we first introduced
the big state machine.  The old functions service_runvel() and the oddly
named service_reload_dynamic() are actually state machine control fns,
so let relocate them.

Also in this commit, remove the global variable 'sm' and rename a few of
the critical functions to better names.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:13:56 +02:00
Joachim Wiberg 39f4aa3f2c Simplify, a daemon is one of service or sysv
Historically we've kept these two apart, but their commonalities are far
more than what separates them.

This fixes a bug where ready:scripts were not called for sysv daemons,
and also opens up for adding support for a reload:script for both types.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-06 03:13:54 +02:00
Joachim Wiberg dedb56a9d0 Merge branch 'refactor-systemd-readiness'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-04 16:45:37 +02:00
Joachim Wiberg a27903c745 libsystemd: translate leading log <LEVEL> in stderr logging
This commit also switches the stdout/stderr log capture backend for all
notify:systemd services from the sysklogd project's logger tool to the
bundled logit tool.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:47:34 +02:00
Joachim Wiberg f24f76fc80 logit: parse leading <LEVEL> in log messages
Add support for systemd daemon log style.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:43:44 +02:00
Joachim Wiberg d95fc5f790 logit: fix parse_prio(), check result of parse
clang-tidy complained that parse_prio() always returned OK(0), turns out
it was right.  This patch adds validation of the parsing.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:43:43 +02:00
Joachim Wiberg 10a1257e47 logit: minor cleanup, reverse christmas tree
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:43:43 +02:00
Joachim Wiberg a7b8e1b94d libsystemd: expand with with some common sd-daemon(3) functions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:43:42 +02:00
Joachim Wiberg da83462731 libsystemd: new library, refactor sd-daemon.[ch] test code
This commit introduces a bare-bones replacement for libsystemd:

 - Build .so file and add --with-libsystemd to configure
 - Add capabilities support to test/src/serv.c
 - Update tests to account for a Finit built w/o libsystemd support

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:42:06 +02:00
Joachim Wiberg 1396177706 test: expand with command line options for increased usability
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:42:05 +02:00
Joachim Wiberg 7cf0a293fd test: adjust unexpected-restart.sh for proper systemd/s6 notify
A systemd service should only use NOTIFY_SOCKET and an s6 style
service reads its notify descript from the command line.  For
details, see notify.sh

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:59 +02:00
Joachim Wiberg d0d9b83b66 test: update serv daemon to use proper systemd abstract socket
This commit introduces a stripped down sd_notify(), taken from the
systemd man page example, which is used by the serv daemon in lieu
of the previous broken implementation.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:59 +02:00
Joachim Wiberg 76f8a18816 Fix #440: refactor systemd and s6 readiness notifcation
This patch fixes issue #440 which details how systemd readiness
notification has been broken since its inception in Finit.

In addition to now providing a proper abstract socket, the refactor
also drops the reliance on the Finit client API to fetch a socket.
Now an s6 descriptor is the write end of a pipe() and the systemd
socket is instead created before forking off the service.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:58 +02:00
Joachim Wiberg dc497bcd57 service: slight improvement of restart log message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:58 +02:00
Joachim Wiberg 0da6bbe4a8 service: minor refactor of stdin redirect
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:57 +02:00
Joachim Wiberg 91f7a75bee api: ensure socket is non-blocking
Also, remember to refactor the client socket handling to prevent
blocking PID 1.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:57 +02:00
Joachim Wiberg f217d493b4 test: add --map-auto to unshare call
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 16:36:52 +02:00
Joachim Wiberg 77404a2ea0 Merge pull request #445 from troglobit/space-in-argval
Space in argval

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:50:21 +02:00
Joachim Wiberg d42a1d438b Merge pull request #444 from aanderse/master
service.c/parse_cmdline_args: handle loooooong arguments

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:43:00 +02:00
Joachim Wiberg 1275f05343 devmon: add error checking to callback
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:29:54 +02:00
Joachim Wiberg 61a92aa0b6 devmon: silence unnecessary dbg() in devmon_add_cond()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:29:54 +02:00
Joachim Wiberg b38ff0cc79 service: fix service log priority in fallback mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:29:53 +02:00
Joachim Wiberg 168f5bf1d8 log: new function, log_parse()
Converts from syslog priority string format to facility and level.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:29:53 +02:00
Joachim Wiberg ffd00f646c Declare unused parameters
When building with global CFLAGS=-Wunused-parameter the output from the
build looks terrible.  This commit explicitly declares unused variables
to prevent triggering -Wunused-parameter

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:29:52 +02:00
Joachim Wiberg 5e2e273cad conf: check return value of strdup() in parse_env()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:28:41 +02:00
Joachim Wiberg 33f6697cd5 conf: new function conf_parse_env()
Reduce code duplication.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:28:27 +02:00
Joachim Wiberg 6f0ba22a0f util: new function unquote()
Refactor to reduce code duplication

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:28:10 +02:00
Joachim Wiberg ecb690c9ec service: support quoted arguments to run/task/service options
Fixes #441

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-02 14:27:39 +02:00
Aaron Andersen 0459bb432a service.c/parse_cmdline_args: handle loooooong arguments 2025-07-01 20:31:59 -04:00
Joachim Wiberg 2760616c73 Merge pull request #443 from liuming50/fix-some-compilation-issues
Fix some compilation issues

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-06-29 12:36:17 +02:00
Ming Liu 62ab990129 cgroup.c: fix some compilation warnings
Fix the following warnings:
| cgroup.c:296:13: warning: implicit declaration of function 'conf_any_change' [-Wimplicit-function-declaration]
| cgroup.c:297:17: warning: implicit declaration of function 'service_reload_dynamic' [-Wimplicit-function-declaration]

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2025-06-27 14:31:18 +02:00
Ming Liu 0c2836d747 Makefile.am: fix tmpfiles linking issues
Fix the following linking issues of tmpfiles:
| exec.c:308: warning: stty is not implemented and will always fail
| plugin.c:237: undefined reference to `run_parts'

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2025-06-27 14:31:02 +02:00
Joachim Wiberg 8be7a462e0 Merge pull request #439 from aanderse/tmpfiles
tmpfiles: refactor into separate executable
2025-06-09 07:56:10 +02:00
Aaron Andersen 5993b4bb5d tmpfiles: refactor code into new is_dir_empty function
The existing implementation seemed useful enough to warrant a new
helper function. Care was taken to ensure the revised code no longer
suffers any memory leaks.
2025-06-05 20:53:25 -04:00
Aaron Andersen 2957246092 tmpfiles: implement remove logic for 'D'
> D
> Similar to d, but in addition the contents of the directory will be removed when --remove is used.
2025-06-02 19:27:24 -04:00
Aaron Andersen a5710125f6 tmpfiles: add --create and --remove flags
Allow execution of tmpfiles to specify which mode(s) of operation to run in.
2025-06-02 19:27:24 -04:00
Aaron Andersen ff95ebeb91 tmpfiles: split up create & remove logic
The tmpfiles.d spec contains entry types that should be acted upon in different modes
of operation: create, clean, remove, and purge - split up create & remove logic to
clarify the modes of operation finit supports.
2025-06-02 19:27:24 -04:00
Aaron Andersen 1921b3f2c5 tmpfiles: refactor into separate executable
The tmpfiles.d spec has proven useful in systemd, enough so that some developers
are starting to ship tmpfiles.d configuration files with their software.
By splitting the tmpfiles functionality in finit out into a separate executable
we are providing a useful piece of software that package managers can hook into.
2025-06-02 19:27:24 -04:00
Joachim Wiberg 051ef81e53 initctl: drop unused function pid_comm()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-30 10:45:01 +02:00
Joachim Wiberg 90993c237a Add hexdump() debug helper function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-30 10:44:05 +02:00
Joachim Wiberg b0b4f7b2ba initctl: refactor pid_cgroup(), return allocated buffer
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-30 10:42:50 +02:00
Joachim Wiberg 4d79d84fb8 Fix #438: refactor initctl ps/top to handle loooooong command lines
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-30 10:21:24 +02:00
Joachim Wiberg d318aa00c3 Merge branch 'asan'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-05 07:37:26 +02:00
Joachim Wiberg 9684127eb7 initctl: refactor runlevel string composition
Related to issue #437

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-05 07:27:06 +02:00
Joachim Wiberg 8d2cfd69fa initctl: buffer overflow in runlevel string formatting
Fix #437

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-05 07:19:12 +02:00
Joachim Wiberg 4f252858e4 Enable address sanitizer, credits to @chipitsine
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-05 06:55:08 +02:00
Joachim Wiberg 1ccd1d6926 Bump version for 4.13 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-05 06:54:27 +02:00
Joachim Wiberg b87c51209d doc: attempt to clarify service sync a bit
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-05-04 07:23:07 +02:00
Joachim Wiberg 6b7478ff43 Update ChangeLog and bump version for v4.12 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-04-28 00:23:10 +02:00
Joachim Wiberg 6c4c16cbdb Update ChangeLog for v4.12
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-04-28 00:04:39 +02:00
Joachim Wiberg e539dbfe37 Merge pull request #436 from sirocyl/patch-1
README.md: typo
2025-04-27 09:56:17 +02:00
Tyler True 90c6bcd365 README.md: typo
`intictl` -> `initctl`, also linkified it to the command description
2025-04-26 23:45:19 -04:00
Joachim Wiberg c9c268f218 plugins: fix variable overloading warning in urandom plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-04-07 12:55:01 +02:00
Joachim Wiberg 3feff37ba5 plugins: use byte addressing of rand_pool_info data buffer
Fix out-of-bounds write in urandom plugin.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-04-07 12:52:16 +02:00
Joachim Wiberg c67d15e272 runparts: update usage text, missing '-b' flag for batch mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-31 14:17:55 +02:00
Joachim Wiberg 8984c655f8 cond_get_agg: minor refactor
- Use stack instead of heap memory
 - constify

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-31 14:16:24 +02:00
Joachim Wiberg 1a7b26bac3 Bump version for 4.12 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-31 07:49:05 +02:00
Joachim Wiberg 1f47718012 Merge pull request #435 from aanderse/master 2025-03-29 13:28:15 +01:00
Aaron Andersen fa13cddc28 improve notify:s6 compatibility 2025-03-29 08:03:43 -04:00
Joachim Wiberg 185a72bd14 Bump version for v4.11 GA release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 19:29:48 +01:00
Joachim Wiberg cd32c623cf plugins: ignore PID file events for svc_t in setup/teardown/cleanup
An svc_t (service/sysv) that is in setup, teardown, or cleanup state
must be ignored by the pidfile plugin for any events regarding PID
files.  E.g., a setup script for a sysv service may create a PID file
to alert the system that a setup process for the service is running.

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

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-03-27 19:10:56 +01:00
103 changed files with 2005 additions and 1103 deletions
+2 -1
View File
@@ -37,7 +37,8 @@ jobs:
run: | run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \ --enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-keventd \
CFLAGS="-fsanitize=address -ggdb"
make -j3 clean make -j3 clean
make -j3 V=1 make -j3 V=1
- name: Install to /tmp - name: Install to /tmp
+19
View File
@@ -3,6 +3,19 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.12][] - 2025-04-28
---------------------
### Changes
- Improve notify:s6 readiness compatibility, by Aaron Andersen
- Update `runparts` usage text
### Fixes
- Fix variable overloading in urandom plugin
- Fix buffer overwrite in urandom plugin, reported by Aaron Andersen
[4.11][] - 2025-03-27 [4.11][] - 2025-03-27
--------------------- ---------------------
@@ -20,6 +33,11 @@ All relevant changes are documented in this file.
the default `SIGTERM` the default `SIGTERM`
- Fix pre/post/ready/cleanup script execution if service has an - Fix pre/post/ready/cleanup script execution if service has an
optional `env:file` (leading `-`) optional `env:file` (leading `-`)
- Fix blocked signals for pre/post/ready/cleanup scripts preventing
normal shutdown when a service/sysv is stopped in its setup state
- Fix issue where a pre:script creates a PID file in `/run` cause the
service to move from setup state to running. Any `svc_t` in setup,
teardown, or cleanup state should be ignored in the pidfile plugin
[4.10][] - 2025-03-03 [4.10][] - 2025-03-03
@@ -1780,6 +1798,7 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.11...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.11...HEAD
[4.12]: https://github.com/troglobit/finit/compare/4.11...4.12
[4.11]: https://github.com/troglobit/finit/compare/4.10...4.11 [4.11]: https://github.com/troglobit/finit/compare/4.10...4.11
[4.10]: https://github.com/troglobit/finit/compare/4.9...4.10 [4.10]: https://github.com/troglobit/finit/compare/4.9...4.10
[4.9]: https://github.com/troglobit/finit/compare/4.8...4.9 [4.9]: https://github.com/troglobit/finit/compare/4.8...4.9
+12 -1
View File
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man plugins src system test tmpfiles.d SUBDIRS = man plugins src system tmpfiles.d
doc_DATA = README.md LICENSE contrib/finit.conf doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
@@ -9,6 +9,17 @@ endif
if DOC if DOC
SUBDIRS += doc SUBDIRS += doc
endif endif
if LIBSYSTEMD
SUBDIRS += libsystemd
endif
if LIBSYSTEMD
SUBDIRS += test
# Explicit dependency to ensure libsystemd is built before test
test: libsystemd
else
SUBDIRS += test
endif
if BASH if BASH
bashdir = $(BASH_DIR) bashdir = $(BASH_DIR)
+1 -1
View File
@@ -251,7 +251,7 @@ All services in runlevel S) are started first, followed by the desired
run-time runlevel. Run tasks in runlevel S can be started in sequence run-time runlevel. Run tasks in runlevel S can be started in sequence
by using `run [S] cmd`. Changing runlevels at runtime is done like any by using `run [S] cmd`. Changing runlevels at runtime is done like any
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
`intictl` tool. [`initctl`](#commands--status) tool.
**Conditions** **Conditions**
+30 -4
View File
@@ -1,7 +1,33 @@
#!/bin/sh #!/bin/sh
# Run test suite with the correct configure params # Run test suite with the correct configure params
run_make=1
jobs=17
make distclean while [ $# -gt 0 ]; do
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ case "$1" in
--enable-x11-common-plugin --with-watchdog --with-keventd -n|--no-make)
make -j17 clean check run_make=0
;;
-j|--jobs)
shift
jobs="$1"
;;
*)
echo "Usage: $0 [--no-make] [-j N]"
exit 1
;;
esac
shift
done
if [ "$run_make" -eq 1 ]; then
make distclean
fi
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --with-watchdog --with-keventd \
CFLAGS='-fsanitize=address -ggdb'
if [ "$run_make" -eq 1 ]; then
make -j"$jobs" clean check
fi
+19 -9
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.11-rc2], [https://github.com/troglobit/finit/issues], AC_INIT([Finit], [4.13-pre], [https://github.com/troglobit/finit/issues],
[finit], [https://troglobit.com/projects/finit/]) [finit], [https://troglobit.com/projects/finit/])
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
@@ -12,6 +12,7 @@ AC_CONFIG_FILES([Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
doc/Makefile doc/Makefile
libsystemd/Makefile libsystemd/libsystemd.pc
man/Makefile man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
@@ -168,6 +169,10 @@ AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Enable built-in watchdog, default: /dev/watchdog]), AS_HELP_STRING([--with-watchdog@<:@=DEV@:>@], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval], [with_watchdog=no watchdog=]) [watchdog=$withval], [with_watchdog=no watchdog=])
AC_ARG_WITH(libsystemd,
AS_HELP_STRING([--with-libsystemd], [Build replacement libsystemd library, default: yes]),
[with_libsystemd=$withval], [with_libsystemd=yes])
AC_ARG_WITH(hook-scripts-path, AC_ARG_WITH(hook-scripts-path,
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]), AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
[hook_scripts_path=$withval], [hook_scripts_path=yes]) [hook_scripts_path=$withval], [hook_scripts_path=yes])
@@ -308,6 +313,9 @@ AS_IF([test "x$with_watchdog" != "xno"], [
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [ AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
watchdog=]) watchdog=])
AS_IF([test "x$with_libsystemd" != "xno"], [
AC_DEFINE(HAVE_LIBSYSTEMD, 1, [Build replacement libsystemd library support])])
AS_IF([test "x$enable_hook_scripts_plugin" != "xno"], [ AS_IF([test "x$enable_hook_scripts_plugin" != "xno"], [
AS_IF([test "x$hook_scripts_path" = "xyes"], [hook_scripts_path=$libexecdir/finit/hook]) AS_IF([test "x$hook_scripts_path" = "xyes"], [hook_scripts_path=$libexecdir/finit/hook])
AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path") AC_EXPAND_DIR(hook_scripts_path, "$hook_scripts_path")
@@ -319,14 +327,15 @@ AS_IF([test "x$with_plugin_path" != "xno"], [
AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])]) AC_DEFINE_UNQUOTED(EXTERNAL_PLUGIN_PATH, "$plugin_path", [Built-in default: /usr/lib/finit/plugins:/usr/local/lib/finit/plugins])])
# Control build with automake flags # Control build with automake flags
AM_CONDITIONAL(BASH, [test "x$bash_dir" != "xno"]) AM_CONDITIONAL(BASH, [test "x$bash_dir" != "xno"])
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"]) AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"]) AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "xno"]) AM_CONDITIONAL(SULOGIN, [test "x$with_sulogin" != "xno"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"]) AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"]) AM_CONDITIONAL(LIBSYSTEMD, [test "x$with_libsystemd" != "xno"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"]) AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"]) AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
# Workaround for as-of-yet unreleased runstatedir support, planned for # Workaround for as-of-yet unreleased runstatedir support, planned for
# autoconf 2.70, which some major distros have backported. # autoconf 2.70, which some major distros have backported.
@@ -408,6 +417,7 @@ Optional features:
Built-in sulogin......: $with_sulogin $sulogin Built-in sulogin......: $with_sulogin $sulogin
Built-in watchdogd....: $with_watchdog $watchdog Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Replacement libsystemd: $with_libsystemd
Use cgroup v2.........: $enable_cgroup Use cgroup v2.........: $enable_cgroup
Parse kernel cmdline..: $enable_kernel_cmdline Parse kernel cmdline..: $enable_kernel_cmdline
Keep kernel logging...: $enable_kernel_logging Keep kernel logging...: $enable_kernel_logging
+20 -9
View File
@@ -393,7 +393,7 @@ like this:
The `notify:none` syntax is for completeness in systems which run in The `notify:none` syntax is for completeness in systems which run in
`readiness pid` mode (default). Services declared with `notify:none` `readiness pid` mode (default). Services declared with `notify:none`
will assert ready condition as soon as Finit has started them, e.g., will transition to ready as soon as Finit has started them, e.g.,
`service/qux/ready`. `service/qux/ready`.
To synchronize two services the following condition can be used: To synchronize two services the following condition can be used:
@@ -405,8 +405,8 @@ For details on the syntax and options, see below.
> [!NOTE] > [!NOTE]
> On `initctl reload` conditions are set in "flux", while figuring out > On `initctl reload` conditions are set in "flux", while figuring out
> which to stop, start or restart. Services that need to be restarted > which services to stop, start or restart. Services that need to be
> have their `ready` condition removed prior to Finit sending them > restarted have their `ready` condition removed before Finit issue a
> SIGHUP (if they support that), or stop-starting them. A daemon is > SIGHUP (if they support that), or stop-starting them. A daemon is
> expected to reassert its readiness, e.g. systemd style daemons to > expected to reassert its readiness, e.g. systemd style daemons to
> write `READY=1\n`. > write `READY=1\n`.
@@ -416,10 +416,10 @@ For details on the syntax and options, see below.
> `\n`. This means s6 style daemons currently must be stop-started. > `\n`. This means s6 style daemons currently must be stop-started.
> (Declare the service with `<!>` in its condition statement.) > (Declare the service with `<!>` in its condition statement.)
> >
> For native PID file style readiness notification a daemon is expected > For default, PID file style readiness notification, daemons are
> to either create its PID file, if it does not exist yet, or touch it > expected to either create their PID files, or touch it using
> using `utimensat()` or similar. This triggers both the `<pid/>` and > `utimensat()` to reassert readiness. Triggering both the `<pid/>`
> `<.../ready>` conditions. > and `<.../ready>` conditions.
Service Wrapper Scripts Service Wrapper Scripts
@@ -913,9 +913,20 @@ configurable with the following options:
endless restarts. Useful in many use-cases, but not what `service` endless restarts. Useful in many use-cases, but not what `service`
was originally designed for so not the default behavior was originally designed for so not the default behavior
* `oncrash:reboot` -- when all retries have failed, and the service * `oncrash:reboot` -- when all retries have failed, and the service
has *crashed*, if this option is set the system is rebooted. has *crashed*, if this option is set the system is rebooted
* `oncrash:script` -- similarly, but instead of rebooting, call the * `oncrash:script` -- similarly, but instead of rebooting, call the
`post:script` action if set, see below. `post:script` action with exit code `crashed`, see below
* `reload:script [args]` -- some services do not support `SIGHUP` but
may have other ways to update the configuration of a running daemon.
When `reload:script` is defined it is preferred over `SIGHUP`
* `stop:script [args]` -- some services may require alternate methods
to be stopped. When `stop:script` is defined it is preferred over
`SIGTERM` and `stop`, for `service` and `sysv` stanzas, respectively
> [!CAUTION]
> Both `reload:script` and `stop:script` are called as PID 1, without
> any timeout! Meaning, it is up to you to ensure the script is not
> blocking for seconds at a time or never terminates.
When stopping a service (run/task/sysv/service), either manually or when When stopping a service (run/task/sysv/service), either manually or when
moving to another runlevel, Finit starts by sending `SIGTERM`, to allow moving to another runlevel, Finit starts by sending `SIGTERM`, to allow
+7
View File
@@ -0,0 +1,7 @@
.deps/*
.libs/*
.dirstamp
*.lo
libsystemd.*
Makefile
Makefile.in
+12
View File
@@ -0,0 +1,12 @@
# libsystemd replacement library
lib_LTLIBRARIES = libsystemd.la
libsystemd_la_SOURCES = sd-daemon.c sd-daemon.h
libsystemd_la_LDFLAGS = -version-info 0:0:0
# pkg-config support
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsystemd.pc
# Install header to systemd/ subdirectory
systemddir = $(includedir)/systemd
systemd_HEADERS = sd-daemon.h
+10
View File
@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libsystemd
Description: Finit replacement for systemd notification library
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lsystemd
Cflags: -I${includedir}
+217
View File
@@ -0,0 +1,217 @@
/* SPDX-License-Identifier: MIT-0 */
/*
* systemd notify protocol, supporting readiness notification on startup
* and reloading, according to the protocol defined at:
* https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
*
* This protocol is guaranteed to be stable as per:
* https://systemd.io/PORTABILITY_AND_STABILITY/
*/
#define _GNU_SOURCE 1
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include "sd-daemon.h"
/*
* NOTE: As per spec., this function does not return POSIX OK(0) but
* rather "a positive value", i.e., 1.
*/
int sd_notify(int unset_environment, const char *state)
{
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
};
size_t len, sun_len;
const char *path;
ssize_t written;
int sd;
if (!state)
return -EINVAL;
len = strlen(state);
if (len == 0)
return -EINVAL;
/* If the variable is not set, the protocol is a noop */
path = getenv("NOTIFY_SOCKET");
if (!path)
return 0; /* Not set? Nothing to do */
/* Only AF_UNIX is supported, with path or abstract sockets */
if (path[0] != '/' && path[0] != '@')
return -EAFNOSUPPORT;
/* Ensure there is room for NUL byte */
sun_len = strlen(path);
if (sun_len >= sizeof(sun.sun_path))
return -E2BIG;
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
if (sun.sun_path[0] == '@')
sun.sun_path[0] = 0;
sd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sd == -1)
return -errno;
if (connect(sd, (struct sockaddr *)&sun, offsetof(struct sockaddr_un, sun_path) + sun_len) == -1) {
close(sd);
return -errno;
}
if (unset_environment)
unsetenv("NOTIFY_SOCKET");
written = write(sd, state, len);
if (written != (ssize_t) len) {
close(sd);
return written < 0 ? -errno : -EPROTO;
}
return 1;
}
/*
* Printf-style notification function
*/
int sd_notifyf(int unset_environment, const char *format, ...)
{
va_list ap;
char *state;
int ret;
if (!format)
return -EINVAL;
va_start(ap, format);
ret = vasprintf(&state, format, ap);
va_end(ap);
if (ret < 0)
return -ENOMEM;
ret = sd_notify(unset_environment, state);
free(state);
return ret;
}
/*
* PID-specific notification function
*/
int sd_pid_notify(pid_t pid, int unset_environment, const char *state)
{
char path[256], *env;
size_t len;
int ret;
if (!state)
return -EINVAL;
len = strlen(state);
if (len == 0)
return -EINVAL;
if (pid == 0)
pid = getpid();
env = getenv("NOTIFY_SOCKET");
if (env) {
env = strdup(env);
if (!env)
return -errno;
}
snprintf(path, sizeof(path), "@run/finit/notify/%d", pid);
setenv("NOTIFY_SOCKET", path, 1);
ret = sd_notify(unset_environment, state);
if (env) {
setenv("NOTIFY_SOCKET", env, 1);
free(env);
}
return ret;
}
/*
* PID-specific printf-style notification function
*/
int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...)
{
va_list ap;
char *state;
int ret;
if (!format)
return -EINVAL;
va_start(ap, format);
ret = vasprintf(&state, format, ap);
va_end(ap);
if (ret < 0)
return -ENOMEM;
ret = sd_pid_notify(pid, unset_environment, state);
free(state);
return ret;
}
/*
* Socket activation stub - Finit doesn't support socket activation yet
* Returns 0 (no file descriptors passed)
*/
int sd_listen_fds(int unset_environment)
{
if (unset_environment) {
unsetenv("LISTEN_PID");
unsetenv("LISTEN_FDS");
unsetenv("LISTEN_FDNAMES");
}
return 0;
}
/*
* Watchdog stub - Finit doesn't support watchdog yet
* Returns 0 (no watchdog enabled)
*/
int sd_watchdog_enabled(int unset_environment, uint64_t *usec)
{
if (unset_environment) {
unsetenv("WATCHDOG_PID");
unsetenv("WATCHDOG_USEC");
}
if (usec)
*usec = 0;
return 0;
}
/*
* System detection function
* Returns 1 to indicate systemd compatibility (trick applications)
*/
int sd_booted(void)
{
return 1;
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef SD_DAEMON_H_
#define SD_DAEMON_H_
#include <stdint.h>
#include <sys/types.h>
#define SD_LISTEN_FDS_START 3
/* systemd logging defines for stderr parsing by Finit */
#define SD_EMERG "<0>" /* system is unusable */
#define SD_ALERT "<1>" /* action must be taken immediately */
#define SD_CRIT "<2>" /* critical conditions */
#define SD_ERR "<3>" /* error conditions */
#define SD_WARNING "<4>" /* warning conditions */
#define SD_NOTICE "<5>" /* normal but significant condition */
#define SD_INFO "<6>" /* informational */
#define SD_DEBUG "<7>" /* debug-level messages */
/* Basic notification function */
int sd_notify(int unset_environment, const char *state);
/* Printf-style notification functions */
int sd_notifyf(int unset_environment, const char *format, ...);
/* PID-specific notification functions */
int sd_pid_notify(pid_t pid, int unset_environment, const char *state);
int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...);
/* Socket activation stubs */
int sd_listen_fds(int unset_environment);
/* Watchdog stub */
int sd_watchdog_enabled(int unset_environment, uint64_t *usec);
/* System detection */
int sd_booted(void);
#endif /* SD_DAEMON_H_ */
+2 -2
View File
@@ -65,12 +65,12 @@ static plugin_t plugin = {
.hook[HOOK_SHUTDOWN] = { .cb = save } .hook[HOOK_SHUTDOWN] = { .cb = save }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+3 -4
View File
@@ -38,7 +38,6 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "tmpfiles.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
@@ -163,7 +162,7 @@ static void setup(void *arg)
kernel_links(); kernel_links();
/* Create all system tmpfiles.d(5) */ /* Create all system tmpfiles.d(5) */
tmpfilesd(); run_interactive(_PATH_TMPFILES " --create", "Creating required directories and files");
/* Set BOOT_TIME UTMP entry */ /* Set BOOT_TIME UTMP entry */
utmp_set_boot(); utmp_set_boot();
@@ -178,12 +177,12 @@ static plugin_t plugin = {
.depends = { "pidfile" }, .depends = { "pidfile" },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+2 -2
View File
@@ -166,12 +166,12 @@ static plugin_t plugin = {
.hook[HOOK_SVC_PLUGIN] = { .cb = setup }, .hook[HOOK_SVC_PLUGIN] = { .cb = setup },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+2 -2
View File
@@ -99,12 +99,12 @@ static plugin_t plugin = {
*/ */
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+2 -2
View File
@@ -232,12 +232,12 @@ static plugin_t plugin = {
.depends = { "bootmisc", } .depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+2 -2
View File
@@ -216,12 +216,12 @@ static plugin_t plugin = {
.hook[HOOK_SVC_PLUGIN] = { .cb = load }, .hook[HOOK_SVC_PLUGIN] = { .cb = load },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+3 -2
View File
@@ -29,6 +29,7 @@
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "log.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "service.h" #include "service.h"
@@ -414,7 +415,7 @@ static plugin_t plugin = {
}, },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
struct sockaddr_nl sa; struct sockaddr_nl sa;
int sd; int sd;
@@ -447,7 +448,7 @@ PLUGIN_INIT(plugin_init)
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
close(plugin.io.fd); close(plugin.io.fd);
+19 -2
View File
@@ -80,6 +80,23 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
return; return;
} }
switch (svc->state) {
case SVC_TEARDOWN_STATE:
case SVC_CLEANUP_STATE:
case SVC_SETUP_STATE:
/*
* E.g., a setup script for a container may run in lieu
* of crun, trying to fetch a container image from the
* network. This script may want to advertise its PID
* to the system for clean and swift reboots.
*/
dbg("%s[%d]: ignoring any PID file changes in CLEANUP/TEARDOWN/SETUP",
svc_ident(svc, NULL, 0), svc->pid);
return;
default:
break;
}
/* Service condistion to set/clear */ /* Service condistion to set/clear */
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
@@ -321,7 +338,7 @@ static plugin_t plugin = {
.depends = { "netlink" }, /* bootmisc depends on us */ .depends = { "netlink" }, /* bootmisc depends on us */
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
int fd; int fd;
@@ -336,7 +353,7 @@ PLUGIN_INIT(plugin_init)
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
iwatch_exit(&iw_pidfile); iwatch_exit(&iw_pidfile);
+4 -2
View File
@@ -31,6 +31,8 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "conf.h"
#include "log.h"
#ifndef SYSCTL_PATH #ifndef SYSCTL_PATH
#define SYSCTL_PATH "/sbin/sysctl" #define SYSCTL_PATH "/sbin/sysctl"
@@ -74,12 +76,12 @@ static plugin_t plugin = {
.depends = { "bootmisc", }, .depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+2 -2
View File
@@ -57,12 +57,12 @@ static plugin_t plugin = {
.depends = { "bootmisc", }, .depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+5 -2
View File
@@ -35,6 +35,9 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "private.h"
#include "conf.h"
#include "log.h"
/* /*
* Kernel RTC driver validates against this date for sanity check. The * Kernel RTC driver validates against this date for sanity check. The
@@ -320,7 +323,7 @@ static plugin_t plugin = {
} }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
struct tm tm = { 0 }; struct tm tm = { 0 };
@@ -336,7 +339,7 @@ PLUGIN_INIT(plugin_init)
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
uev_timer_stop(&rtc_timer); uev_timer_stop(&rtc_timer);
plugin_unregister(&plugin); plugin_unregister(&plugin);
+2 -2
View File
@@ -184,7 +184,7 @@ static plugin_t plugin = {
.depends = { "bootmisc", }, .depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
int fd; int fd;
@@ -199,7 +199,7 @@ PLUGIN_INIT(plugin_init)
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
iwatch_exit(&iw_sys); iwatch_exit(&iw_sys);
+3 -2
View File
@@ -38,6 +38,7 @@
#include "plugin.h" #include "plugin.h"
#include "service.h" #include "service.h"
#include "tty.h" #include "tty.h"
#include "log.h"
static void tty_watcher(void *arg, int fd, int events); static void tty_watcher(void *arg, int fd, int events);
@@ -108,13 +109,13 @@ static void tty_watcher(void *arg, int fd, int events)
} }
} }
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
setup(); setup();
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
if (plugin.io.fd) if (plugin.io.fd)
+12 -7
View File
@@ -38,6 +38,8 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "conf.h"
#include "log.h"
#ifndef RANDOM_BYTES #ifndef RANDOM_BYTES
#define RANDOM_BYTES (32*1024) #define RANDOM_BYTES (32*1024)
@@ -78,6 +80,7 @@ static void setup(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
struct rand_pool_info *rpi; struct rand_pool_info *rpi;
unsigned char *rpi_buf;
ssize_t len = 0; ssize_t len = 0;
struct stat st; struct stat st;
int rc = -1; int rc = -1;
@@ -103,13 +106,14 @@ static void setup(void *arg)
hw = fopen(hwrng, "r"); hw = fopen(hwrng, "r");
if (hw) { if (hw) {
char buf[512]; char buf[512];
size_t len; size_t num;
len = fread(buf, sizeof(buf[0]), sizeof(buf), hw); num = fread(buf, sizeof(buf[0]), sizeof(buf), hw);
if (len == 0) if (num == 0)
fallback(fp); fallback(fp);
else else
len = fwrite(buf, sizeof(buf[0]), len, fp); fwrite(buf, sizeof(buf[0]), num, fp);
fclose(hw); fclose(hw);
} else { } else {
fallback(fp); fallback(fp);
@@ -137,10 +141,11 @@ static void setup(void *arg)
goto fallback; goto fallback;
} }
rpi_buf = (unsigned char *)rpi->buf;
do { do {
ssize_t num; ssize_t num;
num = read(fd, &rpi->buf[len], RANDOM_BYTES - len); num = read(fd, &rpi_buf[len], RANDOM_BYTES - len);
if (num <= 0) { if (num <= 0) {
if (num == -1 && errno == EINTR) if (num == -1 && errno == EINTR)
continue; continue;
@@ -203,12 +208,12 @@ static plugin_t plugin = {
.depends = { "bootmisc", } .depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+2 -2
View File
@@ -108,7 +108,7 @@ static plugin_t plugin = {
.depends = { "bootmisc", }, .depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
int fd; int fd;
@@ -123,7 +123,7 @@ PLUGIN_INIT(plugin_init)
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
iwatch_exit(&iw_usr); iwatch_exit(&iw_usr);
+4 -2
View File
@@ -33,6 +33,8 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "conf.h"
#include "log.h"
static void setup(void *arg) static void setup(void *arg)
{ {
@@ -50,12 +52,12 @@ static plugin_t plugin = {
.hook[HOOK_SVC_PLUGIN] = { .cb = setup }, .hook[HOOK_SVC_PLUGIN] = { .cb = setup },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(__init)
{ {
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
+1
View File
@@ -7,6 +7,7 @@ logit
reboot reboot
runparts runparts
sulogin sulogin
tmpfiles
watchdogd watchdogd
Makefile Makefile
Makefile.in Makefile.in
+7 -2
View File
@@ -8,7 +8,8 @@ AM_LDFLAGS = -export-dynamic
endif endif
sbin_PROGRAMS = finit initctl sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = getty logit runparts pkglibexec_PROGRAMS = getty logit runparts tmpfiles
if SULOGIN if SULOGIN
pkglibexec_PROGRAMS += sulogin pkglibexec_PROGRAMS += sulogin
endif endif
@@ -43,6 +44,11 @@ sulogin_SOURCES = sulogin.c
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99 sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
sulogin_LDADD = -lcrypt sulogin_LDADD = -lcrypt
tmpfiles_SOURCES = tmpfiles.c util.c util.h log.h
tmpfiles_CFLAGS = -W -Wall -Wextra -std=gnu99
tmpfiles_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
tmpfiles_LDADD = $(lite_LIBS) $(uev_LIBS)
logit_SOURCES = logit.c logrotate.c logit_SOURCES = logit.c logrotate.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS) logit_CFLAGS += $(lite_CFLAGS)
@@ -66,7 +72,6 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
sig.c sig.h \ sig.c sig.h \
sm.c sm.h \ sm.c sm.h \
svc.c svc.h \ svc.c svc.h \
tmpfiles.c tmpfiles.h \
tty.c tty.h \ tty.c tty.h \
util.c util.h \ util.c util.h \
utmp-api.c utmp-api.h utmp-api.c utmp-api.h
+26 -21
View File
@@ -47,6 +47,7 @@
#include "private.h" #include "private.h"
#include "schedule.h" #include "schedule.h"
#include "service.h" #include "service.h"
#include "sm.h"
#include "sig.h" #include "sig.h"
#include "util.h" #include "util.h"
@@ -59,6 +60,8 @@ static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
static int stop(svc_t *svc, void *user_data) static int stop(svc_t *svc, void *user_data)
{ {
(void)user_data;
if (!svc) if (!svc)
return 1; return 1;
@@ -72,6 +75,8 @@ static int stop(svc_t *svc, void *user_data)
static int start(svc_t *svc, void *user_data) static int start(svc_t *svc, void *user_data)
{ {
(void)user_data;
if (!svc) if (!svc)
return 1; return 1;
@@ -105,6 +110,8 @@ static int restart(svc_t *svc, void *user_data)
static int reload(svc_t *svc, void *user_data) static int reload(svc_t *svc, void *user_data)
{ {
(void)user_data;
if (!svc) if (!svc)
return 1; return 1;
@@ -149,6 +156,8 @@ static int missing(char *job, char *id, void *user_data)
{ {
char buf[20]; char buf[20];
(void)user_data;
if (!job) if (!job)
job = ""; job = "";
if (!id) if (!id)
@@ -228,6 +237,8 @@ struct wq emergency = { .cb = bypass_shutdown };
static void bypass_shutdown(void *unused) static void bypass_shutdown(void *unused)
{ {
(void)unused;
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n"); cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
do_shutdown(halt); do_shutdown(halt);
} }
@@ -265,19 +276,19 @@ static int do_reboot(int cmd, int timeout, char *buf, size_t len)
case INIT_CMD_REBOOT: case INIT_CMD_REBOOT:
dbg("reboot"); dbg("reboot");
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); sm_runlevel(6);
break; break;
case INIT_CMD_HALT: case INIT_CMD_HALT:
dbg("halt"); dbg("halt");
halt = SHUT_HALT; halt = SHUT_HALT;
service_runlevel(0); sm_runlevel(0);
break; break;
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
dbg("poweroff"); dbg("poweroff");
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); sm_runlevel(0);
break; break;
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
@@ -306,7 +317,7 @@ typedef struct {
} ev_t; } ev_t;
ev_t ev_list[] = { ev_t ev_list[] = {
{ "RELOAD", service_reload_dynamic }, { "RELOAD", sm_reload },
{ NULL, NULL } { NULL, NULL }
}; };
@@ -332,11 +343,19 @@ static void api_cb(uev_t *w, void *arg, int events)
int sd, lvl; int sd, lvl;
svc_t *svc; svc_t *svc;
(void)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("%s(): api socket %d invalid.", __func__, w->fd); dbg("%s(): api socket %d invalid.", __func__, w->fd);
goto error; goto error;
} }
/*
* TODO: refactor to use accept4() and a new uev handler for the
* client socket instead of risking blocking PID 1 if the client
* is misbehaving.
*/
// sd = accept4(w->fd, NULL, NULL, SOCK_NONBLOCK);
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
err(1, "Failed serving API request"); err(1, "Failed serving API request");
@@ -419,7 +438,7 @@ static void api_cb(uev_t *w, void *arg, int events)
if (runlevel == INIT_LEVEL) if (runlevel == INIT_LEVEL)
cfglevel = lvl; cfglevel = lvl;
else else
service_runlevel(lvl); sm_runlevel(lvl);
break; break;
default: default:
@@ -435,7 +454,7 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
dbg("reload"); dbg("reload");
service_reload_dynamic(); sm_reload();
break; break;
case INIT_CMD_START_SVC: case INIT_CMD_START_SVC:
@@ -553,20 +572,6 @@ static void api_cb(uev_t *w, void *arg, int events)
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel); result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
break; break;
case INIT_CMD_NOTIFY_SOCKET:
svc = svc_find_by_pid(rq.runlevel);
if (!svc) {
errx(1, "Unknown PID, cannot register notify socket");
result = 1;
break;
}
if (uev_io_init(ctx, &svc->notify_watcher, service_notify_cb, svc, sd, UEV_READ)) {
err(1, "Failed initializing %s readiness notifier", svc_ident(svc, NULL, 0));
break;
}
return; /* Don't close sd, used for notify */
default: default:
dbg("Unsupported cmd: %d", rq.cmd); dbg("Unsupported cmd: %d", rq.cmd);
break; break;
@@ -601,7 +606,7 @@ int api_init(uev_ctx_t *ctx)
int sd; int sd;
dbg("Setting up external API socket ..."); dbg("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
err(1, "Failed starting external API socket"); err(1, "Failed starting external API socket");
return 1; return 1;
+10 -1
View File
@@ -37,6 +37,8 @@
#include "cgroup.h" #include "cgroup.h"
#include "finit.h" #include "finit.h"
#include "iwatch.h" #include "iwatch.h"
#include "conf.h"
#include "service.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
@@ -86,6 +88,12 @@ static void cgset(const char *path, char *ctrl, char *prop)
} }
*val++ = 0; *val++ = 0;
/* unquote value, if quoted */
if (unquote(&val, NULL)) {
errx(1, "Syntax error, unterminated quote in %s/%s.%s=%s", path, ctrl, prop, val);
return;
}
/* disallow sneaky relative paths */ /* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) { if (strstr(ctrl, "..") || strstr(prop, "..")) {
errx(1, "Possible security violation; '..' not allowed in cgroup config!"); errx(1, "Possible security violation; '..' not allowed in cgroup config!");
@@ -259,6 +267,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
ssize_t sz; ssize_t sz;
size_t off; size_t off;
(void)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("%s(): inotify socket %d invalid.", __func__, w->fd); dbg("%s(): inotify socket %d invalid.", __func__, w->fd);
return; return;
@@ -294,7 +303,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
#ifdef AUTO_RELOAD #ifdef AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); sm_reload();
#endif #endif
} }
+64 -45
View File
@@ -58,73 +58,90 @@ int cgroup_avail(void)
return fismnt(FINIT_CGPATH); return fismnt(FINIT_CGPATH);
} }
char *pid_cmdline(int pid, char *buf, size_t len) static size_t flen(FILE *fp)
{ {
size_t i, sz; size_t total = 0, sz;
char *ptr; char buf[512];
while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
total += sz;
rewind(fp);
return total;
}
char *pid_cmdline(int pid)
{
size_t i, len;
char *buf;
FILE *fp; FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid); fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) { if (!fp)
buf[0] = 0; return strdup(""); /* regular process */
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp); len = flen(fp);
fclose(fp); if (len == 0) {
if (!sz) fail:
fclose(fp);
return NULL; /* kernel thread */ return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
} }
for (i = 0; i < sz; i++) { buf = calloc(len + 1, 1);
if (buf[i] == 0) if (!buf)
goto fail;
if (fread(buf, 1, len, fp) < len) {
free(buf);
goto fail;
}
/* replace all NUL chars with space */
for (i = 0; i < len; i++) {
if (buf[i] == '\0')
buf[i] = ' '; buf[i] = ' ';
} }
return buf; return buf;
} }
char *pid_comm(int pid, char *buf, size_t len) char *pid_cgroup(int pid)
{ {
char *ptr = NULL; char *buf, *ptr = NULL;
FILE *fp; size_t len;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
if (fgets(buf, len, fp))
ptr = chomp(buf);
fclose(fp);
return ptr;
}
char *pid_cgroup(int pid, char *buf, size_t len)
{
char *ptr = NULL;
FILE *fp; FILE *fp;
fp = fopenf("r", "/proc/%d/cgroup", pid); fp = fopenf("r", "/proc/%d/cgroup", pid);
if (!fp) if (!fp)
return NULL; return NULL;
len = flen(fp);
if (len == 0) {
fclose(fp);
return NULL;
}
len++;
buf = calloc(1, len);
if (!buf) {
fclose(fp);
return NULL;
}
if (fgets(buf, len, fp)) if (fgets(buf, len, fp))
ptr = chomp(buf); ptr = chomp(buf);
fclose(fp); fclose(fp);
if (ptr) if (ptr) {
ptr = strchr(buf, '/'); ptr = strchr(ptr, '/');
if (ptr) {
memmove(buf, ptr, strlen(ptr) + 1);
return buf;
}
}
return ptr; free(buf);
return NULL;
} }
static char *cgroup_val(char *path, char *file, char *buf, size_t len) static char *cgroup_val(char *path, char *file, char *buf, size_t len)
@@ -423,7 +440,7 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
i = 0; i = 0;
while (fgets(buf, sizeof(buf), fp)) { while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 }; char *cmdline;
pid_t pid; pid_t pid;
pid = atoi(chomp(buf)); pid = atoi(chomp(buf));
@@ -431,8 +448,8 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
continue; continue;
/* skip kernel threads for now (no cmdline) */ /* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm)); cmdline = pid_cmdline(pid);
if (pid_cmdline(pid, buf, sizeof(buf))) { if (cmdline) {
char proc[ttcols]; char proc[ttcols];
switch (mode) { switch (mode) {
@@ -450,7 +467,7 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
strlcat(row, pfx, rplen); strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen); strlcat(row, ++i == num ? END : FORK, rlen);
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf); snprintf(proc, sizeof(proc), " %d %s", pid, cmdline);
if (plain) { if (plain) {
strlcat(row, proc, rplen); strlcat(row, proc, rplen);
@@ -468,6 +485,8 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
} }
puts(row); puts(row);
free(cmdline);
} }
if (mode == 1) { if (mode == 1) {
+2 -3
View File
@@ -54,9 +54,8 @@ struct cg {
int cgroup_avail(void); int cgroup_avail(void);
char *pid_cmdline (int pid, char *buf, size_t len); char *pid_cmdline (int pid);
char *pid_comm (int pid, char *buf, size_t len); char *pid_cgroup (int pid);
char *pid_cgroup (int pid, char *buf, size_t len);
uint64_t cgroup_memory(char *group); uint64_t cgroup_memory(char *group);
-10
View File
@@ -97,16 +97,6 @@ int client_request(struct init_request *rq, ssize_t len)
return -1; return -1;
} }
/*
* Exception, only command that don't need to wait for a
* response from Finit. Registers the client socket as a
* readiness notification socket with Finit
*/
if (rq->cmd == INIT_CMD_NOTIFY_SOCKET) {
rq->cmd = INIT_CMD_ACK;
return 0;
}
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLIN | POLLERR | POLLHUP; pfd.events = POLLIN | POLLERR | POLLHUP;
if ((rc = poll(&pfd, 1, REQUEST_TIMEOUT)) <= 0) { if ((rc = poll(&pfd, 1, REQUEST_TIMEOUT)) <= 0) {
+6 -1
View File
@@ -202,6 +202,9 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct
{ {
const char *cond, *ptr; const char *cond, *ptr;
(void)sb;
(void)tflag;
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
@@ -215,7 +218,7 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct
err(1, "Failed removing condition %s", fpath); err(1, "Failed removing condition %s", fpath);
cond = ptr + strlen(COND_BASE) + 1; cond = ptr + strlen(COND_BASE) + 1;
if (sm_is_in_teardown(&sm)) if (sm_in_reload())
cond_clear_noupdate(cond); cond_clear_noupdate(cond);
else else
cond_update(cond); cond_update(cond);
@@ -390,6 +393,8 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct
{ {
char *nm; char *nm;
(void)sb;
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
+7 -7
View File
@@ -96,16 +96,16 @@ enum cond_state cond_get(const char *name)
enum cond_state cond_get_agg(const char *names) enum cond_state cond_get_agg(const char *names)
{ {
static char conds[MAX_COND_LEN];
enum cond_state s = COND_ON; enum cond_state s = COND_ON;
char *cond;
if (!names) if (names) {
return COND_ON; char conds[strlen(names) + 1];
const char *cond;
strlcpy(conds, names, sizeof(conds)); strlcpy(conds, names, sizeof(conds));
for (cond = strtok(conds, ","); s && cond; cond = strtok(NULL, ",")) for (cond = strtok(conds, ","); s && cond; cond = strtok(NULL, ","))
s = min(s, cond_get(cond)); s = min(s, cond_get(cond));
}
return s; return s;
} }
+58 -17
View File
@@ -50,6 +50,25 @@
#define BOOTSTRAP (runlevel == INIT_LEVEL) #define BOOTSTRAP (runlevel == INIT_LEVEL)
int runlevel = INIT_LEVEL; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int cmdlevel = 0; /* runlevel override from cmdline */
int prevlevel = -1;
int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootstrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
int syncsec = 0; /* reboot delay */
int readiness = SVC_NOTIFY_PID;
char *finit_conf= NULL;
char *finit_rcsd= NULL;
char *fstab = NULL;
char *sdown = NULL;
char *network = NULL;
char *hostname = NULL;
char *osheading = NULL;
int logfile_size_max = 200000; /* 200 kB */ int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5; int logfile_count_max = 5;
@@ -490,14 +509,16 @@ void conf_save_service(int type, char *cfg, char *file)
fclose(fp); fclose(fp);
} }
/* /**
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line * conf_parse_env - Parse a key=value line
* from finit.conf, or other .conf file. Note, PATH is always reset in * @line: Line buffer without newline
* the conf_reset_env() function. * @value: Whitespace trimmed value
*
* Returns:
* %NULL on error, otherwise a whitespace trimmed key.
*/ */
static void parse_env(char *line) char *conf_parse_env(char *line, char **value)
{ {
struct env_entry *node;
char *key, *val, *end; char *key, *val, *end;
/* skip any leading whitespace */ /* skip any leading whitespace */
@@ -519,7 +540,7 @@ static void parse_env(char *line)
val = strchr(key, '='); val = strchr(key, '=');
if (!val) if (!val)
return; return NULL;
*val++ = 0; *val++ = 0;
/* strip leading whitespace from value */ /* strip leading whitespace from value */
@@ -527,14 +548,8 @@ static void parse_env(char *line)
val++; val++;
/* unquote value, if quoted */ /* unquote value, if quoted */
if (val[0] == '"' || val[0] == '\'') { unquote(&val, end);
char q = val[0]; *value = val;
if (*end == q) {
val = &val[1];
*end = 0;
}
}
/* find end of key */ /* find end of key */
end = key; end = key;
@@ -561,18 +576,42 @@ static void parse_env(char *line)
end++; end++;
if (*end != 0) { if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val); warnx("'%s=%s': not a valid identifier", key, val);
return; /* invalid key */ return NULL; /* invalid key */
} }
return key;
}
/*
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line
* from finit.conf, or other .conf file. Note, PATH is always reset in
* the conf_reset_env() function.
*/
static void parse_env(char *line)
{
struct env_entry *node;
char *key, *val;
key = conf_parse_env(line, &val);
if (!key)
return;
dbg("Global env '%s'='%s'", key, val); dbg("Global env '%s'='%s'", key, val);
setenv(key, val, 1); setenv(key, val, 1);
node = malloc(sizeof(*node)); node = malloc(sizeof(*node));
if (!node) { if (!node) {
nomem:
err(1, "Out of memory cannot track env vars"); err(1, "Out of memory cannot track env vars");
return; return;
} }
node->name = strdup(key); node->name = strdup(key);
if (!node->name) {
free(node);
goto nomem;
}
TAILQ_INSERT_HEAD(&env_list, node, link); TAILQ_INSERT_HEAD(&env_list, node, link);
} }
@@ -1542,6 +1581,8 @@ static int conf_iwatch_read(int fd)
static void conf_cb(uev_t *w, void *arg, int events) static void conf_cb(uev_t *w, void *arg, int events)
{ {
(void)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("%s(): iwatch socket %d invalid.", __func__, w->fd); dbg("%s(): iwatch socket %d invalid.", __func__, w->fd);
return; return;
@@ -1555,7 +1596,7 @@ static void conf_cb(uev_t *w, void *arg, int events)
#ifdef AUTO_RELOAD #ifdef AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); sm_reload();
#endif #endif
} }
+19
View File
@@ -27,6 +27,24 @@
#include "cgroup.h" #include "cgroup.h"
#include "svc.h" #include "svc.h"
extern int runlevel;
extern int cfglevel;
extern int cmdlevel;
extern int prevlevel;
extern int debug;
extern int rescue;
extern int single;
extern int bootstrap;
extern int kerndebug;
extern int syncsec;
extern int readiness;
extern char *fstab;
extern char *sdown;
extern char *network;
extern char *hostname;
extern char *runparts;
extern char *osheading;
extern int logfile_size_max; extern int logfile_size_max;
extern int logfile_count_max; extern int logfile_count_max;
@@ -47,6 +65,7 @@ void conf_saverc (void);
void conf_save_exec_order (svc_t *svc, char *cmdline, int result); void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
void conf_save_service (int type, char *cfg, char *file); void conf_save_service (int type, char *cfg, char *file);
void conf_parse_cmdline (int argc, char *argv[]); void conf_parse_cmdline (int argc, char *argv[]);
char *conf_parse_env (char *line, char **val);
int conf_parse_runlevels (char *runlevels); int conf_parse_runlevels (char *runlevels);
void conf_parse_cond (svc_t *svc, char *cond); void conf_parse_cond (svc_t *svc, char *cond);
+10 -1
View File
@@ -77,7 +77,7 @@ void devmon_add_cond(const char *cond)
struct dev_node *node; struct dev_node *node;
if (!cond || strncmp(cond, "dev/", 4)) { if (!cond || strncmp(cond, "dev/", 4)) {
dbg("no match"); // dbg("no match %s", cond ?: "<NIL>");
return; return;
} }
@@ -164,6 +164,8 @@ static void devmon_scandir(struct iwatch *iw, char *dir, int len)
size_t i; size_t i;
int rc; int rc;
(void)iw;
snprintf(path, sizeof(path), "%s/*", dir); snprintf(path, sizeof(path), "%s/*", dir);
rc = glob(path, GLOB_NOSORT, NULL, &gl); rc = glob(path, GLOB_NOSORT, NULL, &gl);
if (rc && rc != GLOB_NOMATCH) if (rc && rc != GLOB_NOMATCH)
@@ -207,6 +209,13 @@ static void devmon_cb(uev_t *w, void *arg, int events)
ssize_t sz; ssize_t sz;
size_t off; size_t off;
(void)arg;
if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in devmon watcher");
return;
}
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); err(1, "invalid inotify event");
+2 -21
View File
@@ -59,25 +59,6 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
int runlevel = INIT_LEVEL; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int cmdlevel = 0; /* runlevel override from cmdline */
int prevlevel = -1;
int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootstrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
int syncsec = 0; /* reboot delay */
int readiness = SVC_NOTIFY_PID;
char *finit_conf= NULL;
char *finit_rcsd= NULL;
char *fstab = NULL;
char *sdown = NULL;
char *network = NULL;
char *hostname = NULL;
char *osheading = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */ uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */ svc_t *wdog = NULL; /* No watchdog by default */
@@ -745,8 +726,8 @@ int main(int argc, char *argv[])
* Initialize state machine and start all bootstrap tasks * Initialize state machine and start all bootstrap tasks
* NOTE: no network available! * NOTE: no network available!
*/ */
sm_init(&sm); sm_init();
sm_step(&sm); sm_step();
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
+6 -20
View File
@@ -54,11 +54,17 @@
#define FINIT_CGPATH "/sys/fs/cgroup" #define FINIT_CGPATH "/sys/fs/cgroup"
#endif #endif
/* Prevent users from logging in at critical stages */
#ifndef FINIT_NOLOGIN_PATH
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN
#endif
/* To use these, include config.h, or define FINIT_EXECPATH_ */ /* To use these, include config.h, or define FINIT_EXECPATH_ */
#define _PATH_LOGIT FINIT_EXECPATH_ "/logit" #define _PATH_LOGIT FINIT_EXECPATH_ "/logit"
#define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts" #define _PATH_RUNPARTS FINIT_EXECPATH_ "/runparts"
#define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin" #define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin"
#define _PATH_GETTY FINIT_EXECPATH_ "/getty" #define _PATH_GETTY FINIT_EXECPATH_ "/getty"
#define _PATH_TMPFILES FINIT_EXECPATH_ "/tmpfiles"
#define CMD_SIZE 1024 #define CMD_SIZE 1024
#define LINE_SIZE 1024 #define LINE_SIZE 1024
@@ -98,7 +104,6 @@
#define INIT_CMD_SVC_FIND 131 #define INIT_CMD_SVC_FIND 131
#define INIT_CMD_SVC_FIND_BYC 132 #define INIT_CMD_SVC_FIND_BYC 132
#define INIT_CMD_SIGNAL 133 #define INIT_CMD_SIGNAL 133
#define INIT_CMD_NOTIFY_SOCKET 200 /* For readiness notification socket */
#define INIT_CMD_NACK 254 #define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255 #define INIT_CMD_ACK 255
@@ -111,25 +116,6 @@ struct init_request {
char data[368]; char data[368];
}; };
extern int runlevel;
extern int cfglevel;
extern int cmdlevel;
extern int prevlevel;
extern int debug;
extern int rescue;
extern int single;
extern int bootstrap;
extern int kerndebug;
extern int syncsec;
extern int readiness;
extern char *fstab;
extern char *sdown;
extern char *network;
extern char *hostname;
extern char *runparts;
extern char *osheading;
extern uev_ctx_t *ctx;
#endif /* FINIT_H_ */ #endif /* FINIT_H_ */
/** /**
+3 -94
View File
@@ -26,8 +26,6 @@
#include <ctype.h> /* isblank() */ #include <ctype.h> /* isblank() */
#include <errno.h> #include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <limits.h> #include <limits.h>
#include <net/if.h> #include <net/if.h>
#include <netinet/in.h> #include <netinet/in.h>
@@ -42,6 +40,7 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "conf.h"
#include "log.h" #include "log.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
@@ -293,6 +292,8 @@ static size_t print_timestamp(char *buf, size_t len)
sscanf(buf, "%f %f", &stamp, &dummy); sscanf(buf, "%f %f", &stamp, &dummy);
return snprintf(buf, len, "[ %.6f ]", stamp); return snprintf(buf, len, "[ %.6f ]", stamp);
#else #else
(void)buf;
(void)len;
return 0; return 0;
#endif #endif
} }
@@ -370,98 +371,6 @@ int print_result(int fail)
return fail; return fail;
} }
int getuser(char *username, char **home)
{
#ifdef ENABLE_STATIC
if (home)
*home = "/"; /* XXX: Fixme */
return fgetint("/etc/passwd", "x:\n", username);
#else
struct passwd *usr;
if (!username || (usr = getpwnam(username)) == NULL)
return -1;
if (home)
*home = usr->pw_dir;
return usr->pw_uid;
#endif
}
int getgroup(char *group)
{
#ifdef ENABLE_STATIC
return fgetint("/etc/group", "x:\n", group);
#else
struct group *grp;
if ((grp = getgrnam(group)) == NULL)
return -1;
return grp->gr_gid;
#endif
}
/* get current user */
int getcuser(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct passwd *pw = getpwuid(getuid());
if (pw)
ptr = pw->pw_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
/* get current group */
int getcgroup(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct group *gr = getgrgid(getgid());
if (gr)
ptr = gr->gr_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
int uid, gid;
int rc = 0;
omask = umask(0);
uid = getuser(user, NULL);
if (uid >= 0) {
gid = getgroup(group);
if (gid < 0)
gid = 0;
rc = makedir(dir, mode);
if (rc && errno == EEXIST)
rc = chmod(dir, mode);
if (chown(dir, uid, gid))
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
}
umask(omask);
return rc;
}
void set_hostname(char **hostname) void set_hostname(char **hostname)
{ {
FILE *fp; FILE *fp;
+1 -61
View File
@@ -39,7 +39,6 @@
#else #else
# include <lite/lite.h> # include <lite/lite.h>
#endif #endif
#include "log.h"
#define PROGRESS_DEFAULT PROGRESS_MODERN #define PROGRESS_DEFAULT PROGRESS_MODERN
@@ -70,14 +69,6 @@ void print (int action, const char *fmt, ...) __attribute__ ((forma
void print_desc (char *action, char *desc); void print_desc (char *action, char *desc);
int print_result (int fail); int print_result (int fail);
int getuser (char *username, char **home);
int getgroup (char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname); void set_hostname (char **hostname);
void networking (int updown); void networking (int updown);
int in_container (void); int in_container (void);
@@ -91,57 +82,6 @@ pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimi
pid_t run_bg (char *cmd, char *args[]); pid_t run_bg (char *cmd, char *args[]);
int run_parts (char *dir, char *cmd, const char *env[], int progress, int sysv); int run_parts (char *dir, char *cmd, const char *env[], int progress, int sysv);
/*
* Defaults to user "root" and group "wheel" (root) if:
* 1) the user is missing
* 2) the group or the file /etc/group is missing
*/
static inline int create(char *path, mode_t mode, char *user, char *group)
{
int fd, uid, gid;
if (!path) {
errno = EINVAL;
return -1;
}
uid = getuser(user, NULL);
if (uid < 0)
uid = 0;
gid = getgroup(group);
if (gid < 0)
gid = 0;
fd = creat(path, mode);
if (fd == -1) {
warn("Failed creating %s properly", path);
return -1;
}
close(fd);
if (chown(path, uid, gid)) {
warn("Failed chowning %s properly", path);
return -1;
}
return 0;
}
static inline int ln(const char *target, const char *linkpath)
{
if (!target || !linkpath) {
errno = EINVAL;
return -1;
}
if (symlink(target, linkpath)) {
if (errno != EEXIST)
warn("Failed creating %s -> %s symlink", target, linkpath);
return -1;
}
return 0;
}
static inline int dprint(int fd, const char *s, size_t len) static inline int dprint(int fd, const char *s, size_t len)
{ {
size_t loop = 3; size_t loop = 3;
@@ -180,7 +120,7 @@ static inline char *fgetval(const char *line, const char *key, char *sep)
* Check if we should strip optional separator, e.g., * Check if we should strip optional separator, e.g.,
* 'foo = bar' => '= bar' should be stripped to 'bar' * 'foo = bar' => '= bar' should be stripped to 'bar'
*/ */
while (str && strchr(sep, str[0])) { while (strchr(sep, str[0])) {
ptr = strsep(&str, sep); ptr = strsep(&str, sep);
if (!ptr) if (!ptr)
goto fail; goto fail;
+17 -18
View File
@@ -767,27 +767,25 @@ static int plugins_list(char *arg)
*/ */
char *runlevel_string(int currlevel, int levels) char *runlevel_string(int currlevel, int levels)
{ {
static char lvl[21]; static char lvl[32];
int i = INIT_LEVEL; int i = INIT_LEVEL;
int pos = 1;
memset(lvl, 0, sizeof(lvl)); strlcpy(lvl, "[", sizeof(lvl));
lvl[0] = '[';
do { do {
if (ISSET(levels, i)) { if (ISSET(levels, i)) {
if (!plain && currlevel == i) char l[2] = { '0' + i, 0 };
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
if (i == INIT_LEVEL)
lvl[pos++] = 'S';
else
lvl[pos++] = '0' + i;
if (!plain && currlevel == i) if (!plain && currlevel == i)
pos = strlcat(lvl, "\e[0m", sizeof(lvl)); strlcat(lvl, "\e[1m", sizeof(lvl));
strlcat(lvl, i == INIT_LEVEL ? "S" : l, sizeof(lvl));
if (!plain && currlevel == i)
strlcat(lvl, "\e[0m", sizeof(lvl));
} else { } else {
lvl[pos++] = '-'; strlcat(lvl, "-", sizeof(lvl));
} }
/* XXX: ugly hack to get order right: S0123456789 */ /* XXX: ugly hack to get order right: S0123456789 */
@@ -798,8 +796,7 @@ char *runlevel_string(int currlevel, int levels)
} while (i < INIT_LEVEL); } while (i < INIT_LEVEL);
lvl[pos++] = ']'; strlcat(lvl, "]", sizeof(lvl));
lvl[pos] = 0;
return lvl; return lvl;
} }
@@ -1138,14 +1135,14 @@ static int show_status(char *arg)
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max); printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
if (cgrp && svc->pid > 1) { if (cgrp && svc->pid > 1) {
char grbuf[128]; const struct cg *cg;
char path[256]; char path[256];
struct cg *cg;
char *group; char *group;
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf)); group = pid_cgroup(svc->pid);
if (!group) if (!group)
goto no_cgroup; /* ... or PID doesn't exist (anymore) */ goto no_cgroup; /* ... or PID doesn't exist (anymore) */
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group); snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
cg = cg_conf(path); cg = cg_conf(path);
@@ -1154,6 +1151,8 @@ static int show_status(char *arg)
group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max, group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max,
cg->cg_mem.min, cg->cg_mem.max); cg->cg_mem.min, cg->cg_mem.max);
show_cgroup_tree(group, " "); show_cgroup_tree(group, " ");
free(group);
} }
no_cgroup: no_cgroup:
printf("\n"); printf("\n");
+70
View File
@@ -32,6 +32,9 @@
# include <lite/lite.h> # include <lite/lite.h>
#endif #endif
#define SYSLOG_NAMES
#include <syslog.h>
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "log.h" #include "log.h"
@@ -106,6 +109,73 @@ static const char *l2s(int prio)
return "UNK"; return "UNK";
} }
static int s2f(const char *str)
{
for (int i = 0; facilitynames[i].c_name; i++) {
if (string_match(facilitynames[i].c_name, str))
return facilitynames[i].c_val;
}
return -1;
}
static int s2l(const char *str)
{
for (int i = 0; prioritynames[i].c_name; i++) {
if (string_match(prioritynames[i].c_name, str))
return prioritynames[i].c_val;
}
return -1;
}
/*
* Parse syslog facility.priority strings into their numerical values
* using arrays defined in <syslog.h>. Note, the 'prio' argument may
* be one of:
*
* - facility.level
* - facility
* - level
*
* It is up to the callee to assign defaults to 'facility' and 'level'
* before calling this function. If There is any match the respective
* facility or level will be assigned and this function returns OK.
*/
int log_parse(char *prio, int *facility, int *level)
{
char *fac = prio, *lvl;
int f = -1;
int l = -1;
if (!prio || !facility || !level)
return -1;
lvl = strchr(prio, '.');
if (lvl) {
*lvl = 0;
lvl++;
}
if (lvl && *lvl)
l = s2l(lvl);
f = s2f(fac);
if (l < 0)
l = s2l(fac);
if (l < 0 && f <= 0)
return -1;
if (l <= LOG_DEBUG)
*level = l;
if (f <= LOG_LOCAL7)
*facility = f;
return 0;
}
/* /*
* Log to /dev/kmsg until syslogd has started, then openlog() * Log to /dev/kmsg until syslogd has started, then openlog()
* and continue logging as a regular daemon. * and continue logging as a regular daemon.
+1
View File
@@ -78,6 +78,7 @@ void log_init (void);
void log_exit (void); void log_exit (void);
void log_debug(void); void log_debug(void);
int log_parse(char *prio, int *facility, int *level);
void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+66 -13
View File
@@ -36,6 +36,9 @@
# include <lite/lite.h> # include <lite/lite.h>
#endif #endif
#define LOG_MAX (200 * 1024)
#define LOG_NUM 5
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION; static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
extern int logrotate(char *file, int num, off_t sz); extern int logrotate(char *file, int num, off_t sz);
@@ -87,23 +90,61 @@ reopen:
return fclose(fp); return fclose(fp);
} }
static int logit(int level, char *buf, size_t len) /*
* Parse possible systemd style log <level> prefix from message
* For details, see libsystemd/sd-daemon.h
*
* Returns the level, and updates buf to point past the prefix
*/
static int parse_level(char **buf, int default_level)
{ {
if (buf[0]) { char *msg = *buf;
syslog(level, "%s", buf);
return 0; if (msg[0] == '<' && msg[2] == '>' && msg[1] >= '0' && msg[1] <= '7') {
int level;
switch (msg[1]) {
case '0': level = LOG_EMERG; break;
case '1': level = LOG_ALERT; break;
case '2': level = LOG_CRIT; break;
case '3': level = LOG_ERR; break;
case '4': level = LOG_WARNING; break;
case '5': level = LOG_NOTICE; break;
case '6': level = LOG_INFO; break;
case '7': level = LOG_DEBUG; break;
default: level = default_level; break;
}
*buf = msg + 3;
return (default_level & ~LOG_PRIMASK) | level;
} }
return default_level;
}
static int do_log(int level, char *msg)
{
syslog(parse_level(&msg, level), "%s", msg);
return 0;
}
static int logit(int level, char *buf, size_t len)
{
if (buf[0])
return do_log(level, buf);
while ((fgets(buf, len, stdin))) while ((fgets(buf, len, stdin)))
syslog(level, "%s", buf); do_log(level, buf);
return 0; return 0;
} }
static int parse_prio(char *arg, int *f, int *l) static int parse_prio(char *arg, int *fac, int *lvl)
{ {
char *duparg = strdup(arg); char *duparg = strdup(arg);
char *ptr, *prio; const char *prio;
char *ptr;
int found;
if (!duparg) if (!duparg)
prio = arg; prio = arg;
@@ -114,23 +155,34 @@ static int parse_prio(char *arg, int *f, int *l)
if (ptr) { if (ptr) {
*ptr++ = 0; *ptr++ = 0;
found = -1;
for (int i = 0; facilitynames[i].c_name; i++) { for (int i = 0; facilitynames[i].c_name; i++) {
if (!strcmp(facilitynames[i].c_name, prio)) { if (!strcmp(facilitynames[i].c_name, prio)) {
*f = facilitynames[i].c_val; found = facilitynames[i].c_val;
break; break;
} }
} }
if (found == -1)
return 1;
*fac = found;
prio = ptr; prio = ptr;
} }
found = -1;
for (int i = 0; prioritynames[i].c_name; i++) { for (int i = 0; prioritynames[i].c_name; i++) {
if (!strcmp(prioritynames[i].c_name, prio)) { if (!strcmp(prioritynames[i].c_name, prio)) {
*l = prioritynames[i].c_val; found = prioritynames[i].c_val;
break; break;
} }
} }
if (found == -1)
return 1;
*lvl = found;
if (duparg != arg) if (duparg != arg)
free(duparg); free(duparg);
@@ -161,13 +213,14 @@ static int usage(int code)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int c, rc, num = 5; char *ident = NULL, *logfile = NULL;
int log_opts = LOG_NOWAIT;
int facility = LOG_USER; int facility = LOG_USER;
int level = LOG_INFO; int level = LOG_INFO;
int log_opts = LOG_NOWAIT; off_t size = LOG_MAX;
off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL;
char buf[512] = ""; char buf[512] = "";
int num = LOG_NUM;
int c, rc;
while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) { while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) {
switch (c) { switch (c) {
+4 -3
View File
@@ -31,6 +31,7 @@
#include <sys/mount.h> #include <sys/mount.h>
#include "helpers.h" #include "helpers.h"
#include "log.h"
/* /*
* SysV init on Debian/Ubuntu skips these protected mount points * SysV init on Debian/Ubuntu skips these protected mount points
@@ -40,7 +41,7 @@
* and regular filesystems that can be safely unmounted before we do * and regular filesystems that can be safely unmounted before we do
* swapoff, and remount / as read-only, respectively. * swapoff, and remount / as read-only, respectively.
*/ */
static int is_protected(char *dir) static int is_protected(const char *dir)
{ {
size_t i; size_t i;
char *protected_mnts[] = { char *protected_mnts[] = {
@@ -111,7 +112,7 @@ static int unmount(const char *target)
void unmount_tmpfs(void) void unmount_tmpfs(void)
{ {
struct mntent *mnt; const struct mntent *mnt;
FILE *fp = NULL; FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts", &fp))) { while ((mnt = iterator("/proc/mounts", &fp))) {
@@ -122,7 +123,7 @@ void unmount_tmpfs(void)
void unmount_regular(void) void unmount_regular(void)
{ {
struct mntent *mnt; const struct mntent *mnt;
FILE *fp = NULL; FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts", &fp))) { while ((mnt = iterator("/proc/mounts", &fp))) {
+2
View File
@@ -320,6 +320,8 @@ static int init_plugins(uev_ctx_t *ctx)
plugin_t *p, *tmp; plugin_t *p, *tmp;
int fail = 0; int fail = 0;
(void)ctx;
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (plugin_io_init(p)) if (plugin_io_init(p))
fail++; fail++;
+2 -1
View File
@@ -41,12 +41,13 @@ extern int service_interval;
extern char *fsck_mode; extern char *fsck_mode;
extern char *fsck_repair; extern char *fsck_repair;
extern uev_ctx_t *ctx;
int api_init (uev_ctx_t *ctx); int api_init (uev_ctx_t *ctx);
int api_exit (void); int api_exit (void);
void conf_flush_events(void); void conf_flush_events(void);
void service_monitor (pid_t lost, int status); void service_monitor (pid_t lost, int status);
void service_notify_cb(uev_t *w, void *arg, int events);
const char *plugin_hook_str (hook_point_t no); const char *plugin_hook_str (hook_point_t no);
int plugin_exists (hook_point_t no); int plugin_exists (hook_point_t no);
+1 -1
View File
@@ -193,7 +193,7 @@ int run_parts(char *dir, char *cmd, const char *env[], int progress, int sysv)
#ifndef __FINIT__ #ifndef __FINIT__
static int usage(int rc) static int usage(int rc)
{ {
warnx("usage: runparts [-dhps?] DIRECTORY"); warnx("usage: runparts [-bdhps?] DIRECTORY");
return rc; return rc;
} }
+1 -1
View File
@@ -22,7 +22,7 @@
*/ */
#include "config.h" #include "config.h"
#include "finit.h" #include "private.h"
#include "log.h" #include "log.h"
#include "schedule.h" #include "schedule.h"
+321 -188
View File
@@ -30,6 +30,7 @@
#include <sys/reboot.h> #include <sys/reboot.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/un.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <net/if.h> #include <net/if.h>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
@@ -56,6 +57,9 @@
#include "utmp-api.h" #include "utmp-api.h"
#include "schedule.h" #include "schedule.h"
#define NOTIFY_PATH "@run/finit/notify/%d"
/* /*
* run tasks block other tasks/services from starting, we track the * run tasks block other tasks/services from starting, we track the
* current run task here. For service_step() and service_start(). * current run task here. For service_step() and service_start().
@@ -68,6 +72,8 @@ static struct wq work = {
int service_interval = SERVICE_INTERVAL_DEFAULT; int service_interval = SERVICE_INTERVAL_DEFAULT;
static void svc_set_state(svc_t *svc, svc_state_t new_state); static void svc_set_state(svc_t *svc, svc_state_t new_state);
static void service_notify_cb(uev_t *w, void *arg, int events);
/** /**
* service_timeout_cb - libuev callback wrapper for service timeouts * service_timeout_cb - libuev callback wrapper for service timeouts
@@ -208,13 +214,16 @@ static int stdin_redirect(void)
{ {
int fd; int fd;
fd = open("/dev/null", O_RDONLY | O_APPEND); fd = open("/dev/null", O_RDONLY);
if (-1 != fd) { if (fd == -1) {
dup2(fd, STDIN_FILENO); warn("Failed opening /dev/null for stdin redirect");
return close(fd); return -1;
} }
return -1; dup2(fd, STDIN_FILENO);
close(fd);
return 0;
} }
/* /*
@@ -244,6 +253,9 @@ static void fallback_logger(char *ident, char *prio)
int level = LOG_NOTICE; int level = LOG_NOTICE;
char buf[256]; char buf[256];
strlcpy(buf, prio, sizeof(buf));
log_parse(buf, &facility, &level);
prctl(PR_SET_NAME, "finitlog", 0, 0, 0); prctl(PR_SET_NAME, "finitlog", 0, 0, 0);
openlog(ident, LOG_NOWAIT | LOG_PID, facility); openlog(ident, LOG_NOWAIT | LOG_PID, facility);
while ((fgets(buf, sizeof(buf), stdin))) while ((fgets(buf, sizeof(buf), stdin)))
@@ -258,12 +270,10 @@ static void fallback_logger(char *ident, char *prio)
static int lredirect(svc_t *svc) static int lredirect(svc_t *svc)
{ {
static int have_sysklogd = -1; static int have_sysklogd = -1;
pid_t pid, svc_pid; pid_t svc_pid = getpid();
pid_t pid;
int fd; int fd;
svc_pid = getpid();
dbg("%s pid: %d", svc_ident(svc, NULL, 0), svc_pid);
/* /*
* Open PTY to connect to logger. A pty isn't buffered * Open PTY to connect to logger. A pty isn't buffered
* like a pipe, and it eats newlines so they aren't logged * like a pipe, and it eats newlines so they aren't logged
@@ -327,6 +337,12 @@ static int lredirect(svc_t *svc)
/* Default syslog identity name[:id] */ /* Default syslog identity name[:id] */
tag = svc_ident(svc, buf, sizeof(buf)); tag = svc_ident(svc, buf, sizeof(buf));
if (svc->log.ident[0])
tag = svc->log.ident;
if (svc->log.prio[0])
prio = svc->log.prio;
/* Neither sysklogd logger or native logit tool available */
if (!have_sysklogd && !whichp(_PATH_LOGIT)) { if (!have_sysklogd && !whichp(_PATH_LOGIT)) {
logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name); logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
fallback_logger(tag, prio); fallback_logger(tag, prio);
@@ -352,12 +368,13 @@ static int lredirect(svc_t *svc)
_exit(1); _exit(1);
} }
if (svc->log.ident[0]) /*
tag = svc->log.ident; * For now, let systemd programs go via our native logit
if (svc->log.prio[0]) * tool. It supports systemd logging defines for stderr
prio = svc->log.prio; * parsing. The only real downside is that it cannot do
* PID faking, like sysklogd's logger tool.
if (have_sysklogd) { */
if (have_sysklogd && svc->notify != SVC_NOTIFY_SYSTEMD) {
char pid[16]; char pid[16];
snprintf(pid, sizeof(pid), "%d", svc_pid); snprintf(pid, sizeof(pid), "%d", svc_pid);
@@ -425,11 +442,13 @@ static void source_env(svc_t *svc)
line = buf; line = buf;
while (fgets(line, LINE_SIZE, fp)) { while (fgets(line, LINE_SIZE, fp)) {
char *key = chomp(line);
wordexp_t we = { 0 }; wordexp_t we = { 0 };
char *value, *end; char *key, *value;
size_t i; size_t i;
/* Trim newline */
key = chomp(line);
/* skip any leading whitespace */ /* skip any leading whitespace */
while (isspace(*key)) while (isspace(*key))
key++; key++;
@@ -438,64 +457,9 @@ static void source_env(svc_t *svc)
if (*key == '#' || *key == ';') if (*key == '#' || *key == ';')
continue; continue;
/* find end of line */ key = conf_parse_env(key, &value);
end = key; if (!key)
while (*end)
end++;
/* strip trailing whitespace */
if (end > key) {
end--;
while (isspace(*end))
*end-- = 0;
}
value = strchr(key, '=');
if (!value)
continue; continue;
*value++ = 0;
/* strip leading whitespace from value */
while (isspace(*value))
value++;
/* unquote value, if quoted */
if (value[0] == '"' || value[0] == '\'') {
char q = value[0];
if (*end == q) {
value = &value[1];
*end = 0;
}
}
/* find end of key */
end = key;
while (*end)
end++;
/* strip trailing whitespace */
if (end > key) {
end--;
while (isspace(*end))
*end-- = 0;
}
/* strip any leading 'set ' */
end = key;
if (!strncmp(key, "set", 3))
end += 3;
/* check key, no spaces allowed */
while (*end && isspace(*end))
end++;
key = end;
while (*end && !isspace(*end))
end++;
if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, value);
continue; /* invalid key */
}
if (wordexp(value, &we, 0)) { if (wordexp(value, &we, 0)) {
setenv(key, value, 1); setenv(key, value, 1);
@@ -631,6 +595,9 @@ static int service_start(svc_t *svc)
int result = 0, do_progress = 1; int result = 0, do_progress = 1;
char cmdline[CMD_SIZE] = ""; char cmdline[CMD_SIZE] = "";
sigset_t nmask, omask; sigset_t nmask, omask;
int pipefd[2];
int fd = -1;
int sd = -1;
pid_t pid; pid_t pid;
size_t i; size_t i;
@@ -676,11 +643,34 @@ static int service_start(svc_t *svc)
if (svc_is_sysv(svc)) if (svc_is_sysv(svc))
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", cmdline); logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", cmdline);
switch (svc->notify) {
case SVC_NOTIFY_S6:
if (pipe(pipefd) == -1) {
err(1, "%s: failed opening pipe for s6 notify", svc_ident(svc, NULL, 0));
svc_missing(svc);
return 1;
}
fd = pipefd[1];
sd = pipefd[0];
break;
case SVC_NOTIFY_SYSTEMD:
sd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sd == -1) {
err(1, "%s: failed opening notify socket", svc_ident(svc, NULL, 0));
svc_missing(svc);
return 1;
}
break;
default:
break;
}
if (!svc->desc[0]) if (!svc->desc[0])
do_progress = 0; do_progress = 0;
if (do_progress) { if (do_progress) {
if (svc_is_daemon(svc) || svc_is_sysv(svc)) if (svc_is_daemon(svc))
print_desc("Starting ", svc->desc); print_desc("Starting ", svc->desc);
else else
print_desc("", svc->desc); print_desc("", svc->desc);
@@ -690,7 +680,7 @@ static int service_start(svc_t *svc)
svc_starting(svc); svc_starting(svc);
/* Increment total restarts, unless first time or non-service */ /* Increment total restarts, unless first time or non-service */
if (svc_is_daemon(svc) || svc_is_sysv(svc)) { if (svc_is_daemon(svc)) {
if (svc->restart_cnt || svc->restart_tot) if (svc->restart_cnt || svc->restart_tot)
svc->restart_tot++; svc->restart_tot++;
} }
@@ -702,38 +692,72 @@ static int service_start(svc_t *svc)
pid = service_fork(svc); pid = service_fork(svc);
if (pid < 0) { if (pid < 0) {
if (sd != -1)
close(sd);
if (fd != -1)
close(fd);
result = -1; result = -1;
goto fail; goto fail;
} }
if (pid > 1) { if (pid > 1) {
struct sockaddr_un sun;
size_t len;
dbg("Starting %s as PID %d", svc_ident(svc, NULL, 0), pid); dbg("Starting %s as PID %d", svc_ident(svc, NULL, 0), pid);
svc->pid = pid; svc->pid = pid;
svc->start_time = jiffies(); svc->start_time = jiffies();
switch (svc->notify) {
case SVC_NOTIFY_SYSTEMD:
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
snprintf(sun.sun_path, sizeof(sun.sun_path), NOTIFY_PATH, pid);
len = strlen(sun.sun_path);
sun.sun_path[0] = 0;
result = bind(sd, (struct sockaddr *)&sun, offsetof(struct sockaddr_un, sun_path) + len);
if (result == -1) {
err(1, "%s: failed binding to notify socket", svc_ident(svc, NULL, 0));
close(sd);
break;
}
/* fallthrough */
case SVC_NOTIFY_S6:
close(fd); /* client-end of pipefd for s6 notify */
result = uev_io_init(ctx, &svc->notify_watcher, service_notify_cb, svc, sd, UEV_READ);
if (result < 0) {
err(1, "%s: failed setting up notify callback", svc_ident(svc, NULL, 0));
if (svc->notify == SVC_NOTIFY_S6)
close(fd);
close(sd);
break;
}
default:
break;
}
} else if (pid == 0) { } else if (pid == 0) {
char str[strlen(NOTIFY_PATH) + 32];
char *args[MAX_NUM_SVC_ARGS + 1]; char *args[MAX_NUM_SVC_ARGS + 1];
int status; int status;
if (!svc_is_tty(svc)) if (!svc_is_tty(svc))
redirect(svc); redirect(svc);
switch (svc->notify) {
case SVC_NOTIFY_SYSTEMD:
snprintf(str, sizeof(str), NOTIFY_PATH, getpid());
setenv("NOTIFY_SOCKET", str, 1);
/* fallthrough */
case SVC_NOTIFY_S6:
close(sd);
break;
default:
break;
}
if (!svc_is_sysv(svc)) { if (!svc_is_sysv(svc)) {
wordexp_t we = { 0 }; wordexp_t we = { 0 };
int rc; int rc;
if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) {
if (client_command(INIT_CMD_NOTIFY_SOCKET)) {
err(1, "%s: failed creating notify socket", svc_ident(svc, NULL, 0));
client_disconnect();
/* does not change parent, restarting may work. */
svc->notify = SVC_NOTIFY_NONE;
} else {
char val[20];
snprintf(val, sizeof(val), "%d", client_socket());
setenv("NOTIFY_SOCKET", val, 1);
}
}
if ((rc = wordexp(svc->cmd, &we, 0))) { if ((rc = wordexp(svc->cmd, &we, 0))) {
errx(1, "%s: failed wordexp(%s): %d", svc_ident(svc, NULL, 0), svc->cmd, rc); errx(1, "%s: failed wordexp(%s): %d", svc_ident(svc, NULL, 0), svc->cmd, rc);
nomem: nomem:
@@ -750,11 +774,11 @@ static int service_start(svc_t *svc)
if (len == 0) if (len == 0)
break; break;
if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) { if (svc->notify == SVC_NOTIFY_S6) {
char *ptr = strstr(arg, "%n"); char *ptr = strstr(arg, "%n");
if (ptr) { if (ptr) {
len = snprintf(str, sizeof(str), "%d", client_socket()); len = snprintf(str, sizeof(str), "%d", fd);
if (len > 0 && len <= 2) { if (len > 0 && len <= 2) {
ptr[0] = ' '; ptr[0] = ' ';
ptr[1] = ' '; ptr[1] = ' ';
@@ -978,6 +1002,7 @@ static void service_cleanup(svc_t *svc)
*/ */
int service_stop(svc_t *svc) int service_stop(svc_t *svc)
{ {
const char *id = svc_ident(svc, NULL, 0);
char cmdline[CMD_SIZE] = ""; char cmdline[CMD_SIZE] = "";
int do_progress = 1; int do_progress = 1;
int rc = 0; int rc = 0;
@@ -990,11 +1015,11 @@ int service_stop(svc_t *svc)
service_timeout_cancel(svc); service_timeout_cancel(svc);
compose_cmdline(svc, cmdline, sizeof(cmdline)); if (svc->stop_script[0]) {
if (!svc_is_sysv(svc)) { logit(LOG_CONSOLE | LOG_NOTICE, "%s[%d], calling stop:%s ...", id, svc->pid, svc->stop_script);
} else if (!svc_is_sysv(svc)) {
char *nm = pid_get_name(svc->pid, NULL, 0); char *nm = pid_get_name(svc->pid, NULL, 0);
const char *sig = sig_name(svc->sighalt); const char *sig = sig_name(svc->sighalt);
char *id = svc_ident(svc, NULL, 0);
if (svc->pid <= 1) if (svc->pid <= 1)
return 1; return 1;
@@ -1006,9 +1031,10 @@ int service_stop(svc_t *svc)
} }
dbg("Sending %s to pid:%d name:%s(%s)", sig, svc->pid, id, nm); dbg("Sending %s to pid:%d name:%s(%s)", sig, svc->pid, id, nm);
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s[%d], sending %s ...", id, svc->pid, sig); logit(LOG_CONSOLE | LOG_NOTICE, "%s[%d], stopping, sending %s ...", id, svc->pid, sig);
} else { } else {
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", cmdline); compose_cmdline(svc, cmdline, sizeof(cmdline));
logit(LOG_CONSOLE | LOG_NOTICE, "%s[%d], calling '%s stop' ...", id, svc->pid, cmdline);
} }
/* /*
@@ -1033,10 +1059,12 @@ int service_stop(svc_t *svc)
* Skip run/tasks in progress, would otherwise print silly stuff * Skip run/tasks in progress, would otherwise print silly stuff
* like: "Stopping Shutting down" ... * like: "Stopping Shutting down" ...
*/ */
if (runlevel != 1 && do_progress && (svc_is_daemon(svc) || svc_is_sysv(svc))) if (runlevel != 1 && do_progress && svc_is_daemon(svc))
print_desc("Stopping ", svc->desc); print_desc("Stopping ", svc->desc);
if (!svc_is_sysv(svc)) { if (svc->stop_script[0]) {
rc = run(svc->stop_script, NULL);
} else if (!svc_is_sysv(svc)) {
if (svc->pid > 1) { if (svc->pid > 1) {
/* /*
* Send SIGTERM to parent process of process group, not to the * Send SIGTERM to parent process of process group, not to the
@@ -1047,11 +1075,8 @@ int service_stop(svc_t *svc)
rc = kill(svc->pid, svc->sighalt); rc = kill(svc->pid, svc->sighalt);
dbg("kill(%d, %d) => rc %d, errno %d", svc->pid, svc->sighalt, rc, errno); dbg("kill(%d, %d) => rc %d, errno %d", svc->pid, svc->sighalt, rc, errno);
/* PID lost or forking process never really started */ /* PID lost or forking process never really started */
if (rc == -1 && (errno == ESRCH || errno == ENOENT)) { if (rc == -1 && (errno == ESRCH || errno == ENOENT))
service_cleanup(svc); rc = 1;
svc_set_state(svc, SVC_HALTED_STATE);
} else
svc_set_state(svc, SVC_STOPPING_STATE);
} else { } else {
service_cleanup(svc); service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
@@ -1059,7 +1084,6 @@ int service_stop(svc_t *svc)
} else { } else {
char *args[MAX_NUM_SVC_ARGS + 1]; char *args[MAX_NUM_SVC_ARGS + 1];
size_t i = 0, j; size_t i = 0, j;
pid_t pid;
args[i++] = svc->cmd; args[i++] = svc->cmd;
/* this handles, e.g., bridge-stop br0 stop */ /* this handles, e.g., bridge-stop br0 stop */
@@ -1071,8 +1095,7 @@ int service_stop(svc_t *svc)
args[i++] = "stop"; args[i++] = "stop";
args[i] = NULL; args[i] = NULL;
pid = fork(); switch (fork()) {
switch (pid) {
case 0: case 0:
setsid(); setsid();
redirect(svc); redirect(svc);
@@ -1084,46 +1107,44 @@ int service_stop(svc_t *svc)
rc = 1; rc = 1;
break; break;
default: default:
svc_set_state(svc, SVC_STOPPING_STATE);
break; break;
} }
} }
if (runlevel != 1 && do_progress && (svc_is_daemon(svc) || svc_is_sysv(svc))) if (rc == 1) {
service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE);
} else
svc_set_state(svc, SVC_STOPPING_STATE);
if (runlevel != 1 && do_progress && svc_is_daemon(svc))
print_result(rc); print_result(rc);
return rc; return rc;
} }
/** /**
* service_restart - Restart a service by sending %SIGHUP * service_reload - Reload a service
* @svc: Service to reload * @svc: Service to reload
* *
* This function does some basic checks of the runtime state of Finit * This function does some basic checks of the runtime state of Finit
* and a sanity check of the @svc before sending %SIGHUP. * and a sanity check of the @svc before sending %SIGHUP or calling
* the reload:script command.
* *
* Returns: * Returns:
* POSIX OK(0) or non-zero on error. * POSIX OK(0) or non-zero on error.
*/ */
static int service_restart(svc_t *svc) static int service_reload(svc_t *svc)
{ {
const char *id = svc_ident(svc, NULL, 0);
int do_progress = 1; int do_progress = 1;
pid_t lost = 0; pid_t lost = 0;
int rc; int rc;
/* Ignore if finit is SIGSTOP'ed */ /* Ignore if service is invalid or finit is SIGSTOP'ed */
if (is_norespawn()) if (!svc || is_norespawn())
return 1; return 1;
if (!svc || !svc->sighup)
return 1;
if (svc->pid <= 1) {
dbg("%s: bad PID %d for %s, SIGHUP", svc_ident(svc, NULL, 0), svc->pid, svc->cmd);
svc->start_time = svc->pid = 0;
return 1;
}
/* Skip progress if desc disabled or bootstrap task */ /* Skip progress if desc disabled or bootstrap task */
if (!svc->desc[0] || svc_in_runlevel(svc, INIT_LEVEL)) if (!svc->desc[0] || svc_in_runlevel(svc, INIT_LEVEL))
do_progress = 0; do_progress = 0;
@@ -1131,14 +1152,28 @@ static int service_restart(svc_t *svc)
if (do_progress) if (do_progress)
print_desc("Restarting ", svc->desc); print_desc("Restarting ", svc->desc);
dbg("Sending SIGHUP to PID %d", svc->pid); if (svc->reload_script[0]) {
logit(LOG_CONSOLE | LOG_NOTICE, "Restarting %s[%d], sending SIGHUP ...", logit(LOG_CONSOLE | LOG_NOTICE, "%s[%d], calling reload:%s ...", id, svc->pid, svc->reload_script);
svc_ident(svc, NULL, 0), svc->pid); rc = run(svc->reload_script, NULL);
rc = kill(svc->pid, SIGHUP); } else if (svc->sighup) {
if (rc == -1 && (errno == ESRCH || errno == ENOENT)) { if (svc->pid <= 1) {
/* nobody home, reset internal state machine */ dbg("%s[%d]: bad PID, cannot reload service", id, svc->pid);
lost = svc->pid; svc->start_time = svc->pid = 0;
goto done;
}
dbg("%s[%d], sending SIGHUP", id, svc->pid);
logit(LOG_CONSOLE | LOG_NOTICE, "%s[%d], sending SIGHUP ...", id, svc->pid);
rc = kill(svc->pid, SIGHUP);
if (rc == -1 && (errno == ESRCH || errno == ENOENT)) {
/* nobody home, reset internal state machine */
lost = svc->pid;
}
} else { } else {
warnx("%s: neither HUP nor reload:script defined, no action.", id);
rc = 1;
}
if (!rc) {
/* Declare we're waiting for svc to re-assert/touch its pidfile */ /* Declare we're waiting for svc to re-assert/touch its pidfile */
svc_starting(svc); svc_starting(svc);
@@ -1148,7 +1183,7 @@ static int service_restart(svc_t *svc)
touch(pid_file(svc)); touch(pid_file(svc));
} }
} }
done:
if (do_progress) if (do_progress)
print_result(rc); print_result(rc);
@@ -1158,36 +1193,81 @@ static int service_restart(svc_t *svc)
return rc; return rc;
} }
/** /*
* service_reload_dynamic - Called on SIGHUP, 'init q' or 'initctl reload' * Parse run/task/service arguments with support for quoted strings, both
* single and double quotes to allow arguments containing spaces.
* *
* This function is called when Finit has received SIGHUP to reload * Returns next argument or NULL if end of line
* .conf files in /etc/finit.d. It is responsible for starting, * Updates *line to point past the parsed argument
* stopping and reloading (forwarding SIGHUP) to processes affected.
*/ */
void service_reload_dynamic(void) static char *parse_args(char **line)
{ {
sm_set_reload(&sm); char *start, *end = NULL, *arg;
sm_step(&sm); char in_quote = 0;
} int has_colon = 0;
/** if (!line || !*line)
* service_runlevel - Change to a new runlevel return NULL;
* @newlevel: New runlevel to activate
*
* Stops all services not in @newlevel and starts, or lets continue to run,
* those in @newlevel. Also updates @prevlevel and active @runlevel.
*/
void service_runlevel(int newlevel)
{
if (!rescue && (runlevel == 1 || runlevel == INIT_LEVEL) && !IS_RESERVED_RUNLEVEL(newlevel))
networking(1);
sm_set_runlevel(&sm, newlevel); /* Skip leading whitespace */
sm_step(&sm); while (**line && (**line == ' ' || **line == '\t'))
(*line)++;
if (!rescue && IS_RESERVED_RUNLEVEL(runlevel)) if (!**line)
networking(0); return NULL;
start = *line;
/* Parse the token */
while (**line) {
if (in_quote) {
if (**line == in_quote) {
/* End quote found */
in_quote = 0;
}
} else {
if (**line == '\'' || **line == '"') {
/* Start quote */
in_quote = **line;
} else if (**line == ':') {
/* Found colon - this might be key:value format */
has_colon = 1;
} else if (**line == ' ' || **line == '\t') {
/* Whitespace - check if we're in key:value mode */
if (has_colon) {
/* Look ahead to see if there's a comma after whitespace */
char *lookahead = *line;
while (*lookahead && (*lookahead == ' ' || *lookahead == '\t'))
lookahead++;
if (*lookahead == ',') {
/* Continue parsing - this space is part of the token */
(*line)++;
continue;
}
}
/* End token at whitespace */
end = *line;
break;
}
}
(*line)++;
}
/* Set end if we reached end of string */
if (!end)
end = *line;
/* Null terminate the argument */
if (end > start) {
arg = start;
if (*end) {
*end = '\0';
*line = end + 1;
}
return arg;
}
return NULL;
} }
/* /*
@@ -1317,26 +1397,36 @@ static void parse_script(svc_t *svc, char *type, char *script, int *tmo, char *b
if (errstr) { if (errstr) {
errx(1, "%s: tmo %s is %s (0-3600)", svc_ident(svc, NULL, 0), errx(1, "%s: tmo %s is %s (0-3600)", svc_ident(svc, NULL, 0),
script, errstr); script, errstr);
return; goto err;
} }
*tmo = (int)(sec * 1000); *tmo = (int)(sec * 1000);
} else { } else {
path = script; path = script;
*tmo = svc->killdelay; if (tmo)
*tmo = svc->killdelay;
}
if (unquote(&path, NULL)) {
errx(1, "Syntax error, unterminated quote in %s:%s", type, path);
goto err;
} }
found = which(path); found = which(path);
if (!found) { if (!found) {
/* Not in $PATH, check if path exists and is executable */ logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.", type, path);
if (access(path, X_OK)) { goto err;
logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.", }
type, path); free(found);
return;
} if (strlen(path) >= len) {
} else errx(1, "Command too long in %s:%s", type, path);
free(found); goto err;
}
strlcpy(buf, path, len); strlcpy(buf, path, len);
return;
err:
memset(buf, 0, len);
} }
/* /*
@@ -1374,7 +1464,7 @@ static char *parse_name(char *cmd, char *arg)
*/ */
static void parse_cmdline_args(svc_t *svc, char *cmd, char **args) static void parse_cmdline_args(svc_t *svc, char *cmd, char **args)
{ {
char prev[MAX_ARG_LEN]; char prev[MAX_CMD_LEN];
int diff = 0; int diff = 0;
char sep = 0; char sep = 0;
char *arg; char *arg;
@@ -1522,6 +1612,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *id = NULL, *env = NULL, *cgroup = NULL; char *id = NULL, *env = NULL, *cgroup = NULL;
char *pre_script = NULL, *post_script = NULL; char *pre_script = NULL, *post_script = NULL;
char *ready_script = NULL, *conflict = NULL; char *ready_script = NULL, *conflict = NULL;
char *reload_script = NULL, *stop_script = NULL;
char *cleanup_script = NULL; char *cleanup_script = NULL;
char ident[MAX_IDENT_LEN]; char ident[MAX_IDENT_LEN];
char *ifstmt = NULL; char *ifstmt = NULL;
@@ -1564,7 +1655,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
} }
} }
cmd = strtok_r(line, " \t", &args); args = line;
cmd = parse_args(&args);
if (!cmd) { if (!cmd) {
incomplete: incomplete:
errx(1, "Incomplete service '%s', cannot register", cfg); errx(1, "Incomplete service '%s', cannot register", cfg);
@@ -1631,6 +1723,10 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
ready_script = arg; ready_script = arg;
else if (MATCH_CMD(cmd, "cleanup:", arg)) else if (MATCH_CMD(cmd, "cleanup:", arg))
cleanup_script = arg; cleanup_script = arg;
else if (MATCH_CMD(cmd, "reload:", arg))
reload_script = arg;
else if (MATCH_CMD(cmd, "stop:", arg))
stop_script = arg;
else if (MATCH_CMD(cmd, "env:", arg)) else if (MATCH_CMD(cmd, "env:", arg))
env = arg; env = arg;
/* catch both cgroup: and cgroup. handled in parse_cgroup() */ /* catch both cgroup: and cgroup. handled in parse_cgroup() */
@@ -1644,7 +1740,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
break; break;
/* Check if valid command follows... */ /* Check if valid command follows... */
cmd = strtok_r(NULL, " ", &args); cmd = parse_args(&args);
if (!cmd) if (!cmd)
goto incomplete; goto incomplete;
} }
@@ -1826,6 +1922,16 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else else
memset(svc->cleanup_script, 0, sizeof(svc->cleanup_script)); memset(svc->cleanup_script, 0, sizeof(svc->cleanup_script));
if (reload_script)
parse_script(svc, "reload", reload_script, NULL, svc->reload_script, sizeof(svc->reload_script));
else
memset(svc->reload_script, 0, sizeof(svc->reload_script));
if (stop_script)
parse_script(svc, "stop", stop_script, NULL, svc->stop_script, sizeof(svc->stop_script));
else
memset(svc->stop_script, 0, sizeof(svc->stop_script));
if (!svc_is_tty(svc)) { if (!svc_is_tty(svc)) {
if (log) if (log)
parse_log(svc, log); parse_log(svc, log);
@@ -1867,7 +1973,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
svc->oncrash_action = oncrash_action; svc->oncrash_action = oncrash_action;
/* Decode any (optional) pid:/optional/path/to/file.pid */ /* Decode any (optional) pid:/optional/path/to/file.pid */
if (svc_is_daemon(svc) || svc_is_sysv(svc)) { if (svc_is_daemon(svc)) {
char tmp[sizeof(svc->name) + 6]; /* pid:! + svc->name */ char tmp[sizeof(svc->name) + 6]; /* pid:! + svc->name */
/* no pid: set, figure out a default to track this svc */ /* no pid: set, figure out a default to track this svc */
@@ -2007,7 +2113,7 @@ void service_monitor(pid_t lost, int status)
kill(-svc->pid, SIGKILL); kill(-svc->pid, SIGKILL);
/* Try removing PID file (in case service does not clean up after itself) */ /* Try removing PID file (in case service does not clean up after itself) */
if (svc_is_daemon(svc) || svc_is_sysv(svc) || svc_is_tty(svc)) { if (svc_is_daemon(svc) || svc_is_tty(svc)) {
service_cleanup(svc); service_cleanup(svc);
} else if (svc_is_runtask(svc)) { } else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */ /* run/task should run at least once per runlevel */
@@ -2035,7 +2141,7 @@ cont:
dbg("collected bootstrap task %s(%d), removing.", svc_ident(svc, NULL, 0), lost); dbg("collected bootstrap task %s(%d), removing.", svc_ident(svc, NULL, 0), lost);
} }
sm_step(&sm); sm_step();
} }
static void svc_mark_affected(char *cond) static void svc_mark_affected(char *cond)
@@ -2071,7 +2177,7 @@ void service_update_rdeps(void)
continue; continue;
/* Service supports reloading conf without stop/start */ /* Service supports reloading conf without stop/start */
if (!svc_is_nohup(svc)) if (!svc_is_noreload(svc))
continue; /* Yup, no need to stop start rdeps */ continue; /* Yup, no need to stop start rdeps */
svc_mark_affected(mkcond(svc, cond, sizeof(cond))); svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
@@ -2155,6 +2261,8 @@ static void service_pre_script(svc_t *svc)
strlcpy(buf, svc->pre_script, sizeof(buf)); strlcpy(buf, svc->pre_script, sizeof(buf));
set_pre_post_envs(svc, "pre"); set_pre_post_envs(svc, "pre");
sig_unblock();
execvp(_PATH_BSHELL, argv); execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR); _exit(EX_OSERR);
} }
@@ -2192,6 +2300,7 @@ static void service_post_script(svc_t *svc)
else else
strlcpy(buf, svc->post_script, sizeof(buf)); strlcpy(buf, svc->post_script, sizeof(buf));
set_pre_post_envs(svc, "post"); set_pre_post_envs(svc, "post");
sig_unblock();
if (WIFEXITED(svc->status)) { if (WIFEXITED(svc->status)) {
char val[4]; char val[4];
@@ -2253,6 +2362,8 @@ void service_ready_script(svc_t *svc)
strlcpy(buf, svc->ready_script, sizeof(buf)); strlcpy(buf, svc->ready_script, sizeof(buf));
set_pre_post_envs(svc, "ready"); set_pre_post_envs(svc, "ready");
sig_unblock();
execvp(_PATH_BSHELL, argv); execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR); _exit(EX_OSERR);
} }
@@ -2289,6 +2400,8 @@ static void service_cleanup_script(svc_t *svc)
strlcpy(buf, svc->cleanup_script, sizeof(buf)); strlcpy(buf, svc->cleanup_script, sizeof(buf));
set_pre_post_envs(svc, "cleanup"); set_pre_post_envs(svc, "cleanup");
sig_unblock();
execvp(_PATH_BSHELL, argv); execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR); _exit(EX_OSERR);
} }
@@ -2354,7 +2467,7 @@ static void service_retry(svc_t *svc)
timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000; timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000;
/* If a longer timeout was specified in the conf, use that instead. */ /* If a longer timeout was specified in the conf, use that instead. */
svc->restart_tmo = max(svc->restart_tmo, timeout); svc->restart_tmo = max(svc->restart_tmo, timeout);
logit(LOG_CONSOLE|LOG_WARNING, "Service %s[%d] died, restarting in %d msec (%d/%d)", logit(LOG_CONSOLE|LOG_WARNING, "Service %s[%d] died, restarting (retry in %d msec) (attempt: %d/%d)",
svc_ident(svc, NULL, 0), svc->oldpid, svc->restart_tmo, *restart_cnt, svc->restart_max); svc_ident(svc, NULL, 0), svc->oldpid, svc->restart_tmo, *restart_cnt, svc->restart_max);
svc_unblock(svc); svc_unblock(svc);
@@ -2401,7 +2514,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
} }
} }
if (svc_is_daemon(svc) || svc_is_sysv(svc)) { if (svc_is_daemon(svc)) {
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0)); snprintf(cond, sizeof(cond), "service/%s/", svc_ident(svc, NULL, 0));
@@ -2409,7 +2522,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) || if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) ||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE)) (old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
; /* only paused during reload, don't clear conds. */ ; /* only paused during reload, don't clear conds. */
else if (sm_is_in_teardown(&sm)) else if (sm_in_reload())
cond_clear_noupdate(cond); cond_clear_noupdate(cond);
else else
cond_clear(cond); cond_clear(cond);
@@ -2604,7 +2717,7 @@ restart:
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
} else if (cond_get_agg(svc->cond) == COND_ON) { } else if (cond_get_agg(svc->cond) == COND_ON) {
/* wait until all processes have been stopped before continuing... */ /* wait until all processes have been stopped before continuing... */
if (sm_is_in_teardown(&sm)) if (sm_in_reload())
break; break;
/* Don't start if it conflicts with something else already started */ /* Don't start if it conflicts with something else already started */
@@ -2657,7 +2770,7 @@ restart:
} }
if (!svc->pid) { if (!svc->pid) {
if (svc_is_daemon(svc) || svc_is_sysv(svc) || svc_is_tty(svc)) { if (svc_is_daemon(svc) || svc_is_tty(svc)) {
svc_restarting(svc); /* BLOCK_RESTARTING */ svc_restarting(svc); /* BLOCK_RESTARTING */
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
@@ -2699,16 +2812,21 @@ restart:
case COND_ON: case COND_ON:
if (svc_is_changed(svc)) { if (svc_is_changed(svc)) {
if (svc_nohup(svc) || !svc_is_daemon(svc)) /*
* If service does not suport reload, or its command line
* arguments have been modified, we need to stop-start it.
*/
if (svc_is_noreload(svc) || svc->args_dirty) {
service_stop(svc); service_stop(svc);
else { } else {
/* /*
* wait until all processes have been * wait until all processes have been
* stopped before continuing... * stopped before continuing...
*/ */
if (sm_is_in_teardown(&sm)) if (sm_in_reload())
break; break;
service_restart(svc);
service_reload(svc);
} }
svc_mark_clean(svc); svc_mark_clean(svc);
@@ -2791,6 +2909,7 @@ void service_step_all(int types)
void service_worker(void *unused) void service_worker(void *unused)
{ {
(void)unused;
service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK); service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK);
} }
@@ -2892,28 +3011,42 @@ void service_notify_reconf(void)
* Called when a service sends readiness notification, or when * Called when a service sends readiness notification, or when
* the service closes its end of the IPC connection. * the service closes its end of the IPC connection.
*/ */
void service_notify_cb(uev_t *w, void *arg, int events) static void service_notify_cb(uev_t *w, void *arg, int events)
{ {
svc_t *svc = (svc_t *)arg; svc_t *svc = (svc_t *)arg;
int ready = 0;
char buf[32]; char buf[32];
ssize_t len; ssize_t len;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("Spurious problem with %s notify callback, restarting.", svc_ident(svc, NULL, 0)); dbg("%s: spurious problem with notify callback, restarting.", svc_ident(svc, NULL, 0));
uev_io_start(w); uev_io_start(w);
return; return;
} }
len = read(w->fd, buf, sizeof(buf) - 1); len = read(w->fd, buf, sizeof(buf) - 1);
if (len == -1) { if (len <= 0) {
warn("Failed reading notification from %s", svc_ident(svc, NULL, 0)); warn("Failed reading notification from %s", svc_ident(svc, NULL, 0));
return; return;
} }
buf[len] = 0; buf[len] = 0;
/* systemd and s6, respectively. The latter then closes the socket */ /* Check for systemd READY=1 or s6 newline termination */
if (!strcmp(buf, "READY=1\n") || !strcmp(buf, "\n")) { if (svc->notify == SVC_NOTIFY_SYSTEMD) {
char *token = strtok(buf, "\n");
while (token) {
if (!strcmp(token, "READY=1")) {
ready = 1;
break;
}
token = strtok(NULL, "\n");
}
} else if (svc->notify == SVC_NOTIFY_S6 && buf[len - 1] == '\n') {
ready = 1;
}
if (ready) {
/* /*
* native (pidfile) services are marked as started by * native (pidfile) services are marked as started by
* the pidfile plugin. * the pidfile plugin.
@@ -2963,7 +3096,7 @@ static void service_interval_cb(uev_t *w, void *arg, int events)
} }
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc_is_daemon(svc) || svc_is_sysv(svc)) { if (svc_is_daemon(svc)) {
char *restart_cnt = (char *)&svc->restart_cnt; char *restart_cnt = (char *)&svc->restart_cnt;
if (!svc_is_running(svc)) if (!svc_is_running(svc))
-2
View File
@@ -27,12 +27,10 @@
#include "svc.h" #include "svc.h"
void service_runlevel (int newlevel);
int service_register (int type, char *line, struct rlimit rlimit[], char *file); int service_register (int type, char *line, struct rlimit rlimit[], char *file);
void service_unregister (svc_t *svc); void service_unregister (svc_t *svc);
void service_runtask_clean (void); void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_update_rdeps (void); void service_update_rdeps (void);
void service_mark_unavail (void); void service_mark_unavail (void);
+27 -3
View File
@@ -94,6 +94,7 @@
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "service.h" #include "service.h"
#include "sm.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
@@ -260,6 +261,7 @@ static int status_cb(int pid, void *data)
{ {
int *has_proc = (int *)data; int *has_proc = (int *)data;
(void)pid;
*has_proc = 1; *has_proc = 1;
return 1; return 1;
@@ -425,6 +427,8 @@ void do_shutdown(shutop_t op)
*/ */
static void sighup_cb(uev_t *w, void *arg, int events) static void sighup_cb(uev_t *w, void *arg, int events)
{ {
(void)arg;
dbg("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
@@ -441,7 +445,7 @@ static void sighup_cb(uev_t *w, void *arg, int events)
api_init(w->ctx); api_init(w->ctx);
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */ /* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
service_reload_dynamic(); sm_reload();
} }
/* /*
@@ -451,6 +455,9 @@ static void sighup_cb(uev_t *w, void *arg, int events)
*/ */
static void sigint_cb(uev_t *w, void *arg, int events) static void sigint_cb(uev_t *w, void *arg, int events)
{ {
(void)w;
(void)arg;
dbg("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
@@ -467,6 +474,9 @@ static void sigint_cb(uev_t *w, void *arg, int events)
*/ */
static void sigpwr_cb(uev_t *w, void *arg, int events) static void sigpwr_cb(uev_t *w, void *arg, int events)
{ {
(void)w;
(void)arg;
dbg("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
@@ -481,6 +491,8 @@ static void sigpwr_cb(uev_t *w, void *arg, int events)
*/ */
static void sigusr1_cb(uev_t *w, void *arg, int events) static void sigusr1_cb(uev_t *w, void *arg, int events)
{ {
(void)arg;
dbg("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
@@ -502,6 +514,9 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
*/ */
static void sigusr2_cb(uev_t *w, void *arg, int events) static void sigusr2_cb(uev_t *w, void *arg, int events)
{ {
(void)w;
(void)arg;
dbg("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
@@ -514,7 +529,7 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
} }
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); sm_runlevel(0);
} }
/* /*
@@ -522,6 +537,9 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
*/ */
static void sigterm_cb(uev_t *w, void *arg, int events) static void sigterm_cb(uev_t *w, void *arg, int events)
{ {
(void)w;
(void)arg;
dbg("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
@@ -534,7 +552,7 @@ static void sigterm_cb(uev_t *w, void *arg, int events)
} }
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); sm_runlevel(6);
} }
/* /*
@@ -545,6 +563,9 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
int status; int status;
pid_t pid; pid_t pid;
(void)w;
(void)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
errx(1, "Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return; return;
@@ -601,6 +622,9 @@ const char *sig_name(int signo)
*/ */
static void chld_handler(int sig, siginfo_t *info, void *ctx) static void chld_handler(int sig, siginfo_t *info, void *ctx)
{ {
(void)sig;
(void)info;
(void)ctx;
/* NOP */ /* NOP */
} }
+108 -62
View File
@@ -29,6 +29,7 @@
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "conf.h" #include "conf.h"
#include "log.h"
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "schedule.h" #include "schedule.h"
@@ -38,11 +39,25 @@
#include "sm.h" #include "sm.h"
#include "utmp-api.h" #include "utmp-api.h"
sm_t sm; typedef enum {
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
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 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 processes to halt */
SM_RELOAD_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
} sm_state_t;
static struct {
sm_state_t state; /* Running, Changed, Waiting, ... */
int newlevel; /* Set to new runlevel on change, -1 otherwise */
int reload; /* Set on reload event, else 0 */
int in_reload; /* Set when waiting for all processes to be halted */
} sm;
#ifndef FINIT_NOLOGIN_PATH
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
#endif
/* /*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be * Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
@@ -90,7 +105,7 @@ static void sm_check_bootstrap(void *work)
} else } else
dbg("Change to default runlevel(%d), starting all services ...", cfglevel); dbg("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(level); sm_runlevel(level);
/* Clean up bootstrap-only tasks/services that never started */ /* Clean up bootstrap-only tasks/services that never started */
dbg("Clean up all bootstrap-only tasks/services ..."); dbg("Clean up all bootstrap-only tasks/services ...");
@@ -135,7 +150,7 @@ static char *sm_status(sm_state_t state)
return "unknown"; return "unknown";
} }
static char sm_runlevel(int lvl) static char sm_rl2ch(int lvl)
{ {
if (lvl == INIT_LEVEL) if (lvl == INIT_LEVEL)
return 'S'; return 'S';
@@ -158,60 +173,39 @@ static void nologin(void)
erase(FINIT_NOLOGIN_PATH); erase(FINIT_NOLOGIN_PATH);
} }
void sm_init(sm_t *sm) void sm_init(void)
{ {
static struct wq work = { static struct wq work = {
.cb = sm_check_bootstrap, .cb = sm_check_bootstrap,
.delay = 1000 .delay = 1000
}; };
sm->state = SM_BOOTSTRAP_STATE; sm.state = SM_BOOTSTRAP_STATE;
sm->newlevel = -1; sm.newlevel = -1;
sm->reload = 0; sm.reload = 0;
sm->in_teardown = 0; sm.in_reload = 0;
dbg("Starting bootstrap finalize timer ..."); dbg("Starting bootstrap finalize timer ...");
schedule_work(&work); schedule_work(&work);
} }
void sm_set_runlevel(sm_t *sm, int newlevel) void sm_step(void)
{
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)
{
return sm->in_teardown;
}
void sm_step(sm_t *sm)
{ {
sm_state_t old_state; sm_state_t old_state;
svc_t *svc; svc_t *svc;
restart: restart:
old_state = sm->state; old_state = sm.state;
dbg("state: %s, runlevel: %c, newlevel: %d, teardown: %d, reload: %d", dbg("state: %s, runlevel: %c, newlevel: %d, teardown: %d, reload: %d",
sm_status(sm->state), sm_runlevel(runlevel), sm->newlevel, sm->in_teardown, sm->reload); sm_status(sm.state), sm_rl2ch(runlevel), sm.newlevel, sm.in_reload, sm.reload);
switch (sm->state) { switch (sm.state) {
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
dbg("Bootstrapping all services in runlevel S from %s", finit_conf); dbg("Bootstrapping all services in runlevel S from %s", finit_conf);
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV); service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV);
sm->state = SM_BOOTSTRAP_WAIT_STATE; sm.state = SM_BOOTSTRAP_WAIT_STATE;
break; break;
/* /*
@@ -232,7 +226,7 @@ restart:
/* Allow runparts to start */ /* Allow runparts to start */
cond_set_oneshot("int/bootstrap"); cond_set_oneshot("int/bootstrap");
if (sm->newlevel == -1) if (sm.newlevel == -1)
break; break;
/* Hooks that should run at the very end */ /* Hooks that should run at the very end */
@@ -246,7 +240,7 @@ restart:
/* System bootstrapped, launch TTYs et al */ /* System bootstrapped, launch TTYs et al */
bootstrap = 0; bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN); service_step_all(SVC_TYPE_RESPAWN);
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
case SM_RUNNING_STATE: case SM_RUNNING_STATE:
@@ -254,26 +248,26 @@ restart:
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
/* runlevel changed? */ /* runlevel changed? */
if (sm->newlevel >= 0 && sm->newlevel <= 9) { if (sm.newlevel >= 0 && sm.newlevel <= 9) {
if (runlevel == sm->newlevel) { if (runlevel == sm.newlevel) {
sm->newlevel = -1; sm.newlevel = -1;
break; break;
} }
sm->state = SM_RUNLEVEL_CHANGE_STATE; sm.state = SM_RUNLEVEL_CHANGE_STATE;
break; break;
} }
/* reload ? */ /* reload ? */
if (sm->reload) { if (sm.reload) {
sm->reload = 0; sm.reload = 0;
sm->state = SM_RELOAD_CHANGE_STATE; sm.state = SM_RELOAD_CHANGE_STATE;
} }
break; break;
case SM_RUNLEVEL_CHANGE_STATE: case SM_RUNLEVEL_CHANGE_STATE:
prevlevel = runlevel; prevlevel = runlevel;
runlevel = sm->newlevel; runlevel = sm.newlevel;
sm->newlevel = -1; sm.newlevel = -1;
/* Restore terse mode and run hooks before shutdown */ /* Restore terse mode and run hooks before shutdown */
if (runlevel == 0 || runlevel == 6) { if (runlevel == 0 || runlevel == 6) {
@@ -282,11 +276,11 @@ restart:
plugin_run_hooks(HOOK_SHUTDOWN); plugin_run_hooks(HOOK_SHUTDOWN);
} }
dbg("Setting new runlevel --> %c <-- previous %c", sm_runlevel(runlevel), sm_runlevel(prevlevel)); dbg("Setting new runlevel --> %c <-- previous %c", sm_rl2ch(runlevel), sm_rl2ch(prevlevel));
if (osheading) if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %c", osheading, sm_runlevel(runlevel)); logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %c", osheading, sm_rl2ch(runlevel));
else else
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %c", sm_runlevel(runlevel)); logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %c", sm_rl2ch(runlevel));
runlevel_set(prevlevel, runlevel); runlevel_set(prevlevel, runlevel);
/* Disable login in single-user mode as well as shutdown/reboot */ /* Disable login in single-user mode as well as shutdown/reboot */
@@ -302,10 +296,10 @@ restart:
service_runtask_clean(); service_runtask_clean();
dbg("Stopping services not allowed in new runlevel ..."); dbg("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1; sm.in_reload = 1;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_WAIT_STATE; sm.state = SM_RUNLEVEL_WAIT_STATE;
break; break;
case SM_RUNLEVEL_WAIT_STATE: case SM_RUNLEVEL_WAIT_STATE:
@@ -324,10 +318,10 @@ restart:
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
dbg("Starting services new to this runlevel ..."); dbg("Starting services new to this runlevel ...");
sm->in_teardown = 0; sm.in_reload = 0;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_CLEAN_STATE; sm.state = SM_RUNLEVEL_CLEAN_STATE;
break; break;
case SM_RUNLEVEL_CLEAN_STATE: case SM_RUNLEVEL_CLEAN_STATE:
@@ -361,7 +355,7 @@ restart:
if (runlevel == 0 || runlevel == 6) if (runlevel == 0 || runlevel == 6)
do_shutdown(halt); do_shutdown(halt);
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
case SM_RELOAD_CHANGE_STATE: case SM_RELOAD_CHANGE_STATE:
@@ -373,11 +367,11 @@ restart:
* let all affected services move to WAITING/HALTED * let all affected services move to WAITING/HALTED
*/ */
dbg("Stopping services not allowed after reconf ..."); dbg("Stopping services not allowed after reconf ...");
sm->in_teardown = 1; sm.in_reload = 1;
cond_reload(); cond_reload();
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_WAIT_STATE; sm.state = SM_RELOAD_WAIT_STATE;
break; break;
case SM_RELOAD_WAIT_STATE: case SM_RELOAD_WAIT_STATE:
@@ -391,12 +385,12 @@ restart:
break; break;
} }
sm->in_teardown = 0; sm.in_reload = 0;
dbg("Starting services after reconf ..."); dbg("Starting services after reconf ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_CLEAN_STATE; sm.state = SM_RELOAD_CLEAN_STATE;
break; break;
case SM_RELOAD_CLEAN_STATE: case SM_RELOAD_CLEAN_STATE:
@@ -422,14 +416,66 @@ restart:
service_notify_reconf(); service_notify_reconf();
dbg("Reconfiguration done"); dbg("Reconfiguration done");
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
} }
if (sm->state != old_state) if (sm.state != old_state)
goto restart; goto restart;
} }
/**
* sm_in_reload - System currently in reload or runlevel change?
*
* Returns:
* %TRUE(1) or %FALSE(0)
*/
int sm_in_reload(void)
{
return sm.in_reload;
}
/**
* sm_reload - Called on SIGHUP, 'init q' or 'initctl reload'
*
* This function is called when Finit has received SIGHUP to reload
* .conf files in /etc/finit.d. It is responsible for starting,
* stopping and reloading (forwarding SIGHUP) to processes affected.
*/
void sm_reload(void)
{
sm.reload = 1;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
sm_step();
}
/**
* sm_runlevel - Change to a new runlevel
* @newlevel: New runlevel to activate
*
* Stops all services not in @newlevel and starts, or lets continue to run,
* those in @newlevel. Also updates @prevlevel and active @runlevel.
*/
void sm_runlevel(int newlevel)
{
if (!rescue && (runlevel == 1 || runlevel == INIT_LEVEL) && !IS_RESERVED_RUNLEVEL(newlevel))
networking(1);
sm.newlevel = newlevel;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
sm_step();
if (!rescue && IS_RESERVED_RUNLEVEL(runlevel))
networking(0);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+5 -25
View File
@@ -24,32 +24,12 @@
#ifndef FINIT_SM_H_ #ifndef FINIT_SM_H_
#define FINIT_SM_H_ #define FINIT_SM_H_
typedef enum { void sm_init (void);
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */ void sm_step (void);
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 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 processes to halt */
SM_RELOAD_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
} sm_state_t;
typedef struct sm { int sm_in_reload (void);
sm_state_t state; /* Running, Changed, Waiting, ... */ void sm_reload (void);
int newlevel; /* Set on runlevel change to new runlevel, -1 if not change */ void sm_runlevel (int newlevel);
int reload; /* Set on reload event, else 0 */
int in_teardown; /* Set when waiting for all processes to be halted */
} sm_t;
extern sm_t sm;
void sm_init(sm_t *sm);
void sm_step(sm_t *sm);
void sm_set_runlevel(sm_t *sm, int newlevel);
void sm_set_reload(sm_t *sm);
int sm_is_in_teardown(sm_t *sm);
#endif /* FINIT_SM_H_ */ #endif /* FINIT_SM_H_ */
+1
View File
@@ -37,6 +37,7 @@
#endif #endif
#include "finit.h" #include "finit.h"
#include "conf.h"
#include "svc.h" #include "svc.h"
#include "helpers.h" #include "helpers.h"
#include "pid.h" #include "pid.h"
+10 -4
View File
@@ -52,6 +52,7 @@ typedef enum {
} svc_type_t; } svc_type_t;
#define SVC_TYPE_ANY (-1) #define SVC_TYPE_ANY (-1)
#define SVC_TYPE_DAEMON (SVC_TYPE_SERVICE | SVC_TYPE_SYSV)
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_SYSV | SVC_TYPE_TTY) #define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_SYSV | SVC_TYPE_TTY)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK) #define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK)
@@ -85,7 +86,7 @@ typedef enum {
SVC_ONCRASH_SCRIPT, SVC_ONCRASH_SCRIPT,
} svc_oncrash_action_t; } svc_oncrash_action_t;
/* 0: none, 1: systemd, 2: s6 */ /* 0: none, 1: finit (native), 2: systemd, 3: s6 */
typedef enum { typedef enum {
SVC_NOTIFY_NONE = 0, SVC_NOTIFY_NONE = 0,
SVC_NOTIFY_PID, SVC_NOTIFY_PID,
@@ -211,6 +212,12 @@ typedef struct svc {
char cleanup_script[MAX_CMD_LEN]; char cleanup_script[MAX_CMD_LEN];
int cleanup_tmo; int cleanup_tmo;
/* When set, used instead of SIGHUP or stop-start */
char reload_script[MAX_CMD_LEN];
/* When set, used instead of SIGTERM or sysv 'stop' */
char stop_script[MAX_CMD_LEN];
/* /*
* Used to forcefully kill services that won't shutdown on * Used to forcefully kill services that won't shutdown on
* termination and to delay restarts of crashing services. * termination and to delay restarts of crashing services.
@@ -265,16 +272,15 @@ int svc_ifthen (int is_conf, const char *ident, char *stmt,
int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *)); int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; } static inline int svc_is_daemon (svc_t *svc) { return svc && (SVC_TYPE_DAEMON & svc->type); }
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; } static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == svc->type; } static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);} static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);}
static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; } static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; }
static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; } static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; }
static inline int svc_is_nohup (svc_t *svc) { return svc && (0 == svc->sighup); } static inline int svc_is_noreload (svc_t *svc) { return svc && (0 == svc->sighup && 0 == svc->reload_script[0]); }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; } static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; } static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; } static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
+299 -174
View File
@@ -23,12 +23,47 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <dirent.h>
#include <ftw.h>
#include <getopt.h>
#include <glob.h>
#include <libgen.h>
#include <grp.h>
#include <pwd.h>
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#include "finit.h"
#include "helpers.h" #include <stdlib.h>
#include "tmpfiles.h" #include <string.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "log.h"
#include "util.h" #include "util.h"
int debug;
int c_flag = 0;
int r_flag = 0;
static int is_dir_empty(const char *path)
{
struct dirent **namelist;
int num;
num = scandir(path, &namelist, NULL, NULL);
if (num < 0)
return 0;
for (int i = 0; i < num; i++)
free(namelist[i]);
free(namelist);
return num >= 3;
}
static int glob_do(const char *path, int (*cb)(const char *)) static int glob_do(const char *path, int (*cb)(const char *))
{ {
int rc = 0; int rc = 0;
@@ -75,6 +110,9 @@ static int parse_mm(char *arg, int *major, int *minor)
static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw) static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{ {
(void)sb;
(void)tflag;
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
@@ -279,182 +317,214 @@ static void tmpfiles(char *line)
strc = stat(path, &st); strc = stat(path, &st);
switch (type[0]) { // file and directory removal logic
case 'b': if (r_flag) {
rc = parse_mm(arg, &major, &minor); switch (type[0]) {
if (rc) case 'b':
case 'c':
case 'C':
case 'd':
break; break;
if (!strc) { case 'D':
if (type[1] != '+') if (fisdir(path)) {
break; nftw(path, do_delete, 20, FTW_DEPTH | FTW_PHYS);
erase(path);
}
mkparent(path, 0755);
rc = blkdev(path, mode ?: 0644, major, minor);
break;
case 'c':
rc = parse_mm(arg, &major, &minor);
if (rc)
break;
if (!strc) {
if (type[1] != '+')
break;
erase(path);
}
mkparent(path, 0755);
rc = chardev(path, mode ?: 0644, major, minor);
break;
case 'C':
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
arg = buf;
}
if (!strc) {
struct dirent **namelist;
int num;
if (!S_ISDIR(st.st_mode))
break;
num = scandir(path, &namelist, NULL, NULL);
free(namelist);
if (num >= 3)
break; /* not empty */
}
if (fisdir(arg) && !fisslashdir(arg)) {
size_t len = strlen(arg) + 2;
dst = malloc(len);
if (!dst)
break;
snprintf(dst, len, "%s/", arg);
arg = dst;
}
mkparent(path, 0755);
rc = rsync(arg, path, LITE_FOPT_KEEP_MTIME, NULL);
if (rc && errno == ENOENT)
rc = 0;
break;
case 'd':
case 'D':
mkparent(path, 0755);
rc = mksubsys(path, mode ?: 0755, user, group);
break;
case 'e':
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
break;
for (size_t i = 0; i < gl.gl_pathc; i++)
rc += mksubsys(gl.gl_pathv[i], mode ?: 0755, user, group);
break;
case 'f':
case 'F':
mkparent(path, 0755);
if (type[1] == '+' || type[0] == 'F') {
/* f+/F will create or truncate the file */
if (!arg) {
rc = create(path, mode ?: 0644, user, group);
break;
} }
fp = fopen(path, "w+");
} else {
/* f will create the file if it doesn't exist */
if (strc)
fp = fopen(path, "w");
}
if (fp) {
write_arg(fp, arg);
rc = fclose(fp);
}
break;
case 'l': /* Finit extension, like 'L' but only if target exists */
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
if (stat(buf, &ast))
break;
} else if (arg[0] != '/') {
char *tmp;
tmp = dirname(strdupa(path));
paste(buf, sizeof(buf), tmp, arg);
dst = realpath(buf, NULL);
if (!dst)
break;
if (stat(dst, &ast))
break;
} else {
if (stat(arg, &ast))
break;
}
/* fallthrough */
case 'L':
if (!strc) {
if (type[1] != '+')
break;
rmrf(path);
}
mkparent(path, 0755);
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
arg = buf;
}
rc = ln(arg, path);
if (rc && errno == EEXIST)
rc = 0;
break;
case 'p':
if (!strc) {
if (type[1] != '+')
break;
erase(path);
}
mkparent(path, 0755);
rc = mkfifo(path, mode ?: 0644);
break;
case 'r':
rc = glob_do(path, erase);
if (rc && errno == ENOENT)
rc = 0;
break;
case 'R':
rc = glob_do(path, rmrf);
break;
case 'w':
if (!arg)
break; break;
case 'e':
if (glob(path, GLOB_NOESCAPE, NULL, &gl)) case 'f':
case 'F':
case 'l': /* Finit extension, like 'L' but only if target exists */
case 'L':
case 'p':
break; break;
case 'r':
rc = glob_do(path, erase);
if (rc && errno == ENOENT)
rc = 0;
break;
case 'R':
rc = glob_do(path, rmrf);
break;
case 'w':
break;
case 'X':
case 'x':
dbg("Unsupported x/X command, ignoring %s, no support for clean at runtime.", path);
break;
case 'Z':
case 'z':
break;
default:
errx(1, "Unsupported tmpfiles command '%s'", type);
return;
}
}
// file & directory creation logic
if (c_flag) {
switch (type[0]) {
case 'b':
rc = parse_mm(arg, &major, &minor);
if (rc)
break;
if (!strc) {
if (type[1] != '+')
break;
erase(path);
}
mkparent(path, 0755);
rc = blkdev(path, mode ?: 0644, major, minor);
break;
case 'c':
rc = parse_mm(arg, &major, &minor);
if (rc)
break;
if (!strc) {
if (type[1] != '+')
break;
erase(path);
}
mkparent(path, 0755);
rc = chardev(path, mode ?: 0644, major, minor);
break;
case 'C':
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
arg = buf;
}
if (fisdir(path) && !is_dir_empty(path))
break;
if (fisdir(arg) && !fisslashdir(arg)) {
size_t len = strlen(arg) + 2;
dst = malloc(len);
if (!dst)
break;
snprintf(dst, len, "%s/", arg);
arg = dst;
}
mkparent(path, 0755);
rc = rsync(arg, path, LITE_FOPT_KEEP_MTIME, NULL);
if (rc && errno == ENOENT)
rc = 0;
break;
case 'd':
case 'D':
mkparent(path, 0755);
rc = mksubsys(path, mode ?: 0755, user, group);
break;
case 'e':
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
break;
for (size_t i = 0; i < gl.gl_pathc; i++)
rc += mksubsys(gl.gl_pathv[i], mode ?: 0755, user, group);
break;
case 'f':
case 'F':
mkparent(path, 0755);
if (type[1] == '+' || type[0] == 'F') {
/* f+/F will create or truncate the file */
if (!arg) {
rc = create(path, mode ?: 0644, user, group);
break;
}
fp = fopen(path, "w+");
} else {
/* f will create the file if it doesn't exist */
if (strc)
fp = fopen(path, "w");
}
for (size_t i = 0; i < gl.gl_pathc; i++) {
fp = fopen(gl.gl_pathv[i], type[1] == '+' ? "a" : "w");
if (fp) { if (fp) {
write_arg(fp, arg); write_arg(fp, arg);
rc = fclose(fp); rc = fclose(fp);
} }
}
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 */
case 'z':
if (!whichp("restorecon"))
break;
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
break; break;
case 'l': /* Finit extension, like 'L' but only if target exists */
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
if (stat(buf, &ast))
break;
} else if (arg[0] != '/') {
char *tmp;
for (size_t i = 0; i < gl.gl_pathc; i++) { tmp = dirname(strdupa(path));
snprintf(buf, sizeof(buf), "restorecon %s %s", opts, gl.gl_pathv[i]); paste(buf, sizeof(buf), tmp, arg);
run(buf, "restorecon"); dst = realpath(buf, NULL);
if (!dst)
break;
if (stat(dst, &ast))
break;
} else {
if (stat(arg, &ast))
break;
}
/* fallthrough */
case 'L':
if (!strc) {
if (type[1] != '+')
break;
rmrf(path);
}
mkparent(path, 0755);
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
arg = buf;
}
rc = ln(arg, path);
if (rc && errno == EEXIST)
rc = 0;
break;
case 'p':
if (!strc) {
if (type[1] != '+')
break;
erase(path);
}
mkparent(path, 0755);
rc = mkfifo(path, mode ?: 0644);
break;
case 'r':
case 'R':
break;
case 'w':
if (!arg)
break;
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
break;
for (size_t i = 0; i < gl.gl_pathc; i++) {
fp = fopen(gl.gl_pathv[i], type[1] == '+' ? "a" : "w");
if (fp) {
write_arg(fp, arg);
rc = fclose(fp);
}
}
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 */
case 'z':
if (!whichp("restorecon"))
break;
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
break;
for (size_t i = 0; i < gl.gl_pathc; i++) {
snprintf(buf, sizeof(buf), "restorecon %s %s", opts, gl.gl_pathv[i]);
system(buf);
}
break;
default:
errx(1, "Unsupported tmpfiles command '%s'", type);
return;
} }
break;
default:
errx(1, "Unsupported tmpfiles command '%s'", type);
return;
} }
if (dst) if (dst)
@@ -464,14 +534,68 @@ static void tmpfiles(char *line)
warn("Failed %s operation on path %s", type, path); warn("Failed %s operation on path %s", type, path);
} }
/* static int usage(int rc)
* Only the three last tmpfiles.d/ directories are defined in
* tmpfiles.d(5) as system search paths. Finit adds two more
* before that to have Finit specific ones sorted first, and
* a configure prefix specific one after that for user needs.
*/
void tmpfilesd(void)
{ {
fprintf(stderr,
"Usage: tmpfiles [COMMAND...]\n"
"\n"
"Commands:\n"
" -c, --create Create files and directories\n"
" -d, --debug Show developer debug messages\n"
" -r, --remove Remove files and directories marked for removal\n"
" -h, --help This help text\n"
"\n");
return rc;
}
int main(int argc, char *argv[])
{
struct option long_options[] = {
{ "create", 0, NULL, 'c' },
{ "debug", 0, NULL, 'd' },
{ "remove", 0, NULL, 'r' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
int c;
while ((c = getopt_long(argc, argv, "cdrh?", long_options, NULL)) != EOF) {
switch(c) {
case 'c':
c_flag = 1;
break;
case 'd':
debug = 1;
break;
case 'r':
r_flag = 1;
break;
case 'h':
case '?':
return usage(0);
default:
return usage(1);
}
}
if (c_flag + r_flag == 0) {
fprintf(stderr, "You need to specify at least one of --create or --remove.\n");
return 1;
}
/*
* Only the three last tmpfiles.d/ directories are defined in
* tmpfiles.d(5) as system search paths. Finit adds two more
* before that to have Finit specific ones sorted first, and
* a configure prefix specific one after that for user needs.
*/
/* in priority order */ /* in priority order */
char *dir[] = { char *dir[] = {
FINIT_TMPFILES "/*.conf", FINIT_TMPFILES "/*.conf",
@@ -518,6 +642,7 @@ void tmpfilesd(void)
continue; continue;
tmpfiles(line); tmpfiles(line);
free(line);
} }
fclose(fp); fclose(fp);
-42
View File
@@ -1,42 +0,0 @@
/* Limited tmpfiles.d implementation
*
* Copyright (c) 2024 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TMPFILES_H_
#define TMPFILES_H_
#include <dirent.h>
#include <ftw.h>
#include <glob.h>
#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
void tmpfilesd(void);
#endif /* TMPFILES_H_ */
+1
View File
@@ -38,6 +38,7 @@
#endif #endif
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include "log.h"
#include "finit.h" #include "finit.h"
#include "conf.h" #include "conf.h"
#include "helpers.h" #include "helpers.h"
+94
View File
@@ -25,6 +25,8 @@
#include <ctype.h> /* isprint() */ #include <ctype.h> /* isprint() */
#include <errno.h> #include <errno.h>
#include <grp.h>
#include <pwd.h>
#ifdef HAVE_MNTENT_H #ifdef HAVE_MNTENT_H
#include <mntent.h> #include <mntent.h>
#endif #endif
@@ -178,6 +180,98 @@ char *str(char *fmt, ...)
return buf; return buf;
} }
int getuser(char *username, char **home)
{
#ifdef ENABLE_STATIC
if (home)
*home = "/"; /* XXX: Fixme */
return fgetint("/etc/passwd", "x:\n", username);
#else
struct passwd *usr;
if (!username || (usr = getpwnam(username)) == NULL)
return -1;
if (home)
*home = usr->pw_dir;
return usr->pw_uid;
#endif
}
int getgroup(char *group)
{
#ifdef ENABLE_STATIC
return fgetint("/etc/group", "x:\n", group);
#else
struct group *grp;
if ((grp = getgrnam(group)) == NULL)
return -1;
return grp->gr_gid;
#endif
}
/* get current user */
int getcuser(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct passwd *pw = getpwuid(getuid());
if (pw)
ptr = pw->pw_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
/* get current group */
int getcgroup(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct group *gr = getgrgid(getgid());
if (gr)
ptr = gr->gr_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
int uid, gid;
int rc = 0;
omask = umask(0);
uid = getuser(user, NULL);
if (uid >= 0) {
gid = getgroup(group);
if (gid < 0)
gid = 0;
rc = makedir(dir, mode);
if (rc && errno == EEXIST)
rc = chmod(dir, mode);
if (chown(dir, uid, gid))
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
}
umask(omask);
return rc;
}
int fnread(char *buf, size_t len, char *fmt, ...) int fnread(char *buf, size_t len, char *fmt, ...)
{ {
char path[256]; char path[256];
+117
View File
@@ -25,6 +25,8 @@
#define FINIT_UTIL_H_ #define FINIT_UTIL_H_
#include "config.h" #include "config.h"
#include <ctype.h>
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
#include <poll.h> #include <poll.h>
#include <stdio.h> #include <stdio.h>
@@ -53,11 +55,21 @@ extern char *prognm;
# include <lite/conio.h> # include <lite/conio.h>
# include <lite/lite.h> # include <lite/lite.h>
#endif #endif
#include "log.h"
char *progname (char *arg0); char *progname (char *arg0);
const char *basenm (const char *path); const char *basenm (const char *path);
char *str (char *fmt, ...) __attribute__ ((format (printf, 1, 2))); char *str (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
int getuser (char *username, char **home);
int getgroup (char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4))); int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
int fnwrite (char *value, char *fmt, ...) __attribute__ ((format (printf, 2, 3))); int fnwrite (char *value, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
int fngetint (char *path, int *val); int fngetint (char *path, int *val);
@@ -92,6 +104,57 @@ int ttcooked (void);
#define ttcooked() 0 #define ttcooked() 0
#endif #endif
/*
* Defaults to user "root" and group "wheel" (root) if:
* 1) the user is missing
* 2) the group or the file /etc/group is missing
*/
static inline int create(char *path, mode_t mode, char *user, char *group)
{
int fd, uid, gid;
if (!path) {
errno = EINVAL;
return -1;
}
uid = getuser(user, NULL);
if (uid < 0)
uid = 0;
gid = getgroup(group);
if (gid < 0)
gid = 0;
fd = creat(path, mode);
if (fd == -1) {
warn("Failed creating %s properly", path);
return -1;
}
close(fd);
if (chown(path, uid, gid)) {
warn("Failed chowning %s properly", path);
return -1;
}
return 0;
}
static inline int ln(const char *target, const char *linkpath)
{
if (!target || !linkpath) {
errno = EINVAL;
return -1;
}
if (symlink(target, linkpath)) {
if (errno != EEXIST)
warn("Failed creating %s -> %s symlink", target, linkpath);
return -1;
}
return 0;
}
static inline char *strterm(char *str, size_t len) static inline char *strterm(char *str, size_t len)
{ {
str[len - 1] = 0; str[len - 1] = 0;
@@ -130,6 +193,60 @@ static inline int suffix(char *path, size_t len, const char *sfx)
return 0; return 0;
} }
static inline void hexdump(const char *buf, size_t len)
{
size_t i, j;
for (i = 0; i < len; i += 16) {
printf("%08zx ", i);
for (j = 0; j < 8 && i + j < len; j++)
printf("%02x ", buf[i + j]);
if (j == 8) {
printf(" ");
} else {
for (; j < 8; j++)
printf(" ");
printf(" ");
}
for (j = 8; j < 16 && i + j < len; j++)
printf("%02x ", buf[i + j]);
for (; j < 16; j++)
printf(" ");
printf(" |");
for (j = 0; j < 16 && i + j < len; j++) {
char c = buf[i + j];
printf("%c", isprint(c) ? c : '.');
}
printf("|\n");
}
}
/* drop quotes from a string at point, if any */
static inline int unquote(char **str, char *end)
{
char *ptr = *str;
/* unquote value, if quoted */
if (ptr[0] == '"' || ptr[0] == '\'') {
char q = ptr[0];
if (!end && !(end = strchr(&ptr[1], q)))
return -1;
if (*end == q) {
*str = &ptr[1];
*end = 0;
}
}
return 0;
}
#endif /* FINIT_UTIL_H_ */ #endif /* FINIT_UTIL_H_ */
/** /**
@@ -15,8 +15,6 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)"
say "Add a dynamic service in $FINIT_RCSD/service.conf" say "Add a dynamic service in $FINIT_RCSD/service.conf"
run "echo 'service [2345] kill:20 log service.sh -- Dyn serv' > $FINIT_RCSD/service.conf" run "echo 'service [2345] kill:20 log service.sh -- Dyn serv' > $FINIT_RCSD/service.conf"
-2
View File
@@ -15,8 +15,6 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)"
say "Add a dynamic service in $FINIT_CONF" say "Add a dynamic service in $FINIT_CONF"
run "echo 'service [2345] kill:20 log service.sh -- Dyn service' > $FINIT_CONF" run "echo 'service [2345] kill:20 log service.sh -- Dyn service' > $FINIT_CONF"
-5
View File
@@ -11,11 +11,6 @@ BOOTSTRAP="run [S] crasher.sh -- Party crasher"
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-5
View File
@@ -7,11 +7,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-5
View File
@@ -5,11 +5,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-5
View File
@@ -14,11 +14,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-5
View File
@@ -7,11 +7,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-1
View File
@@ -8,7 +8,6 @@ TEST_DIR=$(dirname "$0")
test_setup() test_setup()
{ {
say "Test start $(date)"
run "mkdir -p /etc/default" run "mkdir -p /etc/default"
} }
-5
View File
@@ -8,11 +8,6 @@ set COLORTERM=yes\n\
b aq=oroszoe\n\ b aq=oroszoe\n\
baz=qux" baz=qux"
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-5
View File
@@ -7,11 +7,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
+2
View File
@@ -362,6 +362,8 @@ echo "Finit running as PID $finit_pid"
#tty=/dev/$(texec cat /sys/class/tty/console/active) #tty=/dev/$(texec cat /sys/class/tty/console/active)
#texec cat "$tty" & #texec cat "$tty" &
sep "Test start $(date)"
# Allow Finit to start up properly before launching the test # Allow Finit to start up properly before launching the test
i=0 i=0
set +u set +u
+1 -1
View File
@@ -41,7 +41,7 @@ export PATH
# Not supported by Busybox unshare: # Not supported by Busybox unshare:
# --cgroup --time # --cgroup --time
exec "$unshare" \ exec "$unshare" \
--user --map-root-user \ --user --map-root-user --map-auto \
--fork --pid --mount-proc \ --fork --pid --mount-proc \
--mount \ --mount \
--mount-proc \ --mount-proc \
+9 -1
View File
@@ -8,7 +8,6 @@ TEST_DIR=$(dirname "$0")
test_setup() test_setup()
{ {
say "Test start $(date)"
run "mkdir -p /etc/default" run "mkdir -p /etc/default"
} }
@@ -25,6 +24,15 @@ test_one()
type=$1 type=$1
service=$2 service=$2
# Skip systemd tests if serv doesn't have libsystemd support
if [ "$type" = "systemd" ]; then
if ! "$TEST_DIR/src/serv" -C | grep -q "libsystemd"; then
sep
say "Skipping systemd test - serv built without libsystemd support"
return 0
fi
fi
sep sep
# num=$(run "find /proc/1/fd |wc -l") # num=$(run "find /proc/1/fd |wc -l")
-5
View File
@@ -5,11 +5,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-5
View File
@@ -9,11 +9,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-1
View File
@@ -8,7 +8,6 @@ TEST_DIR=$(dirname "$0")
test_setup() test_setup()
{ {
say "Test start $(date)"
run "mkdir -p /etc/default" run "mkdir -p /etc/default"
} }
+5 -11
View File
@@ -1,26 +1,20 @@
#!/bin/sh #!/bin/sh
# Regression test for basic /etc/rc.local functionality # Regression test for basic /etc/rc.local functionality
# shellcheck disable=SC2317 # shellcheck disable=2317,2034
set -eu set -eu
RCLOCAL="echo 'KILROY WAS HERE' > /tmp/rclocal"
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
#DEBUG=1 #DEBUG=1
# shellcheck disable=SC2034
RCLOCAL="echo 'KILROY WAS HERE' > /tmp/rclocal"
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
} }
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
sleep 1 sleep 1
assert_file_contains /tmp/rclocal "KILROY WAS HERE" assert_file_contains /tmp/rclocal "KILROY WAS HERE"
-5
View File
@@ -4,11 +4,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-1
View File
@@ -6,7 +6,6 @@ TEST_DIR=$(dirname "$0")
test_setup() test_setup()
{ {
say "Test start $(date)"
run "mkdir -p /etc/default" run "mkdir -p /etc/default"
} }
-1
View File
@@ -9,7 +9,6 @@ TEST_DIR=$(dirname "$0")
test_setup() test_setup()
{ {
say "Test start $(date)"
run "mkdir -p /etc/default" run "mkdir -p /etc/default"
export TEST_TIMEOUT=5 export TEST_TIMEOUT=5
} }
+3 -8
View File
@@ -21,19 +21,14 @@ run [4] name:four initctl restart service.sh -- Restart service"
# shellcheck disable=SC2034 # shellcheck disable=SC2034
RCLOCAL="echo \"$0 ==============\" &1>2" RCLOCAL="echo \"$0 ==============\" &1>2"
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
} }
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
say "$FINIT_CONF:" say "$FINIT_CONF:"
run "cat $FINIT_CONF" run "cat $FINIT_CONF"
say "/etc/rc.local:" say "/etc/rc.local:"
+1 -7
View File
@@ -1,17 +1,11 @@
#!/bin/sh #!/bin/sh
# Verify selecting alternate runlevel from cmdline # Verify selecting alternate runlevel from cmdline
# shellcheck disable=SC2034
set -eu set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
# shellcheck disable=SC2034
FINIT_RUNLEVEL=9 FINIT_RUNLEVEL=9
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
+5 -11
View File
@@ -8,26 +8,20 @@
# - SNNfoo should be called with a 'start' argument # - SNNfoo should be called with a 'start' argument
# - KNNfoo should be called with a 'stop' argument # - KNNfoo should be called with a 'stop' argument
# #
# shellcheck disable=SC2034
BOOTSTRAP="runparts /etc/rc.d"
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
#DEBUG=1 #DEBUG=1
# shellcheck disable=SC2034
BOOTSTRAP="runparts /etc/rc.d"
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
} }
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
while true; do while true; do
lvl=$(texec initctl runlevel) lvl=$(texec initctl runlevel)
say "Current runlevel $lvl" say "Current runlevel $lvl"
-2
View File
@@ -16,8 +16,6 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)"
say 'Ensure file system is cleared' say 'Ensure file system is cleared'
run "rm -f /tmp/usr1.log" run "rm -f /tmp/usr1.log"
+35 -27
View File
@@ -6,46 +6,54 @@ PIDFILE="/var/run/service.pid"
start() start()
{ {
printf 'Starting %s: ' "$DAEMON" printf 'Starting %s: ' "$DAEMON"
start-stop-daemon -S -b -q -p "$PIDFILE" -x "$DAEMON" start-stop-daemon -S -b -q -p "$PIDFILE" -x "$DAEMON"
status=$? status=$?
if [ "$status" -eq 0 ]; then if [ "$status" -eq 0 ]; then
echo "OK" echo "OK"
else else
echo "FAIL" echo "FAIL"
fi fi
return "$status" return "$status"
} }
stop() stop()
{ {
printf 'Stopping %s: ' "$DAEMON" printf 'Stopping %s: ' "$DAEMON"
start-stop-daemon -K -q -p "$PIDFILE" start-stop-daemon -K -q -p "$PIDFILE"
status=$? status=$?
if [ "$status" -eq 0 ]; then if [ "$status" -eq 0 ]; then
echo "OK" echo "OK"
else else
echo "FAIL" echo "FAIL"
fi fi
return "$status" return "$status"
}
reload()
{
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
kill -HUP "$PID"
fi
} }
restart() restart()
{ {
stop stop
sleep 1 sleep 1
start start
} }
case "$1" in case "$1" in
start|stop|restart) start|stop|reload|restart)
"$1" "$1"
;; ;;
*) *)
echo "Usage: $0 {start|stop|restart}, got $0 $1" echo "Usage: $0 {start|stop|reload|restart}, got $0 $1"
exit 1 exit 1
esac esac
+15 -5
View File
@@ -2,23 +2,33 @@
cleanup() cleanup()
{ {
echo "Got signal, stopping ..." echo "Got signal, stopping ..."
rm -f /run/service.pid rm -f /run/service.pid
exit 0 exit 0
}
reload()
{
echo
echo "Ready."
echo
sleep 1
echo $$ > /run/service.pid
} }
# Hook SIGUSR1 and dump trace to file system # Hook SIGUSR1 and dump trace to file system
# shellcheck disable=SC2172 # shellcheck disable=SC2172
trap 'echo USR1 > /tmp/usr1.log' USR1 trap 'echo USR1 > /tmp/usr1.log' USR1
trap reload HUP
trap cleanup INT trap cleanup INT
trap cleanup TERM trap cleanup TERM
trap cleanup QUIT trap cleanup QUIT
trap cleanup EXIT trap cleanup EXIT
echo $$ > /run/service.pid reload
# sleep may exit on known signal, so # sleep may exit on known signal, so
# we cannot use 'set -e' # we cannot use 'set -e'
while true; do while true; do
sleep 1 sleep 1
done done
+1 -1
View File
@@ -1,5 +1,5 @@
/Makefile /Makefile
/Makefile.in /Makefile.in
/.libs/
/.deps/ /.deps/
/serv /serv
+5 -1
View File
@@ -1,4 +1,8 @@
noinst_PROGRAMS = serv noinst_PROGRAMS = serv
serv_SOURCES = serv.c serv_SOURCES = serv.c
serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE -I$(top_builddir)
if LIBSYSTEMD
serv_CPPFLAGS += -I$(top_srcdir)/libsystemd
serv_SOURCES += $(top_srcdir)/libsystemd/sd-daemon.c
endif
+47 -12
View File
@@ -5,6 +5,10 @@
* in foregrund it does not create a PID file by default. * in foregrund it does not create a PID file by default.
*/ */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <paths.h> #include <paths.h>
@@ -16,6 +20,9 @@
#include <sysexits.h> #include <sysexits.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef HAVE_LIBSYSTEMD
#include "sd-daemon.h"
#endif
#define PROGNM "serv" #define PROGNM "serv"
@@ -145,6 +152,16 @@ static void pidfile(char *pidfn)
} }
} }
static int capabilities(void)
{
#ifdef HAVE_LIBSYSTEMD
puts("libsystemd");
#endif
puts("s6");
return 0;
}
static int usage(int rc) static int usage(int rc)
{ {
FILE *fp = rc ? stderr : stdout; FILE *fp = rc ? stderr : stdout;
@@ -152,6 +169,7 @@ static int usage(int rc)
fprintf(fp, fprintf(fp,
"%s [-nhp] [-P FILE]\n" "%s [-nhp] [-P FILE]\n"
"\n" "\n"
" -C Show capabilities and exit\n"
" -c Crash (exit) immediately\n" " -c Crash (exit) immediately\n"
" -e K:V Verify K environment variable is V value\n" " -e K:V Verify K environment variable is V value\n"
" -E K Verify K is not set in the environment\n" " -E K Verify K is not set in the environment\n"
@@ -160,7 +178,7 @@ static int usage(int rc)
" -h Show help text (this)\n" " -h Show help text (this)\n"
" -i IDENT Change process identity, incl. logs, pidfile, etc.\n" " -i IDENT Change process identity, incl. logs, pidfile, etc.\n"
" -n Run in foreground\n" " -n Run in foreground\n"
" -N SOCK Send 'READY=1\\n' on $NOTIFY_SOCKET or SOCK\n" " -N SOCK Send '\\n' on SOCK (integer), for s6 readiness\n"
" -p Create PID file despite running in foreground\n" " -p Create PID file despite running in foreground\n"
" -P FILE Create PID file using FILE\n" " -P FILE Create PID file using FILE\n"
" -r SVC Call initctl to restart service SVC (self)\n" " -r SVC Call initctl to restart service SVC (self)\n"
@@ -170,6 +188,8 @@ static int usage(int rc)
"to let the rest of the system know it's done. When the program runs\n" "to let the rest of the system know it's done. When the program runs\n"
"in the foreground it does not create a PID file by default.\n" "in the foreground it does not create a PID file by default.\n"
"\n" "\n"
"When acting as a systemd daemon, this program expects NOTIFY_SOCKET\n"
"\n"
"Regardless of how This daemon is started it provides a single service\n" "Regardless of how This daemon is started it provides a single service\n"
"to others, spice ... guarded by sandworms.\n", "to others, spice ... guarded by sandworms.\n",
ident); ident);
@@ -192,11 +212,13 @@ int main(int argc, char *argv[])
char cmd[80]; char cmd[80];
int c; int c;
while ((c = getopt(argc, argv, "ce:E:f:F:hi:nN:pP:r:")) != EOF) { while ((c = getopt(argc, argv, "cCe:E:f:F:hi:nN:pP:r:")) != EOF) {
switch (c) { switch (c) {
case 'c': case 'c':
do_crash = 1; do_crash = 1;
break; break;
case 'C':
return capabilities();
case 'e': case 'e':
verify_env(optarg); verify_env(optarg);
break; break;
@@ -256,13 +278,13 @@ int main(int argc, char *argv[])
if (do_pidfile > 0) if (do_pidfile > 0)
pidfile(pidfn); pidfile(pidfn);
if (!do_notify) { if (!do_notify && getenv("NOTIFY_SOCKET"))
const char *sock = getenv("NOTIFY_SOCKET"); do_notify = 1;
/* systemd style */ if (do_notify)
if (sock) inf("Will notify %s ...", notify_s6 ? "s6" : "systemd");
do_notify = atoi(sock); else
} inf("No notify socket ...");
if (do_crash) { if (do_crash) {
inf("Simulating crash, exiting with code %d", EX_SOFTWARE); inf("Simulating crash, exiting with code %d", EX_SOFTWARE);
@@ -279,15 +301,28 @@ int main(int argc, char *argv[])
if (reloading) { if (reloading) {
if (do_notify > 0) { if (do_notify > 0) {
/* Issue #343: see notify.sh test for more details */
inf("Delaying notify by 3 seconds for notify.sh ..."); inf("Delaying notify by 3 seconds for notify.sh ...");
sleep(3); sleep(3);
inf("Notifying Finit on socket %d, READY=1", do_notify);
if (write(do_notify, "READY=1\n", 8) == -1)
err(1, "Failed sending ready notification to Finit");
if (notify_s6) { if (notify_s6) {
inf("Notifying Finit on socket %d", do_notify);
if (write(do_notify, "\n", 1) < 1)
err(1, "Failed sending ready notification to Finit");
inf("s6 notify, closing socket %d ...", do_notify); inf("s6 notify, closing socket %d ...", do_notify);
close(do_notify); if (close(do_notify))
err(1, "Failed closing notify socket");
do_notify = 0; do_notify = 0;
} else {
#ifdef HAVE_LIBSYSTEMD
int rc;
inf("Notifying Finit on NOTIFY_SOCKET");
rc = sd_notify(0, "READY=1");
inf("sd_notify () => %d", rc);
#else
inf("Skipping systemd notify - built without libsystemd support");
#endif
} }
} }
if (do_pidfile > 0) if (do_pidfile > 0)
-1
View File
@@ -17,7 +17,6 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)"
rm -f "$SYSROOT"/oldpid rm -f "$SYSROOT"/oldpid
say "Add service stanza in $FINIT_CONF" say "Add service stanza in $FINIT_CONF"
-1
View File
@@ -24,7 +24,6 @@ test_teardown()
say "Check deps ..." say "Check deps ..."
check_dep jq check_dep jq
say "Test start $(date)"
rm -f "$SYSROOT"/oldpid rm -f "$SYSROOT"/oldpid
say "Add service stanza in $FINIT_CONF" say "Add service stanza in $FINIT_CONF"
-5
View File
@@ -3,11 +3,6 @@ set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
}
test_teardown() test_teardown()
{ {
say "Test done $(date)" say "Test done $(date)"
-2
View File
@@ -15,8 +15,6 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)"
say "Add service stanza in $FINIT_RCSD/service.conf" say "Add service stanza in $FINIT_RCSD/service.conf"
run "echo 'service [2345] kill:20 log service.sh -- Subserv' > $FINIT_RCSD/service.conf" run "echo 'service [2345] kill:20 log service.sh -- Subserv' > $FINIT_RCSD/service.conf"
-2
View File
@@ -15,8 +15,6 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)"
say "Add service stanza in $FINIT_CONF" say "Add service stanza in $FINIT_CONF"
run "echo 'service [2345] kill:20 log service.sh -- Test service' > $FINIT_CONF" run "echo 'service [2345] kill:20 log service.sh -- Test service' > $FINIT_CONF"
+44 -22
View File
@@ -1,38 +1,60 @@
#!/bin/sh #!/bin/sh
# shellcheck disable=2034 source=/dev/null
set -eu set -eu
STANZA1="sysv [2345] pid:!/run/service.pid name:service.sh log:stdout \
/etc/init.d/S01-service.sh -- SysV test service"
STANZA2="sysv [2345] pid:!/run/service.pid name:service.sh log:stdout \
reload:'/etc/init.d/S01-service.sh reload' \
stop:'/etc/init.d/S01-service.sh stop' \
/etc/init.d/S01-service.sh -- SysV test service"
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
#DEBUG=1
test_teardown() test_teardown()
{ {
say "Test done $(date)" sep "Test done $(date)"
assert "Running test teardown." "run rm -f $FINIT_RCSD/service.conf"
}
say "Running test teardown." test_one()
run "rm -f $FINIT_CONF" {
say "Add sysv stanza #1 in $FINIT_CONF"
run "echo \"$1\" > $FINIT_RCSD/service.conf"
say "Reload Finit"
run "initctl reload"
retry 'assert_num_children 1 service.sh'
retry 'assert_cond service/service.sh/ready'
sep 'Stop the sysv service'
run "initctl stop service.sh"
retry 'assert_num_children 0 service.sh'
sep 'Start the sysv service again'
run "initctl start service.sh"
retry 'assert_num_children 1 service.sh'
retry 'assert_cond service/service.sh/ready'
sep 'Touch the sysv service'
run "initctl touch service.conf"
run "initctl reload"
#run "initctl cond dump"
retry 'assert_num_children 1 service.sh'
retry 'assert_cond service/service.sh/ready'
} }
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Test start $(date)" sep "―― 1) Basic sysv daemon"
test_one "$STANZA1"
say "Add sysv stanza in $FINIT_CONF" sep "―― 2) Custom reload/start script for sysv daemon"
run "echo 'sysv [2345] pid:!/run/service.pid name:service.sh /etc/init.d/S01-service.sh -- SysV test service' > $FINIT_CONF" test_one "$STANZA2"
say 'Reload Finit'
run "initctl reload"
retry 'assert_num_children 1 service.sh'
say 'Stop the sysv service'
run "initctl stop service.sh"
retry 'assert_num_children 0 service.sh'
say 'Start the sysv service again'
run "initctl start service.sh"
retry 'assert_num_children 1 service.sh'
return 0 return 0

Some files were not shown because too many files have changed in this diff Show More