Compare commits

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

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

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

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

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

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

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

Fixes #429

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

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

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

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

Found by Coverity Scan

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

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

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

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

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

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

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

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

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

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

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

Add cleanup:script support, runs at service removal

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

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 320 KiB

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

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 814 KiB

+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_ */
+39 -25
View File
@@ -175,13 +175,13 @@ task [s] echo "foo" | cat >/tmp/bar
.Ed .Ed
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description .It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
Similar to Similar to
.Cm task .Cm service
is the is the
.Cm sysv .Cm sysv
stanza, which can be used to call SysV style start/stop scripts. The stanza, which can be used to call SysV style start/stop scripts. The
primary intention for this command is to be able to re-use much of primary intention for this command is to be able to re-use traditional
existing setup and init scripts in Linux distributions. SysV init scripts in Linux distributions.
.Pp .Pp
When entering an allowed runlevel, Finit calls When entering an allowed runlevel, Finit calls
.Cm init-script start , .Cm init-script start ,
when entering a disallowed runlevel, Finit calls when entering a disallowed runlevel, Finit calls
@@ -434,30 +434,42 @@ e.g.,
.Cm kill:10 .Cm kill:10
to wait 10 seconds before sending SIGKILL. to wait 10 seconds before sending SIGKILL.
.Pp .Pp
Services support the Services, including the
.Cm pre:script .Cm sysv
and variant, support pre/post/ready and cleanup scripts:
.Cm post:script .Bl -tag -width 1n
command actions as well. These run as the same .It Cm pre:[0-3600,]script
called before the sysv/service is stated
.It Cm post:[0-3600,]script
called after the sysv/service has stopped
.It Cm ready:[0-3600,]script
called when the sysv/service is ready
.It Cm cleanup:[0-3600,]script
called when run/task/sysv/service is removed
.El
.Pp
The optional number (0-3600) is the timeout before Finit kills the
script, it defaults to the kill delay value and can be disabled by
setting it to zero. These scripts run as the same
.Cm @USER:GROUP .Cm @USER:GROUP
as the service itself, with any as the service itself, with any
.Cm env:file .Cm env:file
sourced. The scripts must use an absolute path, but are executed from sourced. The scripts must use an absolute path, but are executed from
the the
.Cm $HOME .Cm $HOME
of the given user. The scripts are not called with any argument of the given user. The scripts are not called with any argument, but
(currently), but both get the both get a set of environment variables:
.Cm SERVICE_IDENT=foo .Pp
environment variable set. Here .Bl -tag -width 1n -compact
.Cm foo .It Cm SERVICE_IDENT=foo:1
denotes the identity of the service, which if there are multiple .It Cm SERVICE_NAME=foo
services named .It Cm SERVICE_ID=1
.Cm foo , .El
may be .Pp
.Cm foo:1 , The
or any unique identifier specified in the .conf file. The
.Cm post:script .Cm post:script
is called with an additional set of environment variables: is called with an additional set of environment variables, and yes the
naming looks like it should be swapped:
.Bl -tag -offset indent -width 1n .Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal,crashed] .It Cm EXIT_CODE=[exited,signal,crashed]
set to one of set to one of
@@ -471,10 +483,12 @@ set to one of exit status code from the program, if it exited normally,
or the signal name (HUP, TERM, etc.) if it exited due to signal or the signal name (HUP, TERM, etc.) if it exited due to signal
.El .El
.Pp .Pp
The scripts have a default execution time of 3 seconds before they are When a run/task/sys/service is removed (disable + reload) it is first
SIGKILLed, this can be adjusted using the stopped and then removed from the runlevel. The
.Cm kill:SEC .Cm post:script
modifier syntax. always runs when the process has stopped, and the
.Cm cleanup:script
runs when the the stanza has been removed from the runlevel.
.It Cm runparts Aq DIR .It Cm runparts Aq DIR
Call Call
.Xr run-parts 8 .Xr run-parts 8
+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);
} }
+3 -3
View File
@@ -41,7 +41,7 @@
* *
* Please note, the :ID is there for your benefit, it ensures that tasks * Please note, the :ID is there for your benefit, it ensures that tasks
* in Finit are unique. If you have two tasks with the same name and ID * in Finit are unique. If you have two tasks with the same name and ID
* (or no ID), the last one read replaces any preceeding one! * (or no ID), the last one read replaces any preceding one!
*/ */
#include <unistd.h> #include <unistd.h>
@@ -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);
+9 -3
View File
@@ -31,6 +31,12 @@
#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
#define SYSCTL_PATH "/sbin/sysctl"
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
@@ -54,7 +60,7 @@ static void setup(void *arg)
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
char cmd[160]; char cmd[160];
snprintf(cmd, sizeof(cmd), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]); snprintf(cmd, sizeof(cmd), "%s -e -p %s >/dev/null", SYSCTL_PATH, gl.gl_pathv[i]);
run(cmd, "sysctl"); run(cmd, "sysctl");
} }
globfree(&gl); globfree(&gl);
@@ -70,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);
} }
+6 -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,8 +339,9 @@ PLUGIN_INIT(plugin_init)
plugin_register(&plugin); plugin_register(&plugin);
} }
PLUGIN_EXIT(plugin_exit) PLUGIN_EXIT(__exit)
{ {
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)
+28 -8
View File
@@ -26,6 +26,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */ #include <sys/time.h> /* gettimeofday() */
#include <sys/types.h> #include <sys/types.h>
#include <sys/resource.h> /* getrusage() */
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
#else #else
@@ -37,19 +38,35 @@
#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)
#endif #endif
#ifdef RANDOMSEED #ifdef RANDOMSEED
/*
* This is the fallback seed function, please make sure you have drivers
* enabling /dev/hwrng instead.
*/
static void fallback(FILE *fp) static void fallback(FILE *fp)
{ {
unsigned long seed;
struct timeval tv; struct timeval tv;
struct rusage ru;
int iter = 128; int iter = 128;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
srandom(tv.tv_sec % 3600); getrusage(RUSAGE_SELF, &ru);
/* Mix multiple sources of "randomness" */
seed = tv.tv_sec ^ (tv.tv_usec << 16);
seed ^= ru.ru_utime.tv_sec ^ (ru.ru_utime.tv_usec << 16);
seed ^= ru.ru_stime.tv_sec ^ (ru.ru_stime.tv_usec << 16);
seed ^= getpid() << 8;
srandom(seed);
while (iter--) { while (iter--) {
uint32_t i, prng = random(); uint32_t i, prng = random();
@@ -63,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;
@@ -88,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);
@@ -122,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;
@@ -188,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
+31 -23
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,6 +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) {
dbg("%s(): api socket %d invalid.", __func__, w->fd);
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");
@@ -414,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:
@@ -430,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:
@@ -548,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, "Falied 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;
@@ -578,8 +588,6 @@ static void api_cb(uev_t *w, void *arg, int events)
leave: leave:
close(sd); close(sd);
if (UEV_ERROR == events)
goto error;
return; return;
error: error:
api_exit(); api_exit();
@@ -598,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;
+16 -2
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,12 @@ 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) {
dbg("%s(): inotify socket %d invalid.", __func__, w->fd);
return;
}
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
err(1, "invalid inotify event"); err(1, "invalid inotify event");
@@ -289,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
} }
@@ -471,7 +485,7 @@ void cgroup_init(uev_ctx_t *ctx)
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
if (errno == ENOENT) if (errno == ENOENT)
logit(LOG_INFO, "Kernel does not support cgroups v2, disabling."); logit(LOG_INFO, "Kernel does not support cgroups v2, disabling.");
else if (errno == EPERM) /* Probably inside an unpriviliged container */ else if (errno == EPERM) /* Probably inside an unprivileged container */
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling."); logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
else else
err(1, "Failed mounting cgroup v2"); err(1, "Failed mounting cgroup v2");
+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) {
+7 -2
View File
@@ -113,7 +113,7 @@ static void cond_boot_strap(void)
* *
* => 'pid/' + 'teamd' + '' = condition <pid/teamd> * => 'pid/' + 'teamd' + '' = condition <pid/teamd>
* *
* When you add a second aggrate you need to tell Finit it's a different * When you add a second aggregate you need to tell Finit it's a different
* instance usugin the :ID syntax: * instance usugin the :ID syntax:
* *
* service :lag2 teamd -f /etc/teamd/lag2.conf -- Aggregate lag2 * service :lag2 teamd -f /etc/teamd/lag2.conf -- Aggregate lag2
@@ -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;
} }
+72 -25
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,13 @@ 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) {
dbg("%s(): iwatch socket %d invalid.", __func__, w->fd);
return;
}
if (conf_iwatch_read(w->fd)) { if (conf_iwatch_read(w->fd)) {
err(1, "invalid inotify event"); err(1, "invalid inotify event");
return; return;
@@ -1550,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
} }
@@ -1574,25 +1620,26 @@ int conf_monitor(void)
* have or not have symlinks in place. We need to monitor for * have or not have symlinks in place. We need to monitor for
* changes to either symlink or target. * changes to either symlink or target.
*/ */
rc += iwatch_add(&iw_conf, finit_rcsd, IN_ONLYDIR); rc |= iwatch_add(&iw_conf, finit_rcsd, IN_ONLYDIR);
snprintf(path, sizeof(path), "%s/available/", finit_rcsd); snprintf(path, sizeof(path), "%s/available/", finit_rcsd);
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW); rc |= iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd); snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd);
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW); rc |= iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
rc += iwatch_add(&iw_conf, finit_conf, 0); rc |= iwatch_add(&iw_conf, finit_conf, 0);
/* /*
* Systems with /etc/default, /etc/conf.d, or similar, can also * Systems with /etc/default, /etc/conf.d, or similar, can also
* monitor changes in env files sourced by .conf files (above) * monitor changes in env files sourced by .conf files (above)
* define your own with --with-sysconfig=/path/to/envfiles * define your own with --with-sysconfig=/path/to/envfiles
*/ */
rc += iwatch_add(&iw_conf, "/etc/default/", IN_ONLYDIR); rc |= iwatch_add(&iw_conf, "/etc/default/", IN_ONLYDIR);
rc += iwatch_add(&iw_conf, "/etc/conf.d/", IN_ONLYDIR); rc |= iwatch_add(&iw_conf, "/etc/conf.d/", IN_ONLYDIR);
#ifdef FINIT_SYSCONFIG #ifdef FINIT_SYSCONFIG
rc += iwatch_add(&iw_conf, FINIT_SYSCONFIG, IN_ONLYDIR); rc |= iwatch_add(&iw_conf, FINIT_SYSCONFIG, IN_ONLYDIR);
#endif #endif
rc |= conf_reload();
return rc + conf_reload(); return rc;
} }
/* /*
+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");
+3 -3
View File
@@ -199,7 +199,6 @@ int run(char *cmd, char *log)
int run_interactive(char *cmd, char *fmt, ...) int run_interactive(char *cmd, char *fmt, ...)
{ {
int status, oldout = 1, olderr = 2; int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
FILE *fp; FILE *fp;
if (!cmd) { if (!cmd) {
@@ -244,6 +243,7 @@ int run_interactive(char *cmd, char *fmt, ...)
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */ /* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
if (fp && !debug) { if (fp && !debug) {
char line[LINE_SIZE];
size_t len, written; size_t len, written;
rewind(fp); rewind(fp);
@@ -281,7 +281,7 @@ int exec_runtask(char *cmd, char *args[])
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[]) static void prepare_tty(char *tty, speed_t speed, const char *procname, struct rlimit rlimit[])
{ {
char name[80]; char name[80];
int fd, dummy; int fd, dummy;
@@ -351,7 +351,7 @@ static int activate_console(int noclear, int nowait)
dprint(STDERR_FILENO, clr, strlen(clr)); dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, msg, strlen(msg)); dprint(STDERR_FILENO, msg, strlen(msg));
while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n') while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
continue; ;
/* On any error (likely EINTR), we avoid starting getty */ /* On any error (likely EINTR), we avoid starting getty */
if (rc == -1) if (rc == -1)
+3 -22
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 bootrapping (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 */
@@ -280,7 +261,7 @@ static int fsck(int pass)
sulogin(1); sulogin(1);
} }
} }
rc += fsck_rc; rc |= fsck_rc;
} }
endmntent(fp); endmntent(fp);
@@ -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_ */
/** /**
+2 -2
View File
@@ -312,9 +312,9 @@ static int exec_login(char *name)
struct stat st; struct stat st;
if (passenv) if (passenv)
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, "-p", "--", name, NULL);
else else
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, "--", name, NULL);
/* /*
* Failed to exec login, should not happen on normal systems. * Failed to exec login, should not happen on normal systems.
+4 -95
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"
@@ -87,7 +86,7 @@ void console_init(void)
/* Enable line wrap, if disabled previously, e.g., qemu */ /* Enable line wrap, if disabled previously, e.g., qemu */
dprint(STDOUT_FILENO, "\033[?7h", 5); dprint(STDOUT_FILENO, "\033[?7h", 5);
/* Reset atttributes, background and foreground color */ /* Reset attributes, background and foreground color */
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14); dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
log_init(); log_init();
@@ -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;
+2 -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,56 +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;
@@ -159,7 +100,7 @@ static inline int dprint(int fd, const char *s, size_t len)
return rc; return rc;
} }
static inline char *fgetval(char *line, const char *key, char *sep) static inline char *fgetval(const char *line, const char *key, char *sep)
{ {
char *ptr, *str, *copy; char *ptr, *str, *copy;
size_t len; size_t len;
@@ -179,7 +120,7 @@ static inline char *fgetval(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;
+18 -19
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;
} }
@@ -872,7 +869,7 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len, int ansi)
{ {
int bold = missing(svc); int bold = missing(svc);
if (!ansi || svc_checkenv(svc) || svc->env[0] == '-') if (!ansi || svc_checkenv(svc))
bold = 0; bold = 0;
strlcpy(buf, bold ? "\e[1m" : "", len); strlcpy(buf, bold ? "\e[1m" : "", len);
@@ -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");
+71 -1
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.
@@ -139,7 +209,7 @@ void logit(int prio, const char *fmt, ...)
goto done; goto done;
} }
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio); fprintf(fp, "<%d>finit[1]: ", LOG_DAEMON | prio);
vfprintf(fp, fmt, ap); vfprintf(fp, fmt, ap);
fclose(fp); fclose(fp);
+1
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))) {
+1 -1
View File
@@ -192,7 +192,7 @@ int pid_file_set(svc_t *svc, char *file, int not)
* pid:foo.tla --> /run/foo.tla.pid * pid:foo.tla --> /run/foo.tla.pid
* pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!) * pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!)
* *
* An exclamation mark, or logial NOT, in the first character position * An exclamation mark, or logical NOT, in the first character position
* indicates that Finit should *not* manage the PID file, and that the * indicates that Finit should *not* manage the PID file, and that the
* process uses a "non-standard" location. Eg. if service is 'bar' * process uses a "non-standard" location. Eg. if service is 'bar'
* Finit would assume the PID file is /run/bar.pid and the below simply * Finit would assume the PID file is /run/bar.pid and the below simply
+2
View File
@@ -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;
} }
+3 -1
View File
@@ -22,7 +22,8 @@
*/ */
#include "config.h" #include "config.h"
#include "finit.h" #include "private.h"
#include "log.h"
#include "schedule.h" #include "schedule.h"
#define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */ #define SC_INIT 0x494E4954 /* "INIT", see ascii(7) */
@@ -35,6 +36,7 @@ static void cb(uev_t *w, void *arg, int events)
struct wq *work = (struct wq *)arg; struct wq *work = (struct wq *)arg;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
dbg("%s(): spurious problem with schedule work timer, restarting.", __func__);
uev_timer_start(w); uev_timer_start(w);
return; return;
} }
+444 -229
View File
File diff suppressed because it is too large Load Diff
-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);
+29 -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"
@@ -232,6 +233,8 @@ void do_iterate_proc(int (*cb)(int, void *), void *data)
dbg("Skipping %s ...", file); dbg("Skipping %s ...", file);
else if (file[0] == '@') else if (file[0] == '@')
dbg("Skipping %s ...", &file[1]); dbg("Skipping %s ...", &file[1]);
else if (strstr(file, "initctl"))
dbg("Skipping %s ...", file);
else else
if (cb(pid, data)) { if (cb(pid, data)) {
dbg("PID %d is still alive (%s)", pid, file); dbg("PID %d is still alive (%s)", pid, file);
@@ -258,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;
@@ -423,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");
@@ -439,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();
} }
/* /*
@@ -449,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");
@@ -465,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");
@@ -479,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");
@@ -500,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");
@@ -512,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);
} }
/* /*
@@ -520,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");
@@ -532,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);
} }
/* /*
@@ -543,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;
@@ -599,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 */
} }
+150 -64
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 ...");
@@ -107,6 +122,9 @@ static char *sm_status(sm_state_t state)
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
return "bootstrap"; return "bootstrap";
case SM_BOOTSTRAP_WAIT_STATE:
return "bootstrap/wait";
case SM_RUNNING_STATE: case SM_RUNNING_STATE:
return "running"; return "running";
@@ -116,18 +134,23 @@ static char *sm_status(sm_state_t state)
case SM_RUNLEVEL_WAIT_STATE: case SM_RUNLEVEL_WAIT_STATE:
return "runlevel/wait"; return "runlevel/wait";
case SM_RUNLEVEL_CLEAN_STATE:
return "runlevel/clean";
case SM_RELOAD_CHANGE_STATE: case SM_RELOAD_CHANGE_STATE:
return "reload/change"; return "reload/change";
case SM_RELOAD_WAIT_STATE: case SM_RELOAD_WAIT_STATE:
return "reload/wait"; return "reload/wait";
default: case SM_RELOAD_CLEAN_STATE:
return "unknown"; return "reload/clean";
} }
return "unknown";
} }
static char sm_runlevel(int lvl) static char sm_rl2ch(int lvl)
{ {
if (lvl == INIT_LEVEL) if (lvl == INIT_LEVEL)
return 'S'; return 'S';
@@ -150,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); 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;
/* /*
@@ -224,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 */
@@ -235,10 +237,10 @@ restart:
/* Disable progress output at normal runtime */ /* Disable progress output at normal runtime */
enable_progress(0); enable_progress(0);
/* System bootrapped, launch TTYs et al */ /* System bootstrapped, launch TTYs et al */
bootstrap = 0; bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN); service_step_all(SVC_TYPE_RESPAWN);
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
case SM_RUNNING_STATE: case SM_RUNNING_STATE:
@@ -246,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) {
@@ -274,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 */
@@ -294,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:
@@ -316,9 +318,25 @@ 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;
break;
case SM_RUNLEVEL_CLEAN_STATE:
/*
* Wait for post:script or cleanup:script to be collected,
* which moves the svc to HALTED or DEAD state. We will
* be called by the service_monitor() on collect.
*/
svc = svc_clean_completed();
if (svc) {
dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break;
}
/* Cleanup stale services */ /* Cleanup stale services */
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
@@ -337,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:
@@ -349,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:
@@ -367,11 +385,27 @@ 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;
break;
case SM_RELOAD_CLEAN_STATE:
/*
* Wait for post:script or cleanup:script to be collected,
* which moves the svc to HALTED or DEAD state. We will
* be called by the service_monitor() on collect.
*/
svc = svc_clean_completed();
if (svc) {
dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break;
}
/* Cleanup stale services */ /* Cleanup stale services */
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
@@ -382,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 -23
View File
@@ -24,30 +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 runlevel processes to be halted */
SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */
} 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_ */
+39 -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"
@@ -348,6 +349,43 @@ svc_t *svc_stop_completed(void)
return NULL; return NULL;
} }
/**
* svc_clean_completed - Have post: and cleanup: scripts finished?
*
* Called late in runlevel and reload transitions to check if all post: and
* cleanup:scripts have completed. A removed service should end up in the
* DEAD state, while a service with a post:script ends up in HALTED.
*
* Returns:
* %NULL if post: and cleanup: scripts have run, otherwise a pointer to
* the first found svc_t waiting for post: or cleanup:script.
*/
svc_t *svc_clean_completed(void)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc_enabled(svc))
continue;
if (svc_is_runtask(svc) && svc->state == SVC_DONE_STATE)
continue;
if (svc_is_removed(svc)) {
if (svc->state == SVC_DEAD_STATE)
continue; /* any cleanup:script done */
} else {
if (svc->state == SVC_HALTED_STATE)
continue; /* any post:script done */
}
return svc;
}
return NULL;
}
/** /**
* svc_find - Find a service object by its full path name * svc_find - Find a service object by its full path name
* @name: Full path name, e.g., /sbin/syslogd * @name: Full path name, e.g., /sbin/syslogd
@@ -580,7 +618,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->removed && cb) if (svc_is_removed(svc) && cb)
cb(svc); cb(svc);
} }
} }
+33 -10
View File
@@ -52,14 +52,17 @@ typedef enum {
} svc_type_t; } svc_type_t;
#define SVC_TYPE_ANY (-1) #define SVC_TYPE_ANY (-1)
#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_TTY) #define SVC_TYPE_DAEMON (SVC_TYPE_SERVICE | SVC_TYPE_SYSV)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV) #define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_SYSV | SVC_TYPE_TTY)
#define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK)
typedef enum { typedef enum {
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */ SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
SVC_DONE_STATE, /* Task/Run job has been run */ SVC_DONE_STATE, /* Task/Run job has been run */
SVC_DEAD_STATE, /* Process is dead and scheduled for removal */
SVC_CLEANUP_STATE, /* Running cleanup: script */
SVC_TEARDOWN_STATE, /* Running post: script */
SVC_STOPPING_STATE, /* Waiting to collect the child process */ SVC_STOPPING_STATE, /* Waiting to collect the child process */
SVC_CLEANUP_STATE, /* Running post: script */
SVC_SETUP_STATE, /* Running pre: script */ SVC_SETUP_STATE, /* Running pre: script */
SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */ SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */
SVC_WAITING_STATE, /* Enabled but condition not satisfied */ SVC_WAITING_STATE, /* Enabled but condition not satisfied */
@@ -83,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,
@@ -146,7 +149,7 @@ typedef struct svc {
char cond[MAX_COND_LEN]; char cond[MAX_COND_LEN];
/* Instance specifics */ /* Instance specifics */
int job; /* For intenal use only, canonical ref is NAME:ID */ int job; /* For internal use only, canonical ref is NAME:ID */
char name[MAX_ARG_LEN]; char name[MAX_ARG_LEN];
char id[MAX_ID_LEN]; /* :ID */ char id[MAX_ID_LEN]; /* :ID */
char ifstmt[MAX_IDENT_LEN]; char ifstmt[MAX_IDENT_LEN];
@@ -196,13 +199,25 @@ typedef struct svc {
char conflict[MAX_ARG_LEN]; char conflict[MAX_ARG_LEN];
char desc[MAX_STR_LEN]; char desc[MAX_STR_LEN];
char env[MAX_CMD_LEN]; char env[MAX_CMD_LEN];
char pre_script[MAX_CMD_LEN]; char pre_script[MAX_CMD_LEN];
int pre_tmo; int pre_tmo;
char post_script[MAX_CMD_LEN]; char post_script[MAX_CMD_LEN];
int post_tmo; int post_tmo;
char ready_script[MAX_CMD_LEN]; char ready_script[MAX_CMD_LEN];
int ready_tmo; int ready_tmo;
char cleanup_script[MAX_CMD_LEN];
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.
@@ -240,6 +255,7 @@ void svc_foreach (int (*cb)(svc_t *));
void svc_foreach_type (int types, int (*cb)(svc_t *)); void svc_foreach_type (int types, int (*cb)(svc_t *));
svc_t *svc_stop_completed (void); svc_t *svc_stop_completed (void);
svc_t *svc_clean_completed (void);
void svc_mark (svc_t *svc); void svc_mark (svc_t *svc);
void svc_mark_dynamic (void); void svc_mark_dynamic (void);
@@ -256,26 +272,27 @@ 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]; }
static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];} static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];}
static inline int svc_has_cleanup (svc_t *svc) { return svc->cleanup_script[0]; }
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; } static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; } static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; } static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_running (svc_t *svc) { return svc && svc->state == SVC_RUNNING_STATE; } static inline int svc_is_running (svc_t *svc) { return svc && svc->state == SVC_RUNNING_STATE; }
static inline int svc_is_stopping (svc_t *svc) { return svc && svc->state == SVC_STOPPING_STATE; } static inline int svc_is_stopping (svc_t *svc) { return svc && svc->state == SVC_STOPPING_STATE; }
static inline int svc_is_stopped (svc_t *svc) { return svc && svc->block == SVC_BLOCK_USER; }
static inline int svc_is_removed (svc_t *svc) { return svc && svc->removed; } static inline int svc_is_removed (svc_t *svc) { return svc && svc->removed; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; } static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
@@ -390,12 +407,15 @@ static inline char *svc_status(svc_t *svc)
return "stopping"; return "stopping";
} }
case SVC_CLEANUP_STATE: case SVC_TEARDOWN_STATE:
return "cleanup"; return "teardown";
case SVC_SETUP_STATE: case SVC_SETUP_STATE:
return "setup"; return "setup";
case SVC_CLEANUP_STATE:
return "cleanup";
case SVC_PAUSED_STATE: case SVC_PAUSED_STATE:
return "paused"; return "paused";
@@ -408,6 +428,9 @@ static inline char *svc_status(svc_t *svc)
case SVC_RUNNING_STATE: case SVC_RUNNING_STATE:
return "running"; return "running";
case SVC_DEAD_STATE:
return "dead";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
+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_ */
+5 -3
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"
@@ -172,7 +173,7 @@ int tty_parse_args(struct tty *tty, char *cmd, char **args)
passenv = 1; passenv = 1;
else if (!strcmp(cmd, "rescue")) else if (!strcmp(cmd, "rescue"))
tty->rescue = 1; /* for rescue shells */ tty->rescue = 1; /* for rescue shells */
else if (whichp(cmd)) /* in $PATH? */ else if (!tty->cmd && whichp(cmd))
tty->cmd = cmd; tty->cmd = cmd;
else else
tty->args[tty->num++] = cmd; tty->args[tty->num++] = cmd;
@@ -229,8 +230,9 @@ int tty_parse_args(struct tty *tty, char *cmd, char **args)
return errno = EINVAL; return errno = EINVAL;
} }
dbg("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in", dbg("Registering %s getty on TTY %s at %s baud with term %s",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A"); tty->cmd ? "external" : "built-in",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
/* Built-in getty now comes as standalone program */ /* Built-in getty now comes as standalone program */
if (!tty->cmd) { if (!tty->cmd) {
+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)"
+1 -6
View File
@@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# Verify handling of dependant services, bug #314 # Verify handling of dependent services, bug #314
# Two services: foo and bar, where bar depends on foo: # Two services: foo and bar, where bar depends on foo:
# #
# - bar must not start until foo is ready # - bar must not start until foo is ready
@@ -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 \
+10 -2
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")
@@ -71,7 +79,7 @@ test_one()
assert_status "serv" "running" assert_status "serv" "running"
assert_cond "service/serv/ready" assert_cond "service/serv/ready"
# Issue #343; pidfile.so inadvertedly marked systemd services as # Issue #343; pidfile.so inadvertently marked systemd services as
# 'started' when they created their PID file. The serv test daemon # 'started' when they created their PID file. The serv test daemon
# waits 3 sec between pidfile and notify to trigger this bug. # waits 3 sec between pidfile and notify to trigger this bug.
if [ "$type" != "pid" ]; then if [ "$type" != "pid" ]; then
-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"
} }

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