Compare commits

..
377 Commits
Author SHA1 Message Date
Joachim Wiberg f4d3ec1a9f Update ChangeLog and bump version for v4.4 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-15 16:40:56 +02:00
Joachim Wiberg 51befb492c Allow conflicting services to start when conflict is resolved
These changes add a new svc_block_t type: SVC_BLOCK_CONFLICT so a user
can more clearly see why a run/task/service has not been started by
Finit.  The reason for the block is by default logged, which can be
escaped by using the `nowarn` flag.

Also, when the conflict is resolved, allow the service to start.

With these changes, the system/hotplug.conf should work better and
cause less questions about "strange" log messages.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-15 16:23:40 +02:00
Joachim Wiberg 7850378e15 Fix issue with messed up linewrap in qemu-system-x86_64
Apparently the qemu-system-x86_64 in -nographics mode does all sorts of
crazy things to the terminal.  Most annoying, it disables linewrap.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-15 16:05:59 +02:00
Joachim Wiberg 438d6b4e63 Document fix of regression from Finit v3.2
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-14 09:33:10 +02:00
Joachim Wiberg 712f68f9da Fix #355: regression stopping a process and its group
In 91a9c83 a regression was introduced that affects the way Finit stops
a supervised process and its process group.

Instead of sending SIGTERM to the process, delegating the responsibility
to that process to inform any children it may have, Finit as of 91a9c83
sends SIGTERM to the entire process group.  For SIGKILL this is fine,
SIGKILL only runs as cleanup and as a last ditch effort if the process
doesn't respond to SIGTERM.

This regression, introduced in v3.2, directly affects services like
avahi-autoipd that have forked off children that it needs to tell to
exit cleanly before it returns. With the patch in question these
children are never allowed to complete, which in turn causes lingering
169.254 link-local addresses on interfaces.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-14 09:27:09 +02:00
Joachim Wiberg 8a32ec5c92 Bump version for v4.4-rc2
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-08 09:58:42 +02:00
Joachim Wiberg 081622a085 Enable more features at distcheck for improved coverage
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-08 09:56:25 +02:00
Joachim Wiberg 2526431d8e Allow Finit to inherit PATH and SHELL from env.
When started as a monitor in a container, or for the test suite run from
distcheck, we must allow Finit to find its own tools like initctl.  This
should also be a more generic solution that cover more cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-08 09:52:59 +02:00
Joachim Wiberg 47d8a83668 Drop hard coded path limit in glob of *.conf files
A simple distcheck revealed that the local path[] was too small to store
something like /home/jocke/src/finit/finit-4.4-rc1/usr/lib/finit/system/

This refactor should cover all cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-08 09:52:21 +02:00
Joachim Wiberg f72f3febf9 Follow-up to e00fda5, allow runlevel 0 for kevent and watchdogd
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-08 09:49:06 +02:00
Joachim Wiberg 138546d008 .github: debug failing tests in release build
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 21:06:17 +02:00
Joachim Wiberg cee244d151 Drop TODO, sysv works as well as they can right now
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 20:55:00 +02:00
Joachim Wiberg 7af4ad5c24 .github: Release General needs a new uniform!
We've prepared this at one of the finest tailors at Saville Row.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 20:53:25 +02:00
Joachim Wiberg c0d3705ed5 Bump version for v4.4-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 20:45:03 +02:00
Joachim Wiberg 5cf2b9a283 Massive update of ChangeLog, add missing early changes after v4.3
Finit v4.3 is now almost a year old ... time to get v4.4 out the door!

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 20:42:20 +02:00
Joachim Wiberg 0e898da6e5 doc: minor, whitespace
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 20:42:11 +02:00
Joachim Wiberg d9f13a1e50 Notable breaking change in runlevels
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-07 11:53:22 +02:00
Joachim Wiberg 4fbcd1bbad Update copyright years
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-05 06:00:34 +02:00
Joachim Wiberg b6c5498f43 initctl: refactor serv_enable() for readability and coding style
Also apply the same logic to serv_disable() to drop 40 char limit on
service names.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-05 05:40:26 +02:00
Joachim Wiberg 9a6c68c952 initctl: follow-up to 700c32fd, fix enable regression
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-05 04:32:09 +02:00
Joachim Wiberg e8170e1216 doc: add use-case for runparts scripts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-04 10:05:40 +02:00
Joachim Wiberg 059a4c8a86 man: document new kernel cmdline options
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-02 00:22:50 +02:00
Joachim Wiberg 0de9db68a3 system: don't warn if neither udev/udevadm or mdev is available
The user may have their own setup of things, by default we should not
cause unnecessary warnings in syslog.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-01 23:53:40 +02:00
Joachim Wiberg aeff68b598 test/rclocal.sh: new test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-05-01 23:50:57 +02:00
Joachim Wiberg 700c32fd73 Initial support for templating and instantiating services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-24 20:14:30 +02:00
Joachim Wiberg 334d4a708a Follow-up to e00fda5: skip bootstrap-only services when not in bootstrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-24 16:28:49 +02:00
Joachim Wiberg 4d472273e1 .github: debug weekly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-23 16:47:47 +02:00
Joachim Wiberg 13f1359b3a .github: fix and simplify weekly distcheck job
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 15:46:06 +02:00
Joachim Wiberg 2e5b97c8b2 plugins: check explicitly for (time_t)-1 from mktime()
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 15:30:29 +02:00
Joachim Wiberg 9a962c86c8 Update docs a bit about runlevel 1.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 15:11:27 +02:00
Joachim Wiberg f954a4cb92 service_stop: add missing && do_progress for print_result()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 15:11:27 +02:00
Joachim Wiberg feb97bc77c Minor, helper function for logging runlevel as a char
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 15:11:27 +02:00
Joachim Wiberg e00fda5dc3 Fix #352: separate runlevel S from runlevel 0
Due to an old design decision runlevel S was encoded internally as '0',
meaning it was the same as halt/poweroff.  If you want to run scripts at
system shutdown this was less then ideal since it meant your scripts
also ran at bootstrap.

This change is quite invasive.  It introduces INIT_LEVEL (10) as the
value for runlevel S, meaning all code that parses and/or evaluates
anything for runlevel 0/S was affected and had to be reworked.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 15:11:27 +02:00
Joachim Wiberg 80191f2fd5 plugins: allow building with -D_FORTIFY_SOURCE[=1,2]
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-22 14:28:08 +02:00
Joachim Wiberg 42ef3d3cf7 plugins: add configure support for overriding RTC fallback datetime
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-10 16:35:42 +02:00
Joachim Wiberg 1f4acb322a m4: simplify and fix issue with AC_PLUGIN() going out of bounds
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-10 16:33:03 +02:00
Joachim Wiberg c3ba15045b plugins: let dbus start as soon as possible
Some services (openresolv) tries to talk to it very early, so lets get
dbus up and running as soon as possible.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-27 14:28:12 +02:00
Joachim Wiberg 8f1d1717f8 initctl: let -n ignore errors from enable/disable as well
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-27 14:27:16 +02:00
Joachim Wiberg 7703d00e82 Fixed TODOs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 16:34:34 +01:00
Joachim Wiberg 3bb826b22b Ensure all a process' children are in the same cgroup
The logger redirection process was sometimes in the init group instead
of the system/<svc>/ group.  This was due to the parent PID being moved
after the logger was forked.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 16:32:28 +01:00
Joachim Wiberg 9ee2be121f Fix in-service cgroup parser regression
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 16:31:43 +01:00
Joachim Wiberg 36c6aa73aa Fix #351: clean run tasks to prevent endless restarts
When we've started a run task, regardless of its exit status, we must
mark it as not dirty to prevent it from being endlessly restarted.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg 13dddb6912 test: new regression test for issue #351
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg bdaf3370d4 test: sectionalize
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg a24077c4af TODO: cond fixes and run/restart-forever test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg 414a49d733 Fix regression in .conf reload when changing runlevel
When the inotify callback is called for a file that has been removed,
the call to realpath() obviously fails (ENOENT).  This fix only takes
the regular case into account, files removed from a symlinked enabled
directory is not handled -- needs more work for that.

Regression introduced in 0a94f3f, PR #347, issue #346.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg be9d54561d Fix WIFEXITED() logic when collecting reaped processes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg 3ce5a2a965 Minor, increased logging on failure to start external logger
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg 1d03e9aa8b Follow-up to a47abfd, fix logged PID reported by external logger tool
Issue #344

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg 5340e47e81 Flush .conf event queue before leaving boostrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg 98bbf4a1e5 test: add watchdog to catch runaway tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-21 11:24:17 +01:00
Joachim Wiberg d70e2f1ed0 Minor refactor, rgen only needed here
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-19 09:33:33 +01:00
Joachim Wiberg ecb5949679 Follow-up to a613ee9, handle reload as well (existing svc)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-19 09:33:33 +01:00
Joachim Wiberg e55fd19091 Remember to audit sysv services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-19 08:36:52 +01:00
Joachim Wiberg 19c64e323c Add another log macro, note()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-19 08:35:02 +01:00
Joachim Wiberg 9354221bdd initctl: fix regression in error handling, cmmmit 4542fdb
Issue #339

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-19 08:32:06 +01:00
Joachim Wiberg 1d7048d3ae system: trim delay starting udevd
- Apply dracut[1] and void[] linux lessons
 - Drop udev-finish, not needed anymore (2015)

[1]: https://github.com/dracutdevs/dracut/blob/1ff306a3964840fc548188a8aa4862143b347e8a/modules.d/99base/init.sh#L152-L170
[2]: https://github.com/void-linux/void-runit/blob/master/core-services/02-udev.sh

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-10 18:30:50 +01:00
Joachim Wiberg 8251d713e0 devmon: reduce debug loglevel
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-10 07:52:18 +01:00
Joachim Wiberg 44d0d87ad3 Simplify, drop ifdef: let if: become ifdef
Post audit, concensus is to drop if: and always do post-eval of all
ifdef: statements.  Also, rename ifdef: to if: for completeness.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-06 17:03:37 +01:00
Joachim Wiberg 571250a9b9 test: drop hotplug.conf system file during tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-05 10:49:14 +01:00
Joachim Wiberg 91540fd793 Issue #351: unable to reproduce
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-05 10:48:45 +01:00
Joachim Wiberg 2566e00ef0 .github: finit -h now require super user privs.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-05 10:12:56 +01:00
Joachim Wiberg 1e5ce38928 Return EX_NOPERM for non-root users calling init/telinit
See issue #301 for future per-user support.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-05 10:05:24 +01:00
Joachim Wiberg c679b0a93d Cannot be const, we're modifying it
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-05 09:56:54 +01:00
Joachim Wiberg c4dcf568c8 Extend conditional load support with if:<cond> and if:<!cond>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-05 09:47:51 +01:00
Joachim Wiberg b65773d508 Drop logically dead code, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 12:09:50 +01:00
Joachim Wiberg e75c1792aa tmpfiles.d: propagate possible error from remove() in mksubsys()
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 12:02:52 +01:00
Joachim Wiberg 0be4838e01 Fix potential NULL pointer deref, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 12:01:24 +01:00
Joachim Wiberg b71605c819 Fix potential NULL pointer deref, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 11:59:09 +01:00
Joachim Wiberg 3bb2eb2a8d Fix potential socket leak at bootstrap and shutdown
This is a follow-up to a39ee0b, found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 11:44:48 +01:00
Joachim Wiberg 8ce35771c3 initctl: fix potential memory leak
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 11:43:57 +01:00
Joachim Wiberg 67e80ea989 initctl: ensure the returned plugin buffer data is terminated
Found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-04 11:43:10 +01:00
Joachim Wiberg f4d8ecfb5b getty: fix memory leak in parsing /etc/os-release
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-03-02 21:24:24 +01:00
Joachim Wiberg 2eb3ebb404 test: /var a tmpfs to prevent tests from trampling on each other
Multiple parallel tests writing to the same /var leads to quite
unexpected results.  Let's start fresh in each test instead.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 1e1b3564dd Support for fsck_mode=[auto,skip,force] + fsck_repair=[preen,no,yes]
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 7af257cccf Migrate hotplug plugin to a conditional system/hotplug.conf file
This is the first example of the just minted advanced stanza syntax with
if-statements, conflict handling, and nowarn flags.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 84ddac65e5 Migrate testserv plugin to a couple of lines of ASCII .conf files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 62d6b56fd3 plugins: only run pam_console_apply if available
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg fa71f97dcf Add support for if:[!]ident and ifdef:[!]ident for run/task/services
Conditional loading of stanza depending on ident is already loaded.  The
if: checks as in-band while ifdef: checks out-of-band, i.e., post eval.

The optional leading '!' negates the comparison, if NOT foo then ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg ed383eaaa6 Minor refactor, break out new fn svc_find_by_str()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 9cb6a54493 Minor refactor, new fn svc_mark()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg f796d59f77 Minor, improve log message slightly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 97ca450966 Add support for static services in /lib/finit/system/*.conf
Slowly migrating away from hard-coded services in plugins.  This way
it's possible for the user to both inspect and override as needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg e0c1c08e68 plugins: remember that bootmisc depends on pidfile plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg c81b722691 tmpfiles.d: add support for 'l' and 'l+', Finit extensions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 11da93b759 Add support for Finit specific tmpfiles.d/
The tmpfilesd() glob sorts files according to name, we could name our
.conf as 00finit.conf to prevent ordering issues with, e.g. dnsmasq,
but this is more elegant and allows for multi-level override.

NOTE: bootmisc depends on the pidfile plugins since the latter need
      to set up its iwatches of /run before bootmisc creates /run.
      Depending on if it's a system with /var/run or /run we need to
      drop /var/run before recreating it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 28a90a793d New log macro info() for LOG_INFO level logging
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg 56a89db889 Add support run/task/service 'conflict:foo' handling
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:49:55 +01:00
Joachim Wiberg a613ee908f If a run/task/service command does not exist, skip registering it
This changes the semantics of Finit a bit by checking for the command to
run when registering it, skipping commands that cannot be found in the
absolute path provided in the command, or in $PATH.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg d3f58b5df0 Minor, add missing else statement
For completeness, never triggered.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg af88b10322 Minor, simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg 966ae9fb0d Rename FINIT_LIBPATH_ -> FINIT_EXECPATH_
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg a69cef0a11 paste(): only add '/' if dir and file are missing a '/'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg b396a018c5 test: install busybox symlinks with relative path
Then it works to run the applets from outside the unshare.  Useful for
testing and test development.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg a4af9ba248 Add limited tmpfiles.d(5) support
This change adds very basic tmpfiles.d/ support to Finit.  Much of the
basic types are supported, but not all, so for now, please check the
code for details on what is working.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg 6942e38413 Relocate ln() helper function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg e03bd093b5 mksubsys(): if makedir() fails with EEXIST, make sure to chmod()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg d6070a0d10 Drop old now unused /etc/network/run/ifstate
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg bd4efb59ca Refactor, change API for create()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg a90cfd4a75 plugins: fix octal permission on /run/lock and /var/lock
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg ab5a983bf7 test: only run checkself.sh on enabled tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-28 00:48:25 +01:00
Joachim Wiberg f7bacf8b1b Add 'set' keyword for environment variables set in /etc/finit.conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-22 19:56:29 +01:00
Joachim Wiberg d363501e21 Fix parse/diff of command line args, e.g., nginx -g 'daemon off;'
Starting a service like this works fine:

    service [2345789] env:-/etc/default/nginx nginx -g 'daemon off;'

However, on `initctl reload` the previous argument was lost while
parsing the end of the new one.  So the comparison always resulted
in a 'diff' causing the service to be unnecessarily restarted.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-22 19:46:33 +01:00
Joachim Wiberg 4150fdfe59 Gracefully handle leading 'boot/' in command line conditions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-17 13:14:42 +01:00
Joachim Wiberg 4d2b97e4cc plugins: spellcheck
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-15 19:29:11 +01:00
Joachim Wiberg 21eda9dd1c plugins: create cache dir for nginx and runstatedir for clixon
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-15 19:21:09 +01:00
Joachim Wiberg 4f7e726a3e test: initial regression test for #351 (WIP)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-11 16:48:21 +01:00
Joachim Wiberg 74fc16b832 test: add support for running a bootstrap config
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-11 16:47:03 +01:00
Joachim Wiberg 7393072120 Log exit status of run tasks in debug mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-11 16:30:34 +01:00
Joachim Wiberg 96bb3f5c6c Merge pull request #349 from wkz/initctl-cond
initctl: Follow priciple of least surprise for "cond get"

Fixes regression in 4.4 release cycle, commit c3c662f, when support for FLUX condition state was added.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-06 16:13:13 +01:00
Tobias Waldekranz c23a867485 initctl: Follow priciple of least surprise for "cond get"
The accepted standard in Unix is to report successful executions with
exitcode 0. Therefore, map a "initctl cond get" of a condition to the
following exitcodes:

- On: 0
- Off: 1
- Flux: 255

Fixes: c3c662fe64 ("initctl: ensure 'cond get' support flux state")
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
2023-02-06 16:01:18 +01:00
Joachim Wiberg 4f8a5a6531 Follw-up to 4894580, unbreak TTYs
The TTY and log configuration is overloaded using a union in svc_t, so
we cannot "reset" the log attribute on reload.  In fact, a TTY should
never be allowed to have a tty:anything set, so change the parser to
ignore log options for ttys.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-06 15:47:19 +01:00
Joachim Wiberg 48e1a0b17f Another ChangeLog update for the upcoming v4.4 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-06 12:54:54 +01:00
Joachim Wiberg 9c727ed08d test: namespace cleanup, common -> src, tenv -> lib, etc.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 19:04:48 +01:00
Joachim Wiberg 49cd3a441d test: whitespace only
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 18:40:06 +01:00
Joachim Wiberg 8b9258aa73 test: rename tenv-root -> sysroot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 18:19:23 +01:00
Joachim Wiberg 82c6e4680f Fix issue with pid://../run/foo..pid parser
Unfortunately we cannot use realpath(3) here since the the PID files
usually do not yet exist at this point.

Add and modify my ugly de_dotdot() from Merecat httpd.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 18:16:27 +01:00
Joachim Wiberg a67b2ba9a7 test: new regression test, //run/foo.pid -> /var/run///run/foo.pid
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 18:07:12 +01:00
Joachim Wiberg a96edf0291 test: minor, rename fn
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 17:09:47 +01:00
Joachim Wiberg c26dded49c Further box-in testserv so it doesn't conflict with other serv tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 14:42:32 +01:00
Joachim Wiberg 3bd15a53d6 We take conditions on the kernel command line now
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 14:25:20 +01:00
Joachim Wiberg f0a888c0d1 test: update Makefile.am and build instructions, new testserv plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 12:07:35 +01:00
Joachim Wiberg b6769356b2 test: minor, replace 'texec sh -c' -> 'run'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 11:22:26 +01:00
Joachim Wiberg 5a9cafc2fc Minor issues, as reminders in Magit
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 11:02:25 +01:00
Joachim Wiberg 2e36590bbd plugins/bootmisc: create /run/dnsmasq directory, for resolv.conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 11:02:25 +01:00
Joachim Wiberg 33e504b928 Check if WDT_DEVNODE is defined, may not be enabled in configure
When this code was moved from finit.c we rely on WDT_DEVNODE to be
defined in config.h, which is controlled by configure.  Meaning, before
the relocation, Finit did not honor the configure settings.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 02:00:14 +01:00
Joachim Wiberg 6a4f197cc0 .github: enable new testserv plugin, regression testing
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 01:48:57 +01:00
Joachim Wiberg 4894580e8b Fix issue where env:/pre:/post:/etc. is removed from a service stanza
This issue was found when the test pre-post-serv.sh suddnely started
failing, unclear how it could ever have worked before considering the
nature of this patch.

The trick here is when reloading a service like this:

     service env:/etc/env    serv -np -e foo:bar

into this:

     service pre:/bin/pre.sh serv -np

In the second the env: has been removed and pre: added.  But with Finit
prior to this patch, env: was kept leading to unintended behavior.  The
same goes for the other options included in this patch.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 01:46:13 +01:00
Joachim Wiberg 70997fb4c5 Allow Finit watchdogd and keventd to start as early as possible
In particular watchdogd we want to start in runlevel S to ensure we
start kicking the WDT.  (Some HW WDTs are enabled from power on.)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 01:16:55 +01:00
Joachim Wiberg e9515971ea Refactor, move registration of static services to conf_init()
All services registered in the system rely on conf_init() having been
set up properly, e.g., global_rlimit.  Having conf_init() be responsible
also for registering static services is only logical, and also helps us
clean up main() a bit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 01:15:49 +01:00
Joachim Wiberg 82dfa002ff Follow-up to c9fe9afa, proper fix for HOOK_BASFFS_UP mess
Instead of the revert in c9fe9afa, we restore HOOK_BASFFS_UP to its
proper place at the end of fs_mount_all().  For this to not cause any
regressions we add a new hook, HOOK_SVC_PLUGIN, and update all plugins
that call service_register() to run at the new hook.

This will cause regressions for external 3rd party plugins that rely on
HOOK_BASEFS_UP to be called at its previous postion.  Nevertheless, this
is the proper fix to the problem.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 01:15:49 +01:00
Joachim Wiberg a664fd7192 Add plugin and test to reproduce failing tests since c9fe9af
With all the change and revert mess of the HOOK_BASEFS_UP tests started
failing due to usr.so and sys.so failed to set up their conditions.  It
then became clear that boomisc.so cleaned up /run ... with the revert
we got service_register() to be called before global_rlimit[] was setup
causing services registered by plugins, before conf_init(), to crash.

This plugin and test is here to ensure we catch this problem in case of
any future refactor.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-05 01:15:49 +01:00
Joachim Wiberg b62ee0ef6d test/common: add missing newline to usage text
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-04 23:05:05 +01:00
Joachim Wiberg 0cfd867816 plugins: fix memory leak in sys.so
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-02-04 23:04:41 +01:00
Joachim Wiberg 934a4db7d5 plugins/dbus: adapt to other operating systems
Not all Linux systems are based on Debian, and even if they are inspired
by Debian (Buildroot), they do not necessarily use the same defaults.

This patch probes the current system for:

 - dbus user and group
 - dbus PID file

If the user/group cannot be found we fall back to "root", if the PID
file cannot be determined we ignore PID file readiness.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-25 12:29:08 +01:00
Joachim Wiberg c9fe9afa61 Revert "HOOK_BASEFS_UP must run as soon as all filesystems are mounted"
This reverts commit 5b41c6e since it causes regressions in plugins
adding services to the system.  The proper fix for early bootmisc
is to add a new hook, which will be added in subsequent commits.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-24 10:00:47 +01:00
Joachim Wiberg 320e91653b initctl: add -n,--noerr to return OK(0) if svc doesn't exist
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-19 19:57:25 +01:00
Joachim Wiberg 859d215ca4 Merge pull request #347 from JackNewman12/master
Use realpath in do_change() to ensure symlink directories are correctly reloaded

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-18 07:07:15 +01:00
Jack Newman 0a94f3f303 Use realpath in do_change() to ensure symlink directories are
correctly reloaded.
2023-01-18 15:59:50 +10:00
Joachim Wiberg e83a26b256 Simplify readiness handling for s6 style services
No need for special handling of s6 services, we now have the new
service_notify_reconf() function that is called on init reload.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-17 07:22:59 +01:00
Joachim Wiberg 2508495b8c Fix #343: only mark native services as started in pidfile plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-17 07:22:28 +01:00
Joachim Wiberg 3ef52d9324 test: reproduce pidfile plugin marking systemd services 'started'
Issue #343

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-17 07:22:20 +01:00
Joachim Wiberg 05b72281e6 plugins: minor, fix clang warning
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-16 22:12:03 +01:00
Joachim Wiberg a47abfdc16 Fix #344: check for sysklogd logger tool at runtime, replacing logit
The sysklogd logger is capable of logging the PID of the actual service,
rather than just the logger tool itself (which would be a differet PID
every time).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 15:45:53 +01:00
Joachim Wiberg 2c9265f8c8 initctl: add 'cat' as alias for 'show'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 14:13:01 +01:00
Joachim Wiberg 56cb6f8613 Fix #343: handle 'initctl reload' of unmodified non-native services
When 'initctl reload' is called new "configuration generation" is
started by Finit.  This mechanism ensures services reaffirm their
readiness and helps distinguish between their ready before reload
and after.

For native services the pidfile plugin handles this.  The s6 style
services are one-shot only, and thus use a static ready condition.

This patch adds support for stepping the configuration generation
of unmodified systemd style services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 3dd56ed8af test: minor refactor for readability
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg fddeeb683c test: drop debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 25835b0edf test: clean up properly between tests phases in notify.sh
To reset the crash counter of the 'serv' daemon we need to remove it
from /etc/finit.conf (and reload) between each test phase.

The repeated test phases can now be dropped, since the lack of reset
was the root cause for it failing sometimes in extended runs.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 27fcfada66 test: add native pid file based daemon to notify.sh for completeness
Correct an obvious oversight, we need to ensure the native pidfile based
daemons also work as intended in the new readiness notification world.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 2dd5fb0f77 test: s6/systemd style services do not necessarily create a PID file
Ensure the test mimics actual real-world scenario where daemons do not
create a PID file at all.

Issue #343.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 63044a4b81 test: extend notify.sh with checks for 'restart serv' and 'reload'
Issue #343.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 4e74f9783a test: minor fix to 'serv' test service
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg c3c662fe64 initctl: ensure 'cond get' support flux state
The 'initctl cond' commands were initiallý added to only manage usr
conditions.  Recently the 'cond get' command was expanded to allow
reading general conditions as well.

However, since general conditions support three states the command
returned 'on' for conditions that were in flux.  This patch fixes
that oversight.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:34:05 +01:00
Joachim Wiberg 7c2764790c Issue #343: READY state lost when service is restarted after crash
The svc_set_state() function not only handles state transitions, it also
makes sure to cancel any outstaing service timeouts.  Before the service
readiness support was added, service conditions were simply a single bit
of information, so clearing its conditions before changing states had no
impact.  With readiness we have multiple bits of information that are
set as conditions.

When restarting a crashing service we set a timeout to retry restarting,
or ensure the service is now running.  This means that the service can
signal READY before RUNNING is reasserted, but the RUNNING state can be
set also by the service state machine, so we need to ensure we do not
re-assert RUNNING if it's already set, and thereby clearing READY.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-15 06:33:55 +01:00
Joachim Wiberg 732d500cf5 Avoid reserved keyword 'new'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-13 07:50:45 +01:00
Joachim Wiberg 4e1ac726ac Minor, update debug message to align PID output
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-13 07:49:17 +01:00
Joachim Wiberg fba58085c6 test: verify readiness notification on crash/restart
Issue #343 reports that readiness notification on crash/restart is lost.
This update to notify.sh reproduces that bug.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-12 09:03:10 +01:00
Joachim Wiberg d1ebb255c6 initctl: add 'kill' alias to 'signal' command
Principle of least surprise.  When no even the maintainer remembers
the correct command, it is time to add an alias.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-12 09:00:57 +01:00
Joachim Wiberg a39ee0b44d Fix #342: prevent certain commands at bootstrap and shutdown
Over the years there have been multiple cases of invalid and/or unsafe
uses of signals and initctl commands at bootstrap and shutdown.  These
cases cannot be safely supported.  This commit locks down finit a bit
to avoid the most common cases.

If you run into this, please open a new discussion at GitHub and we'll
talk about it.  Maybe I've been overzealous or you have another use-case
that warrants opening up some or parts of the API.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-10 17:18:50 +01:00
Joachim Wiberg 947d91acd5 Minor, whitespace only
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 10:51:12 +01:00
Joachim Wiberg 8c206d52e5 Fix imlicit declaration build warning
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 10:49:32 +01:00
Joachim Wiberg 0f445dc9f4 doc: add missing /etc/finit.d and fix indendation (untabify)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 10:38:14 +01:00
Joachim Wiberg c71b35e61a doc: document recommended filesystem layout + automounter
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 10:00:43 +01:00
Joachim Wiberg d20308b7d7 Follow-up to e4e54a8: spellcheck hook name
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 09:08:33 +01:00
Joachim Wiberg e4e54a88d9 Adjust boundaries for when conditions are allowed
Not until fs_mount_all() has completed do we have /run, or /var/run, and
never after shutdown has been initiated.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 09:00:35 +01:00
Joachim Wiberg 308b7c5197 Follow-up to #338: return early if conditions are not available
As pointed out by Jack Newman in issue #338, when conditions are not
available (at boot before cond_init(), or shutdown when /var or /var/run
have been unmounted) we should not try to create any conditions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 08:17:09 +01:00
Joachim Wiberg 89772bf6c0 Merge pull request #338 from JackNewman12/shutdown-hook-fix
Fix shutdown hooks not running

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 08:08:32 +01:00
Joachim Wiberg 077002e647 doc: follow-up to 02a110e, rename new section, add list of runlevels
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 07:07:18 +01:00
Joachim Wiberg 988cdf9878 Merge pull request #341 from troglobit/reboot-delay
Add support for optional reboot delay

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-09 06:25:45 +01:00
Joachim Wiberg 97db2078f5 Follow-up, spell check
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-08 14:19:09 +01:00
Joachim Wiberg 02a110e6c2 Fix #336: update main config.md Introduction with new .conf overview
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-08 14:18:30 +01:00
Joachim Wiberg 681ef8de6f doc: minor, styling
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-08 14:18:30 +01:00
Joachim Wiberg 0147e0e64f Minor, ensure pointer is legit in --enable-static mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-08 14:18:30 +01:00
Joachim Wiberg 311c6be9aa Add support for optional reboot delay
Slightly different take on issue #334 making it optional, possible to
enable per system.

    reboot-delay <0-60>           # default: 0 (disabled)

When enabled (non-zero), runs after filesystems have been unmounted,
the root filesystems has been remounted read-only, and sync(2) has
been called, twice.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-08 09:37:19 +01:00
Joachim Wiberg c22d123ab0 plugins: sys.so and usr.so must run after bootmisc
The bootmisc plugin sets up the base path for finit and figures out if
the system has /run or /var/run -- needed by the other plugins.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 18:07:40 +01:00
Joachim Wiberg 96b5991355 .github: ::set-output is deprecated
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 18:04:45 +01:00
Joachim Wiberg 06bd352863 .github: fix missing quote
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 17:59:47 +01:00
Joachim Wiberg a918f7497a .github: revise weekly check
For details, see https://stackoverflow.com/a/67527144/1708249

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 17:56:42 +01:00
Joachim Wiberg 8bbae3d414 plugins: ensure /var/run/finit/cond exists
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 17:42:49 +01:00
Joachim Wiberg bfd1f36fdd .github: spellcheck yaml
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 17:26:08 +01:00
Joachim Wiberg 6fc702a560 .github: enable manual trigger as well
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 17:24:45 +01:00
Joachim Wiberg 40dcc5541b .github: split build in a regular build+test and weekly distcheck
This to save time during regular pushes and pull requests, the tests
run from distcheck take a long time, and they're not getting fewer.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 17:19:33 +01:00
Joachim Wiberg 5b41c6e327 HOOK_BASEFS_UP must run as soon as all filesystems are mounted
At this hook point the bootmisc.so plugin runs and creates all relevant
system directories.  Much of the rest of system bootstrap relies on this
so it should be called together with the other FS hooks as soon as all
the filesystems have been mounted.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 15:09:50 +01:00
Joachim Wiberg 4bd81b4d4c conf_saverc: enaure /var/run/finit exists first
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 15:08:58 +01:00
Joachim Wiberg c29589c2ca initctl: add support for --json to list command
This allows for easy access to the *disabled* services:

    root@anarchy:~# initctl ls --json |jq '.available - .enabled'
    [
      "chronyd.conf",
      "dnsmasq.conf",
      "gdbserver.conf",
      "inadyn.conf",
      "inetd.conf",
      "isisd.conf",
      "lldpd.conf",
      "mstpd.conf",
      "ntpd.conf",
      "ospf6d.conf",
      "ospfd.conf",
      "querierd.conf",
      "ripd.conf",
      "ripng.conf",
      "sshd.conf",
      "syslogd.conf",
      "telnetd.conf",
      "uftpd.conf",
      "wpa_supplicant.conf",
      "zebra.conf"
    ]

As discussed in PR issue #335

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 14:00:28 +01:00
Joachim Wiberg 73b89430e4 Fix #340: Finit ignores deleted/moved .conf files in /etc/finit.d
Works locally with/without this patch, but I cannot understand why it
would ever be needed in the first place, so removing.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 14:00:28 +01:00
Joachim Wiberg 2a62fa3d85 Fix #329: add support for multiple args to initctl cond set/clr
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 14:00:16 +01:00
Joachim Wiberg 4542fdb5bb Fix #339: use absolute path in enabled symlinks
When the /etc/finit.d/enabled directory is a symlink elsehwere, e.g.,
/var/finit.d/enabled, a relative symlink will not work.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 11:30:46 +01:00
Joachim Wiberg 525aaf1f66 plugins: s/Initializing/Seeding/ better choice of words
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 09:43:27 +01:00
Joachim Wiberg 910dac5f3a Merge pull request #337 from JackNewman12/autoreload-fix
Fix AUTO_RELOAD #define typo

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-07 09:31:57 +01:00
Jack Newman 2a5c319084 Fix shutdown hooks not running
sig.c was calling plugin_run_hooks for the shutdown hooks, but the
shutdown hooks do not have registered callbacks in hook-scripts.c

Instead directly call plugin_script_run()
2023-01-06 10:29:58 +10:00
Jack 612f4fe288 Merge branch 'troglobit:master' into autoreload-fix 2023-01-06 10:00:03 +10:00
Jack Newman c058c05651 Fix AUTO_RELOAD typo 2023-01-05 09:00:51 +10:00
Joachim Wiberg 0de4578e50 Merge pull request #333 from JackNewman12/config-reload
Mark service dirty if the command line args have changed

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-04 17:00:29 +01:00
Jack Newman b9e94e4695 Consider a service dirty if the command line args have changed
This fixes `initctl reload` correctly restarting all daemons that have new
command line arguments.

Previously command line arguments changes were only acted upon if the
service was explicitly reloaded `initctl reload myservice`.
2023-01-04 13:31:41 +10:00
Joachim Wiberg 9887003b25 doc: clarify and fix confusing pid:/run/bar.pid example
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-01-02 00:30:19 +01:00
Joachim Wiberg f6bfbc86e9 Drop mdevd plugin, not needed since notify:s6 support was added
For an example how to set it up, see the finit-skel repository:

https://github.com/troglobit/finit-skel/blob/main/skel/etc/finit.d/available/mdevd.conf

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-21 11:57:48 +01:00
Joachim Wiberg 4c90b51d80 doc: update conditions wrt new devmon plugin
Issue #185

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-20 14:08:03 +01:00
Joachim Wiberg 48cfbfe5ed Follow-up to 685e0a80, drop extra snprintf() args
Issue #332

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-20 13:35:19 +01:00
Joachim Wiberg f98be8540f test/devmon.sh: new test
Issue #185

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-19 17:20:21 +01:00
Joachim Wiberg 17c69fef3d Fix #185: add devmon support, <dev/foo> condition provider
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-18 22:21:15 +01:00
Joachim Wiberg c586204fc1 New API, iwatch_add1() with no preset mask
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-18 22:21:15 +01:00
Joachim Wiberg ff5e3219be Optional debug support of actual execve() args
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-18 14:43:51 +01:00
Joachim Wiberg 07c52f1566 Improve logging of modified command args, show parsed args as well
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-18 14:43:51 +01:00
Joachim Wiberg 0e04ffd68f service_register(): guard against loss of command args
We use strtok() quite extensively in .conf parser.  Parsing of a service
line is split in two, with the command arguments being parsed very late.

Adding a new sub-option parser, based on strtok(), risk losing command
arguments.  Thus, change to use strtok_r() so that we always know where
the arg parser is.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-18 14:43:51 +01:00
Joachim Wiberg 8b5435908c Simplify, conds[] need not be static
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-17 15:19:08 +01:00
Joachim Wiberg 3a74310aa6 Drop deprecated autoconf statements
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-16 15:45:36 +01:00
Joachim Wiberg 180859a429 plugins: update mdevd to use new notification framework
Last update of plugin before dropping it in favor of an equivalent .conf
snippet.  (Pending new finit-conf repository).

Issue #327

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-16 13:54:46 +01:00
Joachim Wiberg 1d92819d92 Add more progress messages at shutdown/reboot
This patch changes the way progress for unmounting filesystems is
displayed.  To see each filesystem being unmounted Finit now must
be in debug mode.

A general "Unmounting filesystems ..." have been added instead as well
as progress for last hook script and finall call to reboot().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-16 13:27:24 +01:00
Joachim Wiberg cfa871c548 doc: update with some of the latest changes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-16 13:09:47 +01:00
Joachim Wiberg 685e0a80a4 Fix #332: silence confusing [ OK ] status progress from modules-load
This change drops the confusing status progress output, which was always
OK since the actual modprobe operation runs in the background.  No need
to show status of the "added a task to finit, found modprobe" command.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-11 10:05:53 +01:00
Joachim Wiberg 19847d2997 plugins/modules-load: change default, load modules in runlevel S
Instead of waiting until runlevel 2, 3, 4, or 5 to load modules with
this plugin, we should load modules as soon as possible, i.e., in
runlevel S.  This also means that Finit will clean up all the tasks
so we don't have to see them in `initctl status`.

To allow users to revert to the old behavior, we add a new setting

    set runlevel 2345

that per modules-load.d/file changes the runlevel for all modprobe
commands.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-10 13:29:53 +01:00
Joachim Wiberg 7108b0e789 test: disable shell exit on error during shutdown phase (after test)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 10:30:35 +01:00
Joachim Wiberg 7eac0b0341 test: dump shell options to debug 'exit status: 143'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 09:09:04 +01:00
Joachim Wiberg 33ec4e7d19 test: instrument shutdown to debug failing tests on GitHub
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 08:07:40 +01:00
Joachim Wiberg 1b78f2b364 .github: test with libite and libuev from distro (new!)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 07:40:04 +01:00
Joachim Wiberg 119363ff75 .github: attempt to split configure line
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 07:32:54 +01:00
Joachim Wiberg f89b508c01 .github: don't fail because tree returns non-zero ...
Has it always been like this, or have they "recently" changed the
behavior of the tree command?

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 07:32:02 +01:00
Joachim Wiberg dda8c7c3ac .github: split install & inspect into separate steps to debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 07:26:20 +01:00
Joachim Wiberg e2d9aab70d .github: step libuEv and libite versions
GitHub have now stepped their 'ubuntu-latest' image to Ubuntu 22.04.
With that comes the GLIBC bump to >= 2.34 which require aligning the
time bits and file offset bits to 64 bits.  This is libuev v2.4.0.

We also make sure to align libite to the same version between the
workflows to avoid any nasty surprises.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-07 07:00:51 +01:00
Joachim Wiberg 3df154b798 Fix #325: allow custom modprobe command in modules-load.d plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-06 14:01:20 +01:00
Joachim Wiberg 3d9cbaf040 fgetval(): handle extra separators betwee key and value
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-12-06 14:00:31 +01:00
Joachim Wiberg 70c2939596 Fix #315: add environment variables to hook scripts
All hook scripts are called with at least one environment variable set,
FINIT_HOOK_NAME, useful when reusing the same hook script for multiple
hook points.  It is set to the string name, also used by the path, e.g.,
hook/net/up.

For all hook points from hook/sys/shutdown and later, FINIT_SHUTDOWN is
also set, to one of:  halt, poweroff, reboot.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 22:51:40 +01:00
Joachim Wiberg d4ee44e27f doc: minor, fix spelling
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 22:51:06 +01:00
Joachim Wiberg 0498326962 Fix #318: only show "[ OK ] Calling foo" progress for runparts ...
For all other run_parts() use-cases we just give the script control over
stdout/stderr to prevent clobbering ANSI color escape codes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 21:49:30 +01:00
Joachim Wiberg c84ababbd4 Fix #317: make sure hook scripts don't run twice
Also fixes #316

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 21:44:31 +01:00
Joachim Wiberg 67ea42c87b Reduce debug noise from run_parts() slightly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 21:12:30 +01:00
Joachim Wiberg a2ab45cad7 network: log output from ifup without trailing newline
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 21:08:50 +01:00
Joachim Wiberg f3fcca6150 Revert "Refactor, enter main event loop earlier"
This reverts commit 03c08d3970 due to it
breaking handling of bootstrap tasks, causing endless boot loop.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 17:54:32 +01:00
Joachim Wiberg 2b65d8f1e9 Follow-up to 9046b6f, don't warn() on empty lines, drop line debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 17:54:32 +01:00
Joachim Wiberg 7c2abf69c4 Add missing (new) service state strings
Also, reorder to match state enum order.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-23 17:54:32 +01:00
Joachim Wiberg 457c0a246f Fix #320: close API/IPC socket immediately in shutdown/reboot
This protects against any hook scripts or services calling initctl when
we're going down.  There's no way to service these requests anyway, so
no point in keeping the socket open.  This way initctl can check for
the existence of the socket before attempting any IPC command.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-22 08:10:49 +01:00
Joachim Wiberg 1a439baf68 Further improve runparts/hook script docs, add limitations
Issue #320

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-22 08:06:26 +01:00
Joachim Wiberg 68ce78b3fe Fix #319: add missing HOOK_NETWORK_DN
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-21 20:04:32 +01:00
Joachim Wiberg 72e27e99e5 .github: run tests as recommended in test/README.md
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-21 20:04:32 +01:00
Joachim Wiberg a436382886 Improve documentation for runparts and hook scripts
Issue #315.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-21 18:11:58 +01:00
Joachim Wiberg 6bbfc7b1f1 getty: extend issue parser with /etc/os-release support
Add support for /etC/os-release, with fallback to /usr/lib/os-release,
overriding some traditional modifiers with the os-release variant.  The
traditional values for these modifiers are taken from uname, which on
Linux systems are pretty useless since they always return the kernel
name and version instead of the distro/OS values.

E.g., \s becomes PRETTY_NAME instead of 'Linux' and \v becomes the
pretty VERSION, while \r becomes VERSION_ID.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-20 10:44:30 +01:00
Joachim Wiberg 006dd12b09 initctl: recactor, share fgetval() with rest of Finit
Note: this highly useful function should probably migrate to libite.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-20 10:43:30 +01:00
Joachim Wiberg d3a0631390 Add references to service-per-conf and link to finit.conf(5)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-19 10:11:18 +01:00
Joachim Wiberg 9046b6fe2b Fix #186: allow continuation character in .conf files
This change makes it possible to use the standard continuation character
in .conf files.  For example:

    service [23456]     \
         log:stderr     \
         <!pid/foo>     \
         bar            \
         -- This is the bar service, it depends on foo # comment that's dropped

Notice the additional support for comments on .conf lines.  If you use
the comment character today you must change to escape it.  Like this:

    service :2 foo -- Foo \#2

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-19 10:11:11 +01:00
Joachim Wiberg f217794773 Fix #314: call cond_update() also when removing all service/foo/*
When removing a whole tree of conditions we need to walk through each
and call cond_update() to ensure that all dependant tasks are stopped
and put back in waiting.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-15 09:24:54 +01:00
Joachim Wiberg 11e02fabb3 test: update test to detect all corner cases of bug #314
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-15 09:24:28 +01:00
Joachim Wiberg 8b9bb1c7ce Fix #314: skip restart if conditions are lost
This change prevents Finit from attempting to continue restarting
crashing services that've lost their conditions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-15 01:03:10 +01:00
Joachim Wiberg 2a7abf1db0 test: enable new depserv test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-15 00:56:03 +01:00
Joachim Wiberg 34b8c5a3ba test: check for spice on Arrakis, I hear it's good
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 23:45:37 +01:00
Joachim Wiberg 8e056422fc test: new shell helper function run()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 23:45:17 +01:00
Joachim Wiberg 94e8b185b4 test: add some spice to serv
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 23:45:04 +01:00
Joachim Wiberg 996d3960e1 test: touch pidfile on SIGHUP, the default Finit notify mechanism
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 18:53:24 +01:00
Joachim Wiberg 3b19734f63 test: new regression test, verify dependency vs restart handling
Bug #314

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 08:39:10 +01:00
Joachim Wiberg 64e7c0cd9e test: -P implies -p
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 08:18:18 +01:00
Joachim Wiberg 4d0c88bd7a man: add missing period at end of sentences
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 07:33:44 +01:00
Joachim Wiberg 9e8c601507 man: rewrite status command description
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-14 07:28:59 +01:00
Joachim Wiberg b26104466a Overdue version bump into v4.4-dev release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 21:54:22 +01:00
Joachim Wiberg bd25bf65ce test: ensure /run is a unique ramdisk for each test instance
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 21:54:01 +01:00
Joachim Wiberg edd3b41adf .github: only archive .log files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 18:20:17 +01:00
Joachim Wiberg 28ca3fcc5c .github: restore -j1 for distcheck
Hopefully prevent spurious failures, dunno why atm., parallel checks
should work now. (Works locally with make -j20 check)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 16:50:45 +01:00
Joachim Wiberg 3b601d0115 Fix issue with services suddenly converting to 'forking' type
When a service starts up the pidfile.so plugin automatically detects any
new pidfiles that show up in the system.  If it finds a matching service
which does not have a custom configured pidfile, it auto-assigns the
detected pidfile to the service.

However, when the user later calls `initctl reload` to update the
running configuration set, Finit service_register() mistakenly will
set the service to forking since it now suddenly has a pidfile ...

The new style way of configuring a forking service is to set the

     type:forking

option.  The old compat way was to set `pid:!/path/to/pidfile`, so
in service_register() we must be very careful not to interpret the
registered pidfile as confiugred by the user -- i.e., check the pid
option.

Bug #313

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 16:35:58 +01:00
Joachim Wiberg 1ef9a9f128 Cancel pending restart timer on initctl stop/start/restart/reload
When a service crashes Finit launches a restart timer that periodically
will try restart the service.  If a user calls `initctl stop foo` finit
must cancel this timer callback, otherwise we may end up with a weird
state where Finit thinkgs the service is running, but pid: 0, i.e., not
started.

Note: this fix has been expanded upon from the proof of concept
      submitted by Jack Newman.

Bug #313

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 16:34:50 +01:00
Joachim Wiberg d612a86c3b test. drop fozza dependency
Obvious dev. test :-)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 16:19:54 +01:00
Joachim Wiberg 5d3cd05202 .github: split distcheck into separate check + distcheck steps
- We want log files from check archived
 - We want to verify that checks can run from tarball

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 16:13:32 +01:00
Joachim Wiberg 726f7ba229 test: new regression test for bug #313
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 15:53:48 +01:00
Joachim Wiberg 61dab8bd75 test: add support for new helper functions, skip/fail/check_dep
Also, make sure to exit each test with the correct exit status.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 15:52:37 +01:00
Joachim Wiberg a7e81f45c6 test: slay must cache oldpid to an instance specific ramdisk
Running tests in massive parallel causes failing startk-kill-service.sh.
This in turn was caused by the shared test tool 'slay' that cached its
"oldpid" file to a shared area of the rootfs (tenv-root).  It must use a
test instance-specific ramdisk.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 12:38:14 +01:00
Joachim Wiberg be4062f019 test: log service.sh output to stderr
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 12:37:57 +01:00
Joachim Wiberg 035f06e137 initctl: dump svc type and forking in JSON output
First set of svc_t properties not possible to debug otherwise.  See
issue #313 for a background.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-11 12:11:24 +01:00
Joachim Wiberg 2f91ab5eac Fix #235: support for overriding /etc/finit.conf and /etc/finit.d
This change adds support for a new command line option finit.config=PATH
which can be used to tell Finit to start with /etc/factory.conf instead
of /etc/finit.conf.

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
as follows to override /etc/finit.d:

    rcsd /etc/factory.d

Manually verified in myLinux

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-10 17:21:33 +01:00
Joachim Wiberg f6b5b0260b Fix #312: restart services with respawn set, e.g. ttys, immediately
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 20:27:16 +01:00
Joachim Wiberg bb9bd7e0b0 Fix #311: document how to combine device tree with conditions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 18:20:57 +01:00
Joachim Wiberg 287abd5c7c Fix #254: document limitations in rc.local and runparts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 18:05:21 +01:00
Joachim Wiberg d27f114742 Document new -j,--json output option in initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 16:43:11 +01:00
Joachim Wiberg 505c370898 Follow-up to 912a281: let s6 services use oneshot 'ready' condition
Since the s6 readiness notification mechanism closes the notification
socket to Finit (as per s6 spec.) the 'ready' condition should then
always be asserted, even across system reconfiguration events.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 14:27:38 +01:00
Joachim Wiberg f26009ec3e Follow-up to 912a281: reassert 'ready' condition on reload
Services that have not been changed, or otherwise needed to be reloaded,
need to have their 'ready' condition reasserted on 'initctl reload',
otherwise it will remain in 'flux'.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 14:25:49 +01:00
Joachim Wiberg 8425bc313b initctl: add --json support for condition status and dump
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 14:11:46 +01:00
Joachim Wiberg f4d3cf2835 Fix #273: add JSON output option to initctl status [foo]
This change adds support for JSON output to `initctl status` using a
`-j` or `--json` command line option.

Example:

    root@anarchy:~# initctl status -j mdevd
    {
      "identity": "mdevd",
      "description": "MDEVD Extended Hotplug Daemon",
      "status": "crashed",
      "exit": { "code": 100 },
      "origin": "built-in",
      "command": "/bin/mdevd -C -O 4",
      "restarts": 10,
      "pidfile": "/run/mdevd.pid",
      "pid": 0,
      "user": "root",
      "group": "root",
      "uptime": 0,
      "runlevels": [ "S", 1, 2, 3, 4, 5, 7, 8, 9 ]
    }

The excellent tool `jq` can be used to extract certain parts of the
output for further scripting.  E.g. `initctl status -j foo | jq .exit`

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-09 13:49:03 +01:00
Joachim Wiberg bd5cf7c9eb Drop confusing leading / in sig2str() and code2str()
This was added for the benefit of `initctl status foo`, but we have
other users of these functions that don't expect a leading slash.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-08 22:37:13 +01:00
Joachim Wiberg 4709394ae6 .github: bump actions to fix Node.js 12 warnings
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-08 21:49:17 +01:00
Joachim Wiberg 729002ef82 Fix regression introduced in d810ac0: drop 'return;' statement
The condition subsystem starts very late, this should not affect running
callbacks in plugins.  In d810ac0 an extra return statement was included
in a new if() statement block that run plugins hooks.  This caused a
massive regression in how Finit starts up and runs.

 - drop return; statement that causes regression
 - move #ifdefs to surround if() statement

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-08 21:32:07 +01:00
Joachim Wiberg 58b075dbf2 Follow-up to 7cfbca6, remove extra , that disables all plugins
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-08 19:49:23 +01:00
Joachim Wiberg d679840fa3 m4: minor, whistespace cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-08 19:48:56 +01:00
Joachim Wiberg 0e70bdbf6a Handle if building with hook script but no script path
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-07 19:04:59 +01:00
Joachim Wiberg d810ac00bc Fix #302: add support for running scripts at shutdown
This change adds support for running hook scripts at two new hook points
during the shutdown process:

 - HOOK_SVC_DN: after all services and non-reserved processes have been
   killed (and collected)
 - HOOK_SYS_DN: after all file systems have been unmounted, *just prior*
   to Finit calling reboot() to shut down or reboot the system

See doc/plugins.md for details and an example.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-07 07:01:21 +01:00
Joachim Wiberg 5f74adad06 Take down condition subsystem at shutdown/reboot
Leave the system as it was when we found it.  Also, we'll build things
on top of cond_is_available() to handle pre bootstrap and pre shutdown
tasks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-07 06:44:21 +01:00
Joachim Wiberg 585ac51f45 Refactor local helper function, change readdir() to recursive nftw()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-07 06:43:02 +01:00
Joachim Wiberg ce36797506 doc: update documentation for hook-scripts and all hook points
Follow-up to b62c975.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 14:46:17 +01:00
Joachim Wiberg a61c2ceb79 Fix #310: service restart delay not working
Always use the configured restart delay for crashing services.  If no
delay is configured, we default to an initial 2000 msec for forking
daemons and start-stop scripts, and 1 msec for non-forking daemons.

We must track the increasing delay in the svc_t because processes can
fail quickly in differing ways.  E.g, the test daemon 'serv' behaves
extra evil by crashing right after having created its pidile (i.e.,
when it's signaled to Finit it is 'ready' and all is dandy ...).  If
we don't track the delay per svc_t we would restart 'serv' after only
1 msec every (other) time.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:54:56 +01:00
Joachim Wiberg 383339dc4a Add timestamp to log messages when in fallback and logging to stderr
When there is no log daemon, and we are running in a container, or we
cannot log to the kernel ring buffer, then we log to stderr.  This patch
improves the log output by prefixing each message with a timestamp.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:54:55 +01:00
Joachim Wiberg 36b46ba134 Improve and increase debug messages for dying services and scripts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:54:55 +01:00
Joachim Wiberg a2d0859bf1 test: serv: die immediately (no sleep) if started with -c
We want to stress test Finit as much as possible.  We add a small log
though (which admittedly gives us a little bit of a delay) just to see
what happens when debugging failing tests.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:54:55 +01:00
Joachim Wiberg d5f542dcca test: slay must wait up to five seconds before giving up
Default max delay before restarting a crashing service is five seconds.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:54:55 +01:00
Joachim Wiberg fb86042cb3 test: add crashing.sh, verify oncrash:script support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:07:10 +01:00
Joachim Wiberg 359fb8530e Minor, spelling
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:07:10 +01:00
Joachim Wiberg 5dd1a676ac Fix #282: add support for oncrash:script to call post:script action
This patch adds the oncrash:script option to call the post:script
action, if defined, for a crashing service.  The EXIT_CODE variable
sent to the script is set to `crashed`.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-11-06 13:07:10 +01:00
Joachim Wiberg 278994781b Follow-up to c9f1bff, only allow daemons to reach 'READY' state
By definition, the readiness notification is reserved for daemons.  May
possibly be extended also to sysv services later.  If you arrive hare
with that request, please file an issue with the project.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 23:52:32 +02:00
Joachim Wiberg c9f1bff419 Refactor ready:script to support new notify framework
Follow-up to abcb3ce, calling the service ready:script when readiness
has been signaled to or detected by Finit.

Issue #300

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 22:11:12 +02:00
Joachim Wiberg 1f9621cf4b Fix #288: enable built-in sulogin in Alpine and Void Linux builds
Always better to have at least one sulogin available, if the system
provided sulogin isn't available we fall back to the built-in one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 21:08:44 +02:00
Joachim Wiberg 9458e4e66b Update docs with new readiness notification support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:36:15 +02:00
Joachim Wiberg ff08038bdc test: add notify.sh to distribution
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:13:04 +02:00
Joachim Wiberg ce330e8bf1 Minor, whitespace
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:12:12 +02:00
Joachim Wiberg 47d837bf6a test: new test, notify.sh, systemd and s6 readiness notification
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:06:26 +02:00
Joachim Wiberg 912a281ee2 Fix #299: add support for service readiness notification
This patch adds service readiness notification to support daemons
employing systemd and s6 notification.  Complementing the native
Finit readiness support using PID files that exist already.

The two have slightly different ways of implementing readiness:

 - https://www.freedesktop.org/software/systemd/man/sd_notify.html
 - https://skarnet.org/software/s6/notifywhenup.html

Finit now provides both a NOTIFY_SOCKET environemnt variable, for
systemd, and a way to start s6 daemons with a descriptor argument.

For details on the syntax, see the `service` documentation.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:06:26 +02:00
Joachim Wiberg 6e863a7872 client_connect(): ensure socket is also reset to -1 on error
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg 488e6c2adf Follow-up to d82d1197: ensure PID file doesn't exist
Before we clear all conditions for a service, ensure pid_file_read()
didn't fail due to the PID file not (yet) containing a valid number.

If the process dies while starting up, and subsequently removes its
PID file, then the file shouldn't exist.  However, if we get inotify
before the process has finished writing the PID, then we just return
and wait for the next event.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg e177b866a9 initctl: follow-up to e1c59a25, simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg 15dcebd0f1 .globalrc: skip tenv-root/ in gtags indexing
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg af60d8d3ec initctl: fix off-by-one in column width when dumping conditions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg 5a32fdb309 Simplify error message, same style as other log messages in module
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg 19f5cb1d01 logit: fall back to package name if $LOGNAME and $USER are unset
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-16 20:01:29 +02:00
Joachim Wiberg c5bca5f80a Add 'function():' prefix to new log macros, like the old ones
Also, mark old log macros as DEPRECATED.  Will be removed in a later release.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-09 12:52:49 +02:00
Joachim Wiberg d22dea74e3 Finalize refactor to new log macros, following-up to 37e3be9
This possible also mitigates the issue tracked in #307.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-10-09 12:52:40 +02:00
Joachim Wiberg dc547317c9 .github: drop setup-root.log from failed builds, no longer a test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 11:45:27 +02:00
Joachim Wiberg 6bf5d98569 test: fix distcheck BusyBox downloader for users with ~/.cache
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 07:48:36 +02:00
Joachim Wiberg 29dea38f50 test: fix path to setup-root.sh in distcheck mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 05:53:57 +02:00
Joachim Wiberg 34567dd244 Use new log macros also in keventd and iwatch.c
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 05:49:39 +02:00
Joachim Wiberg a3584c6ca3 .github: enable parallel distcheck
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 05:06:28 +02:00
Joachim Wiberg 165d0633f6 test: add support for make -j17 check massive parallel testing
This was a tough nut to crack.  Spent 3-4 calendar weeks (getting blurry
now) to try and figure it all out.  In the end, the following changes
were necessary:

 - Ensure setup-root.sh is guaranteed to run before any of the tests
 - Give all tests their unique /etc, /tmp, and /run inside the chroot.
   Turns out tmpfs+overlayfs can be used unprivileged inside an unshare
 - A static BusyBox binary with support for mount helpers so we can
   do the mkdir magic in skel/etc/fstab for the /etc overlay

The last item turned out to be a bit of a roller coaster ride of its
own.  First of all, the original binary we used was from the upstream
BusyBox project[1].  I was sure it couldn't be that hard to enable the
CONFIG_FEATURE_MOUNT_HELPERS ... oh boy was I wrong.  To make long and
tedious story short; two new projects on GitHub were created for this
task: 1) troglobit/misc[2] to house a mirror of https://musl.cc
toolchains and 2) troglobit/busybox-builder[3] to download, patch, and
build the thing using a .config from the myLinux[4] project.  Patching
this .config was necessary, however, since musl libc is strict POSIX
and does not have any of the BSD extensions, e.g. REG_STARTEND that is
in GLIBC regex(3).

[1]: https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/
[2]: https://github.com/troglobit/misc/releases/tag/11-20211120
[3]: https://github.com/troglobit/busybox-builder/releases/tag/1_35_0
[4]: https://github.com/troglobit/myLinux

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 04:38:24 +02:00
Joachim Wiberg 9f7ba1ba79 test: upgrade BusyBox binary to v1.35.0 w/ mount helpers
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-25 04:37:16 +02:00
Joachim Wiberg 03c08d3970 Refactor, enter main event loop earlier
This delays the start of the .conf monitor and service initialization
slightly to allow the event loop to run earlier to process any events
from the initial setup.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-05 13:47:08 +02:00
Joachim Wiberg 37e3be9a0d Refactor to share err/warn log API between daemon and client code
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-09-05 09:57:45 +02:00
Joachim Wiberg 10437887fc Minor, staticify local function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-29 07:49:40 +02:00
Joachim Wiberg faf1c6233d doc: document new auto-conditions from run/task/service states
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-24 19:55:13 +02:00
Joachim Wiberg c36960350c doc: minor, update table of contents
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-24 19:54:35 +02:00
Joachim Wiberg 51ade57723 Rename internal run/task/service states => changes condition names
This patch renames the internal states for run/task/services to avoid
any confusion with the introduction of 'ready:scripts'.

 * WAITING -> PAUSED
 * READY   -> WAITING

A service condition that used, e.g., <service/foo/ready> should now
instead use <service/foo/waiting>.

Note: A new condition with the old name <service/foo/ready> will be
      introduced shortly to signify service "readiness", a concept
      used in other PID 1, like systemd and s6.

For details, see issue #299.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-23 15:31:24 +02:00
Joachim Wiberg 9d79673cea Follow-up #278: update documentation for pre:script
The pre:scripts no longer have to be idempotent.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-23 15:23:02 +02:00
Joachim Wiberg 762bf5f031 test: minor shellcheck fix of chrootsetup.sh
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-23 09:44:24 +02:00
Joachim Wiberg abcb3ceb67 Fix #300: add ready:script for services, called when daemon is ready
In Finit a daemon can signal its readiness by creating/touching its PID
file.  An ancient UNIX concept -- a daemon creates its PID file when it
has set up signal handlers and is ready to receive IPC (signals), right
before entering its while(1) loop.

Finit took the concept a bit further, adding readiness signalling also
to daemon's by touching their PID file after having processed a SIGHUP.

For both these conditions Finit now supports a ready:script, called when
readiness is detected.  In later commits support for s6 and systemd
style readiness notification will be added that will hook the mechanism
implemented in this commit as well.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-23 09:37:18 +02:00
Joachim Wiberg 0ae5071f94 API for associtative list of svc_t and pre/post/ready support scripts
This support API is for running things like pre:script, post:script and
the upcoming ready:script for services.  A ready:script will be called
when a service notifies Finit of its readyness.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-18 14:06:43 +02:00
Joachim Wiberg e589c5b269 Refactor, create svc_find_by_cond() from api.c callback
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-18 14:00:02 +02:00
Joachim Wiberg 12e341fe73 doc: script actions *must terminate* or they will be killed
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-18 11:04:13 +02:00
Joachim Wiberg 9be3c4cb9a .github: change to ubuntu-latest, 18.04 deprecated
For details, see https://github.com/actions/runner-images/issues/6002

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-17 07:52:34 +02:00
Joachim Wiberg 433f3ce17e .github: update libite version in release build
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-17 07:52:22 +02:00
Joachim Wiberg 2c29ede0da .github: sync with changes in finit-plugins
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-17 07:52:07 +02:00
Joachim Wiberg 7cfbca6d74 Add --enable-all-plugins configure flag
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-17 07:49:11 +02:00
Joachim Wiberg b30621bb3b Minor, coding style
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-16 14:00:57 +02:00
Joachim Wiberg d6f35903d6 Fix #195: add mdevd plugin as complement to hotplug.sh
This patch adds the mdevd[1] plugin from Andy's fork of Finit, only
minor whitespace adjustment and group fix introduced, along with
updates to configure.ac and the documentation.

Currently the hotplug.sh calls mdevd-coldplug (instad of mdev -s), but
since mdevd is not guaranteed to have started and be ready, it is also
started with mdevd -C.  A better solution is to use the s6 notify code
to synchronize the start of mdevd-coldplug.  Support for this is coming
soon to Finit.

[1]: https://github.com/hongkongkiwi/finit/blob/master/plugins/mdevd.c

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-16 14:00:57 +02:00
Joachim Wiberg a61a8015d7 initctl: new command, list installed plugins
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-15 11:00:39 +02:00
Joachim Wiberg e2f070649d Fix wrong path to command in service after reload, may have changed
Service/task/run are today identified by name:id this means the path to
a command is not necessarily the basis for the identity.  Hence, an
svc_t can now be updated with new the correct path to a command.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:08:14 +02:00
Joachim Wiberg f08d375c36 Fix lingering cond in service after reload with no cond
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:08:14 +02:00
Joachim Wiberg 1c4bad5b73 initctl: only show log if PID != 0
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:08:14 +02:00
Joachim Wiberg 6c874895dc Fix #295: add reboot/shutdown/poweroff timeout -t SEC to initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 15:06:54 +02:00
Joachim Wiberg ef1a05425d Minor, reduce scope
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-08-12 08:16:50 +02:00
Joachim Wiberg fc3bad2160 Fix #284: search for plugins in /usr and /usr/local
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-07-17 22:08:05 +02:00
Joachim Wiberg 31ac933d64 Fix #294: shutdown --help mistakenly shuts down system
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-29 08:13:33 +02:00
Joachim Wiberg caa398816d Fix #250: support finit.cond=foo for cmdline <boot/foo> conditions
As a real life example, this can be useful for a mfgtest mode for the
device, wherein the bootloader can be set to finit.cond=test_mode or
finit.cond=user_mode and then specific services can start up in test
mode which wouldn't normally start.

Or even custom firmware update mode such as finit.cond=fw_update_mode
which performs some specific firmware update steps and doesn't launch
normal processes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-26 21:55:50 +02:00
Joachim Wiberg 0d57a507f3 doc: update SVC state machine with latest changes
In fbcd7d2 and 96257cc the state machine transitions were changed to
ensure that pre:scripts only run when conditions have been asserted, and
that manual:yes run/tasks end properly in 'DONE' as their siblilngs.

Also include SVG in repo for reference since that's the best export from
the original at draw.io

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-26 20:43:28 +02:00
Joachim Wiberg 96257cc5c7 Let manual:yes run/tasks also end in state 'done'
This is a follow-up to #274, in which Andy pointed out that ending up in
state 'stopped' was a bit confusing for manual:yes run/tasks.

With the following change we let all run/tasks end in state 'done', yet
still allow manual:yes ones to transition to 'ready' when the user calls
`initctl start foo`.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-25 15:41:34 +02:00
Joachim Wiberg fbcd7d2384 Fix #278: enforce conditions also for running pre: scripts
The whole point of pre: scripts is that they run just before the actual
run/task/service process, hence they need to be locked behind the same
conditions as the process.  Otherwise pre: scripts would be nothing more
than plain tasks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-25 15:05:16 +02:00
Joachim Wiberg d9dbfa23c7 plugins: minor whitespace and coding style cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-25 13:42:53 +02:00
Joachim Wiberg 91f077bd08 Merge pull request #293 from wkz/modprobe-uevent
Support for coldplugging more devices

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-23 16:55:06 +02:00
Tobias Waldekranz 5fb4a6ae8f plugins: modprobe: Support for coldplugging more devices
As it turns out, not all buses in Linux will add `modalias` attributes
to their devices in sysfs. One notable exception are MDIO buses. The
plugin's scan routine would thus not pick them up.

Fortunately, the information is always available in the device's
`uevent` attribute.

Therefore, default to looking for modaliases in the device's `uevent`
attribute instead, falling back to `modalias` only if `uevent` is not
availble (this should never happen).
2022-06-23 14:23:04 +02:00
Tobias Waldekranz e84333662e plugins: hotplug: Run after modprobe plugin, if available
Since the modprobe plugin (unsurpricingly) may cause new modules to be
loaded, it makes sense to to delay the initial device node scan until
we know that no mode nodes will show up within the next few
microseconds.
2022-06-23 14:19:30 +02:00
Joachim Wiberg ddb42fda70 Fix #289: minor spelling issue in build.md
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-21 22:28:00 +02:00
Joachim Wiberg 621eb21b42 test: regression test for issue #275
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-19 15:43:46 +02:00
Joachim Wiberg 6cc44a87c9 Fix #275: 'initctl status foo' should list all instances
This is a fix to a regression introduced in e51587c.  The idea is to
have 'initctl status foo' show all instances of foo, provided there are
more than one. E.g.

    root@anarchy:~# initctl -t status foo
    0    foo:1        stopped  [--234-----] hej knekt
    0    foo:2        stopped  [--234-----] hej snigel
    0    foo:3        stopped  [--234-----] hej kalas

And not foobar:

    0    foo:1        stopped  [--234-----] hej knekt
    0    foo:2        stopped  [--234-----] hej snigel
    0    foo:3        stopped  [--234-----] hej kalas
    0    foobar       stopped  [--234-----] hej foobar

If you type an exact match you can drill down like so:

    root@anarchy:~# initctl status foo:1
         Status : stopped (code=exited, status=0/SUCCESS, manual=yes)
       Identity : foo:1
    Description : hej knekt
    ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-19 15:39:01 +02:00
Joachim Wiberg 5f908cae49 Fix #274: allow manual:yes on sysv/service/run/task
Prior to this change only service stanzas respected the manual:yes
option, now it (should) work on any svc_t type.  Not tested on ttys.

The initctl tool has been given an extra manual=yes output for these
types of run/task/service entries, shown only when the manual option
is set.

Example:

root@anarchy:~# initctl status foo.sh
     Status : stopped (code=exited, status=0/SUCCESS, manual=yes)
   Identity : foo.sh                                ~~~~~~~~~~~~
Description : Hej foo
     Origin : /etc/finit.d/enabled/foo.conf
Environment :
Condition(s):
    Command : /root/foo.sh
   PID file : none
        PID : 0
       User : root
      Group : root
     Uptime : N/A
     Starts : 1
   Restarts : 0 (0/10)
  Runlevels : [--2345----]

Notice also the new `Starts : 1` which is a counter for the number of
starts in the current runlevel.  On runlevel change it is reset to 0.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-16 17:57:55 +02:00
Joachim Wiberg 6de67ed107 Fix #268: Merge branch 'dbus-fixes'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-15 06:35:30 +02:00
Andy Savage c129f06195 plugins: create missing d-bus directories
- Prefix plugin defines with DBUS_ to allow overriding at build-time
 - Create missing directories for modern d-bus

Signed-off-by: Andy Savage <andy@savage.hk>
2022-06-15 06:33:12 +02:00
Joachim Wiberg d11dc63bc2 Fix #269: add compulsory kernel symlinks in /dev
Add missing symlinks for compulsory and recommended /dev symlinks, as
specified in the Linux Administrator's Guide, section Linux Allocated
devices.

https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-14 21:49:19 +02:00
Joachim Wiberg d904ec1602 tty: add support for 'passenv' flag to /bin/login
This change makes it possible tell the bundled/built-in getty to call
/bin/login with the '-p' flag, to preserve any environment set in the
Finit .conf files.

Issue #286

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-13 21:20:16 +02:00
Joachim Wiberg 2a98b2f055 Minor, fix shadowing of variable in outer scope
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-13 21:18:22 +02:00
Joachim Wiberg 6286ede985 initctl: add optional TYPE argument to 'cond dump' command
Allow filtering of conditions in raw dump, i.e., the possibility to show
only conditions of a certain type.  The TYPE argument is not validated
in any way, so any prefix match is allowed, e.g.

   initctl cond dump service/s

Shows all conditions for services starting with 's'.

Fixes issue #272

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 23:37:03 +02:00
Joachim Wiberg 4a8ca787d3 Minor refactor, reuse MATCH_CMD() from conf.c in service_register()
When adding a new service option recently, I copy-pasted and forgot to
update the length argument to strncasecmp(), so it was time to fix that
once and for all.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 23:13:50 +02:00
Joachim Wiberg 561c88a868 Allow changing restart + oncrash settings of existing services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 22:48:12 +02:00
Joachim Wiberg f8dfcdf024 Fix #279: allow restart:always, restart:-1, of crashing services
This change allows endless restarts, similar to `respawn` but honors
`restart_sec`.  There is no upper limit on the number of times Finit
tries to restart a crashing service.  Same as `restart:-1`

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 22:48:12 +02:00
Joachim Wiberg f50b5a4ac6 Fix #285: initctl restart should start crashed service
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-12 22:24:27 +02:00
Joachim Wiberg 59525b80e3 Export finit/conf.h and finit/service.h for external plugins
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-06 10:38:19 +02:00
Joachim Wiberg 0ce028add1 Fix #283: too quick timeout at bootstrap of lingering tasks
This seemed odd at first, but it turns out we seem to have adjusted the
time scale for the bootstrap worker, so we were off by ... a factor 10.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-06-03 03:45:10 +02:00
Joachim Wiberg 75c5c85f07 test: new regression test for initctl restart self
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-30 06:59:14 +02:00
Joachim Wiberg ccb84ced2e Fix #280: allow calling initctl restart foo from within foo
Move the stop+start from initctl to the state machine by allowing
stopped tasks to restart once it has been collected.  This should
prevent finit from blocking, allowing it to handle other requests
while waiting for the service's PID to be collected.

Effectively, this will allow a service to call initctl to restart
itself, as reported in issue #280.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-30 06:54:27 +02:00
Joachim Wiberg 5ef669fb6c test: fix use-after-free by switching to strdupa()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-30 06:53:37 +02:00
Joachim Wiberg 96ff9c115c Follow-up to 9304024: skip . and ..
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-18 07:25:39 +02:00
Joachim Wiberg 83aa6abb1c Fix potential NULL ptr dereference, found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:37:06 +02:00
Joachim Wiberg 9304024841 Check return value of remove() when deleting full condition path
Found by Coverity.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:35:42 +02:00
Joachim Wiberg 0de5981948 initctl: minor, fix invalid format string for print_header()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:33:45 +02:00
Joachim Wiberg c39106906d Check return value from iwatch_init(), found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:28:04 +02:00
Joachim Wiberg 929a6e7aac test: fix minor memory leak in test daemon, found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:26:48 +02:00
Joachim Wiberg ceb4d04d23 Fix potential array overrun, found by Coverity
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-05-17 14:22:36 +02:00
177 changed files with 7722 additions and 2106 deletions
+3 -4
View File
@@ -9,9 +9,9 @@ For email questions/requests/alerts there is always the risk of memory
exhaustion on the part of the maintainer(s). exhaustion on the part of the maintainer(s).
If you are unsure of what to do, or how to implement an idea or bugfix, If you are unsure of what to do, or how to implement an idea or bugfix,
open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or similar, open an issue with `"[RFC: Unsure if this is a bug ... ?"`, or use the
so we can discuss it. Talking about the code first is the best way to GitHub discussions forum, so we can discuss it. Talking about the code
get started before submitting a pull request. first is the best way to get started before submitting a pull request.
Either way, when sending an email, patch, or pull request, start by Either way, when sending an email, patch, or pull request, start by
stating the version the change is made against, what it does, and why. stating the version the change is made against, what it does, and why.
@@ -73,7 +73,6 @@ It is expected of everyone engaging in the project to, in the words of
Bill & Ted; [be excellent to each other][conduct]. Bill & Ted; [be excellent to each other][conduct].
[github]: https://github.com/troglobit/finit/
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
[gitbook]: https://git-scm.com/book/ch5-2.html [gitbook]: https://git-scm.com/book/ch5-2.html
[conduct]: CODE-OF-CONDUCT.md [conduct]: CODE-OF-CONDUCT.md
+17 -20
View File
@@ -15,7 +15,7 @@ jobs:
build: build:
# Verify we can build on latest Ubuntu with both gcc and clang # Verify we can build on latest Ubuntu with both gcc and clang
name: ${{ matrix.compiler }} name: ${{ matrix.compiler }}
runs-on: ubuntu-18.04 runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
compiler: [gcc, clang] compiler: [gcc, clang]
@@ -27,15 +27,8 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install pkg-config sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz - uses: actions/checkout@v3
wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
tar xf libuev-2.3.2.tar.xz
tar xf libite-2.5.2.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v2
- name: Static Finit - name: Static Finit
run: | run: |
./autogen.sh ./autogen.sh
@@ -43,28 +36,32 @@ jobs:
make V=1 make V=1
- name: Regular Finit - name: Regular Finit
run: | run: |
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd
make clean make clean
make V=1 make V=1
- name: Install to ~/tmp and Inspect - name: Install to /tmp
run: | run: |
DESTDIR=/tmp make install-strip DESTDIR=/tmp make install-strip
tree /tmp tree /tmp || true
- name: Check dependencies
run: |
ldd /tmp/sbin/finit ldd /tmp/sbin/finit
size /tmp/sbin/finit size /tmp/sbin/finit
ldd /tmp/sbin/initctl ldd /tmp/sbin/initctl
size /tmp/sbin/initctl size /tmp/sbin/initctl
ldd /tmp/sbin/reboot ldd /tmp/sbin/reboot
size /tmp/sbin/reboot size /tmp/sbin/reboot
/tmp/sbin/finit -h - name: Verify starting and showing usage text
/tmp/sbin/initctl -h run: |
sudo /tmp/sbin/finit -h
sudo /tmp/sbin/initctl -h
- name: Run Unit Tests - name: Run Unit Tests
run: | run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}') make check || (cat test/test-suite.log; false)
dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/setup-root.log; cat $dir/test/test-suite.log; false)
- name: Upload Test Results - name: Upload Test Results
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: finit-test-${{ matrix.compiler }} name: finit-test-${{ matrix.compiler }}
path: test/* path: test/*.log
+8 -8
View File
@@ -15,8 +15,8 @@ jobs:
coverity: coverity:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/cache@v2 - uses: actions/cache@v3
id: coverity-toolchain-cache id: coverity-toolchain-cache
with: with:
path: cov-analysis-linux64 path: cov-analysis-linux64
@@ -35,12 +35,12 @@ jobs:
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install pkg-config sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz wget https://github.com/troglobit/libuev/releases/download/v2.4.0/libuev-2.4.0.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
tar xf libuev-2.3.2.tar.xz tar xf libuev-2.4.0.tar.xz
tar xf libite-2.3.1.tar.gz tar xf libite-2.5.2.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip) (cd libuev-2.4.0 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip) (cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig sudo ldconfig
- name: Build - name: Build
run: | run: |
+23 -52
View File
@@ -7,63 +7,34 @@ on:
jobs: jobs:
release: release:
name: Create GitHub release name: Build and upload release tarball
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
outputs: runs-on: ubuntu-latest
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Installing dependencies ...
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
- name: Creating Makefiles ...
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd
- name: Build release ...
run: |
make release || (cat test/test-suite.log; false)
ls -lF ../
mkdir -p artifacts/
mv ../*.tar.* artifacts/
- name: Extract ChangeLog entry ... - name: Extract ChangeLog entry ...
# Hack to extract latest entry for body_path below
run: | run: |
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \ awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|head -n -1 > release.md |head -n -1 > release.md
cat release.md cat release.md
- name: Create release ... - uses: ncipollo/release-action@v1
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref }} name: Finit v${{ github.ref_name }}
release_name: Finit v${{ github.ref }} bodyFile: "release.md"
body_path: release.md artifacts: "artifacts/*"
draft: false
prerelease: false
tarball:
name: Build and upload release tarball
needs: release
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Installing dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config
wget https://github.com/troglobit/libuev/releases/download/v2.3.2/libuev-2.3.2.tar.xz
wget https://github.com/troglobit/libite/releases/download/v2.3.1/libite-2.3.1.tar.gz
tar xf libuev-2.3.2.tar.xz
tar xf libite-2.3.1.tar.gz
(cd libuev-2.3.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.3.1 && ./configure --prefix=/usr && make && sudo make install-strip)
sudo ldconfig
- name: Creating Makefiles
run: |
./autogen.sh
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
- name: Building release
run: |
make release
ls -lF ../
mkdir -p artifacts/
mv ../*.tar.* artifacts/
- name: Uploading release artifacts
uses: skx/github-action-publish-binaries@release-0.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
releaseId: ${{ needs.release.outputs.release_id }}
args: artifacts/*
+48
View File
@@ -0,0 +1,48 @@
name: Weekly Distcheck
# Verify that `make distcheck` works as intended: all tests pass, and
# any new files are distributed in the tarballs
on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
jobs:
schedule:
runs-on: ubuntu-latest
outputs:
run: ${{ steps.check.outputs.run }}
steps:
- uses: actions/checkout@v3
- name: Check for any commits since last week
run: |
commits="$(git log --format=%H --since='1 week ago')"
if [ -n "$commits" ]; then
echo "run=true" >> $GITHUB_OUTPUT
else
echo "run=false" >> $GITHUB_OUTPUT
fi
distcheck:
needs: schedule
if: ${{ needs.schedule.outputs.run == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
- uses: actions/checkout@v3
- name: Base run
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
make -j9 V=1
- name: Install to /tmp
run: |
DESTDIR=/tmp make install-strip
- name: Check dist tarball
run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
+7
View File
@@ -0,0 +1,7 @@
# GNU Global .conf file, see /etc/gtags/gtags.conf for details.
default:\
:tc=default@/etc/gtags/gtags.conf:\
:tc=common:
common:\
:skip=tags,TAGS,ID,y.tab.c,y.tab.h,gtags.files,cscope.files,cscope.out,cscope.po.out,cscope.in.out,,{arch}/,autom4te.cache/,*.orig,*.rej,*.bak,*~,#*#,*.swp,*.tmp,*_flymake.*,*_flymake,*.o,*.a,*.so,*.lo,*.zip,*.gz,*.bz2,*.xz,*.lzh,*.Z,*.tgz,*.min.js,*min.css,sysroot/:
+360 -1
View File
@@ -4,6 +4,364 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.4][] - 2023-05-15
--------------------
> **Note 1:** this release contains changes to the `.conf` parser. If you
> have .conf file statements with comment character (`#`) in the command
> options or description, you must now escape them (`\#`). Issue #186
>
> **Note 2:** prior to this release, runlevel `S` and `0` were after
> boot treated as the same runlevel. This caused `task [06] ...` to
> also run at bootstrap instead of just at shutdown and reboot. The
> changes made to Finit to separate `S` from `0` require you to update
> the allowed runlevels for services that are allowed to continue
> running at shutdown. I.e., change `[S123456789]` to `[S0123456789]`
> for, e.g., `sysklogd`. Issue #352
### Changes
* Add limited tmpfiles.d(5) support
This change adds very basic `tmpfiles.d/` support to Finit. Most of
the basic types are supported, but not all, so for now, please check
the code and examples for details on what is working.
* If a run/task/service command does not exist, skip registering it
This changes the semantics of Finit a bit by checking for the command
to run when registering it, skipping commands that cannot be found in
the absolute path provided in the command, or in `$PATH`
This change includes a new `nowarn` flag that can be used to prevent
Finit from warning for missing commands. See below for an example.
* Add run/task/service support for `conflict:foo` handling
* Add run/task/service support for `if:[!]ident` and `if:<[!]cond>`
Conditional loading of stanza depending on ident is already loaded (or
not), or condition satisfied (or not). E.g., do not run `mdev` if we
found and registered `udevd`, or load service only if `<boot/testing>`
condition is set.
The optional leading `!` negates the comparison, if NOT foo then ...
* Add support for static services in `/lib/finit/system/*.conf`
Slowly migrating away from hard-coded services in plugins. This way
it's possible for the user to both inspect and override as needed.
* Migrate hotplug plugin to a conditional `/lib/finit/system/hotplug.conf`
This is the first example of the just minted advanced stanza syntax with
`if:`-statements, `conflict:` handling, and `nowarn` flags.
* Initial support for template services, `foo@.conf`, similar to systemd
$ initctl show avahi-autoipd@
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
To enable ZeroConf for, e.g., `eth0`, use
$ initctl enable avahi-autoipd@eth0.conf
The enabled symlink will be set up to `avahi-autoipd@.conf` and every
instance of `%i` will in the instantiated directive be replaced with
`eth0`. Inspect the result with:
$ initctl status avahi-autoipd:eth0
* Add `devmon`, a `<dev/foo>` condition provider, issue #185
* Support for line continuation character `\` in .conf files, issue #186
service name:sysklogd [S123456789] \
env:-/etc/default/sysklogd \
syslogd -F $SYSLOGD_ARGS \
-- System log daemon
* `HOOK_BASEFS_UP` has been moved! External plugins that need to call
`service_register()`, please use `HOOK_SVC_PLUGIN` from now on.
Apologies for any inconveniences this might cause!
* getty: add support for `/etc/os-release` to replace `uname` output
This change, which has a fallback to `/usr/lib/os-release`, overrides
traditional modifiers with the os-release variant. These values were
taken from `uname`, which on Linux systems are pretty useless since
they always return the kernel name and version instead of the
distro/OS values.
E.g., \s becomes `PRETTY_NAME` instead of 'Linux' and \v becomes the
pretty `VERSION`, while \r becomes `VERSION_ID`.
* Support for overriding `/etc/finit.conf` and `/etc/finit.d` issue #235
New (kernel) command line option `finit.config=PATH` which can be used
to redirect Finit to start up with, e.g., `/etc/factory.conf` instead of
`/etc/finit.conf`.
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`
as follows to override `/etc/finit.d`:
rcsd /etc/factory.d
* Support for overriding `/etc/finit.d` from the alternate `finit.conf`
with a new `rcsd /path/to/dot.d/` .conf file directive
* Support for `fsck_mode=[auto,skip,force]` + `fsck_repair=[preen,no,yes]`
* Add `set` keyword for environment variables set in `/etc/finit.conf`
* Support `finit.cond=foo` cmdline `<boot/foo>` conditions, issue #250
* `initctl` JSON output support for status and conditions, issue #273
Example:
root@infix:~$ initctl status -j resolvconf
{
"identity": "resolvconf",
"description": "Update DNS configuration",
"type": "task",
"forking": false,
"status": "done",
"exit": { "code": 0 },
"origin": "/etc/finit.d/enabled/sysrepo.conf",
"command": "resolvconf -u",
"restarts": 0,
"pidfile": "none",
"pid": 0,
"user": "root",
"group": "root",
"uptime": 0,
"runlevels": [ 1, 2, 3, 4, 5, 7, 8, 9 ]
}
The excellent tool `jq` can be used to extract parts of the output for
further scripting. E.g. `initctl status -j foo | jq .exit.status`
* Add JSON support to `initctl ls` command
This allows for easy access to the *disabled* services:
root@anarchy:~# initctl ls --json |jq '.available - .enabled'
[
"chronyd.conf",
"dnsmasq.conf",
"gdbserver.conf",
"inadyn.conf",
"inetd.conf",
"isisd.conf",
"lldpd.conf",
"mstpd.conf",
"ntpd.conf",
"ospf6d.conf",
"ospfd.conf",
"querierd.conf",
"ripd.conf",
"ripng.conf",
"sshd.conf",
"syslogd.conf",
"telnetd.conf",
"uftpd.conf",
"wpa_supplicant.conf",
"zebra.conf"
]
* Allow `manual:yes` on sysv/service/run/task stanzas, issue #274
* Add support for `oncrash:script` to call the `post:script` action, if
defined, for a crashing service. The `EXIT_CODE` variable sent to the
script is set to `crashed`. Issue #282
* Search for plugins in `/usr` and `/usr/local` as well, issue #284
* tty: add support for `passenv` flag to `/bin/login`, issue #286
* Add reboot/shutdown/poweroff timeout `-t SEC` to initctl, issue #295
* Add support for s6 and systemd readiness notification, issue #299.
Service readiness notification to support daemons employing systemd
and s6 notification. Complementing the native Finit readiness support
using PID files that exist already.
The two have slightly different ways of implementing readiness:
- https://www.freedesktop.org/software/systemd/man/sd_notify.html
- https://skarnet.org/software/s6/notifywhenup.html
Finit now provides both a `NOTIFY_SOCKET` environment variable, for
systemd, and a way to start s6 daemons with a descriptor argument.
For details on the syntax, see the `service` documentation.
This change also renames internal states for run/task/services to
avoid any confusion with the introduction of `ready:scripts`:
- `WAITING -> PAUSED`
- `READY -> WAITING`
A service condition that used, e.g., `<service/foo/ready>` should now
instead use `<service/foo/waiting>`
* Add `ready:script` for services, called when daemon is ready, issue #300
* Add support for running scripts at shutdown at two new hook points
during the shutdown process, issue #302. See doc/plugins.md for details:
- `HOOK_SVC_DN`: after all services and non-reserved processes have been
killed (and collected)
- `HOOK_SYS_DN`: after all file systems have been unmounted, *just prior*
to Finit calling `reboot()` to shut down or reboot the system
* The `modules-load` plugin now default to runlevel `[S]`, in previous
releases it was `[2345]`. This breaking change is to align it more
with what users mostly want (modules loaded before services start) and
can be changed back to the old behavior with a per-file setting:
set runlevel 2345
* The `modules-load` plugin now adds silent tasks for modprobe. This to
prevent confusing `[ OK ]` boot messages when in fact modprobe failed.
* The `modules-load` plugin now support `set modprobe /path/to/modprobe`
* The header files `finit/conf.h` and `finit/service.h` are now exported
for external plugins
* Add support for multiple args to `initctl cond set/clr`, issue #329
* Silence confusing `[ OK ]` progress from modules-load plugin, issue #332
This change drops the confusing status progress output, which was always
OK since the actual modprobe operation runs in the background. No need
to show status of the "added a task to finit, found modprobe" command.
* dbus plugin: adapt to other operating systems
Not all Linux systems are based on Debian, and even if they are inspired
by Debian (Buildroot), they do not necessarily use the same defaults.
Probes the system at runtime for:
- dbus user and group
- dbus PID file
If the user/group cannot be found we fall back to `root`, if the PID
file cannot be determined we ignore PID file readiness.
* Improve documentation for runparts and hook scripts. Issue #315, #320
* Add `HOOK_NETWORK_DN`, called after change to runlevel 6 or 0, issue #319
* Use sysklogd `logger` tool instead of legacy `logit` tool, issue #344
For log redirection Finit has the legacy `logit` tool. This change
allows Finit to use the `sysklogd` project's extended `logger` tool
instead, when available. Allowing logging with the process' PID.
* Add `initctl` aliases: `cat -> show`, `kill -> signal`
* Add `initctl -n,--noerr` to return OK(0) if services do not exist, for
integration with openresolv and scripts with similar requirements
* Add `initctl plugins`, list installed plugins
* Add timestamp to log messages in fallback and logging to `stderr`.
When there is no log daemon, and we are running in a container, or we
cannot log to the kernel ring buffer, then we log to `stderr`. This
change improves log output by prefixing each message with a timestamp.
### Fixes
* Fix #254: document limitations in `rc.local` and `runparts` scripts
* Fix #269: add compulsory kernel symlinks in `/dev`
* Fix #275: `initctl status foo` should list all instances, regression
introduced in v4.3
* Fix #278: enforce conditions also for running `pre:` scripts
* Fix #279: allow `restart:always`, of crashing services. Similar to
`respawn` but honors `restart_sec`
* Fix #280: allow calling `initctl restart foo` from within foo
* Fix #283: too quick timeout at bootstrap of lingering tasks
* Fix #285: `initctl restart` should start crashed service
* Fix #288: enable built-in `sulogin` in Alpine and Void Linux builds
* Fix #293: modprobe plugin: support for coldplugging more devices. It
turns out, not all buses in Linux add `modalias` attributes to their
devices in sysfs. One notable exception are MDIO buses. The plugin's
scan routine would thus not pick them up.
* Fix #294: `shutdown --help` mistakenly shuts down system
* Fix #310: Always use configured restart delay for crashing services.
If no delay is configured, default to an initial 2000 msec for forking
daemons and start-stop scripts, and 1 msec for non-forking daemons
* Fix #311: document how to combine device tree with conditions
* Fix #312: restart services with respawn set, e.g. ttys, immediately
* Fix #313: Cancel pending restart timer on stop/start/restart/reload
* Fix #314: skip run/task/service restart if conditions are lost
* Fix #315: add environment variables to hook scripts
All hook scripts are called with at least one environment variable
set, `FINIT_HOOK_NAME`, useful when reusing the same hook script for
multiple hook points. It is set to the string name, also used by the
path, e.g., `hook/net/up`
For all hook points from hook/sys/shutdown and later, `FINIT_SHUTDOWN`
is also set, to one of: `halt`, `poweroff`, `reboot`
* Fix #317: make sure hook scripts don't run twice, also fixes #316
* Fix #318: only show `[ OK ] Calling foo` progress for `runparts ...`
* Fix #320: the API/IPC socket is closed immediately at shutdown/reboot
to protect hook scripts or services calling initctl. There is no way
to service these requests safely at that time
* Fix #333: consider a service dirty if command line args have changed
This fixes `initctl reload` correctly restarting all daemons that have
new command line arguments.
Previously command line arguments changes were only acted upon if the
service was explicitly reloaded `initctl reload myservice`.
Found and fixed by Jack Newman
* Fix #338: ensure shutdown hooks are called properly; `hook/sys/down`
and `hook/svc/down` hook scripts, found and fixed by Jack Newman
* Fix #339: use absolute path in `/etc/finit.d/enabled/` symlinks, for
use-cases when `/etc` is read-only and `/etc/finit.d/enabled ->
/mnt/finit.d/enabled`, reported by Jack Newman
* Fix #340: Finit ignores deleted/moved `.conf` file sin `/etc/finit.d`
* Fix #342: in runlevel S (bootstrap), not all `initctl` commands can be
supported, block the following: runlevel, reload, start/stop, restart,
reload, halt, poweroff, suspend. Also, prevent `SIGHUP` and `SIGUSR1`
signals when in shutdown or reboot
* Fix #352: separate runlevel S from runlevel 0
* Fix #355: regression in v3.2 stopping a process and its group
In Finit v3.2 a regression was introduced that affects the way Finit
stops a supervised process and its process group.
Instead of sending SIGTERM to the process, and thus delegating the
responsibility to that process to inform any children it may have, as
of commit 91a9c83 Finit sends SIGTERM to the entire process group.
For SIGKILL this is fine, SIGKILL only runs as cleanup and as a last
ditch effort if the process doesn't respond to SIGTERM.
This regression, introduced in v3.2, directly affects services like
`avahi-autoipd` that have forked off children that it needs to tell to
exit cleanly before it returns. With the patch in question these
children are never allowed to complete, which in turn causes lingering
169.254 link-local addresses on interfaces.
* Fix bootmisc plugin: octal permission on `/run/lock` and `/var/lock`
* Ensure `initctl cond get` support the flux state (exit code 255)
* Fix potential socket leak at bootstrap and shutdown
* Fix potential NULL pointer deref in kernel command line parser
* Fix lingering condition in service after reload of service with new
config that has no condition
* Fix wrong path to command in service after reload, may have changed
* `logit`: fall back to package name if `$LOGNAME` and `$USER` are
unset. **Note:** you should probably not use `logit` in your own
scripts, it is only meant for internal use by Finit. We recommend
using `logger` from the `bsdutils` or `sysklogd` packages instead
* Fix issue where `env:`/`pre:`/`post:`/etc. is removed from a service
The trick is when reloading a service like this:
service env:/etc/env serv -np -e foo:bar
into this:
service pre:/bin/pre.sh serv -np
In the second the `env:` has been removed and `pre:` added. Prior to
this patch, `env:` was kept leading to unintended behavior.
* Fix parse/diff of command line args, e.g., `nginx -g 'daemon off;'`
Starting a service like this works fine:
service [2345789] env:-/etc/default/nginx nginx -g 'daemon off;'
However, on `initctl reload` (and no change to .conf files) the arg
list was lost while parsing the .conf files. Leading to a false
positive 'diff' in args causing nginx to be unnecessarily restarted.
* Fix issue with disabled "linewrap" on the console TTY after login.
The root cause is `qemu-system-x86_64 -nographics` disabling it when
starting up. The correct `\e[?7h` escape code is now used.
[4.3][] - 2022-05-15 [4.3][] - 2022-05-15
-------------------- --------------------
@@ -1152,7 +1510,8 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.3...HEAD
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3 [4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
[4.2]: https://github.com/troglobit/finit/compare/4.1...4.2 [4.2]: https://github.com/troglobit/finit/compare/4.1...4.2
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1 [4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+4 -2
View File
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man plugins src test SUBDIRS = man plugins src system test 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
@@ -30,4 +30,6 @@ release: distcheck
printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \ printf "$$file.sha256\t"; cat ../$$file.sha256 | cut -f1 -d' '; \
done done
DISTCHECK_CONFIGURE_FLAGS = --with-fstab=/etc/fstab DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
--enable-testserv-plugin --enable-x11-common-plugin \
--with-watchdog --with-keventd --with-fstab=/etc/fstab
+104 -21
View File
@@ -37,7 +37,11 @@ Features include:
* Process supervision similar to [systemd][] * Process supervision similar to [systemd][]
* Sourcing environment files * Sourcing environment files
* Conditions for network/process/custom dependencies * Conditions for network/process/custom dependencies
* Readiness notification; PID files (native) for synchronizing system
startup, support for systemd [sd_notify()][], or [s6 style][] too
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
* Pre/Post script actions * Pre/Post script actions
* Rudimentary [templating support](doc/config.md#templating)
* Tooling to enable/disable services * Tooling to enable/disable services
* Built-in getty * Built-in getty
* Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html) * Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html)
@@ -52,7 +56,8 @@ Features include:
Focus is on small and embedded systems, although Finit is fully usable Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section with tutorials for: [contrib/](contrib/) section with tutorials for the following Linux
distributions:
* [Void Linux](contrib/void/), * [Void Linux](contrib/void/),
* [Alpine Linux](contrib/alpine/), and * [Alpine Linux](contrib/alpine/), and
@@ -70,7 +75,14 @@ Example
This example `/etc/finit.conf` can also be split up in multiple `.conf` This example `/etc/finit.conf` can also be split up in multiple `.conf`
files in `/etc/finit.d`. Available, but not yet enabled, services can files in `/etc/finit.d`. Available, but not yet enabled, services can
be placed in `/etc/finit.d/available` and enabled by an operator using be placed in `/etc/finit.d/available` and enabled by an operator using
the [initctl](#commands--status) tool. the [initctl](#commands--status) tool. See the above mentioned Linux
distributions, or [myLinux][].
> **Note:** as of Finit v4.4, .conf lines can be broken up using the
> standard UNIX continuation character (`\`), also trailing comments are
> now supported. The latter means you need to escape any hashes used in
> directives and descriptions (`\#`). For more on this and examples,
> see the [finit.conf(5)][] manual or [doc/config.md](doc/config.md).
```ApacheConf ```ApacheConf
# Fallback if /etc/hostname is missing # Fallback if /etc/hostname is missing
@@ -233,13 +245,50 @@ networking here. The configured `runlevel NUM` from `/etc/finit.conf`
is what Finit changes to after bootstrap, unless 'single' (or 'S') is is what Finit changes to after bootstrap, unless 'single' (or 'S') is
given on the kernel cmdline, in which case runlevel 1 is started. given on the kernel cmdline, in which case runlevel 1 is started.
All services in runlevel S(0) are started first, followed by the desired 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. `intictl` tool.
**Conditions**
As mentioned previously, Finit has an advanced dependency system to
handle synchronization, called [conditions](doc/conditions.md). It can
be used in many ways; depend on another service, network availability,
etc.
One *really cool* example useful for embedded systems is to run certain
scripts if a board has a certain feature encoded in its device tree. At
bootstrap we run the following `ident` script:
```sh
#!/bin/sh
conddir=/var/run/finit/cond/hw/model
dtmodel=/sys/firmware/devicetree/base/model
if ! test -e $dtmodel; then
exit 0
fi
model=$(cat $dtmodel | tr "[A-Z] " "[a-z]-")
mkdir -p $conddir && ln -s ../../reconf $conddir/$model
```
Provided the device tree node exists, and is a string, we can then use
the condition `<hw/model/foo>` when starting other scripts. Here is an
example:
```
run [S] /path/to/ident --
task [2] <hw/model/foo> /path/to/foo-init -- Initializing Foo board
```
> Notice the trick with an empty description to hide the call to `ident`
> in the Finit progress output.
**Plugins** **Plugins**
Plugins can *extend* the functionality of Finit and *hook into* the Plugins can *extend* the functionality of Finit and *hook into* the
@@ -346,6 +395,33 @@ in [doc/plugins.md](doc/plugins.md#hooks), and about the system bootstrap
in [doc/bootstrap.md](doc/bootstrap.md). in [doc/bootstrap.md](doc/bootstrap.md).
### Limitations
It is not possible to call Finit via signals or use `initctl` in any
runparts or `/etc/rc.local` script. This because Finit is single
threaded and is calling these scripts in a blocking fashion at the end
of runlevel S, at which point the event loop has not yet been started.
The event loop is the whole thing which Finit is built around, except
for runlevel S, which remains a slow procession through a lot of set up,
with a few hooks and blocking call outs to external scripts.
However, not all `initctl` commands are prohibited, supported commands:
are:
- `inictl cond`: only operate of files in `/run/finit/cond`
- `initctl enable/disable`: enabled run/task/service is activated on
the runlevel change from S to 2
- `initctl touch/show/create/delete/list`: `create`, provided the
non-interactive mode is used, again changes take effect in the
runlevel change directly after bootstrap
- `initctl -f reboot/poweroff/halt`: provided the `-f` flag is used to
force direct kernel commands
**Example:** you can set a `usr/` condition in `/etc/rc.local` and have
a service/task in runlevel 2 depend on it to execute.
Runlevels Runlevels
--------- ---------
@@ -438,6 +514,7 @@ Options:
-c, --create Create missing paths (and files) as needed -c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt -f, --force Ignore missing files and arguments, never prompt
-h, --help This help text -h, --help This help text
-j, --json JSON output in 'status' and 'cond' commands
-1, --once Only one lap in commands like 'top' -1, --once Only one lap in commands like 'top'
-p, --plain Use plain table headings, no ctrl chars -p, --plain Use plain table headings, no ctrl chars
-q, --quiet Silent, only return status of command -q, --quiet Silent, only return status of command
@@ -460,11 +537,11 @@ Commands:
disable <CONF> Disable .conf in /etc/finit.d/enabled disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes) reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined condition +usr/COND cond set <COND> Set (assert) user-defined conditions +usr/COND
cond get <COND> Get status of user-defined condition, see $? and -v cond get <COND> Get status of user-defined condition, see $? and -v
cond clear <COND> Clear (deassert) user-defined condition -usr/COND cond clear <COND> Clear (deassert) user-defined conditions -usr/COND
cond status Show condition status, default cond command cond status Show condition status, default cond command
cond dump Dump all conditions and their status cond dump [TYPE] Dump all, or a type of, conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog log [NAME] Show ten last Finit, or NAME, messages from syslog
start <NAME>[:ID] Start service by name, with optional ID start <NAME>[:ID] Start service by name, with optional ID
@@ -482,6 +559,8 @@ Commands:
ps List processes based on cgroups ps List processes based on cgroups
top Show top-like listing based on cgroups top Show top-like listing based on cgroups
plugins List installed plugins
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system reboot Reboot system
halt Halt system halt Halt system
@@ -507,24 +586,24 @@ scripting and documentation:
``` ```
alpine:~# initctl -p alpine:~# initctl -p
PID IDENT STATUS RUNLEVELS DESCRIPTION PID IDENT STATUS RUNLEVELS DESCRIPTION
====================================================================== ======================================================================
1506 acpid running [--2345----] ACPI daemon 1506 acpid running [---2345----] ACPI daemon
1509 crond running [--2345----] Cron daemon 1509 crond running [---2345----] Cron daemon
1489 dropbear running [--2345----] Dropbear SSH daemon 1489 dropbear running [---2345----] Dropbear SSH daemon
1511 klogd running [S12345----] Kernel log daemon 1511 klogd running [S-12345----] Kernel log daemon
1512 ntpd running [--2345----] NTP daemon 1512 ntpd running [---2345----] NTP daemon
1473 syslogd running [S12345----] Syslog daemon 1473 syslogd running [S-12345----] Syslog daemon
alpine:~# initctl -pv alpine:~# initctl -pv
PID IDENT STATUS RUNLEVELS COMMAND PID IDENT STATUS RUNLEVELS COMMAND
====================================================================== ======================================================================
1506 acpid running [--2345----] acpid -f 1506 acpid running [---2345----] acpid -f
1509 crond running [--2345----] crond -f -S $CRON_OPTS 1509 crond running [---2345----] crond -f -S $CRON_OPTS
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS 1489 dropbear running [---2345----] dropbear -R -F $DROPBEAR_OPTS
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS 1511 klogd running [S-12345----] klogd -n $KLOGD_OPTS
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS 1512 ntpd running [---2345----] ntpd -n $NTPD_OPTS
1473 syslogd running [S12345----] syslogd -n 1473 syslogd running [S-12345----] syslogd -n
``` ```
The environment variables to each of the services above are read from, The environment variables to each of the services above are read from,
@@ -549,7 +628,7 @@ Condition(s):
User : root User : root
Group : root Group : root
Uptime : 2 hour 46 min 56 sec Uptime : 2 hour 46 min 56 sec
Runlevels : [--2345----] Runlevels : [---2345----]
Memory : 1.2M Memory : 1.2M
CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max] CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max]
|- 1485 dropbear -R -F |- 1485 dropbear -R -F
@@ -620,11 +699,15 @@ and proposed extensions.
[upstart]: https://upstart.ubuntu.com/ [upstart]: https://upstart.ubuntu.com/
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/
[openrc]: https://www.gentoo.org/proj/en/base/openrc/ [openrc]: https://www.gentoo.org/proj/en/base/openrc/
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html
[run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[tmpfiles.d(5)]: https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
[original finit]: http://helllabs.org/finit/ [original finit]: http://helllabs.org/finit/
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process [EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
[Claudio Matsuoka]: https://github.com/cmatsuoka [Claudio Matsuoka]: https://github.com/cmatsuoka
[Joachim Wiberg]: https://troglobit.com [Joachim Wiberg]: https://troglobit.com
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
[License]: https://en.wikipedia.org/wiki/MIT_License [License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg [License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[myLinux]: https://github.com/troglobit/myLinux/ [myLinux]: https://github.com/troglobit/myLinux/
+1
View File
@@ -0,0 +1 @@
+48 -10
View File
@@ -1,10 +1,10 @@
AC_INIT(Finit, 4.3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/projects/finit/) AC_INIT([Finit],[4.4],[https://github.com/troglobit/finit/issues],[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])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([src/finit.c]) AC_CONFIG_SRCDIR([src/finit.c])
AC_CONFIG_HEADER([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
contrib/Makefile contrib/Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
@@ -14,8 +14,10 @@ AC_CONFIG_FILES([Makefile
man/Makefile man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
system/Makefile
tmpfiles.d/Makefile
test/test.env test/Makefile test/test.env test/Makefile
test/tenv/Makefile test/common/Makefile]) test/lib/Makefile test/src/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR() # Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4]) #m4_include([m4/plugin.m4])
@@ -32,7 +34,6 @@ AC_PROG_INSTALL
AC_PROG_MKDIR_P AC_PROG_MKDIR_P
# Configuration. # Configuration.
AC_HEADER_STDC
AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h]) AC_CHECK_HEADERS([termios.h sys/ioctl.h mntent.h sys/sysmacros.h])
AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r]) AC_CHECK_FUNCS([strstr getopt getmntent getmntent_r])
@@ -82,7 +83,9 @@ AC_ARG_ENABLE(contrib,
enable_contrib=yes]) enable_contrib=yes])
# Check for extra plugins to enable # Check for extra plugins to enable
enable_all_plugins=auto AC_ARG_ENABLE(all_plugins,
AS_HELP_STRING([--enable-all-plugins], [Enable all plugins, default: auto]),
enable_all_plugins=$enableval, enable_all_plugins=auto)
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl]) AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus]) AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load]) AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
@@ -95,6 +98,7 @@ AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock]) AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
AC_PLUGIN([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial]) AC_PLUGIN([tty], [yes], [Automatically activate new TTYs, e.g. USB-to-serial])
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown]) AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
AC_PLUGIN([testserv], [no], [Test plugin to start test serv daemon])
# Check for extra arguments or packages # Check for extra arguments or packages
AC_ARG_WITH(fstab, AC_ARG_WITH(fstab,
@@ -151,6 +155,13 @@ 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])
AC_ARG_WITH(plugin-path,
AS_HELP_STRING([--with-plugin-path=DIR], [Search path for external plugins, default /usr/lib/finit/plugins:/usr/local/lib/finit/plugins]),
[plugin_path=$withval], [plugin_path=yes])
AC_ARG_WITH(rtc-date,
AS_HELP_STRING([--with-rtc-date=DATE], [If RTC date/time is too old, restore to DATE, format "YYYY-MM-DD HH:MM", default "2000-01-01 00:00"]),
[rtc_date=$withval], [rtc_date=no])
### Enable features ########################################################################### ### Enable features ###########################################################################
@@ -235,6 +246,10 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_EXPAND_DIR(random_path, "$random_seed") AC_EXPAND_DIR(random_path, "$random_seed")
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])]) AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
AS_IF([test "x$rtc_date" != "xno"], [
AC_DEFINE(RTC_TIMESTAMP_CUSTOM, "$rtc_date", [Custom RTC restore date, default: 2000-01-01 00:00])], [
rtc_date=""])
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes]) AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
AS_IF([test "x$with_sulogin" != "xno"], [with_sulogin=yes]) AS_IF([test "x$with_sulogin" != "xno"], [with_sulogin=yes])
@@ -246,11 +261,16 @@ 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$enable_hook_scripts_plugin" = "xyes" -a "x$with_hook_scripts_path" != "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")
AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])]) AC_DEFINE_UNQUOTED(PLUGIN_HOOK_SCRIPTS_PATH, "$hook_scripts_path", [Built-in default: $libexecdir/finit])])
AS_IF([test "x$with_plugin_path" != "xno"], [
AS_IF([test "x$plugin_path" = "xyes"], [plugin_path=/usr/lib/finit/plugins:/usr/local/lib/finit/plugins])
AC_EXPAND_DIR(plugin_path, "$plugin_path")
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(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"])
@@ -258,25 +278,42 @@ 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(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"]) AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
AM_CONDITIONAL(TESTSERV, [test "x$enable_testserv_plugin" = "xyes"])
# Override default libdir, used for plugins and rescue.conf # Override default libdir, used for plugins and rescue.conf
pkglibdir=$libdir/finit pkglibdir=$libdir/finit
AC_SUBST(pkglibexecdir) AC_SUBST(pkglibexecdir)
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit") AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
AC_DEFINE_UNQUOTED(FINIT_LIBPATH_, "$pkglibexecdir", [Finit libexec path]) AC_DEFINE_UNQUOTED(FINIT_EXECPATH_, "$pkglibexecdir", [Finit libexec path])
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins") AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins")
AC_SUBST(plugin_path) AC_SUBST(plugin_path)
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path]) AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf") AC_EXPAND_DIR(tmpfiles_path, "$libdir/tmpfiles.d")
AC_SUBST(tmpfiles_path)
AC_DEFINE_UNQUOTED(TMPFILES_PATH, "$tmpfiles_path", [Main tmpfiles.d/, override in /etc])
AC_EXPAND_DIR(finit_tmpfiles, "$pkglibdir/tmpfiles.d")
AC_SUBST(finit_tmpfiles)
AC_DEFINE_UNQUOTED(FINIT_TMPFILES, "$finit_tmpfiles", [Finit tmpfiles.d/ .conf path])
AC_EXPAND_DIR(system_path, "$pkglibdir/system")
AC_SUBST(system_path)
AC_DEFINE_UNQUOTED(FINIT_SYSPATH, "$system_path", [Static services, previously generated at runtime by plugins])
AC_EXPAND_DIR(rescue_conf, "$pkglibdir/rescue.conf")
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf]) AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf") AC_EXPAND_DIR(sample_conf, "$pkglibdir/sample.conf")
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create]) AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"]) AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
AS_IF([test "$heading" = "no"], [heading="<NONE>"]) AS_IF([test "$heading" = "no"], [heading="<NONE>"])
RTC_DATE=$rtc_date
AS_IF([test "$rtc_date" = ""], [RTC_DATE="<DEFAULT>"])
AS_IF([test "$rtc_date" = "no"], [RTC_DATE="<DEFAULT>"])
# Generate all files # Generate all files
AC_OUTPUT AC_OUTPUT
@@ -301,6 +338,7 @@ cat <<EOF
Behavior: Behavior:
Boot heading..........: $heading Boot heading..........: $heading
Plugins...............: $plugins Plugins...............: $plugins
RTC restore date......: $RTC_DATE
Optional features: Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
+1 -2
View File
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --with-watchdog \
--with-keventd --with-keventd --with-sulogin
echo echo
echo "*** Building ..." echo "*** Building ..."
@@ -35,4 +35,3 @@ fi
echo echo
echo "*** Done" echo "*** Done"
echo echo
-3
View File
@@ -59,6 +59,3 @@ else
echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit" echo "*** Use 'sudo contrib/debian/install.sh' later to install Finit"
echo echo
fi fi
+1 -2
View File
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --with-watchdog \
--with-keventd --with-keventd --with-sulogin
echo echo
echo "*** Building ..." echo "*** Building ..."
@@ -35,4 +35,3 @@ fi
echo echo
echo "*** Done" echo "*** Done"
echo echo
+1 -1
View File
@@ -65,7 +65,7 @@ Below are a few of the main switches to configure:
* `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin. * `--enable-x11-common-plugin`: Enable the optional X Window `x11-common.so` plugin.
* `--with-sulgin`: Enable bundled `sulogin` program. Default is to use the * `--with-sulogin`: Enable bundled `sulogin` program. Default is to use the
system `sulogin(8)`. The sulogin shipped with Finit *allows password-less* system `sulogin(8)`. The sulogin shipped with Finit *allows password-less*
login if the `root` user is disabled or has no password at all. login if the `root` user is disabled or has no password at all.
+21
View File
@@ -12,6 +12,27 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
* `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.
* `finit.cond=foo[,bar[,baz]]`: set `<boot/foo>` condition, optionally
multiple conditions can be set using the same option, separated with a
comma. Alternatively, multiple `foo.cond=arg` can be given. Each will
result in a `<boot/arg>` condition being set to control the rest of the
system bootstrap.
Very useful for selecting different boot modes, e.g. manufacturing test,
firmware upgrade, or rescue mode.
> Note: `<boot/...>` conditions cannot be cleared with `initctl`!
* `finit.config=/path/to/alternative/finit.conf`: override the
compile-time bootstrap config file, default:
./configure --with-config=/etc/finit.conf
Useful when starting up in various rescue mode, factory, or
production test setups. Use the top-level configuration file
directive `rcsd /path/to/finit.d` to override the default
rcS.d directory.
* `finit.debug[=bool]`: Enable finit debug. This is operated * `finit.debug[=bool]`: Enable finit debug. This is operated
independently of the kernel `debug` setting. New as of Finit v4. independently of the kernel `debug` setting. New as of Finit v4.
+48 -19
View File
@@ -13,9 +13,9 @@ Finit Conditions
Introduction Introduction
------------ ------------
Conditions are a new addition to Finit, introduced in v3, with the Conditions are a relatively new addition to Finit, introduced in v3,
intention of providing a mechanism for common synchronization problems. with the intention of providing a mechanism for common synchronization
For example: problems. For example:
- *"wait for service A to start before starting service B"*, or - *"wait for service A to start before starting service B"*, or
- *"wait for basic network access to be available"* - *"wait for basic network access to be available"*
@@ -35,7 +35,7 @@ service to run.
In this example the Network monitor daemon `netd` is not started until In this example the Network monitor daemon `netd` is not started until
both the `pid/setupd` *and* `pid/zebra` conditions are satisfied. A 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. being created, i.e., the service's default readiness notification.
**NOTE:** Conditions also stop services when a condition is no longer **NOTE:** Conditions also stop services when a condition is no longer
asserted. I.e., if the Zebra process above stops or restarts, netd asserted. I.e., if the Zebra process above stops or restarts, netd
@@ -46,11 +46,16 @@ Triggering
---------- ----------
Conditions are mainly triggered (asserted) by built-in plugins, e.g., Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static - `netlink.so`: provides `<net/...>`
or one-shot conditions. They live in the `<usr/...` namespace and are - `pidfile.so`: provides `<pid/...>`
constrained to a flat hierarchy without any sub-directories, unlike the - Cmdline `finit.cond=arg`: provides `<boot/arg>`
pidfile plugin, for instance.
See below for built-in conditions. Finit also supports user-defined
conditions, sometimes referred to as static or one-shot conditions.
They live in the `<usr/...>` namespace and are constrained to a flat
hierarchy without any sub-directories, unlike the pidfile plugin, for
instance.
User-defined conditions are controlled using the `initctl cond set` and User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands: `initctl cond clear` commands:
@@ -87,15 +92,35 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions Built-in Conditions
------------------- -------------------
Finit comes with a set of plugins for conditions: `pidfile`, `netlink`, Finit comes with a set of plugins for conditions:
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
(recursively) for PID files created by the monitored services, and sets - `devmon` (built-in)
a corresponding condition in the `pid/` namespace. Similarly, the - `netlink`
`netlink` plugin provides basic conditions for when an interface is - `pidfile`
brought up/down and when a default route (gateway) is set, in the `net/` - `sys`
namespace. The `sys` and `usr` plugins monitor are passive condition - `usr`
monitors where the action is provided by `keventd`, signal handlers,
and in the case of `usr`, the end-user via the `initctl` tool The `devmon` (built-in) plugin monitors `/dev` and `/dev/dir` for device
nodes being created and removed. It is active only when a run, task, or
service has declared a `<dev/foo>` or `<dev/dir/bar>` condition.
The `pidfile` plugin (recursively) watches `/run/` (recursively) for PID
files created by the monitored services, and sets a corresponding
condition in the `pid/` namespace.
Similarly, the `netlink` plugin provides basic conditions for when an
interface is brought up/down and when a default route (gateway) is set,
in the `net/` namespace.
The `sys` and `usr` plugins monitor are passive condition monitors where
the action is provided by `keventd`, signal handlers, and in the case of
`usr`, the end-user via the `initctl` tool.
Additionally, the various states of a run/task/sysv/service can also be
used as conditions, the image above shows the state names. The syntax
for a `service` type process: `<service/foo/STATE>`. The other types,
in particular run/task/sysv, there are the additional states `success`
and `failure`.
With the example listed above, finit does not start the `/sbin/netd` With the example listed above, finit does not start the `/sbin/netd`
daemon until `setupd` and `zebra` has started *and* created their PID daemon until `setupd` and `zebra` has started *and* created their PID
@@ -114,10 +139,14 @@ Built-in conditions:
- `net/<IFNAME>/exist` - `net/<IFNAME>/exist`
- `net/<IFNAME>/up` - `net/<IFNAME>/up`
- `net/<IFNAME>/running` - `net/<IFNAME>/running`
- `service/<NAME[:ID]>/<STATE>`
- `{run, task, sysv}/<NAME[:ID]>/{<STATE>, success, failure}`
- `sys/pwr/ac` - `sys/pwr/ac`
- `sys/pwr/fail` - `sys/pwr/fail`
- `sys/key/ctrlaltdel` - `sys/key/ctrlaltdel`
- `usr/foo` - `usr/foo`
- `boot/arg`
- `dev/node` and `dev/dir/node`
**Note:** `up` means administratively up, the interface flag `IFF_UP`. **Note:** `up` means administratively up, the interface flag `IFF_UP`.
`running` is the `IFF_RUNNING` flag, meaning operatively up. The `running` is the `IFF_RUNNING` flag, meaning operatively up. The
@@ -163,7 +192,7 @@ Debugging
If a service is not being started as it should, the problem might be If a service is not being started as it should, the problem might be
that one of its conditions is not in the expected state. Use the that one of its conditions is not in the expected state. Use the
command `initctl status` to inspect service status. Services in the command `initctl status` to inspect service status. Services in the
`ready` state are pending a condition. `waiting` state are pending a condition.
In that situation, running `initctl cond show` reveals which of the In that situation, running `initctl cond show` reveals which of the
conditions that are not satisfied. Listed as `off` below. conditions that are not satisfied. Listed as `off` below.
+424 -46
View File
@@ -1,14 +1,21 @@
Configuration Finit Configuration
============= ===================
* [Introduction](#introduction) * [Introduction](#introduction)
* [Configuration Files](#configuration-files)
* [Runlevels](#runlevels)
* [Managing Services](#managing-services)
* [Environment Variables](#environment-variables)
* [Service Environment](#service-environment) * [Service Environment](#service-environment)
* [Service Synchronization](#service-synchronization)
* [Service Wrapper Scripts](#service-wrapper-scripts) * [Service Wrapper Scripts](#service-wrapper-scripts)
* [Templating](#templating)
* [Cgroups](#cgroups) * [Cgroups](#cgroups)
* [Configuration File Syntax](#configuration-file-syntax) * [Configuration File Syntax](#configuration-file-syntax)
* [Hostname](#hostname) * [Hostname](#hostname)
* [Kernel Modules](#kernel-modules) * [Kernel Modules](#kernel-modules)
* [Networking](#networking) * [Networking](#networking)
* [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-)
@@ -32,29 +39,154 @@ Configuration
Introduction Introduction
------------ ------------
Finit can be configured using only the original `/etc/finit.conf` file Finit is a process starter and service monitor designed to run as PID 1
or in combination with `/etc/finit.d/*.conf`. Useful for package-based on Linux. It consists of [plugins](plugins.md) and configuration files.
Linux distributions -- each package can provide its own "script" file. 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.,
PID file monitoring, or [conditions](conditions.md).
- `/etc/finit.conf`: main configuration file
- `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf` ### Configuration Files
and some directives are only available at [bootstrap][], runlevel `S`,
see the section [Limitations](#limitations) below for details.
To add a new service, drop a `.conf` file in `/etc/finit.d` and run Originally Finit was configured using a single file, `/etc/finit.conf`.
`initctl reload`. (It is also possible to `SIGHUP` to PID 1, or call Although still possible, today we recommended using the Apache-style
`available/*.conf` and `enabled/*.conf` layaout. These are called
dynamic services, in contrast to static -- the only difference being
where they are installed and if the `initctl` tool can manage them.
/etc/
|- finit.d/
| |- available/
| | `- my-service.conf
: |- enabled/
: | `- my-service.conf -> ../available/my-service.conf
: :
: |- static-service.conf
: `- another-static.conf # eg. bootstrap run/task commands
:
`- finit.conf # global settings
At bootstrap, and `initctl reload`, all .conf files are read, starting
with `finit.conf`, then, in alphabetical order, all `finit.d/*.conf`
files, and finally (still in alphabetical order) all `enabled/*.conf`
files.
> **Note:** the `finit.conf` and `finit.d/` names 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`.
>
> They can also be overridden from the [kernel command line](cmdline.md)
> using: `-- finit.config=/etc/bar.conf` and in that file use the
> top-level configuration directive `rcsd /path/to/finit.d`.
### Filesystem Layout
Finit is most comfortable with a traditional style Linux filesystem
layout, as specified in the [FHS][]:
/.
|- bin/
|- dev/ # Mounted automatically if devtmpfs is available
| |- pts/ # Mounted automatically by Finit if it exists
| `- shm/ # Mounted automatically by Finit if it exists
|- etc/
| |- finit.d/
| | |- available/
| | `- enabled/
| `- finit.conf
|- home/
|- lib/
|- libexec/
|- mnt/
|- proc/ # Mounted automatically by Finit if it exists
|- root/
|- run/ # Mounted automatically by Finit if it exists
| `- lock/ # Created automatically if Finit mounts /run
|- sbin/
|- sys/ # Mounted automatically by Finit if it exists
|- tmp/ # Mounted automatically by Finit if it exists
|- usr/
`- var/
|- cache/
|- db/
|- lib/
| `- misc/
|- lock/
|- log/
|- run -> ../run
|- spool/
`- tmp/
Finit starts (very early) by mounting `/dev`, `/proc/`, and `/sys`,
unless they (i.e., `/dev`) are already mounted. When all plugins and
other, core Finit functions, have been set up, all relevant filesystems
(`PASS > 0`) are checked and mounted from the selected `fstab`, either
the default `/etc/fstab`, or any custom one selected from the command
line, or at build time.
Any file system not listed `fstab` are automatically mounted by Finit,
as listed above, provided their respective mount point exists.
With all filesystems mounted, Finit calls `swapon`.
At shutdown, and after having stopped all services and other lingering
processes killed, filesystems are unmounted in the reverse order, and
`swapoff` is called.
[FHS]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
### Runlevels
- ` S`: bootStrap
- ` 1`: Single user mode
- `2-5`: traditional multi-user mode
- ` 6`: reboot
- `7-9`: multi-user mode (extra)
- ` 0`: shutdown
Runlevels are declared per service/run/task/sysv command. Starting in
runlevel S ([bootStrap](bootstrap.md)), usually only for tasks supposed
to run once at boot, and services like `syslogd`, which you need to
start and run throughout the whole time your system is up. Before `S`
is started, however, Finit performs a lot of housekeeping tasks like
mounting all filesystems, calling `fsck` if needed, and making sure the
everything is OK.
task [S] /lib/console-setup/console-setup.sh
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
After runlevel S, Finit proceeds to runlevel 2. (This can be changed in
`/etc/finit.conf` using the `runlevel N` directive.) Before starting
the services in runlevel 2, Finit first stops everything that is not
allowed to run in 2, and then brings up networking. Networking is
expected to be available in all runlevels except: S, 1 (single user
level), 6, and 0. Networking is enabled either by the `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
> 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
Using `initctl disable my-service` the symlink (above) is removed and
the service is queued for removal. Several changes can be made to the
system, but it is not until `initctl reload` is called that the changes
are activated.
To add a new static service, drop a `.conf` file in `/etc/finit.d/` and
run `initctl reload`. (It is also possible to `SIGHUP` PID 1, or call
`finit q`, but that has been deprecated with the `initctl` tool). Finit `finit q`, but that has been deprecated with the `initctl` tool). Finit
monitors all known active `.conf` files, so if you want to force a monitors all known active `.conf` files, so if you want to force a
restart of any service you can touch its corresponding `.conf` file in restart of any service you can touch its corresponding `.conf` file in
`/etc/finit.d` and call `initctl reload`. Finit handle any and all `/etc/finit.d` and call `initctl reload`. Finit handles all conditions
conditions and dependencies between services automatically. and dependencies between services automatically, see below section on
[Service Synchronization](#service-synchronization) for more details.
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
and use `initctl enable` to enable a service `.conf` file. This may be
useful in particular to Linux distributions that may want to install all
files for a package and let the user decide when to enable a service.
On `initctl reload` the following is checked for all services: On `initctl reload` the following is checked for all services:
@@ -68,10 +200,6 @@ On `initctl reload` the following is checked for all services:
For more info on the different states of a service, see the separate For more info on the different states of a service, see the separate
document [Finit Services](service.md). document [Finit Services](service.md).
> When running <kbd>make install</kbd> no default `/etc/finit.conf` is
> installed since system requirements differ too much. There are some
> examples in the `contrib/` directory, which can be used as a base.
### Environment Variables ### Environment Variables
@@ -80,10 +208,12 @@ and any `*.conf`, was added. It is worth noting that these are global
and *shared with all* services -- the only way to have service-local and *shared with all* services -- the only way to have service-local
environment is detailed in the next section. environment is detailed in the next section.
The syntax for global environment variables is straight forward: The syntax for global environment variables is straight forward. In
Finit v4.4 the `set` keyword was added for completeness, but the old
syntax (without the `set ` prefix) is still honored:
foo=bar set foo=bar
baz="qux" set baz="qux"
On reload of .conf files, all tracked environment variables are cleared On reload of .conf files, all tracked environment variables are cleared
so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf` so if `foo=bar` is removed from `finit.conf`, or any `finit.d/*.conf`
@@ -131,6 +261,67 @@ the `ps` command we can see that the process is started with:
> `-` is used, a missing environment file does *not* block the start. > `-` is used, a missing environment file does *not* block the start.
Service Synchronization
-----------------------
Finit was created for fast booting systems. Faster than a regular SysV
Init based system at the time. Early on the need for a guaranteed start
order of services (daemons) arose. I.e., service `A` must be guaranteed
to have started (and be ready!) before `B`. The model that was chosen
to determine this was very simple: PID files.
Early on in UNIX daemons were controlled with basic IPC like signals,
and the way for a user to know that a daemon was ready to respond to
signals (minimally having set up its signal handler), was to tell the
user;
> "Hey, you can send signals to me using the PID in this file:
> `/var/run/daemon.pid`".
Since most systems run fairly unchanged after boot, Finit could rely on
the PID file for `A` being created before launching `B`. This method
has worked well for a long time, and for systems based on Open Source it
was easy to either add PID file support to a daemon without support for
it, or fix ordering issues (PID file created before signal handler is
set up) in existing daemons.
However, with the advent of other Init systems (Finit is rather old),
most notably systemd and s6, other methods for signaling "readiness"
arrived and daemons were adapted to these new schemes to a larger
extent.
As of Finit v4.4 partial support for systemd and s6 is available. The
native PID file mode of operation is still default, the other's can be
defined per service, like this:
service notify:systemd foo
service notify:s6 bar
To synchronize two services the following condition can be used:
service notify:systemd A
service <service/A/ready> B
For details on the syntax and options, see below.
> **Note:** on `initctl reload` conditions are normally set in "flux",
> while figuring out which to stop, start or restart. Services that
> need to be restarted have their `ready` condition removed prior to
> Finit sending them SIGHUP (if they support that), or stop-starting
> them. A daemon is 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
> are expected to close your notify descriptor after having written
> `\n`. This means s6 style daemons currently must be stop-started.
> (Declare the service with `<!>` in its condition statement.)
>
> For native PID file style readiness notification a daemon is expected
> to either create its PID file, if it does not exist yet, or touch it
> using `utimensat()` or similar. This triggers both the `<pid/>` and
> `<.../ready>` conditions.
Service Wrapper Scripts Service Wrapper Scripts
----------------------- -----------------------
@@ -163,6 +354,26 @@ example employs a wrapper script in `/etc/start.d`.
> instead of sending SIGHUP at restart/reload events. > instead of sending SIGHUP at restart/reload events.
Templating
----------
Finit comes with rudimentary support for templating, similar to that of
systemd. Best illustrated with an example:
$ initctl show avahi-autoipd@
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
To enable ZeroConf for, e.g., eth0, use
$ initctl enable avahi-autoipd@eth0.conf
The enabled symlink will be set up to avahi-autoipd@.conf and every in‐
stance of %i will in the instantiated directive be replaced with eth0.
Inspect the result with:
$ initctl status avahi-autoipd:eth0
Cgroups Cgroups
------- -------
@@ -227,6 +438,31 @@ Linux kernel, or the name is misspelled.
Configuration File Syntax Configuration File Syntax
------------------------- -------------------------
The file format is line based, empty lines and comments, lines starting
with `#`, are ignored. A configuration directive starts with a keyword
followed by a space and the rest of the line is treated as the value.
As of Finit v4.4, configuration directives can be broken up in multiple
lines using the continuation character `\`, and trailing comments are
also allowed. Example:
```aconf
# Escape \# chars if you want them literal in, e.g., descriptions
service name:sysklogd [S123456789] \
env:-/etc/default/sysklogd \
syslogd -F $SYSLOGD_ARGS \
-- System log daemon \# 1 # Comments allowed now
```
The .conf files `/etc/finit.conf` and `/etc/finit.d/*` support the
following directives:
> **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
**Syntax:** `host <NAME>`, or `hostname <NAME>` **Syntax:** `host <NAME>`, or `hostname <NAME>`
@@ -270,6 +506,27 @@ 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/
**Syntax:** `rcsd /path/to/finit.d`
The Finit rcS.d directory is set at compile time with:
./configure --with-rcsd=/etc/finit.d
A system with multiple use-cases may be bootstrapped with different
configurations, starting with the kernel command line option:
-- finit.config=/etc/factory.conf
This file in turn can use the `rcsd` directive to tell Finit to use
another set of .conf files, e.g.:
rcsd /etc/factory.d
> **Note:** this directive is only available from the top-level
> bootstrap .conf file.
### Resource Limits ### Resource Limits
**Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` **Syntax:** `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>`
@@ -305,7 +562,10 @@ The system runlevel to go to after [bootstrap][] (S) has completed. `N`
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt. halt.
Default: 2 It is recommended to keep runlevel 1 as single-user mode, because
Finit disables networking in this mode.
*Default:* 2
> **Note:** only read and executed in runlevel S ([bootstrap][]). > **Note:** only read and executed in runlevel S ([bootstrap][]).
@@ -419,10 +679,56 @@ modified with an optional `pid:`-argument:
pid[:[/path/to/]filename[.pid]] pid[:[/path/to/]filename[.pid]]
For example, by adding `pid:/run/foo.pid` to the service `/sbin/bar`, For example, by adding `pid:/run/bar.pid` to the service `/sbin/bar`,
that PID file will, not only be created and removed automatically, but that PID file will, not only be created and removed automatically, but
also be used by the Finit condition subsystem. So a service/run/task also be used by the Finit condition subsystem. So a service/run/task
can depend on `<pid/bar>`. can depend on `<pid/bar>`, like this foo will not be started until bar
has started:
service pid:/run/bar.pid bar -- Bar Service
service <pid/bar> foo -- Foo Service
Needless to say, it is better if `bar` creates its own PID file when it
has completed starting up and is ready for service.
As an alternative "readiness" notification, Finit supports both systemd
and s6 style notification. This can be enabled by using the `notify`
option:
* `notify:systemd` -- tells Finit the service uses the `sd_notify()`
API to signal PID 1 when it has completed its startup and is ready
to service events. The [sd_notify()][] API expects `NOTIFY_SOCKET`
to be set to the socket where the application can send `"READY=1\n"`
when it is starting up or has processed a `SIGHUP`.
* `notify:s6` -- puts Finit in s6 compatibility mode. Compared to the
systemd notification, [s6 expect][] compliant daemons to send `"\n"`
and then close their socket. Finit takes care of "hard-wiring" the
READY state as long as the application is running, events across any
`SIGHUP`. Since s6 can give its applications the descriptor number
(must be >3) on then command line, Finit provides the following
syntax (`%n` is replaced by Finit with then descriptor number):
service [S12345789] notify:s6 mdevd -O 4 -D %n
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
[s6 expect]: https://skarnet.org/software/s6/notifywhenup.html
When a service is ready, either by Finit detecting its PID file, or
their respective readiness mechanism has been triggered, Finit creates
then service's ready condition which other services can depend on:
$ initctl -v cond get service/mdevd/ready
on
This can be used to synchronize the start of another run/task/service:
task [S] <service/mdevd/ready> @root:root mdevd-coldplug
Finit waits for `mdevd` to notify it, before starting `mdevd-coldplug`.
Notice how both start in runlevel S, and the coldplug task only runs in
S. When the system moves to runlevel 2 (the default), coldplug is no
longer part of the running configuration (`initctl show`), this is to
ensure that coldplug is not called more than once.
> For a detailed description of conditions, and how to debug them, > For a detailed description of conditions, and how to debug them,
> see the [Finit Conditions](conditions.md) document. > see the [Finit Conditions](conditions.md) document.
@@ -439,6 +745,24 @@ optional `name` argument:
name:<service-name> name:<service-name>
If multiple instances of a service, with the same `name`, exist. Set
the identifier `:ID` to prevent Finit from replacing previous instances:
service :eth1 ssdpd eth1 -- Windows discovery on eth1
service :eth2 ssdpd eth2 -- Windows discovery on eth2
If you have conflicting services and want to prevent them from starting,
use the `conflict:` argument:
service [S12345789] udevd -- Device event management daemon
run [S] conflict:udevd mdev -s -- Populating device tree
Multiple conflicting services can be separated using `,`:
service :1 abc
service :2 abc
service conflict:abc:1,abc:2 cde
As mentioned previously, services are automatically restarted, this is As mentioned previously, services are automatically restarted, this is
configurable with the following options: configurable with the following options:
@@ -450,13 +774,16 @@ configurable with the following options:
a crashing service, default: 2 seconds for the first five retries, a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used and the above (2 and 5) will be used
* `restart:always` -- no upper limit on the number of times Finit
tries to restart a crashing service. Same as `restart:-1`
* `norestart` -- dont restart on failures, same as `restart:0` * `norestart` -- dont restart on failures, same as `restart:0`
* `respawn` -- bypasses the `restart` mechanism completely, allows * `respawn` -- bypasses the `restart` mechanism completely, allows
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.
Note, future releases may include other `oncrash:` actions * `oncrash:script` -- similarly, but instead of rebooting, call the
`post:script` action if set, see below.
When stopping a service (run/task/sysv/service), either manually or When stopping a service (run/task/sysv/service), either manually or
when moving to another runlevel, Finit starts by sending `SIGTERM`, to when moving to another runlevel, Finit starts by sending `SIGTERM`, to
@@ -482,12 +809,9 @@ with an additional set of environment variables:
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
The scripts have a default execution time of 3 seconds before they are These script actions *must terminate*, so they have a default execution
SIGKILLed, this can be adjusted using the above `kill:SEC` syntax. time of 3 seconds before they are SIGKILLed, this can be adjusted using
the above `kill:SEC` syntax.
> **Note:** the `pre:script` _must_ be idempotent, because a service
> can transition between READY and HALTED states any number
> of times before going to RUNNING.
### Run-parts Scripts ### Run-parts Scripts
@@ -499,15 +823,48 @@ files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel The scripts in this directory are executed at the very end of runlevel
`S`, [bootstrap][]. `S`, [bootstrap][].
It can be beneficial to use `S01name`, `S02othername`, etc. if there A common use-case for runparts scripts is to create and enable/disable
is a dependency order between the scripts. Symlinks to existing services, which Finit will then apply when changing runlevel from S to
daemons can talso be used, but make sure they daemonize by default. whatever the next runlevel is set to be (default 2). E.g., generate a
`/etc/chrony.conf` and `initctl enable chronyd`.
Similar to the `/etc/rc.local` shell script, make sure that all your **Limitations:**
services and programs either terminate or start in the background or
you will block Finit.
> **Note:** only read and executed in runlevel S ([bootstrap][]). Scripts called from `runparts`, or hook scripts (see below), are limited
in what they can do and expect from Finit:
1. Finit is single threaded and all scripts executed from these two
mechanisms are limited to file system operations (early or late
hooks may not have write access!) and standard UNIX programs.
2. Thus, it is *not possible* to call `initctl` to query status of
services since that calls Finit over the `/run/finit/socket`.
3. Confusingly enough, there are some commands to `initctl` that are
available; `ls`, `enable`, `disable`, `touch`, `create`, `delete`,
and all the `cond` commands. Because all these are basically just
wrappers for manipulating files (again, write access for early/late
hooks may not be available).
Also, similar to the `/etc/rc.local` shell script, make sure that all
your services and programs either terminate or start in the background
or you will block Finit.
> **Note:** `runparts` scrips are only read and executed in runlevel S
> ([bootstrap][]). See [hook scripts](plugins.md#hooks) for other ways
> to run scripts at certain points during the complete lifetime of the
> system.
**Recommendations:**
It can be beneficial to use `01-name`, `02-othername`, etc., to ensure
the scripts are started in that order, e.g., if there is a dependency
order between scripts. Symlinks to existing daemons can talso be used,
but make sure they daemonize (background) themselves properly, otherwise
Finit will lock up.
If `S[0-9]foo` and `K[0-9]bar` style naming is used, the executable will
be called with an extra argument, `start` and `stop`, respectively.
E.g., `S01foo` will be called as `S01foo start`. Of course, `S01foo`
and `K01foo` may be a symlink to to `another/directory/foo`.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
@@ -666,6 +1023,27 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
### Misc Settings
**Syntax:** `reboot-delay <0-60>`
Optional delay at reboot (or shutdown or halt) to allow kernel
filesystem threads to complete after calling `sync(2)` before
rebooting. This applies primarily to filesystems that do not
have a reboot notifier implemented. At the point of writing,
the only known filesystems affected are: ubifs, jffs2.
*Default:* 0 (disabled)
When enabled (non-zero), this delay runs after file systems have been
unmounted and the root filesystem has been remounted read-only, and
sync(2) has been called, twice.
> "On Linux, sync is only guaranteed to schedule the dirty blocks for
> writing; it can actually take a short time before all the blocks are
> finally written.
Rescue Mode Rescue Mode
----------- -----------
@@ -739,9 +1117,9 @@ 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 > **Note:** The `/etc/finit.d` directory was previously the default
> Finit [runparts](#run-parts-scripts) directory. Finit no > Finit [runparts](#run-parts-scripts) directory. Finit >=v4.0
> longer has a default `runparts`, make sure to update your > no longer has a default `runparts` directory, make sure to
> setup, or the finit configuration, accordingly. > update your setup, or the finit configuration, accordingly.
Watchdog Watchdog
+121 -37
View File
@@ -8,7 +8,8 @@ Hooks & Plugins
* [Shutdown Hooks](#shutdown-hooks) * [Shutdown Hooks](#shutdown-hooks)
Finit can be extended to add general functionality in the form of I/O Finit can be extended to add general functionality in the form of I/O
monitors, or hook plugins. monitors, or hook plugins. It is even possible to run scripts at these
hook points, see below.
The following sections detail existing plugins and hook points. For The following sections detail existing plugins and hook points. For
more information, see the plugins listed below. more information, see the plugins listed below.
@@ -35,13 +36,25 @@ For your convenience a set of *optional* plugins are available:
_Optional plugin._ _Optional plugin._
* *hook-scripts.so*: Trigger the execution of scripts from plugin hook * *hook-scripts.so*: Trigger the execution of scripts from plugin hook
points (see [Hooks](#hooks)). Scripts are located in points (see [Hooks](#hooks)). _Optional plugin._
`/libexec/finit/hook` by default, this can be build-time customized
by using the `--with-hook-scripts-path=PATH` argument to This plugin is particularly useful for early boot debugging that needs
`configure`. _Optional plugin._ to take place before regular services are available.
For example, say that you want to enable some kernel tracing before
modules are loaded. With hook scripts, you can just drop in a shell
script in `/libexec/finit/hook/mount/all/` that will poke the right
control files in tracefs.
Scripts are located in `/libexec/finit/hook` by default, this can be
customized at build-time using the `--with-hook-scripts-path=PATH`
argument to `configure`.
* *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if * *hotplug.so*: Setup and start either udev or mdev hotplug daemon, if
available. Enabled by default. available. Enabled by default.
> 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.
@@ -49,20 +62,33 @@ For your convenience a set of *optional* plugins are available:
* *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to * *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to
load using `modprobe`. Each file can contain multiple lines with the load using `modprobe`. Each file can contain multiple lines with the
name of the module to load. Any line starting with the standard UNIX name of the module to load. Any line starting with the standard UNIX
comment character, `#`, is skipped. comment character, `#`, or `;`, is skipped.
Modules are loaded when entering runlevel `[2345]` using the `task` Modules are by default loaded in runlevel `S` using the `task` stanza.
stanza. Each module gets a unique `name:modprobe.foo`, and Each module gets a unique `name:modprobe.foo`, and optional`:ID`. The
optional`:ID`. The `:ID` is a globally incremented index, which can runlevel can be changed per file using:
be disabled per file (anywhere) using the following config line:
set runlevel 2345
The `:ID` is a globally incremented index, which can be disabled per
file (anywhere) using the following config line:
set noindex set noindex
To change the index used by the plugin:
set index 1234
**Note:** unlike the traditional .conf `module` directive, which load **Note:** unlike the traditional .conf `module` directive, which load
any listed module immediately, this plugin creates standard `taks` any listed module immediately, this plugin creates a background `task`
directives which load the module(s) in the background. As long as which load the module(s) in the background. The program is modprobe,
the `modprobe` program is found in the path, these tasks will always `/sbin/modprobe`, which you can override per .conf file:
return `[ OK ]` at boot. Check the actual status using `initctl`.
set modprobe /path/to/maybe-a-modprobe-wrapper
Since these tasks run in the background, they usually return `[ OK ]`
at boot, unless the modprobe tool does not exist. Check syslog for
warnings and the actual status of the operation using `initctl`.
* *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
@@ -91,43 +117,101 @@ state, etc. Available hook points are:
Hooks Hooks
----- -----
In the below listings, the first label is the hook point for a C plugin,
the second is the condition name and hook script path. A hook script is
a plain shell script, or program, that does a very small dedicated job
at the below hook points.
See [Run-parts Scripts](config.md#run-parts-scripts) for details on the
requirements, possibilities, and *limitations* of hook scripts.
All hook scripts are called with at least one environment variable set,
the hook name, useful when reusing the same hook script for multiple
hook points:
- `FINIT_HOOK_NAME`: set to the second label, e.g., `hook/net/up`
- `FINIT_SHUTDOWN`: set for `hook/sys/shutdown` and later to one
of `halt`, `poweroff`, or `reboot`.
**Example:**
$ mkdir -p /libexec/finit/hook/sys/down
$ cat <<EOF >/libexec/finit/hook/sys/foo.sh
#!/bin/sh
echo 'I run just before the reboot() syscall at shutdown/reboot'
echo 'I have access to /dev since devtmpfs is exempt from umount'
echo "FINIT_HOOK_NAME: $FINIT_HOOK_NAME"
if [ -n "$FINIT_SHUTDOWN" ]; then
echo "FINIT_SHUTDOWN: $FINIT_SHUTDOWN"
fi
exit 0
EOF
$ chmod +x /libexec/finit/hook/sys/foo.sh
> **Note:** 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
* `HOOK_BANNER`: The very first point at which a plugin can run. * `HOOK_BANNER`, `hook/sys/banner`: The very first point at which a
Intended to be used as a banner replacement. Essentially this runs plugin can run. Intended to be used as a banner replacement.
just before entering runlevel S. Assume nothing is available, so Essentially this runs just before entering runlevel S. Assume nothing
be prepared to use absolute paths, etc. is available, so be prepared to use absolute paths, etc.
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is * `HOOK_ROOTFS_UP`, `hook/mount/root`: When `finit.conf` has been read
mounted — very early and `/` has is mounted — very early
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available * `HOOK_MOUNT_ERROR`, `hook/mount/error`: executed if `mount -a` fails
and default init signals are setup
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and * `HOOK_MOUNT_POST`, `hook/mount/post`: always executed after `mount -a`
networking is up (`lo` is up and the `network` script has run)
* `HOOK_SVC_UP`: All services in the active runlevel has been launched * `HOOK_BASEFS_UP`, `hook/mount/all`: All of `/etc/fstab` is mounted,
swap is available and default init signals are setup
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d` * `HOOK_SVC_PLUGIN`, `hook/svc/plugin`: Called in `conf_init()` right
has been launched before loading `/etc/finit.conf`. For plugins to register any early
run/task/services, please do NOT use any earlier hook point. That
will cause uninitialized rlimits that lead to unpredictable results
when Finit later tries to start the run/task/service.
* `HOOK_NETWORK_UP`, `hook/net/up`: System bootstrap, runlevel S, has
completed and networking is up (`lo` is up and the `network` script
has run)
* `HOOK_SVC_UP`, `hook/svc/up`: All services in the active runlevel have
been launched
* `HOOK_SYSTEM_UP`, `hook/sys/up`: All services *and* everything in
`/etc/finit.d` has been launched
### Runtime Hooks ### Runtime Hooks
* `HOOK_SVC_RECONF`: Called when the user has changed something in the * `HOOK_SVC_RECONF`, N/A: Called when the user has changed something in
`/etc/finit.d` directory and issued `SIGHUP`. The hook is called when the `/etc/finit.d` directory and issued `SIGHUP`. The hook is called
all modified/removed services have been stopped. When the hook has when all modified/removed services have been stopped. When the hook
completed, Finit continues to start all modified and new services. has completed, Finit continues to start all modified and new services.
* `HOOK_RUNLEVEL_CHANGE`: Called when the user has issued a runlevel * `HOOK_RUNLEVEL_CHANGE`, N/A: Called when the user has issued a
change. The hook is called when services not matching the new runlevel change. The hook is called when services not matching the
runlevel have been been stopped. When the hook has completed, Finit new runlevel have been been stopped. When the hook has completed,
continues to start all services in the new runlevel. Finit continues to start all services in the new runlevel.
### Shutdown Hooks ### Shutdown Hooks
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all * `HOOK_NETWORK_DN`, `hook/net/down`: Called right after having changed
services are sent `SIGTERM` to runlevel 6, or 0, when all services have received their 'stop' signal.
* `HOOK_SHUTDOWN`, `hook/sys/shutdown`: Called at shutdown/reboot, right
before all services are sent `SIGTERM`
* `HOOK_SVC_DN`, `hook/svc/down`: In shutdown/reboot, all services and
non-reserved processes have been killed. **Note:** only hook scripts
can run here!
* `HOOK_SYSTEM_DN`, `hook/sys/down`: In shutdown/reboot, called after
all non-reserved file systems have been unmounted, just before Finit
tells the kernel to reboot or shut down. **Note:** only hook scripts
can run here!
Plugins like `tty.so` extend finit by acting on events, they are called Plugins like `tty.so` extend finit by acting on events, they are called
I/O plugins and are called from the finit main loop when `poll()` I/O plugins and are called from the finit main loop when `poll()`
Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 230 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 164 KiB

+19 -23
View File
@@ -1,8 +1,6 @@
# From https://github.com/collectd/collectd/blob/master/configure.ac # From https://github.com/collectd/collectd/blob/master/configure.ac
# Dependency handling currently unused # Dependency handling currently unused
define([toupper], [translit([$1], [a-z], [A-Z])])
define([sanitize], [translit([$1], [-], [_])])
define([enadis], [ifelse([$1], [yes], [disable], [enable])]) define([enadis], [ifelse([$1], [yes], [disable], [enable])])
# AC_PLUGIN(name, default, info) # AC_PLUGIN(name, default, info)
@@ -10,27 +8,25 @@ define([enadis], [ifelse([$1], [yes], [disable], [enable])])
AC_DEFUN([AC_PLUGIN],[ AC_DEFUN([AC_PLUGIN],[
m4_divert_once([HELP_ENABLE], [ m4_divert_once([HELP_ENABLE], [
Optional Plugins:]) Optional Plugins:])
define(PLUGGY, m4_translit([$1],[a-z-][A-Z-]))dnl
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [
if test "x$enableval" = "xyes"; then
enable_plugin="yes"
else
enable_plugin="no" enable_plugin="no"
define(pluggy, sanitize($1)) fi], [
define(PLUGGY, toupper(pluggy)) if test "x$enable_all_plugins" = "xauto"; then
AC_ARG_ENABLE([$1-plugin], AS_HELP_STRING([--enadis($2)-$1-plugin], [$3]), [ if test "x$2" = "xyes"; then
if test "x$enableval" = "xyes"; then enable_plugin="yes"
enable_plugin="yes"
else else
enable_plugin="no" enable_plugin="no"
fi], [
if test "x$enable_all_plugins" = "xauto"; then
if test "x$2" = "xyes"; then
enable_plugin="yes"
else
enable_plugin="no"
fi
else
enable_plugin="$enable_all_plugins"
fi])
if test "x$enable_plugin" = "xyes"; then
AC_DEFINE([HAVE_]PLUGGY[_PLUGIN], 1, [Define to 1 if the $1 plugin is enabled.])
plugins="$1 $plugins"
fi fi
AM_CONDITIONAL([BUILD_]PLUGGY[_PLUGIN], test "x$enable_plugin" = "xyes") else
enable_pluggy="$enable_plugin"]) enable_plugin="$enable_all_plugins"
fi])
if test "x$enable_plugin" = "xyes"; then
AC_DEFINE(HAVE_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, 1, [Define to 1 if the $1 plugin is enabled.])
plugins="$1 $plugins"
fi
AM_CONDITIONAL(BUILD_[]m4_translit($1,[-a-z],[_A-Z])_PLUGIN, test "x$enable_plugin" = "xyes")])
+89 -2
View File
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps .\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1) .\" other parameters are allowed: see man(7), man(1)
.Dd June 6, 2021 .Dd Apr 22, 2023
.\" Please adjust this date whenever revising the manpage. .\" Please adjust this date whenever revising the manpage.
.Dt FINIT 8 SMM .Dt FINIT 8 SMM
.Os Linux .Os Linux
@@ -12,10 +12,16 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm /sbin/finit .Nm /sbin/finit
.Op -- .Op --
.Op debug
.Op finit.cond=foo[,bar[,baz]]
.Op finit.config=/path/to/alt/finit.conf
.Op finit.debug .Op finit.debug
.Op finit.fstab=/etc/fstab.alt .Op finit.fstab=/etc/fstab.alt
.Op finit.status=<on,off> .Op finit.status=<on,off>
.Op finit.status_style=<classic,modern> .Op finit.status_style=<classic,modern>
.Op loglevel=<0-7>
.Op panic=SEC
.Op quiet
.Op rescue | recover .Op rescue | recover
.Op single | S .Op single | S
.Nm /sbin/telinit .Nm /sbin/telinit
@@ -45,6 +51,15 @@ Sourcing environment files
.It .It
Conditions for network/process/custom dependencies Conditions for network/process/custom dependencies
.It .It
Process readiness notification for synchronizing system startup as well
as reconfiguration at runtime. Natively PID files are used, but systemd
.Cm sd_notify()
and s6 notification is also supported.
.It
Limited support for
.Xr tmpfiles.d 5 ,
no aging, attributes, or subvolumes
.It
Pre/Post script actions Pre/Post script actions
.It .It
Tooling to enable/disable services Tooling to enable/disable services
@@ -73,6 +88,38 @@ PID 1. For more information on this, and available kernel command line
options, see options, see
.Xr bootparam 7 . .Xr bootparam 7 .
.Bl -tag -width pid -offset indent .Bl -tag -width pid -offset indent
.It Cm debug
Enable kernel debug. Debug messages are printed to the console until
Finit starts up, unless
.Cm loglevel=7
(below) is used.
.It Cm finit.cond=foo[,bar[,baz]]
set
.Cm <boot/foo>
condition, optionally multiple conditions can be set using the same
option, separated with a comma. Alternatively, multiple
.Cm foo.cond=arg
can be given. Each will result in a
.Cm <boot/arg>
condition being set to control the rest of the system bootstrap.
.Pp
Very useful for selecting different boot modes, e.g. manufacturing test,
firmware upgrade, or rescue mode.
.Pp
.Sy Note:
.Cm <boot/...>
conditions cannot be cleared with
.Nm initctl !
.It Cm finit.config=/path/to/alternative/finit.conf
override the compile-time bootstrap config file, default:
.Bd -unfilled -offset indent
./configure --with-config=/etc/finit.conf
.Ed
Useful when starting up in various rescue mode, factory, or
production test setups. Use the top-level configuration file
directive
.Cm rcsd Ar /path/to/finit.d
to override the default rcS.d directory.
.It Cm finit.debug .It Cm finit.debug
Enable Finit debug messages on system console and log. Sometimes useful Enable Finit debug messages on system console and log. Sometimes useful
when doing board bringup. Before the system log daemon has started, when doing board bringup. Before the system log daemon has started,
@@ -121,6 +168,33 @@ Control finit boot progress, including banner. This used to be called
which still works but is deprecated. which still works but is deprecated.
.It Cm finit.status_style=<classic,modern> .It Cm finit.status_style=<classic,modern>
Set Finit boot progress style, when enabled. Set Finit boot progress style, when enabled.
.It Cm loglevel=<0-7>
set the kernel's log level, which is more granular than
.Cm debug .
Also, when
.Cm loglevel=7 ,
Finit will
.Em not disable
kernel logs to the console. This is very useful when debugging the
kernel at system bring-up. Since
.Cm loglevel=7
is the same as
.Cm debug
this means you have to use
.Cm quiet
for a quiet boot, until sysklogd takes over logging of kernel events.
.It Cm panic=SEC
By default the kernel does not reboot after a kernel panic. This
setting will cause a kernel reboot after
.Ar SEC
seconds.
.It Cm quiet
Suppress kernel logging to console, except for warnings and errors.
Also, see
.Cm loglevel
and
.Cm quiet
above.
.It Cm rescue | recover .It Cm rescue | recover
Start rescue/maintenance mode. If your system comes with the bundled Start rescue/maintenance mode. If your system comes with the bundled
.Cm sulogin .Cm sulogin
@@ -139,6 +213,10 @@ the regular runlevel. All services and TTYs in
will be started, e.g., a will be started, e.g., a
.Cm tty [1] @console nologin .Cm tty [1] @console nologin
configuration presents you with a root console without login. configuration presents you with a root console without login.
.Pp
.Sy Note:
by definition, single user mode does not have networking enabled. This
is also true for runlevel 1.
.El .El
.Sh BOOTING .Sh BOOTING
.Pp .Pp
@@ -192,7 +270,7 @@ file removed, service B is also stopped.
.Pp .Pp
The following condition families are available today: The following condition families are available today:
.Pp .Pp
.Bl -tag -width pid -offset indent .Bl -tag -width service -offset indent
.It Cm net .It Cm net
Linux netlink events, e.g. net/route/default, net/eth0/up, and Linux netlink events, e.g. net/route/default, net/eth0/up, and
net/et0/running net/et0/running
@@ -200,6 +278,14 @@ net/et0/running
PID files basd on the service declaration PID files basd on the service declaration
.Cm name:id , .Cm name:id ,
gives the condition pid/name:id gives the condition pid/name:id
.It Cm service
Tracks run/task/service state stansitions, including readiness. E.g.,
.Cm service/foo/ready
can be used as a condition for service
.Cm bar ,
provided
.Cm foo
properly signals its readiness to Finit.
.It Cm sys .It Cm sys
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
.It Cm usr .It Cm usr
@@ -255,6 +341,7 @@ Runtime files, including the condition subsystem
.Sh SEE ALSO .Sh SEE ALSO
.Xr finit.conf 5 .Xr finit.conf 5
.Xr initctl 8 .Xr initctl 8
.Xr tmpfiles.d 5
.Sh AUTHORS .Sh AUTHORS
.Nm .Nm
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0, was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
+112 -21
View File
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps .\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1) .\" other parameters are allowed: see man(7), man(1)
.Dd Oct 17, 2020 .Dd Apr 24, 2023
.Dt FINIT.CONF 5 SMM .Dt FINIT.CONF 5 SMM
.Os Linux .Os Linux
.Sh NAME .Sh NAME
@@ -47,13 +47,20 @@ Available services
Enabled services, symlinks from available Enabled services, symlinks from available
.El .El
.Sh FILE FORMAT .Sh FILE FORMAT
Lines starting with `#' are ignored. Note that end-of-line comments are The file format is line based, empty lines and comments, lines starting
NOT supported, comments must be on a line of their own. with `#', are ignored. A configuration directive starts with a keyword
followed by a space and the rest of the line is treated as the value.
.Pp .Pp
A configuration stanza must be on a single (long) line and As of Finit v4.4, configuration directives can be broken up in multiple
.Em cannot lines using the continuation character `\\', and trailing comments are
be broken up in multiple lines by use of a backslash as the last also allowed. Example:
character. .Bd -unfilled -offset indent
# Escape \\# chars if you want them literal in, e.g., descriptions
service name:sysklogd [S123456789] \\
env:-/etc/default/sysklogd \\
syslogd -F $SYSLOGD_ARGS \\
-- System log daemon \\# 1 # Comments allowed now
.Ed
.Sh DIRECTIVES .Sh DIRECTIVES
This section lists all supported configuration directives. There also exist This section lists all supported configuration directives. There also exist
deprecated directives, see the Markdown documentation for details on these. deprecated directives, see the Markdown documentation for details on these.
@@ -108,6 +115,10 @@ is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt. All other can be used by operating system administrators. halt. All other can be used by operating system administrators.
Default: 2 Default: 2
.Pp .Pp
It s recommended to keep runlevel 1 as single-user mode, because
.Nm
disables networking in this mode.
.Pp
.Sy Note: .Sy Note:
only read and executed in runlevel S (bootstrap). only read and executed in runlevel S (bootstrap).
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description .It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
@@ -275,6 +286,51 @@ can depend on the
.Cm <pid/bar> .Cm <pid/bar>
condition. condition.
.Pp .Pp
As an alternative "readiness" notification, Finit supports both systemd
and s6 style notification. This can be enabled by using the `notify`
option:
.Bl -tag -width 1n
.It Cm notify:systemd
tells Finit the service uses the
.Cm sd_notify()
API to signal PID 1 when it has completed its startup and is ready
to service events. This API expects
the environment variable
.Cm NOTIFY_SOCKET
to be set to the socket where the application can send
.Cm "READY=1\n"
when it is starting up or has processed a
.Cm SIGHUP .
For details, see:
.Pp
.Lk https://www.freedesktop.org/software/systemd/man/sd_notify.html
.It Cm notify:s6
puts Finit in s6 compatibility mode. Compared to the systemd
notification, s6 expect compliant daemons to send
.Cm "\\n"
and then close their socket. For details, see:
.Pp
.Lk https://skarnet.org/software/s6/notifywhenup.html
.Pp
Finit takes care of "hard-wiring" the READY state as long as the
application is running, events across any `SIGHUP`. Since s6 can give
its applications the descriptor number (must be >3) on then command
line, Finit provides the following syntax (
.Cm %n
is replaced by Finit with then descriptor number):
.Bd -unfilled -offset indent
service notify:s6 mdevd -C -O 4 -D %n
.Ed
.Pp
When a service is ready, either by Finit detecting its PID file, or
their respective readiness mechanism has been triggered, Finit creates
then service's ready condition which other services can depend on:
.Bd -unfilled -offset indent
$ initctl -v cond get service/mdevd/ready
on
.Ed
.El
.Pp
If a service should not be automatically started, it can be configured If a service should not be automatically started, it can be configured
as manual with the as manual with the
.Cm manual:yes .Cm manual:yes
@@ -304,6 +360,10 @@ number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries, a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used and the above (2 and 5) will be used
.It Cm restart:always
no upper limit on the number of times Finit tries to restart a crashing
service. Same as
.Cm restart:-1
.It Cm norestart .It Cm norestart
dont restart on failures, same as dont restart on failures, same as
.Cm restart:0 .Cm restart:0
@@ -318,10 +378,21 @@ was originally designed for so not the default behavior.
when all retries have failed, and the service when all retries have failed, and the service
has has
.Em crashed , .Em crashed ,
if this option is set the system is rebooted. Note, future releases if this option is set the system is rebooted.
may include other .It Cm oncrash:script
.Cm oncrash: Similar to
actions. .Cm oncrash:reboot ,
but instead of rebooting this action calls the
.Cm post:script
(see below) to let the operator decide the best course of action. If
the post:script option is not set, this is a no-op.
.Pp
The post:script is called with the same environment variables
.Sy except
for the
.Cm EXIT_CODE
variable which is set to
.Cm "crashed" .
.El .El
.Pp .Pp
When stopping a service (run/task/sysv/service), either manually or when When stopping a service (run/task/sysv/service), either manually or when
@@ -364,11 +435,13 @@ 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:
.Bl -tag -offset indent -width 1n .Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal] .It Cm EXIT_CODE=[exited,signal,crashed]
set to one of set to one of
.Cm exited .Cm exited ,
.Cm signal ,
or or
.Cm signal .Cm crashed
(see above).
.It Cm EXIT_STATUS=[num,SIGNAME] .It Cm EXIT_STATUS=[num,SIGNAME]
set to one of exit status code from the program, if it exited normally, 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
@@ -378,13 +451,6 @@ The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the SIGKILLed, this can be adjusted using the
.Cm kill:SEC .Cm kill:SEC
modifier syntax. modifier syntax.
.Pp
.Sy Note:
the
.Cm pre:script
.Em must
be idempotent, because a service can transition between READY and HALTED
states any number of times before going to RUNNING.
.It Cm runparts Aq DIR .It Cm runparts Aq DIR
Call Call
.Xr run-parts 8 .Xr run-parts 8
@@ -753,6 +819,31 @@ the example sets
to denote that it doesn't support SIGHUP. That way Finit will to denote that it doesn't support SIGHUP. That way Finit will
stop/start the service instead of sending SIGHUP at restart/reload stop/start the service instead of sending SIGHUP at restart/reload
events. events.
.Sh TEMPLATING
Finit comes with rudimentary support for templating, similar to that of
systemd. Best illustrated with an example:
.Bd -unfilled -offset indent
$ initctl show avahi-autoipd@
service :%i avahi-autoipd --syslog %i -- ZeroConf for %i
.Ed
.Pp
To enable ZeroConf for, e.g.,
.Cm eth0 ,
use
.Bd -unfilled -offset indent
$ initctl enable avahi-autoipd@eth0.conf
.Ed
.Pp
The enabled symlink will be set up to
.Cm avahi-autoipd@.conf
and every instance of
.Cm %i
will in the instantiated directive be replaced with
.Cm eth0 .
Inspect the result with:
.Bd -unfilled -offset indent
$ initctl status avahi-autoipd:eth0
.Ed
.Sh CGROUPS .Sh CGROUPS
There are three major cgroup configuration directives: There are three major cgroup configuration directives:
.Pp .Pp
+67 -31
View File
@@ -11,7 +11,7 @@
.Nd Control tool for Finit .Nd Control tool for Finit
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm /sbin/initctl .Nm /sbin/initctl
.Op Fl bcfhlpqtvV .Op Fl bcfhjlpqtvV
.Op COMMAND .Op COMMAND
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@@ -33,6 +33,18 @@ command.
Ignore missing files and arguments, never prompt. Ignore missing files and arguments, never prompt.
.It Fl h, -help .It Fl h, -help
Show built-in help text. Show built-in help text.
.It Fl j, -json
JSON output in
.Ar status
and
.Ar cond
commands.
.It Fl n, -noerr
When scripting
.Nm
to stop, start, restart, or signal a task or service, this option can be
used to ignore error 69 (no such task or service) and instead return 0
(OK).
.It Fl 1, -once .It Fl 1, -once
Only one lap in commands like top. Only one lap in commands like top.
.It Fl p, -plain .It Fl p, -plain
@@ -54,9 +66,9 @@ Toggle
(daemon) debug to (daemon) debug to
.Pa /dev/console .Pa /dev/console
.It Nm Ar help .It Nm Ar help
Show built-in help text Show built-in help text.
.It Nm Ar version .It Nm Ar version
Show program version Show program version.
.It Nm Ar ls | list .It Nm Ar ls | list
List all List all
.Cm *.conf .Cm *.conf
@@ -115,13 +127,13 @@ Disable
.Cm .conf .Cm .conf
in in
.Pa /etc/finit.d/enabled , .Pa /etc/finit.d/enabled ,
i.e., removes symlink i.e., removes symlink.
.It Nm Ar reload .It Nm Ar reload
Reload Reload
.Cm *.conf in .Cm *.conf in
.Pa /etc/finit.d , .Pa /etc/finit.d ,
i.e., activates changes i.e., activates changes.
.It Nm Ar cond set Ar COND .It Nm Ar cond set Ar COND Op COND ...
Set (assert) user-defined condition, Set (assert) user-defined condition,
.Cm +usr/COND .Cm +usr/COND
.It Nm Ar cond get Ar COND .It Nm Ar cond get Ar COND
@@ -133,36 +145,40 @@ or
then any condition can be read. then any condition can be read.
.Pp .Pp
The command is geared for scripting, check the exit code to get the The command is geared for scripting, check the exit code to get the
status of the condition, or give status of the condition: 0 - on, 1 - off, 255 - flux. For a more
.Nm verbose output, use the
the
.Fl v .Fl v
option for a more verbose output. option.
.It Nm Ar cond clr | clear Ar COND .It Nm Ar cond clr | clear Ar COND Op COND ...
Clear (deassert) user-defined condition, Clear (deassert) user-defined condition,
.Cm -usr/COND .Cm -usr/COND
.It Nm Ar cond status .It Nm Ar cond status
Show condition status, default Show condition status, default
.Cm cond .Cm cond
command. command. Also supports the
.It Nm Ar cond dump .Fl j
Dump all conditions and their status option for detailed JSON output.
.It Nm Ar cond dump Op Cm TYPE
Dump all, or a given type of, conditions and their status. Also
supports the
.Fl j
option for detailed JSON output.
.It Nm Ar ident Op Cm NAME .It Nm Ar ident Op Cm NAME
Display indentities of all run/task/services, or only instances Display indentities of all run/task/services, or only instances
matching matching
.Cm NAME . .Cm NAME .
A partial string, e.g., A partial string, e.g.,
.Cm NAM , .Cm NAM ,
will not match anything will not match anything.
.It Nm Ar log Op Cm NAME .It Nm Ar log Op Cm NAME
Show ten last Finit, or Show ten last Finit, or
.Cm NAME , .Cm NAME ,
messages from syslog messages from syslog.
.It Nm Ar start Cm NAME[:ID] .It Nm Ar start Cm NAME[:ID]
Start service by name, with optional ID, e.g., Start service by name, with optional ID, e.g.,
.Cm initctl start tty:1 .Cm initctl start tty:1
.It Nm Ar stop Cm NAME[:ID] .It Nm Ar stop Cm NAME[:ID]
Stop/Pause a running service by name Stop/Pause a running service by name.
.It Nm Ar reload Cm NAME[:ID] .It Nm Ar reload Cm NAME[:ID]
Reload service as if its configuration had changed. Internally, Reload service as if its configuration had changed. Internally,
.Nm finit .Nm finit
@@ -180,52 +196,72 @@ Except for two things:
if the service supports HUP, it's signaled instead of stop-started, and if the service supports HUP, it's signaled instead of stop-started, and
.It .It
it allows restarting run/task in the same runlevel -- which is otherwise it allows restarting run/task in the same runlevel -- which is otherwise
restricted restricted.
.El .El
.It Nm Ar signal Cm NAME:[ID] S .It Nm Ar signal Cm NAME:[ID] S
Send signal S to service, by name. Send signal S to service, by name.
.Cm S .Cm S
may be a complete signal name such as SIGHUP, or short name such as HUP, or the signal number such as 1 (SIGHUP). may be a complete signal name such as SIGHUP, or short name such as HUP, or the signal number such as 1 (SIGHUP).
.It Nm Ar restart Cm NAME[:ID] .It Nm Ar restart Cm NAME[:ID]
Restart (stop/start) service by name Restart (stop/start) service by name.
.It Nm Ar status Cm NAME[:ID] .It Nm Ar status Cm NAME[:ID]
Show service status, by name. If only Show service status, by name. If only
.Cm NAME .Cm NAME
is given and multiple instances exits, a summary of all matching is given and multiple instances exits, a summary of all matching
instances are shown. Only an exact match displays the detailed status instances are shown. Only an exact match displays detailed status
for a particular instance for a given instance.
.Pp
With the
.Fl q
option this command is silent, provided the
.Ar NAME[:ID]
selection matches a single run/task/service. The exit code of
.Nm
is non-zero if there is a problem. Zero is returned if a run/task has
run (at least) once in the current runlevel, and when a service is running
(as expected). See the
.Fl j
option for detailed JSON output suitable for machine parsing.
.Pp
.Sy Tip:
.Xr jq 1
is a useful scripting tool in combination with JSON output.
.It Nm Ar status .It Nm Ar status
Show status of all services, default command Show status of all services, default command. Also supports the
.Fl j
option for detailed JSON output.
.It Nm Ar cgroup .It Nm Ar cgroup
List cgroup config overview List cgroup config overview.
.It Nm Ar ps .It Nm Ar ps
List processes based on cgroups List processes based on cgroups.
.It Nm Ar top .It Nm Ar top
Show top-like listing based on cgroups Show top-like listing based on cgroups.
.It Nm Ar plugins
List installed plugins.
.It Nm Ar runlevel Op Ar 0-9 .It Nm Ar runlevel Op Ar 0-9
Show or set runlevel: 0 halt, 6 reboot Show or set runlevel: 0 halt, 6 reboot.
.It Nm Ar reboot .It Nm Ar reboot
Reboot system, default if Reboot system, default if
.Cm reboot .Cm reboot
is symlinked to is symlinked to
.Nm .Nm .
.It Nm Ar halt .It Nm Ar halt
Halt system, default if Halt system, default if
.Cm halt .Cm halt
is symlinked to is symlinked to
.Nm .Nm .
.It Nm Ar poweroff .It Nm Ar poweroff
Power-off system, default if Power-off system, default if
.Cm poweroff .Cm poweroff
is symlinked to is symlinked to
.Nm .Nm .
.It Nm Ar suspend .It Nm Ar suspend
Suspend system, default if Suspend system, default if
.Cm suspend .Cm suspend
is symlinked to is symlinked to
.Nm .Nm .
.It Nm Ar utmp show .It Nm Ar utmp show
Raw dump of UTMP/WTMP db Raw dump of UTMP/WTMP db.
.El .El
.Sh SEE ALSO .Sh SEE ALSO
.Xr finit.conf 5 .Xr finit.conf 5
+2 -10
View File
@@ -1,9 +1,9 @@
pkglibdir = $(plugin_path) pkglibdir = $(plugin_path)
AM_LDFLAGS = -module -avoid-version -shared AM_LDFLAGS = -module -avoid-version -shared
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE AM_CPPFLAGS = -I$(top_srcdir)/src
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
AM_CPPFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) AM_CPPFLAGS += -D__FINIT__ $(lite_CFLAGS) $(uev_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
@@ -17,10 +17,6 @@ if BUILD_DBUS_PLUGIN
libplug_la_SOURCES += dbus.c libplug_la_SOURCES += dbus.c
endif endif
if BUILD_HOTPLUG_PLUGIN
libplug_la_SOURCES += hotplug.c
endif
if BUILD_MODULES_LOAD_PLUGIN if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c libplug_la_SOURCES += modules-load.c
endif endif
@@ -68,10 +64,6 @@ if BUILD_HOOK_SCRIPTS_PLUGIN
pkglib_LTLIBRARIES += hook-scripts.la pkglib_LTLIBRARIES += hook-scripts.la
endif endif
if BUILD_HOTPLUG_PLUGIN
pkglib_LTLIBRARIES += hotplug.la
endif
if BUILD_MODULES_LOAD_PLUGIN if BUILD_MODULES_LOAD_PLUGIN
pkglib_LTLIBRARIES += modules-load.la pkglib_LTLIBRARIES += modules-load.la
endif endif
+5 -5
View File
@@ -1,6 +1,6 @@
/* Save and restore ALSA sound settings using alsactl /* Save and restore ALSA sound settings using alsactl
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -36,12 +36,12 @@
static void save(void *arg) static void save(void *arg)
{ {
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
_d("Saving sound settings ..."); dbg("Saving sound settings ...");
run_interactive(ALSACTL " -g store", "Saving sound settings"); run_interactive(ALSACTL " -g store", "Saving sound settings");
} }
} }
@@ -49,12 +49,12 @@ static void save(void *arg)
static void restore(void *arg) static void restore(void *arg)
{ {
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
_d("Restoring sound settings ..."); dbg("Restoring sound settings ...");
run_interactive(ALSACTL " -g restore", "Restoring sound settings"); run_interactive(ALSACTL " -g restore", "Restoring sound settings");
} }
} }
+43 -81
View File
@@ -1,6 +1,6 @@
/* Setup necessary system files for, e.g. UTMP (tracking logins) /* Setup necessary system files for, e.g. UTMP (tracking logins)
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,7 @@
#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"
@@ -80,7 +81,7 @@ static int bootclean(const char *fpath, const struct stat *sb, int tflag, struct
if (ftw->level == 0) if (ftw->level == 0)
return 1; return 1;
_d("Removing %s ...", fpath); dbg("Removing %s ...", fpath);
(void)remove(fpath); (void)remove(fpath);
return 0; return 0;
@@ -110,10 +111,43 @@ static void clean(void *arg)
} }
} }
static void ln(const char *target, const char *linkpath) /* Kernel defines the following compulsory and recommended links
* https://github.com/torvalds/linux/blob/v5.18/Documentation/admin-guide/devices.rst#compulsory-links
*/
static void kernel_links(void)
{ {
if (symlink(target, linkpath) && errno != EEXIST) struct {
_pe("Failed creating %s -> %s symlink", target, linkpath); char *tgt, *lnk;
int optional;
} k[] = {
{ "/proc/self/fd", "/dev/fd", 0 },
{ "fd/0", "/dev/stdin", 0 },
{ "fd/1", "/dev/stdout", 0 },
{ "fd/2", "/dev/stderr", 0 },
{ "socksys", "/dev/nfsd", 0 },
{ "null", "/dev/X0R", 0 },
{ "/proc/kcore", "/dev/core", 1 },
{ "ram0", "/dev/ramdisk", 1 },
{ "qft0", "/dev/ftape", 1 },
{ "video0", "/dev/bttv0", 1 },
{ "radio0", "/dev/radio", 1 },
};
char *fn, buf[80];
size_t i;
for (i = 0; i < NELEMS(k); i++) {
fn = k[i].tgt;
if (k[i].optional) {
if (fn[0] != '/') {
snprintf(buf, sizeof(buf), "/dev/%s", k[i].tgt);
fn = buf;
}
if (!fexist(fn))
continue;
}
ln(k[i].tgt, k[i].lnk);
}
} }
/* /*
@@ -125,87 +159,15 @@ static void setup(void *arg)
prev = umask(0); prev = umask(0);
_d("Setting up FHS structure in /var ..."); /* Kernel symlinks, e.g. /proc/self/fd -> /dev/fd */
makedir("/var/cache", 0755); kernel_links();
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755);
makedir("/var/lib", 0755);
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
makedir("/var/lib/alarm", 0755);
makedir("/var/lib/urandom",0755);
if (fisdir("/run")) {
_d("System with new /run tmpfs ...");
if (!fisdir("/run/lock"))
makedir("/run/lock", 1777);
ln("/run/lock", "/var/lock");
ln("/dev/shm", "/run/shm");
/* compat only, should really be set up by OS/dist */ /* Create all system tmpfiles.d(5) */
ln("/run", "/var/run"); tmpfilesd();
} else {
makedir("/var/lock", 1777);
makedir("/var/run", 0755);
}
makedir("/var/log", 0755);
makedir("/var/mail", 0755);
makedir("/var/opt", 0755);
makedir("/var/spool", 0755);
makedir("/var/spool/cron", 0755);
makedir("/var/tmp", 0755);
makedir("/var/empty", 0755);
/*
* UTMP actually needs multiple db files
*/
if (has_utmp()) {
int gid;
/*
* If /etc/group or "utmp" group is missing, default to
* "root", or "wheel", group.
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
create("/var/log/lastlog", 0644, 0, gid);
}
/* Set BOOT_TIME UTMP entry */ /* Set BOOT_TIME UTMP entry */
utmp_set_boot(); utmp_set_boot();
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
touch("/etc/network/run/ifstate");
#else
erase("/etc/network/run/ifstate");
#endif
_d("Setting up misc files ...");
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
mksubsys("/var/run/frr", 0755, "frr", "frr");
mksubsys("/var/log/frr", 0755, "frr", "frr");
makedir("/var/run/sshd", 01755); /* OpenSSH */
if (!fexist("/etc/mtab"))
ln("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
/* Debian has /run/sudo, ensure correct perms and SELinux label */
mksubsys("/var/run/sudo", 0711, "root", "root");
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
if (whichp("restorecon"))
run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
umask(prev); umask(prev);
} }
+102 -26
View File
@@ -1,6 +1,6 @@
/* Setup and start system message bus, D-Bus /* Setup and start system message bus, D-Bus
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -35,60 +35,136 @@
#include "service.h" #include "service.h"
#include "conf.h" #include "conf.h"
#define DAEMON "dbus-daemon" #define DBUS_DAEMON "dbus-daemon"
#define ARGS "--nofork --system --syslog-only" #define DBUS_ARGS "--nofork --system --syslog-only"
#define DESC "D-Bus message bus daemon" #define DBUS_DESC "D-Bus message bus daemon"
#ifndef DAEMONUSER #ifndef DBUS_DAEMONUSER
#define DAEMONUSER "messagebus" #define DBUS_DAEMONUSER "messagebus"
#endif #endif
#ifndef DAEMONPIDFILE #ifndef DBUS_DAEMONGROUP
#define DAEMONPIDFILE "/var/run/dbus/pid" #define DBUS_DAEMONGROUP "messagebus"
#endif #endif
/*
* Dumnpster diving for the D-Bus main configuration file
* where <pidfile> is defined.
*/
static char *dbus_pidfn(void)
{
char *alt[] = {
"/usr/share/dbus-1/system.conf",
"/etc/dbus-1/system.conf",
NULL
};
int i;
for (i = 0; alt[i]; i++) {
char *fn = alt[i];
char buf[256];
FILE *fp;
fp = fopen(fn, "r");
if (!fp)
continue;
fn = NULL;
while (fgets(buf, sizeof(buf), fp)) {
char *pos;
pos = strstr(buf, "<pidfile>");
if (!pos)
continue;
fn = pos + strlen("<pidfile>");
pos = strstr(fn, "</pidfile>");
if (!pos) {
fn = NULL;
break;
}
*pos = 0;
}
fclose(fp);
if (fn)
return strdup(fn);
}
return NULL;
}
static void setup(void *arg) static void setup(void *arg)
{ {
char *group = DBUS_DAEMONGROUP;
char *user = DBUS_DAEMONUSER;
char line[256]; char line[256];
char *pidfn;
mode_t prev; mode_t prev;
char *cmd; char *cmd;
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
cmd = which(DAEMON); cmd = which(DBUS_DAEMON);
if (!cmd) { if (!cmd) {
_d("Skipping plugin, %s is not installed.", DAEMON); dbg("Skipping plugin, %s is not installed.", DBUS_DAEMON);
return; return;
} }
prev =umask(0); if (getuser(user, NULL) == -1) {
if (getuser("dbus", NULL) == -1)
user = "root"; /* fallback */
else
user = "dbus"; /* e.g., Buildroot */
}
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER); if (getgroup(group) == -1) {
if (getgroup("dbus") == -1)
group = "root"; /* fallback */
else
group = "dbus"; /* e.g., Buildroot */
}
/* Clean up from any previous pre-bootstrap run */
pidfn = dbus_pidfn();
if (pidfn)
remove(pidfn);
dbg("Creating D-Bus Required Directories ...");
prev = umask(0);
mksubsys("/var/run/dbus", 0755, user, group);
mksubsys("/var/run/lock/subsys", 0755, user, group);
mksubsys("/var/lib/dbus", 0755, user, group);
mksubsys("/tmp/dbus", 0755, user, group);
umask(prev);
/* Generate machine id for dbus */
if (whichp("dbus-uuidgen")) if (whichp("dbus-uuidgen"))
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus"); run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
/* Clean up from any previous pre-bootstrap run */ /*
remove(DAEMONPIDFILE); * Register service with Finit
* Note: dbus drops privs after starting up.
*/
if (pidfn) {
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus pid:!%s %s %s -- %s",
pidfn, cmd, DBUS_ARGS, DBUS_DESC);
free(pidfn);
} else
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus %s %s -- %s",
cmd, DBUS_ARGS, DBUS_DESC);
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
DAEMONPIDFILE, DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL)) if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON); err(1, "Failed registering %s", DBUS_DAEMON);
free(cmd);
umask(prev); free(cmd);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_SVC_PLUGIN] = { .cb = setup },
.cb = setup
},
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+11 -10
View File
@@ -26,18 +26,11 @@
#include "plugin.h" #include "plugin.h"
#include "config.h" #include "config.h"
#define CHOOSE(x, y) y extern void plugin_script_run(hook_point_t no);
static const char *hscript_paths[] = HOOK_TYPES;
#undef CHOOSE
static void hscript_run_parts(hook_point_t hook) static void hscript_run_parts(hook_point_t no)
{ {
char path[CMD_SIZE] = ""; plugin_script_run(no);
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
strlcat(path, hscript_paths[hook] + 4, sizeof(path));
run_parts(path, NULL);
} }
static void hscript_banner(void *arg) static void hscript_banner(void *arg)
@@ -96,6 +89,14 @@ static plugin_t plugin = {
.hook[HOOK_SVC_UP] = { .cb = hscript_svc_up }, .hook[HOOK_SVC_UP] = { .cb = hscript_svc_up },
.hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up }, .hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up },
.hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown }, .hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown },
/*
* We have exited here, so these are handled a bit differently by Finit,
* when hook scripts are enabled, just to provide the user with the same
* interface:
*
* .hook[HOOK_SVC_DN] = { .cb = hscript_svc_down },
* .hook[HOOK_SYS_DN] = { .cb = hscript_system_down },
*/
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
-116
View File
@@ -1,116 +0,0 @@
/* Heuristically find and initialize a suitable hotplug daemon
*
* Copyright (c) 2012-2022 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.
*/
#include <stdio.h>
#include "config.h"
#include "conf.h"
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "service.h"
static void setup(void *arg)
{
char cmd[256];
char *path;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
/*
* Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system.
*/
path = which("udevd");
if (!path)
path = which("/lib/systemd/systemd-udevd");
if (path) {
/* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
"-- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
} else {
snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
"udevadm trigger -c add -t devices "
"-- Requesting device events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
"udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
}
free(path);
/* Debian has this little script to copy generated
* rules while the system was read-only. TODO: When
* this functionality was hardcoded in finit.c, this
* call was made in crank_worker(). Now that
* filesystems are mounted earlier we should be able
* to make this call directly after the triggers have
* run, but this has not been tested AT ALL. */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
} else {
path = which("mdev");
if (path) {
/* Embedded Linux systems usually have BusyBox mdev */
if (debug)
touch("/dev/mdev.log");
snprintf(cmd, sizeof(cmd), "%s -s", path);
free(path);
run_interactive(cmd, "Populating device tree");
}
}
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc" },
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+73 -13
View File
@@ -1,6 +1,6 @@
/* Coldplug modules using modalias magic /* Coldplug modules using modalias magic
* *
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -71,14 +71,14 @@ static int modprobe(char *alias)
pid = fork(); pid = fork();
switch (pid) { switch (pid) {
case -1: case -1:
_pe("Failed forking modprobe child"); err(1, "Failed forking modprobe child");
return 1; return 1;
case 0: case 0:
execvp(args[0], args); execvp(args[0], args);
break; break;
default: default:
if (!complete(args[0], pid)) if (!complete(args[0], pid))
_d("Successful modprobe of %s", alias); dbg("Successful modprobe of %s", alias);
break; break;
} }
@@ -106,7 +106,7 @@ static void alias_remove(struct module *m)
TAILQ_REMOVE(&modules, m, link); TAILQ_REMOVE(&modules, m, link);
free(m->alias); free(m->alias);
free(m); free(m);
} }
static int alias_exist(char *alias) static int alias_exist(char *alias)
{ {
@@ -120,27 +120,87 @@ static int alias_exist(char *alias)
return 0; return 0;
} }
static int scan_alias(const char *path, const struct stat *st, int flag, struct FTW *unused) static void alias_add_uniq(char *alias)
{
if (alias_exist(alias))
return;
alias_add(alias);
}
static FILE *maybe_fopen_alias(const char *file, const char *path)
{
const char *basename;
basename = rindex(path, '/');
if (!basename)
return NULL;
basename++;
if (strcmp(file, basename))
return NULL;
return fopen(path, "r");
}
static int scan_modalias(const char *path)
{ {
FILE *fp;
char buf[256]; char buf[256];
FILE *fp;
if (fnmatch("*/modalias", path, 0)) fp = maybe_fopen_alias("modalias", path);
return 0;
fp = fopen(path, "r");
if (!fp) if (!fp)
return 0; return 1;
if (fgets(buf, sizeof(buf), fp)) { if (fgets(buf, sizeof(buf), fp)) {
chomp(buf); chomp(buf);
if (!alias_exist(buf)) alias_add_uniq(buf);
alias_add(buf);
} }
fclose(fp); fclose(fp);
return 0; return 0;
} }
static int scan_uevent(const char *path)
{
char buf[256];
FILE *fp;
fp = maybe_fopen_alias("uevent", path);
if (!fp)
return 1;
while (fgets(buf, sizeof(buf), fp)) {
if (strstr(buf, "MODALIAS=") != buf)
continue;
chomp(buf);
alias_add_uniq(buf + strlen("MODALIAS="));
break;
}
fclose(fp);
return 0;
}
static int scan_alias(const char *path, const struct stat *st,
int flag, struct FTW *unused)
{
/* Sanity check: is this a regular file we can read? */
if (!(S_ISREG(st->st_mode) && (st->st_mode & S_IRUSR)))
return 0;
if (!scan_uevent(path))
return 0;
scan_modalias(path);
return 0;
}
static void coldplug(void *arg) static void coldplug(void *arg)
{ {
struct module *m, *tmp; struct module *m, *tmp;
+87 -31
View File
@@ -62,62 +62,120 @@
#ifndef MODULES_LOAD_PATH #ifndef MODULES_LOAD_PATH
#define MODULES_LOAD_PATH "/etc/modules-load.d" #define MODULES_LOAD_PATH "/etc/modules-load.d"
#endif #endif
#define MODPROBE_PATH "/sbin/modprobe"
#define SERVICE_LINE \ #define SERVICE_LINE \
"cgroup.init name:modprobe.%s :%d [2345] /sbin/modprobe %s %s -- Kernel module: %s" "cgroup.init name:modprobe.%s :%d [%s] %s %s %s --"
#define SERVICE_LINE_NOINDEX \ #define SERVICE_LINE_NOINDEX \
"cgroup.init name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s" "cgroup.init name:modprobe.%s [%s] %s %s %s --"
static int load_file(const char *file, int index) static int modules_load(const char *file, int index)
{ {
char module_path[PATH_MAX]; char module_path[PATH_MAX];
char line[256]; char *modprobe_path;
int num = 0; int num = 0;
char *line;
char *lvl;
FILE *fp; FILE *fp;
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path)); strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, file, sizeof(module_path)); strlcat(module_path, file, sizeof(module_path));
fp = fopen(module_path, "r"); fp = fopen(module_path, "r");
if (!fp) if (!fp) {
warnx("failed opening %s for reading, skipping ...", module_path);
return 0; return 0;
}
while (fgets(line, sizeof(line), fp)) { modprobe_path = strdup(MODPROBE_PATH);
char cmd[CMD_SIZE], *mod, *args; if (!modprobe_path) {
fail:
warnx("failed allocating memory in modules-load plugin.");
fclose(fp);
return -1;
}
chomp(line); lvl = strdup("S");
if (!lvl) {
free(modprobe_path);
goto fail;
}
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
char cmd[CMD_SIZE * 2], *mod, *args, *set;
/*
* fparseln() skips regular UNIX comments only.
* This is for modules-load.d(5) compat.
*/
if (line[0] == ';')
goto next;
/* trim whitespace */
mod = strip_line(line); mod = strip_line(line);
if (!mod[0]) if (!mod[0])
continue; goto next;
if (mod[0] == '#' || mod[0] == ';') /* Finit extension 'set foo bar' */
continue; if ((set = fgetval(mod, "set", "= \t"))) {
char *val = mod;
if (!strcmp(mod, "set noindex")) { if (!strcmp(set, "noindex")) {
index = 0; index = 0;
continue; free(set);
} goto next;
if (!strncmp(mod, "set index", 9)) { }
mod += 9;
args = strchr(mod, '='); if ((val = fgetval(set, "index", "= \t"))) {
if (args) /* optional */ index = atoi(val);
mod = args + 1; free(set);
index = atoi(mod); free(val);
continue; goto next;
}
if ((val = fgetval(set, "runlevel", "= \t"))) {
free(lvl);
lvl = val;
free(set);
goto next;
}
if ((val = fgetval(set, "modprobe", "= \t"))) {
if (access(val, X_OK)) {
warn("%s: cannot use %s", module_path, val);
free(set);
free(val);
free(line);
goto skip;
} else {
free(modprobe_path);
modprobe_path = val;
}
free(set);
goto next;
}
/* unknown 'set' command, ignore. */
goto next;
} }
mod = strtok_r(mod, " ", &args); mod = strtok_r(mod, " ", &args);
if (!mod) if (!mod)
continue; goto next;
if (!index) if (!index)
snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, mod, args, mod); snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, lvl, modprobe_path, mod, args);
else else
snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, mod, args, mod); snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, lvl, modprobe_path, mod, args);
dbg("task %s", cmd);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL); service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
num++; num++;
next:
free(line);
} }
skip:
free(modprobe_path);
free(lvl);
fclose(fp); fclose(fp);
return num; return num;
@@ -142,11 +200,11 @@ static void load(void *arg)
int index = 1; int index = 1;
int i, num; int i, num;
_d("Scanning " MODULES_LOAD_PATH " for config files ..."); dbg("Scanning " MODULES_LOAD_PATH " for config files ...");
num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort); num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort);
if (num > 0) { if (num > 0) {
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
index += load_file(dentry[i]->d_name, index); index += modules_load(dentry[i]->d_name, index);
free(dentry[i]); free(dentry[i]);
} }
free(dentry); free(dentry);
@@ -154,10 +212,8 @@ static void load(void *arg)
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_SVC_PLUGIN] = { .cb = load },
.cb = load
},
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+36 -36
View File
@@ -2,7 +2,7 @@
/* Netlink plugin for IFUP/IFDN and GW events /* Netlink plugin for IFUP/IFDN and GW events
* *
* Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se> * Copyright (C) 2009-2011 Mårten Wikström <marten.wikstrom@keystream.se>
* Copyright (C) 2009-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (C) 2009-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -62,7 +62,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
int la; int la;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
_e("Packet too small or truncated!"); errx(1, "Packet too small or truncated!");
return; return;
} }
@@ -70,7 +70,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
a = RTM_RTA(r); a = RTM_RTA(r);
la = RTM_PAYLOAD(nlmsg); la = RTM_PAYLOAD(nlmsg);
if (la >= len) { if (la >= len) {
_e("Packet too large!"); errx(1, "Packet too large!");
return; return;
} }
@@ -80,18 +80,18 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
switch (a->rta_type) { switch (a->rta_type) {
case RTA_GATEWAY: case RTA_GATEWAY:
gw = *((int *)data); gw = *((int *)data);
//_d("GW: 0x%04x", gw); //dbg("GW: 0x%04x", gw);
break; break;
case RTA_DST: case RTA_DST:
dst = *((int *)data); dst = *((int *)data);
plen = r->rtm_dst_len; plen = r->rtm_dst_len;
//_d("Prefix LEN: 0x%04x", plen); //dbg("Prefix LEN: 0x%04x", plen);
break; break;
case RTA_OIF: case RTA_OIF:
idx = *((int *)data); idx = *((int *)data);
//_d("IDX: 0x%04x", idx); //dbg("IDX: 0x%04x", idx);
break; break;
} }
@@ -102,7 +102,7 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
ing.s_addr = gw; ing.s_addr = gw;
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr)); inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr)); inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx); dbg("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
if ((!dst && !plen) && (gw || idx)) { if ((!dst && !plen) && (gw || idx)) {
if (nlmsg->nlmsg_type == RTM_DELROUTE) { if (nlmsg->nlmsg_type == RTM_DELROUTE) {
@@ -167,7 +167,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
int la; int la;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
_e("Packet too small or truncated!"); errx(1, "Packet too small or truncated!");
return; return;
} }
@@ -175,7 +175,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg))); a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg)); la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
if (la >= len) { if (la >= len) {
_e("Packet too large!"); errx(1, "Packet too large!");
return; return;
} }
@@ -185,7 +185,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
strlcpy(ifname, RTA_DATA(a), sizeof(ifname)); strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
if (validate_ifname(ifname)) { if (validate_ifname(ifname)) {
_d("Invalid interface name '%s', skipping ...", ifname); dbg("Invalid interface name '%s', skipping ...", ifname);
continue; continue;
} }
@@ -195,7 +195,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
* New interface has appeared, or interface flags has changed. * New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN * Check ifi_flags here to see if the interface is UP/DOWN
*/ */
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change); dbg("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1); net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP); net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING); net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
@@ -205,7 +205,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
case RTM_DELLINK: case RTM_DELLINK:
/* NOTE: Interface has disappeared, not link down ... */ /* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname); dbg("%s: Delete link", ifname);
net_cond_set(ifname, "exist", 0); net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0); net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0); net_cond_set(ifname, "running", 0);
@@ -213,15 +213,15 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
break; break;
case RTM_NEWADDR: case RTM_NEWADDR:
_d("%s: New Address", ifname); dbg("%s: New Address", ifname);
break; break;
case RTM_DELADDR: case RTM_DELADDR:
_d("%s: Deconfig Address", ifname); dbg("%s: Deconfig Address", ifname);
break; break;
default: default:
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type); dbg("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
break; break;
} }
} }
@@ -246,46 +246,46 @@ static int nl_parse(int sd)
break; break;
default: default:
_pe("recv()"); err(1, "recv()");
break; break;
} }
return -1; return -1;
} }
// _d("recv %zd bytes", len); // dbg("recv %zd bytes", len);
l = (size_t)len; l = (size_t)len;
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) { for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
struct nlmsgerr *nle; struct nlmsgerr *nle;
switch (nh->nlmsg_type) { switch (nh->nlmsg_type) {
case NLMSG_DONE: case NLMSG_DONE:
// _d("Done with netlink messages."); // dbg("Done with netlink messages.");
return 0; return 0;
case NLMSG_ERROR: case NLMSG_ERROR:
// _d("Kernel netlink comm. error."); // dbg("Kernel netlink comm. error.");
nle = NLMSG_DATA(nh); nle = NLMSG_DATA(nh);
if (nle) { if (nle) {
errno = -nle->error; errno = -nle->error;
_pe("Kernel netlink error %d", errno); err(1, "Kernel netlink error %d", errno);
} }
return -1; return -1;
case RTM_NEWROUTE: case RTM_NEWROUTE:
case RTM_DELROUTE: case RTM_DELROUTE:
// _d("Netlink route ..."); // dbg("Netlink route ...");
nl_route(nh, len); nl_route(nh, len);
break; break;
case RTM_NEWLINK: case RTM_NEWLINK:
case RTM_DELLINK: case RTM_DELLINK:
// _d("Netlink link ..."); // dbg("Netlink link ...");
nl_link(nh, len); nl_link(nh, len);
break; break;
default: default:
_w("unhandled netlink message, type %d", nh->nlmsg_type); warnx("unhandled netlink message, type %d", nh->nlmsg_type);
break; break;
} }
} }
@@ -304,21 +304,21 @@ static int nl_request(int sd, unsigned int seq, int type)
switch (type) { switch (type) {
case RTM_GETROUTE: case RTM_GETROUTE:
// _d("RTM_GETROUTE"); // dbg("RTM_GETROUTE");
nlr->rtm.rtm_family = AF_INET; nlr->rtm.rtm_family = AF_INET;
nlr->rtm.rtm_table = RT_TABLE_MAIN; nlr->rtm.rtm_table = RT_TABLE_MAIN;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
break; break;
case RTM_GETLINK: case RTM_GETLINK:
// _d("RTM_GETLINK"); // dbg("RTM_GETLINK");
nlr->ifi.ifi_family = AF_UNSPEC; nlr->ifi.ifi_family = AF_UNSPEC;
nlr->ifi.ifi_change = 0xFFFFFFFF; nlr->ifi.ifi_change = 0xFFFFFFFF;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
break; break;
default: default:
_w("Cannot resync, unhandled message type %d", type); warnx("Cannot resync, unhandled message type %d", type);
return -1; return -1;
} }
@@ -331,13 +331,13 @@ static int nl_request(int sd, unsigned int seq, int type)
static void nl_resync_routes(int sd, unsigned int seq) static void nl_resync_routes(int sd, unsigned int seq)
{ {
if (nl_request(sd, seq, RTM_GETROUTE)) if (nl_request(sd, seq, RTM_GETROUTE))
_pe("Failed netlink route request"); err(1, "Failed netlink route request");
} }
static void nl_resync_ifaces(int sd, unsigned int seq) static void nl_resync_ifaces(int sd, unsigned int seq)
{ {
if (nl_request(sd, seq, RTM_GETLINK)) if (nl_request(sd, seq, RTM_GETLINK))
_pe("Failed netlink link request"); err(1, "Failed netlink link request");
} }
/* /*
@@ -350,12 +350,12 @@ static void nl_resync(int all)
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (sd < 0) { if (sd < 0) {
_pe("netlink socket"); err(1, "netlink socket");
return; return;
} }
if (all) { if (all) {
_d("============================ RESYNC ================================="); dbg("============================ RESYNC =================================");
/* this doesn't update conditions, and thus does not stop services */ /* this doesn't update conditions, and thus does not stop services */
cond_deassert("net/"); cond_deassert("net/");
@@ -364,7 +364,7 @@ static void nl_resync(int all)
/* delayed update after we've corrected things */ /* delayed update after we've corrected things */
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
_d("=========================== RESYNCED ================================"); dbg("=========================== RESYNCED ================================");
} else } else
nl_resync_routes(sd, seq++); nl_resync_routes(sd, seq++);
@@ -375,7 +375,7 @@ static void nl_callback(void *arg, int sd, int events)
{ {
if (nl_parse(sd) < 0) { if (nl_parse(sd) < 0) {
if (errno == ENOBUFS) { /* netlink(7) */ if (errno == ENOBUFS) { /* netlink(7) */
_w("busy system, resynchronizing with kernel."); warnx("busy system, resynchronizing with kernel.");
nl_resync(1); nl_resync(1);
return; return;
} }
@@ -386,7 +386,7 @@ static void nl_callback(void *arg, int sd, int events)
* we need to check ourselves, e.g. for loss of default route. * we need to check ourselves, e.g. for loss of default route.
*/ */
if (nl_ifdown) { if (nl_ifdown) {
_d("interface down, checking default route."); dbg("interface down, checking default route.");
if (nl_defidx > 0) { if (nl_defidx > 0) {
nl_defidx = 0; nl_defidx = 0;
nl_resync(0); nl_resync(0);
@@ -421,7 +421,7 @@ PLUGIN_INIT(plugin_init)
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE); sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sd < 0) { if (sd < 0) {
_pe("socket()"); err(1, "socket()");
return; return;
} }
@@ -431,14 +431,14 @@ PLUGIN_INIT(plugin_init)
sa.nl_pid = getpid(); sa.nl_pid = getpid();
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
_pe("bind()"); err(1, "bind()");
close(sd); close(sd);
return; return;
} }
nl_buf = malloc(NL_BUFSZ); nl_buf = malloc(NL_BUFSZ);
if (!nl_buf) { if (!nl_buf) {
_pe("malloc()"); err(1, "malloc()");
close(sd); close(sd);
return; return;
} }
+42 -25
View File
@@ -1,7 +1,7 @@
/* Simple pidfile event monitor for the Finit condition engine /* Simple pidfile event monitor for the Finit condition engine
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
ptr = slash++; ptr = slash++;
slash = strchr(ptr, '/'); slash = strchr(ptr, '/');
if (slash) { if (slash) {
_d("Path too deep, skipping."); dbg("Path too deep, skipping.");
return -1; return -1;
} }
} }
@@ -71,30 +71,39 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0)) if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return; return;
_d("path: %s, mask: %08x", fn, mask); dbg("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn); svc = svc_find_by_pidfile(fn);
if (!svc) { if (!svc) {
_d("No matching svc for %s", fn); dbg("No matching svc for %s", fn);
return; return;
} }
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid); dbg("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc); /*
* only mark native services as started, the s6 and
* systemd style services rely on their respective
* readiness notification. Issue #343
*/
if (!svc->notify)
svc_started(svc);
if (!svc_has_pidfile(svc)) { if (!svc_has_pidfile(svc)) {
_d("Setting %s PID file to %s", svc->name, fn); dbg("Setting %s PID file to %s", svc->name, fn);
pid_file_set(svc, fn, 1); pid_file_set(svc, fn, 1);
} }
if (svc_is_forking(svc)) { if (svc_is_forking(svc)) {
pid_t pid; pid_t pid;
pid = pid_file_read(pid_file(svc)); pid = pid_file_read(fn);
if (pid == -1) { if (pid == -1) {
cond_clear(cond); /* handle case when service dies immediately */
if (!fexist(fn))
cond_clear(cond);
return; return;
} }
@@ -102,7 +111,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
service_forked(svc); service_forked(svc);
if (pid != svc->pid) { if (pid != svc->pid) {
_d("Forking service %s (cmd %s) changed PID from %d to %d", dbg("Forking service %s (cmd %s) changed PID from %d to %d",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid); svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
svc->pid = pid; svc->pid = pid;
@@ -112,8 +121,13 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
} }
cond_set(cond); cond_set(cond);
} else if (mask & IN_DELETE) if (!svc->notify)
service_ready(svc);
} else if (mask & IN_DELETE) {
cond_clear(cond); cond_clear(cond);
if (!svc->notify)
service_ready(svc);
}
} }
/* synthesize events in case of new run dirs */ /* synthesize events in case of new run dirs */
@@ -135,7 +149,7 @@ static void pidfile_scandir(struct iwatch *iw, char *dir, int len)
return; return;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
_d("scan found %s", gl.gl_pathv[i]); dbg("scan found %s", gl.gl_pathv[i]);
pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE); pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
} }
globfree(&gl); globfree(&gl);
@@ -150,7 +164,7 @@ static void pidfile_handle_dir(struct iwatch *iw, char *dir, char *name, int mas
struct iwatch_path *iwp; struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name); paste(path, sizeof(path), dir, name);
_d("path: %s", path); dbg("path: %s", path);
iwp = iwatch_find_by_path(iw, path); iwp = iwatch_find_by_path(iw, path);
@@ -174,7 +188,7 @@ static void pidfile_callback(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
_pe("invalid inotify event"); err(1, "invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -226,6 +240,9 @@ static void pidfile_reconf(void *arg)
if (svc_is_changed(svc) || svc_is_starting(svc)) if (svc_is_changed(svc) || svc_is_starting(svc))
continue; continue;
if (!svc->notify)
service_ready(svc);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
if (cond_get(cond) == COND_ON) if (cond_get(cond) == COND_ON)
continue; continue;
@@ -234,9 +251,9 @@ static void pidfile_reconf(void *arg)
} }
/* /*
* This will call service_step(), which in turn will schedule itself * This calls service_step(), which in turn schedules itself as
* as long as stepped services change state. Services going from * long as stepped services change state. Services going from
* WAITING to RUNNING will reassert their conditions in that loop, * PAUSED to RUNNING reassert their conditions in that loop,
* which in turn may unlock other services, and so on. * which in turn may unlock other services, and so on.
*/ */
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK); service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
@@ -248,7 +265,7 @@ static void pidfile_init(void *arg)
char *path; char *path;
if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_CONDPID, piddir, sizeof(piddir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID); err(1, "Failed creating %s condition directory, %s", COND_PID, _PATH_CONDPID);
return; return;
} }
@@ -259,10 +276,10 @@ static void pidfile_init(void *arg)
*/ */
path = realpath(_PATH_VARRUN, NULL); path = realpath(_PATH_VARRUN, NULL);
if (!path) { if (!path) {
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno)); dbg("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL); path = realpath("/run", NULL);
if (!path) { if (!path) {
_e("System does not have %s or /run, aborting.", _PATH_VARRUN); errx(1, "System does not have %s or /run, aborting.", _PATH_VARRUN);
return; return;
} }
} }
@@ -283,15 +300,15 @@ static void pidfile_init(void *arg)
* *
* service <net/iface/lo> /sbin/dropbear ... * service <net/iface/lo> /sbin/dropbear ...
* *
* Which provides the <pid/dropbear> condition, will not be set by * This service provides the <pid/dropbear> condition, which will not be
* pidfile.so during `initctl reload` because dropbear is still * set by pidfile.so during `initctl reload` because dropbear is still
* SIGSTP:ed waiting for <net/iface/lo>. * SIGSTP:ed (in state PAUSED) waiting for <net/iface/lo>.
*/ */
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init }, .hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf }, .hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
.depends = { "netlink" }, .depends = { "netlink" }, /* bootmisc depends on us */
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+2 -2
View File
@@ -1,6 +1,6 @@
/* Set kernel variables from /etc/sysctl.conf et al /* Set kernel variables from /etc/sysctl.conf et al
* *
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@ static void setup(void *arg)
glob_t gl; glob_t gl;
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
+3 -3
View File
@@ -1,6 +1,6 @@
/* Setup necessary files for resolvconf /* Setup necessary files for resolvconf
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@
*/ */
static void setup(void *arg) static void setup(void *arg)
{ {
_d("Setting up the resolver ..."); dbg("Setting up the resolver ...");
mkpath("/etc/resolvconf", 0755); mkpath("/etc/resolvconf", 0755);
mkpath("/etc/resolvconf/run", 0755); mkpath("/etc/resolvconf/run", 0755);
mkpath("/var/run/resolvconf", 0755); mkpath("/var/run/resolvconf", 0755);
@@ -45,7 +45,7 @@ static void setup(void *arg)
touch("/etc/resolvconf/run/enable-updates"); touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface"); chdir("/etc/resolvconf/run/interface");
run_parts("/etc/resolvconf/update.d", "-i"); run_parts("/etc/resolvconf/update.d", "-i", NULL, 0);
chdir("/"); chdir("/");
} }
+28 -11
View File
@@ -1,6 +1,6 @@
/* Save and restore RTC using hwclock /* Save and restore RTC using hwclock
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -21,6 +21,8 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h"
#include <time.h> #include <time.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@@ -35,7 +37,13 @@
#include "plugin.h" #include "plugin.h"
/* Kernel RTC driver validates against this date for sanity check */ /* Kernel RTC driver validates against this date for sanity check */
#define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */ #define RTC_TIMESTAMP_BEGIN_2000 "2000-01-01 00:00:00"
#ifdef RTC_TIMESTAMP_CUSTOM
static char *rtc_timestamp = RTC_TIMESTAMP_CUSTOM;
#else
static char *rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
#endif
static time_t rtc_date_fallback = 946684800LL;
static void tz_set(char *tz, size_t len) static void tz_set(char *tz, size_t len)
{ {
@@ -89,7 +97,7 @@ static void rtc_save(void *arg)
char tz[128]; char tz[128];
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
@@ -99,10 +107,10 @@ static void rtc_save(void *arg)
tz_set(tz, sizeof(tz)); tz_set(tz, sizeof(tz));
rc = gettimeofday(&tv, NULL); rc = gettimeofday(&tv, NULL);
if (rc < 0 || tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) { if (rc < 0 || tv.tv_sec < rtc_date_fallback) {
print_desc(NULL, "System clock invalid, not saving to RTC"); print_desc(NULL, "System clock invalid, not saving to RTC");
invalid: invalid:
logit(LOG_ERR, "System clock invalid, before 2000-01-01 00:00, not saving to RTC"); logit(LOG_ERR, "System clock invalid, before %s, not saving to RTC", rtc_timestamp);
rc = 2; rc = 2;
goto out; goto out;
} }
@@ -131,13 +139,15 @@ static void rtc_restore(void *arg)
char tz[128]; char tz[128];
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
fd = rtc_open(); fd = rtc_open();
if (fd < 0) if (fd < 0) {
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
return; return;
}
tz_set(tz, sizeof(tz)); tz_set(tz, sizeof(tz));
if (ioctl(fd, RTC_RD_TIME, &tm) < 0) { if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
@@ -151,7 +161,7 @@ static void rtc_restore(void *arg)
invalid: invalid:
logit(LOG_ERR, "Failed restoring system clock from RTC."); logit(LOG_ERR, "Failed restoring system clock from RTC.");
if (EINVAL == errno) if (EINVAL == errno)
logit(LOG_ERR, "RTC time is too old (before 2000-01-01 00:00)"); logit(LOG_ERR, "RTC time is too old (before %s)", rtc_timestamp);
else if (ENOENT == errno) else if (ENOENT == errno)
logit(LOG_ERR, "RTC has no previously saved (valid) time."); logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else else
@@ -162,18 +172,18 @@ static void rtc_restore(void *arg)
goto out; goto out;
/* Attempt to set RTC to a sane value ... */ /* Attempt to set RTC to a sane value ... */
tv.tv_sec = RTC_TIMESTAMP_BEGIN_2000; tv.tv_sec = rtc_date_fallback;
if (!gmtime_r(&tv.tv_sec, &tm)) if (!gmtime_r(&tv.tv_sec, &tm))
goto out; goto out;
logit(LOG_NOTICE, "Resetting RTC to kernel default, 2000-01-01 00:00."); logit(LOG_NOTICE, "Resetting RTC to kernel default, %s.", rtc_timestamp);
rc = 2; rc = 2;
} }
print_desc(NULL, "Restoring system clock (UTC) from RTC"); print_desc(NULL, "Restoring system clock (UTC) from RTC");
tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */ tm.tm_isdst = -1; /* Use tzdata to figure it out, please. */
tv.tv_sec = mktime(&tm); tv.tv_sec = mktime(&tm);
if (tv.tv_sec < RTC_TIMESTAMP_BEGIN_2000) { if (tv.tv_sec == (time_t)-1 || tv.tv_sec < rtc_date_fallback) {
errno = EINVAL; errno = EINVAL;
goto invalid; goto invalid;
} }
@@ -199,6 +209,13 @@ static plugin_t plugin = {
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
{ {
struct tm tm = { 0 };
if (!strptime(rtc_timestamp, "%Y-%m-%d %H:%M", &tm))
rtc_date_fallback = mktime(&tm);
else
rtc_timestamp = RTC_TIMESTAMP_BEGIN_2000;
plugin_register(&plugin); plugin_register(&plugin);
} }
+12 -8
View File
@@ -1,6 +1,6 @@
/* System condition event monitor for the Finit condition engine /* System condition event monitor for the Finit condition engine
* *
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -63,7 +63,7 @@ static void sys_update_conds(char *dir, char *name, uint32_t mask)
return; return;
cond += strlen(COND_BASE) + 1; cond += strlen(COND_BASE) + 1;
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0); dbg("cond: %s set: %d", cond, (mask & IN_CREATE) ? 1 : 0);
if (!cond_update(cond)) if (!cond_update(cond))
unlink(path); unlink(path);
} }
@@ -82,7 +82,7 @@ static void sys_scandir(struct iwatch *iw, char *dir, int len)
return; return;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
_d("scan found %s", gl.gl_pathv[i]); dbg("scan found %s", gl.gl_pathv[i]);
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE); sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
} }
globfree(&gl); globfree(&gl);
@@ -97,7 +97,7 @@ static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
struct iwatch_path *iwp; struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name); paste(path, sizeof(path), dir, name);
_d("path: %s", path); dbg("path: %s", path);
iwp = iwatch_find_by_path(iw, path); iwp = iwatch_find_by_path(iw, path);
@@ -121,7 +121,7 @@ static void sys_callback(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
_pe("invalid inotify event"); err(1, "invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -136,7 +136,7 @@ static void sys_callback(void *arg, int fd, int events)
if (off + sizeof(*ev) + ev->len > (size_t)sz) if (off + sizeof(*ev) + ev->len > (size_t)sz)
break; break;
_d("name %s, event: 0x%08x", ev->name, ev->mask); dbg("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask) if (!ev->mask)
continue; continue;
@@ -160,24 +160,28 @@ static void sys_init(void *arg)
char sysdir[MAX_ARG_LEN]; char sysdir[MAX_ARG_LEN];
char *path; char *path;
mkpath(_PATH_COND, 0755);
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS); err(1, "Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
return; return;
} }
path = realpath(_PATH_CONDSYS, NULL); path = realpath(_PATH_CONDSYS, NULL);
if (!path) { if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS); err(1, "Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
return; return;
} }
if (iwatch_add(&iw_sys, path, IN_ONLYDIR)) if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
iwatch_exit(&iw_sys); iwatch_exit(&iw_sys);
free(path);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = sys_init }, .hook[HOOK_BASEFS_UP] = { .cb = sys_init },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+6 -6
View File
@@ -1,7 +1,7 @@
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB /* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
* *
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com> * Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2013-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -55,7 +55,7 @@ static void setup(void)
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0) if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
_pe("Failed starting TTY watcher"); err(1, "Failed starting TTY watcher");
} }
static void do_tty(char *tty, size_t len, int creat) static void do_tty(char *tty, size_t len, int creat)
@@ -67,10 +67,10 @@ static void do_tty(char *tty, size_t len, int creat)
svc = svc_find_by_tty(name); svc = svc_find_by_tty(name);
if (svc) { if (svc) {
if (svc_is_blocked(svc) && creat) { if (svc_is_blocked(svc) && creat) {
_d("%s: blocked, re-enabling", svc_ident(svc, NULL, 0)); dbg("%s: blocked, re-enabling", svc_ident(svc, NULL, 0));
svc_start(svc); svc_start(svc);
} else if (svc->pid) { } else if (svc->pid) {
_d("%s: missing ...", svc_ident(svc, NULL, 0)); dbg("%s: missing ...", svc_ident(svc, NULL, 0));
svc_missing(svc); svc_missing(svc);
} }
@@ -87,7 +87,7 @@ static void tty_watcher(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
_pe("invalid inotify event"); err(1, "invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -103,7 +103,7 @@ static void tty_watcher(void *arg, int fd, int events)
if (!ev->mask) if (!ev->mask)
continue; continue;
_d("tty %s, event: 0x%08x", ev->name, ev->mask); dbg("tty %s, event: 0x%08x", ev->name, ev->mask);
do_tty(ev->name, ev->len, ev->mask & IN_CREATE); do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
} }
} }
+4 -4
View File
@@ -1,6 +1,6 @@
/* Setup and save random seed at boot/shutdown /* Setup and save random seed at boot/shutdown
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -51,7 +51,7 @@ static void setup(void *arg)
int fd, err; int fd, err;
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
@@ -81,7 +81,7 @@ static void setup(void *arg)
umask(prev); umask(prev);
} }
print_desc("Initializing random number generator", NULL); print_desc("Seeding random number generator", NULL);
/* /*
* Simply copying our saved entropy to /dev/urandom doesn't * Simply copying our saved entropy to /dev/urandom doesn't
@@ -144,7 +144,7 @@ static void save(void *arg)
mode_t prev; mode_t prev;
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
+7 -5
View File
@@ -1,6 +1,6 @@
/* User-defined condition event monitor for the Finit condition engine /* User-defined condition event monitor for the Finit condition engine
* *
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ static void usr_callback(void *arg, int fd, int events)
sz = read(fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0) {
_pe("invalid inotify event"); err(1, "invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -65,7 +65,7 @@ static void usr_callback(void *arg, int fd, int events)
if (off + sizeof(*ev) + ev->len > (size_t)sz) if (off + sizeof(*ev) + ev->len > (size_t)sz)
break; break;
_d("name %s, event: 0x%08x", ev->name, ev->mask); dbg("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask) if (!ev->mask)
continue; continue;
@@ -84,14 +84,15 @@ static void usr_init(void *arg)
char usrdir[MAX_ARG_LEN]; char usrdir[MAX_ARG_LEN];
char *path; char *path;
mkpath(_PATH_COND, 0755);
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR); err(1, "Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
return; return;
} }
path = realpath(_PATH_CONDUSR, NULL); path = realpath(_PATH_CONDUSR, NULL);
if (!path) { if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR); err(1, "Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
return; return;
} }
@@ -104,6 +105,7 @@ static void usr_init(void *arg)
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = usr_init }, .hook[HOOK_BASEFS_UP] = { .cb = usr_init },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+4 -33
View File
@@ -1,6 +1,6 @@
/* Console setup (for X) /* Console setup (for X)
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -36,47 +36,18 @@
static void setup(void *arg) static void setup(void *arg)
{ {
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE
int fd;
#endif
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); dbg("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
} }
prev = umask(0); if (fexist("/var/run/console/console.lock") && which("pam_console_apply"))
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
touch(line);
#ifdef PAM_CONSOLE
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username));
close(fd);
run("pam_console_apply", "pam-console"); run("pam_console_apply", "pam-console");
}
#endif
makedir("/tmp/.X11-unix", 01777);
makedir("/tmp/.ICE-unix", 01777);
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix", "restorecon");
umask(prev);
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = __FILE__, .name = __FILE__,
.hook[HOOK_NETWORK_UP] = { .hook[HOOK_SVC_PLUGIN] = { .cb = setup },
.cb = setup
},
.depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+7 -2
View File
@@ -42,8 +42,10 @@ logit_CFLAGS += $(lite_CFLAGS)
logit_LDADD = $(lite_LIBS) logit_LDADD = $(lite_LIBS)
finit_SOURCES = api.c cgroup.c cgroup.h \ finit_SOURCES = api.c cgroup.c cgroup.h \
client.c client.h \
cond.c cond-w.c cond.h \ cond.c cond-w.c cond.h \
conf.c conf.h \ conf.c conf.h \
devmon.c devmon.h \
exec.c finit.c finit.h \ exec.c finit.c finit.h \
stty.c \ stty.c \
helpers.c helpers.h \ helpers.c helpers.h \
@@ -57,6 +59,7 @@ 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
@@ -64,8 +67,10 @@ if LOGROTATE
finit_SOURCES += logrotate.c finit_SOURCES += logrotate.c
endif endif
pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h pkginclude_HEADERS = cgroup.h cond.h conf.h finit.h helpers.h log.h \
plugin.h svc.h service.h
finit_CPPFLAGS = $(AM_CPPFLAGS) -D__FINIT__
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
finit_LDADD = $(lite_LIBS) $(uev_LIBS) finit_LDADD = $(lite_LIBS) $(uev_LIBS)
@@ -77,7 +82,7 @@ endif
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \ initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
client.c client.h cond.c cond.h reboot.c \ client.c client.h cond.c cond.h reboot.c \
serv.c serv.h svc.h util.c util.h serv.c serv.h svc.h util.c util.h log.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) initctl_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
initctl_LDADD = $(lite_LIBS) $(uev_LIBS) initctl_LDADD = $(lite_LIBS) $(uev_LIBS)
+130 -53
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket. /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -45,11 +45,11 @@
#include "log.h" #include "log.h"
#include "plugin.h" #include "plugin.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "schedule.h"
#include "service.h" #include "service.h"
#include "sig.h"
#include "util.h" #include "util.h"
extern svc_t *wdog;
static uev_t api_watcher; static uev_t api_watcher;
static int call(int (*action)(svc_t *, void *), char *buf, size_t len) static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
@@ -62,8 +62,10 @@ static int stop(svc_t *svc, void *user_data)
if (!svc) if (!svc)
return 1; return 1;
service_timeout_cancel(svc);
svc_stop(svc); svc_stop(svc);
service_step(svc); service_step(svc);
service_step_all(SVC_TYPE_ANY);
return 0; return 0;
} }
@@ -73,8 +75,10 @@ static int start(svc_t *svc, void *user_data)
if (!svc) if (!svc)
return 1; return 1;
service_timeout_cancel(svc);
svc_start(svc); svc_start(svc);
service_step(svc); service_step(svc);
service_step_all(SVC_TYPE_ANY);
return 0; return 0;
} }
@@ -89,8 +93,11 @@ static int restart(svc_t *svc, void *user_data)
if (!svc) if (!svc)
return 1; return 1;
svc_mark_dirty(svc); if (!svc_is_running(svc))
svc_start(svc); return start(svc, user_data);
service_timeout_cancel(svc);
service_stop(svc);
service_step(svc); service_step(svc);
return 0; return 0;
@@ -103,6 +110,8 @@ static int reload(svc_t *svc, void *user_data)
if (svc_is_blocked(svc)) if (svc_is_blocked(svc))
svc_start(svc); svc_start(svc);
else
service_timeout_cancel(svc);
svc_mark_dirty(svc); svc_mark_dirty(svc);
service_step(svc); service_step(svc);
@@ -203,53 +212,76 @@ static svc_t *do_find(char *buf, size_t len)
static svc_t *do_find_byc(char *buf, size_t len) static svc_t *do_find_byc(char *buf, size_t len)
{ {
svc_t *svc, *iter = NULL;
char *input; char *input;
input = sanitize(buf, len); input = sanitize(buf, len);
if (!input) { if (!input) {
_d("Invalid input"); dbg("Invalid input");
return NULL; return NULL;
} }
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { return svc_find_by_cond(input);
char cond[MAX_COND_LEN];
mkcond(svc, cond, sizeof(cond));
_d("Comparing input %s with cond %s", input, cond);
if (string_compare(cond, input))
return svc;
}
_d("None found");
return NULL;
} }
static int do_reboot(int cmd, char *buf, size_t len) static void bypass_shutdown(void *);
struct wq emergency = { .cb = bypass_shutdown };
static void bypass_shutdown(void *unused)
{
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
do_shutdown(halt);
}
static int do_reboot(int cmd, int timeout, char *buf, size_t len)
{ {
int rc = 1; int rc = 1;
switch (cmd) { switch (cmd) {
case INIT_CMD_REBOOT: case INIT_CMD_REBOOT:
_d("reboot"); halt = SHUT_REBOOT;
break;
case INIT_CMD_HALT:
halt = SHUT_HALT;
break;
case INIT_CMD_POWEROFF:
halt = SHUT_OFF;
break;
case INIT_CMD_SUSPEND:
break;
default:
return 255;
}
if (timeout > 0) {
emergency.delay = timeout * 1000;
schedule_work(&emergency);
}
switch (cmd) {
case INIT_CMD_REBOOT:
dbg("reboot");
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); service_runlevel(6);
break; break;
case INIT_CMD_HALT: case INIT_CMD_HALT:
_d("halt"); dbg("halt");
halt = SHUT_HALT; halt = SHUT_HALT;
service_runlevel(0); service_runlevel(0);
break; break;
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
_d("poweroff"); dbg("poweroff");
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); service_runlevel(0);
break; break;
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
_d("suspend"); dbg("suspend");
sync(); sync();
rc = suspend(); rc = suspend();
if (rc) { if (rc) {
@@ -261,7 +293,7 @@ static int do_reboot(int cmd, char *buf, size_t len)
break; break;
default: default:
rc = 255; /* Handled previously */
break; break;
} }
@@ -290,7 +322,7 @@ static void send_svc(int sd, svc_t *svc)
len = write(sd, svc, sizeof(*svc)); len = write(sd, svc, sizeof(*svc));
if (len != sizeof(*svc)) if (len != sizeof(*svc))
_d("Failed sending svc_t to client"); dbg("Failed sending svc_t to client");
} }
static void api_cb(uev_t *w, void *arg, int events) static void api_cb(uev_t *w, void *arg, int events)
@@ -302,7 +334,7 @@ static void api_cb(uev_t *w, void *arg, int events)
sd = accept(w->fd, NULL, NULL); sd = accept(w->fd, NULL, NULL);
if (sd < 0) { if (sd < 0) {
_pe("Failed serving API request"); err(1, "Failed serving API request");
goto error; goto error;
} }
@@ -319,14 +351,37 @@ static void api_cb(uev_t *w, void *arg, int events)
if (EAGAIN == errno) if (EAGAIN == errno)
break; break;
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno)); /* we get here when client restarts itself */
if (ECONNRESET == errno)
break;
errx(1, "Failed reading initctl request, error %d: %s", errno, strerror(errno));
} }
break; break;
} }
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) { if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
_e("Invalid initctl request"); errx(1, "Invalid initctl request");
break;
}
switch (rq.cmd) {
case INIT_CMD_RUNLVL:
case INIT_CMD_RELOAD:
case INIT_CMD_START_SVC:
case INIT_CMD_RESTART_SVC:
case INIT_CMD_STOP_SVC:
case INIT_CMD_RELOAD_SVC:
case INIT_CMD_REBOOT:
case INIT_CMD_HALT:
case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND:
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("Unsupported command in runlevel S and 6.");
goto leave;
}
default:
break; break;
} }
@@ -339,7 +394,7 @@ static void api_cb(uev_t *w, void *arg, int events)
/* fallthrough */ /* fallthrough */
case '0'...'9': case '0'...'9':
_d("Setting new runlevel %c", rq.runlevel); dbg("Setting new runlevel %c", rq.runlevel);
lvl = rq.runlevel - '0'; lvl = rq.runlevel - '0';
if (lvl == 0) if (lvl == 0)
halt = SHUT_OFF; halt = SHUT_OFF;
@@ -349,47 +404,55 @@ static void api_cb(uev_t *w, void *arg, int events)
break; break;
default: default:
_d("Unsupported runlevel: %d", rq.runlevel); dbg("Unsupported runlevel: %d", rq.runlevel);
break; break;
} }
break; break;
case INIT_CMD_DEBUG: case INIT_CMD_DEBUG:
_d("debug"); dbg("debug");
log_debug(); log_debug();
break; break;
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
_d("reload"); dbg("reload");
service_reload_dynamic(); service_reload_dynamic();
break; break;
case INIT_CMD_START_SVC: case INIT_CMD_START_SVC:
_d("start %s", rq.data); dbg("start %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_start(rq.data, sizeof(rq.data)); result = do_start(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RESTART_SVC: case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data); dbg("restart %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_restart(rq.data, sizeof(rq.data)); result = do_restart(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_STOP_SVC: case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data); dbg("stop %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_stop(rq.data, sizeof(rq.data)); result = do_stop(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_RELOAD_SVC: case INIT_CMD_RELOAD_SVC:
_d("reload %s", rq.data); dbg("reload %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_reload(rq.data, sizeof(rq.data)); result = do_reload(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_GET_PLUGINS:
result = plugin_list(rq.data, sizeof(rq.data));
break;
case INIT_CMD_PLUGIN_DEPS:
result = plugin_deps(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_RUNLEVEL: case INIT_CMD_GET_RUNLEVEL:
_d("get runlevel"); dbg("get runlevel");
rq.runlevel = runlevel; rq.runlevel = runlevel;
rq.sleeptime = prevlevel; rq.sleeptime = prevlevel;
break; break;
@@ -398,21 +461,21 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_HALT: case INIT_CMD_HALT:
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
result = do_reboot(rq.cmd, rq.data, sizeof(rq.data)); result = do_reboot(rq.cmd, rq.sleeptime, rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_ACK: case INIT_CMD_ACK:
_d("Client failed reading ACK"); dbg("Client failed reading ACK");
goto leave; goto leave;
case INIT_CMD_WDOG_HELLO: case INIT_CMD_WDOG_HELLO:
_d("wdog hello"); dbg("wdog hello");
if (rq.runlevel <= 0) { if (rq.runlevel <= 0) {
result = 1; result = 1;
break; break;
} }
_d("Request to hand-over wdog ... to PID %d", rq.runlevel); dbg("Request to hand-over wdog ... to PID %d", rq.runlevel);
svc = svc_find_by_pid(rq.runlevel); svc = svc_find_by_pid(rq.runlevel);
if (!svc) { if (!svc) {
logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel); logit(LOG_ERR, "Cannot find PID %d, not registered.", rq.runlevel);
@@ -436,7 +499,7 @@ static void api_cb(uev_t *w, void *arg, int events)
break; break;
case INIT_CMD_SVC_ITER: case INIT_CMD_SVC_ITER:
// _d("svc iter, first: %d", rq.runlevel); // dbg("svc iter, first: %d", rq.runlevel);
/* /*
* XXX: This severely limits the number of * XXX: This severely limits the number of
* simultaneous client connections, but will * simultaneous client connections, but will
@@ -447,32 +510,46 @@ static void api_cb(uev_t *w, void *arg, int events)
goto leave; goto leave;
case INIT_CMD_SVC_QUERY: case INIT_CMD_SVC_QUERY:
_d("svc query: %s", rq.data); dbg("svc query: %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
result = do_query(rq.data, sizeof(rq.data)); result = do_query(rq.data, sizeof(rq.data));
break; break;
case INIT_CMD_SVC_FIND: case INIT_CMD_SVC_FIND:
_d("svc find: %s", rq.data); dbg("svc find: %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
send_svc(sd, do_find(rq.data, sizeof(rq.data))); send_svc(sd, do_find(rq.data, sizeof(rq.data)));
goto leave; goto leave;
case INIT_CMD_SVC_FIND_BYC: case INIT_CMD_SVC_FIND_BYC:
_d("svc find by cond: %s", rq.data); dbg("svc find by cond: %s", rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
send_svc(sd, do_find_byc(rq.data, sizeof(rq.data))); send_svc(sd, do_find_byc(rq.data, sizeof(rq.data)));
goto leave; goto leave;
case INIT_CMD_SIGNAL: case INIT_CMD_SIGNAL:
/* runlevel is reused for signal */ /* runlevel is reused for signal */
_d("svc signal %d: %s", rq.runlevel, rq.data); dbg("svc signal %d: %s", rq.runlevel, rq.data);
strterm(rq.data, sizeof(rq.data)); strterm(rq.data, sizeof(rq.data));
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:
_d("Unsupported cmd: %d", rq.cmd); dbg("Unsupported cmd: %d", rq.cmd);
break; break;
} }
@@ -482,7 +559,7 @@ static void api_cb(uev_t *w, void *arg, int events)
rq.cmd = INIT_CMD_ACK; rq.cmd = INIT_CMD_ACK;
len = write(sd, &rq, sizeof(rq)); len = write(sd, &rq, sizeof(rq));
if (len != sizeof(rq)) if (len != sizeof(rq))
_d("Failed sending ACK/NACK back to client"); dbg("Failed sending ACK/NACK back to client");
} }
leave: leave:
@@ -493,7 +570,7 @@ leave:
error: error:
api_exit(); api_exit();
if (api_init(w->ctx)) if (api_init(w->ctx))
_e("Unrecoverable error on API socket"); errx(1, "Unrecoverable error on API socket");
} }
int api_init(uev_ctx_t *ctx) int api_init(uev_ctx_t *ctx)
@@ -506,10 +583,10 @@ int api_init(uev_ctx_t *ctx)
int uid, gid; int uid, gid;
int sd; int sd;
_d("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_CLOEXEC, 0);
if (-1 == sd) { if (-1 == sd) {
_pe("Failed starting external API socket"); err(1, "Failed starting external API socket");
return 1; return 1;
} }
@@ -525,14 +602,14 @@ int api_init(uev_ctx_t *ctx)
goto error; goto error;
if (chown(INIT_SOCKET, uid, gid)) if (chown(INIT_SOCKET, uid, gid))
_pe("Failed setting group %s on %s", DEFGROUP, INIT_SOCKET); err(1, "Failed setting group %s on %s", DEFGROUP, INIT_SOCKET);
umask(oldmask); umask(oldmask);
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ)) if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
return 0; return 0;
error: error:
_pe("Failed initializing API socket"); err(1, "Failed initializing API socket");
umask(oldmask); umask(oldmask);
close(sd); close(sd);
return 1; return 1;
+32 -29
View File
@@ -1,6 +1,6 @@
/* Finit control group support functions /* Finit control group support functions
* *
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -63,16 +63,16 @@ static void cgset(const char *path, char *ctrl, char *prop)
{ {
char *val; char *val;
_d("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL"); dbg("path %s, ctrl %s, prop %s", path ?: "NIL", ctrl ?: "NIL", prop ?: "NIL");
if (!path || !ctrl) { if (!path || !ctrl) {
_e("Missing path or controller, skipping!"); errx(1, "Missing path or controller, skipping!");
return; return;
} }
if (!prop) { if (!prop) {
prop = strchr(ctrl, '.'); prop = strchr(ctrl, '.');
if (!prop) { if (!prop) {
_e("Invalid cgroup ctrl syntax: %s", ctrl); errx(1, "Invalid cgroup ctrl syntax: %s", ctrl);
return; return;
} }
@@ -81,20 +81,20 @@ static void cgset(const char *path, char *ctrl, char *prop)
val = strchr(prop, ':'); val = strchr(prop, ':');
if (!val) { if (!val) {
_e("Missing cgroup ctrl value, prop %s", prop); errx(1, "Missing cgroup ctrl value, prop %s", prop);
return; return;
} }
*val++ = 0; *val++ = 0;
/* disallow sneaky relative paths */ /* disallow sneaky relative paths */
if (strstr(ctrl, "..") || strstr(prop, "..")) { if (strstr(ctrl, "..") || strstr(prop, "..")) {
_e("Possible security violation; '..' not allowed in cgroup config!"); errx(1, "Possible security violation; '..' not allowed in cgroup config!");
return; return;
} }
_d("%s/%s.%s <= %s", path, ctrl, prop, val); dbg("%s/%s.%s <= %s", path, ctrl, prop, val);
if (fnwrite(val, "%s/%s.%s", path, ctrl, prop)) if (fnwrite(val, "%s/%s.%s", path, ctrl, prop))
_pe("Failed setting %s/%s.%s = %s", path, ctrl, prop, val); err(1, "Failed setting %s/%s.%s = %s", path, ctrl, prop, val);
} }
/* /*
@@ -106,16 +106,16 @@ static void group_init(char *path, int leaf, const char *cfg)
{ {
char *ptr, *s; char *ptr, *s;
_d("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL"); dbg("path %s, leaf %d, cfg %s", path, leaf, cfg ?: "NIL");
if (!fisdir(path)) { if (!fisdir(path)) {
if (mkdir(path, 0755)) { if (mkdir(path, 0755)) {
_pe("Failed creating cgroup %s", path); err(1, "Failed creating cgroup %s", path);
return; return;
} }
/* enable detected controllers on domain groups */ /* enable detected controllers on domain groups */
if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path)) if (!leaf && fnwrite(controllers, "%s/cgroup.subtree_control", path))
_pe("Failed enabling %s for %s", controllers, path); err(1, "Failed enabling %s for %s", controllers, path);
} }
if (!cfg || !cfg[0]) if (!cfg || !cfg[0])
@@ -123,14 +123,14 @@ static void group_init(char *path, int leaf, const char *cfg)
s = strdupa(cfg); s = strdupa(cfg);
if (!s) { if (!s) {
_pe("Failed activating cgroup cfg for %s", path); err(1, "Failed activating cgroup cfg for %s", path);
return; return;
} }
_d("%s <=> %s", path, s); dbg("%s <=> %s", path, s);
ptr = strtok(s, ","); ptr = strtok(s, ",");
while (ptr) { while (ptr) {
_d("ptr: %s", ptr); dbg("ptr: %s", ptr);
if (!strncmp("mem.", ptr, 4)) if (!strncmp("mem.", ptr, 4))
cgset(path, "memory", &ptr[4]); cgset(path, "memory", &ptr[4]);
else else
@@ -144,7 +144,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
{ {
char path[256]; char path[256];
_d("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL"); dbg("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
if (pid < 0 || pid == 1) { if (pid < 0 || pid == 1) {
errno = EINVAL; errno = EINVAL;
return 1; return 1;
@@ -156,7 +156,7 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
/* move process to new group */ /* move process to new group */
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path)) if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
_pe("Failed moving pid %d to group %s", pid, path); err(1, "Failed moving pid %d to group %s", pid, path);
strlcat(path, "/cgroup.events", sizeof(path)); strlcat(path, "/cgroup.events", sizeof(path));
@@ -211,13 +211,13 @@ static void cgroup_handle_event(char *event, uint32_t mask)
char *ptr; char *ptr;
FILE *fp; FILE *fp;
_d("event: '%s', mask: %08x", event, mask); dbg("event: '%s', mask: %08x", event, mask);
if (!(mask & IN_MODIFY)) if (!(mask & IN_MODIFY))
return; return;
fp = fopen(event, "r"); fp = fopen(event, "r");
if (!fp) { if (!fp) {
_d("Failed opening %s, skipping ...", event); dbg("Failed opening %s, skipping ...", event);
return; return;
} }
@@ -261,7 +261,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
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) {
_pe("invalid inotify event"); err(1, "invalid inotify event");
return; return;
} }
ev_buf[sz] = 0; ev_buf[sz] = 0;
@@ -287,7 +287,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
cgroup_handle_event(iwp->path, ev->mask); cgroup_handle_event(iwp->path, ev->mask);
} }
#ifdef ENABLE_AUTO_RELOAD #ifdef AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); service_reload_dynamic();
#endif #endif
@@ -364,12 +364,12 @@ int cgroup_add(char *name, char *cfg, int is_protected)
if (!cg) { if (!cg) {
cg = malloc(sizeof(struct cg)); cg = malloc(sizeof(struct cg));
if (!cg) { if (!cg) {
_pe("Failed allocating 'struct cg' for %s", name); err(1, "Failed allocating 'struct cg' for %s", name);
return -1; return -1;
} }
cg->name = strdup(name); cg->name = strdup(name);
if (!cg->name) { if (!cg->name) {
_pe("Failed setting cgroup name %s", name); err(1, "Failed setting cgroup name %s", name);
free(cg); free(cg);
return -1; return -1;
} }
@@ -379,7 +379,7 @@ int cgroup_add(char *name, char *cfg, int is_protected)
cg->cfg = strdup(cfg); cg->cfg = strdup(cfg);
if (!cg->cfg) { if (!cg->cfg) {
_pe("Failed add/update of cgroup %s", name); err(1, "Failed add/update of cgroup %s", name);
TAILQ_REMOVE(&cgroups, cg, link); TAILQ_REMOVE(&cgroups, cg, link);
free(cg->name); free(cg->name);
free(cg); free(cg);
@@ -414,7 +414,7 @@ int cgroup_del(char *dir)
} }
if (rmdir(dir) && errno != ENOENT) { if (rmdir(dir) && errno != ENOENT) {
_d("Failed removing %s: %s", dir, strerror(errno)); dbg("Failed removing %s: %s", dir, strerror(errno));
return -1; return -1;
} }
@@ -469,7 +469,7 @@ void cgroup_init(uev_ctx_t *ctx)
else if (errno == EPERM) /* Probably inside an unpriviliged container */ else if (errno == EPERM) /* Probably inside an unpriviliged container */
logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling."); logit(LOG_INFO, "Not allowed to mount cgroups v2, disabling.");
else else
_pe("Failed mounting cgroup v2"); err(1, "Failed mounting cgroup v2");
avail = 0; avail = 0;
return; return;
} }
@@ -478,7 +478,7 @@ void cgroup_init(uev_ctx_t *ctx)
/* Find available controllers */ /* Find available controllers */
fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r"); fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r");
if (!fp) { if (!fp) {
_pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers"); err(1, "Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
abort: abort:
umount(FINIT_CGPATH); umount(FINIT_CGPATH);
avail = 0; avail = 0;
@@ -505,14 +505,17 @@ void cgroup_init(uev_ctx_t *ctx)
/* Enable all controllers */ /* Enable all controllers */
if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) { if (fnwrite(controllers, FINIT_CGPATH "/cgroup.subtree_control")) {
_pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control"); err(1, "Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
goto abort; goto abort;
} }
/* prepare cgroup.events watcher */ /* prepare cgroup.events watcher */
fd = iwatch_init(&iw_cgroup); fd = iwatch_init(&iw_cgroup);
if (fd == -1)
goto abort;
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) { if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
_pe("Failed setting up cgroup.events watcher"); err(1, "Failed setting up cgroup.events watcher");
iwatch_exit(&iw_cgroup); iwatch_exit(&iw_cgroup);
close(fd); close(fd);
goto abort; goto abort;
@@ -526,7 +529,7 @@ void cgroup_init(uev_ctx_t *ctx)
/* Move ourselves to init (best effort, otherwise run in 'root' group */ /* Move ourselves to init (best effort, otherwise run in 'root' group */
if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) { if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) {
_pe("Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init"); err(1, "Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init");
uev_io_stop(&cgw); uev_io_stop(&cgw);
iwatch_exit(&iw_cgroup); iwatch_exit(&iw_cgroup);
close(fd); close(fd);
+1 -1
View File
@@ -1,6 +1,6 @@
/* Finit control group support functions /* Finit control group support functions
* *
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+2 -1
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool /* New client tool, replaces old /dev/initctl API and telinit tool
* *
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,7 @@
#include "cgutil.h" #include "cgutil.h"
#include "initctl.h" #include "initctl.h"
#include "log.h"
#define CDIM plain ? "" : "\e[2m" #define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m" #define CRST plain ? "" : "\e[0m"
+1 -1
View File
@@ -1,6 +1,6 @@
/* Cgroup utility functions for initctl /* Cgroup utility functions for initctl
* *
* Copyright (c) 2019-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2019-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+61 -28
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket. /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,7 +27,7 @@
#include <sys/un.h> #include <sys/un.h>
#include "client.h" #include "client.h"
#include "initctl.h" #include "log.h"
static int sd = -1; static int sd = -1;
@@ -40,15 +40,15 @@ int client_connect(void)
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0); sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (-1 == sd) { if (-1 == sd) {
WARN("Failed creating UNIX domain socket"); warn("Failed creating UNIX domain socket");
return -1; return -1;
} }
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
if (errno != ENOENT) if (errno != ENOENT)
WARNX("Failed connecting to finit"); warnx("Failed connecting to finit");
close(sd); close(sd);
return -1; sd = -1;
} }
return sd; return sd;
@@ -69,10 +69,15 @@ int client_disconnect(void)
return rc; return rc;
} }
int client_send(struct init_request *rq, ssize_t len) /* Only valid after client_connect() */
int client_socket(void)
{
return sd;
}
int client_request(struct init_request *rq, ssize_t len)
{ {
struct pollfd pfd = { 0 }; struct pollfd pfd = { 0 };
int result = 255;
int rc; int rc;
if (client_connect() == -1) if (client_connect() == -1)
@@ -81,13 +86,23 @@ int client_send(struct init_request *rq, ssize_t len)
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLOUT; pfd.events = POLLOUT;
if (poll(&pfd, 1, 2000) <= 0) { if (poll(&pfd, 1, 2000) <= 0) {
WARN("Timed out waiting for Finit, errno %d", errno); warn("Timed out waiting for Finit, errno %d", errno);
goto exit; return -1;
} }
if (write(sd, rq, len) != len) { if (write(sd, rq, len) != len) {
WARN("Failed communicating with Finit, errno %d", errno); warn("Failed communicating with Finit, errno %d", errno);
goto exit; 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;
@@ -95,26 +110,45 @@ int client_send(struct init_request *rq, ssize_t len)
if ((rc = poll(&pfd, 1, 2000)) <= 0) { if ((rc = poll(&pfd, 1, 2000)) <= 0) {
if (rc) { if (rc) {
if (errno == EINTR) /* shutdown/reboot */ if (errno == EINTR) /* shutdown/reboot */
goto exit; return -1;
WARN("poll(), errno %d", errno); warn("poll(), errno %d", errno);
} else } else
WARNX("Timed out waiting for reply from Finit."); warnx("Timed out waiting for reply from Finit.");
goto exit; return -1;
} }
if (read(sd, rq, len) != len) { if (read(sd, rq, len) != len) {
WARN("Failed reading reply from Finit, errno %d", errno); warn("Failed reading reply from Finit, errno %d", errno);
goto exit; return -1;
} }
if (rq->cmd == INIT_CMD_NACK) if (rq->cmd == INIT_CMD_NACK)
result = 1; return -1;
else
result = 0; return 0;
exit: }
int client_send(struct init_request *rq, ssize_t len)
{
int rc = 0;
if (client_request(rq, len))
rc = 255;
client_disconnect(); client_disconnect();
return result; return rc;
}
int client_command(int cmd)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.runlevel = getpid(),
.cmd = cmd,
};
return client_request(&rq, sizeof(rq));
} }
svc_t *client_svc_iterator(int first) svc_t *client_svc_iterator(int first)
@@ -144,14 +178,14 @@ svc_t *client_svc_iterator(int first)
return &svc; return &svc;
error: error:
WARN("Failed communicating with finit, error %d", errno); warn("Failed communicating with finit, error %d", errno);
client_disconnect(); client_disconnect();
sd = -1; sd = -1;
return NULL; return NULL;
} }
svc_t *do_cmd(int cmd, const char *arg) static svc_t *do_find(int cmd, const char *arg)
{ {
struct init_request rq = { struct init_request rq = {
.magic = INIT_MAGIC, .magic = INIT_MAGIC,
@@ -175,20 +209,19 @@ svc_t *do_cmd(int cmd, const char *arg)
return &svc; return &svc;
error: error:
client_disconnect(); client_disconnect();
WARN("Failed communicating with finit, error %d", errno); warn("Failed communicating with finit, error %d", errno);
return NULL; return NULL;
} }
svc_t *client_svc_find(const char *arg) svc_t *client_svc_find(const char *arg)
{ {
return do_cmd(INIT_CMD_SVC_FIND, arg); return do_find(INIT_CMD_SVC_FIND, arg);
} }
svc_t *client_svc_find_by_cond(const char *arg) svc_t *client_svc_find_by_cond(const char *arg)
{ {
return do_cmd(INIT_CMD_SVC_FIND_BYC, arg); return do_find(INIT_CMD_SVC_FIND_BYC, arg);
} }
/** /**
+5 -1
View File
@@ -1,6 +1,6 @@
/* External client API, using UNIX domain socket. /* External client API, using UNIX domain socket.
* *
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,8 +29,12 @@
int client_connect (void); int client_connect (void);
int client_disconnect (void); int client_disconnect (void);
int client_socket (void);
int client_request (struct init_request *rq, ssize_t len);
int client_send (struct init_request *rq, ssize_t len); int client_send (struct init_request *rq, ssize_t len);
int client_command (int cmd);
svc_t *client_svc_iterator (int first); svc_t *client_svc_iterator (int first);
svc_t *client_svc_find (const char *arg); svc_t *client_svc_find (const char *arg);
svc_t *client_svc_find_by_cond (const char *arg); svc_t *client_svc_find_by_cond (const char *arg);
+137 -43
View File
@@ -36,6 +36,70 @@
#include "pid.h" #include "pid.h"
#include "service.h" #include "service.h"
struct cond_boot {
TAILQ_ENTRY(cond_boot) link;
char *name;
};
static TAILQ_HEAD(, cond_boot) cond_boot_list = TAILQ_HEAD_INITIALIZER(cond_boot_list);
/*
* Parse finit.cond=cond[,cond[,...]] from command line. It creates a
* temporary linked list of conds which are freed when finit reaches
* cond_init(), which calls cond_boot_strap() to realize the conditions
* in the file system as regular static conditions. Policy, however,
* dictates that these are read-only and should not be possible to
* manage from initctl.
*/
void cond_boot_parse(char *arg)
{
struct cond_boot *node;
if (!arg)
return;
node = malloc(sizeof(*node));
if (!node) {
err(1, "Out of memory cannot track boot conditions");
return;
}
node->name = strdup(arg);
TAILQ_INSERT_HEAD(&cond_boot_list, node, link);
}
/*
* Convert all conditions read from the kernel command line (above) into
* proper finit conditions.
*/
static void cond_boot_strap(void)
{
struct cond_boot *node, *tmp;
TAILQ_FOREACH_SAFE(node, &cond_boot_list, link, tmp) {
TAILQ_REMOVE(&cond_boot_list, node, link);
if (node->name) {
char *ptr;
ptr = strtok(node->name, ",");
while (ptr) {
char cond[strlen(ptr) + 6];
if (strncmp(ptr, "boot/", 5))
snprintf(cond, sizeof(cond), "boot/%s", ptr);
else
snprintf(cond, sizeof(cond), "%s", ptr);
cond_set_oneshot_noupdate(cond);
ptr = strtok(NULL, ",");
}
free(node->name);
}
free(node);
}
}
/* /*
* The service condition name is constructed from the 'pid/' prefix and * The service condition name is constructed from the 'pid/' prefix and
* the unique NAME:ID tuple that identify each process in Finit. Here * the unique NAME:ID tuple that identify each process in Finit. Here
@@ -111,7 +175,7 @@ static void cond_bump_reconf(void)
rgen++; rgen++;
if (cond_set_gen(_PATH_RECONF, rgen)) if (cond_set_gen(_PATH_RECONF, rgen))
_pe("Failed setting %s to gen %d", _PATH_RECONF, rgen); err(1, "Failed setting %s to gen %d", _PATH_RECONF, rgen);
} }
static int cond_checkpath(const char *path) static int cond_checkpath(const char *path)
@@ -121,35 +185,45 @@ static int cond_checkpath(const char *path)
strlcpy(buf, path, sizeof(buf)); strlcpy(buf, path, sizeof(buf));
dir = dirname(buf); dir = dirname(buf);
if (!dir) { if (!dir) {
_e("Invalid path '%s' for condition", path); errx(1, "Invalid path '%s' for condition", path);
return 1; return 1;
} }
if (mkpath(dir, 0755) && errno != EEXIST) { if (mkpath(dir, 0755) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path); err(1, "Failed creating dir '%s' for condition '%s'", dir, path);
return 1; return 1;
} }
return 0; return 0;
} }
/* Intended for 'service/foo/`, nothing else */ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{
const char *cond, *ptr;
if (ftw->level == 0)
return 1;
ptr = strstr(fpath, COND_BASE);
if (!ptr) {
warnx("%s does not seem to be a Finit condition, skipping", fpath);
return 0;
}
if (remove(fpath))
err(1, "Failed removing condition %s", fpath);
cond = ptr + strlen(COND_BASE) + 1;
cond_update(cond);
return 0;
}
static void cond_delpath(const char *path) static void cond_delpath(const char *path)
{ {
char fn[PATH_MAX]; nftw(path, do_delete, 20, FTW_DEPTH);
struct dirent *d; if (remove(path) && errno != ENOENT)
DIR *dir; err(1, "Failed removing condition path %s", path);
dir = opendir(path);
if (!dir)
return;
while ((d = readdir(dir))) {
paste(fn, sizeof(fn), path, d->d_name);
remove(fn);
}
closedir(dir);
unlink(path);
} }
int cond_set_path(const char *path, enum cond_state next) int cond_set_path(const char *path, enum cond_state next)
@@ -157,20 +231,19 @@ int cond_set_path(const char *path, enum cond_state next)
enum cond_state prev; enum cond_state prev;
unsigned int rgen; unsigned int rgen;
_d("%s <= %d", path, next); dbg("%s <= %d", path, next);
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
}
prev = cond_get_path(path); prev = cond_get_path(path);
switch (next) { switch (next) {
case COND_ON: case COND_ON:
if (cond_checkpath(path)) if (cond_checkpath(path))
return 0; return 0;
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
errx(1, "Unable to read configuration generation (%s)", path);
return -1;
}
cond_set_gen(path, rgen); cond_set_gen(path, rgen);
break; break;
@@ -183,14 +256,14 @@ int cond_set_path(const char *path, enum cond_state next)
cond_delpath(path); cond_delpath(path);
break; break;
default: default:
_pe("Failed removing condition '%s'", path); err(1, "Failed removing condition '%s'", path);
break; break;
} }
} }
break; break;
default: default:
_e("Invalid condition state"); errx(1, "Invalid condition state");
return 0; return 0;
} }
@@ -203,13 +276,19 @@ int cond_update(const char *name)
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
int affects = 0; int affects = 0;
_d("%s", name); dbg("%s", name);
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue; continue;
affects++; affects++;
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd); dbg("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
/* Fix bug #314: race condition between crashing services and conditions */
if (svc_is_restart(svc) && cond_get_agg(svc->cond) == COND_OFF) {
dbg("%s: cancel timer & unblock => WAITING state.", name ?: "nil");
service_timeout_cancel(svc);
svc_unblock(svc);
}
service_step(svc); service_step(svc);
} }
@@ -218,7 +297,7 @@ int cond_update(const char *name)
int cond_set_noupdate(const char *name) int cond_set_noupdate(const char *name)
{ {
_d("%s", name); dbg("%s", name);
if (string_compare(name, "nop")) if (string_compare(name, "nop"))
return 1; return 1;
@@ -230,7 +309,7 @@ int cond_set_noupdate(const char *name)
void cond_set(const char *name) void cond_set(const char *name)
{ {
_d("%s", name); dbg("%s", name);
if (cond_set_noupdate(name)) if (cond_set_noupdate(name))
return; return;
@@ -245,13 +324,13 @@ int cond_set_oneshot_noupdate(const char *name)
return 1; return 1;
path = cond_path(name); path = cond_path(name);
_d("%s => %s", name, path); dbg("%s => %s", name, path);
if (cond_checkpath(path)) if (cond_checkpath(path))
return 1; return 1;
if (symlink(_PATH_RECONF, path) && errno != EEXIST) { if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
_pe("Failed creating onshot cond %s", name); err(1, "Failed creating onshot cond %s", name);
return 1; return 1;
} }
@@ -260,7 +339,7 @@ int cond_set_oneshot_noupdate(const char *name)
void cond_set_oneshot(const char *name) void cond_set_oneshot(const char *name)
{ {
_d("%s", name); dbg("%s", name);
if (cond_set_oneshot_noupdate(name)) if (cond_set_oneshot_noupdate(name))
return; return;
@@ -269,7 +348,7 @@ void cond_set_oneshot(const char *name)
int cond_clear_noupdate(const char *name) int cond_clear_noupdate(const char *name)
{ {
_d("%s", name); dbg("%s", name);
if (string_compare(name, "nop")) if (string_compare(name, "nop"))
return 1; return 1;
@@ -281,7 +360,7 @@ int cond_clear_noupdate(const char *name)
void cond_clear(const char *name) void cond_clear(const char *name)
{ {
_d("%s", name); dbg("%s", name);
if (cond_clear_noupdate(name)) if (cond_clear_noupdate(name))
return; return;
@@ -290,7 +369,7 @@ void cond_clear(const char *name)
void cond_reload(void) void cond_reload(void)
{ {
_d(""); dbg("");
cond_bump_reconf(); cond_bump_reconf();
} }
@@ -307,12 +386,12 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct
nm = strstr((char *)fpath, COND_BASE); nm = strstr((char *)fpath, COND_BASE);
if (!nm) { if (!nm) {
_e("Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de"); errx(1, "Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
return 1; return 1;
} }
nm += strlen(COND_BASE); nm += strlen(COND_BASE);
_d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm); dbg("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
if (set) if (set)
cond_set(nm); cond_set(nm);
else else
@@ -337,7 +416,7 @@ static int deassert(const char *fpath, const struct stat *sb, int tflg, struct F
*/ */
void cond_reassert(const char *pat) void cond_reassert(const char *pat)
{ {
_d("%s", pat); dbg("%s", pat);
nftw(cond_path(pat), reassert, 20, FTW_DEPTH); nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
} }
@@ -346,20 +425,35 @@ void cond_reassert(const char *pat)
*/ */
void cond_deassert(const char *pat) void cond_deassert(const char *pat)
{ {
_d("%s", pat); dbg("%s", pat);
nftw(cond_path(pat), deassert, 20, FTW_DEPTH); nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
} }
/*
* Check if we have bootstrapped enough of the system to use conditions.
* Will answer 'No' before bootstrap done *and* at shutdown/reboot.
*/
int cond_is_available(void)
{
return fisdir(_PATH_COND);
}
void cond_init(void) void cond_init(void)
{ {
char path[MAX_ARG_LEN]; char path[MAX_ARG_LEN];
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) { if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND); err(1, "Failed creating condition base directory '%s'", _PATH_COND);
return; return;
} }
cond_bump_reconf(); cond_bump_reconf();
cond_boot_strap();
}
void cond_exit(void)
{
cond_delpath(_PATH_COND);
} }
/** /**
+1 -1
View File
@@ -112,7 +112,7 @@ enum cond_state cond_get_agg(const char *names)
int cond_affects(const char *name, const char *names) int cond_affects(const char *name, const char *names)
{ {
static char conds[MAX_COND_LEN]; char conds[MAX_COND_LEN];
char *cond; char *cond;
if (!name || !names) if (!name || !names)
+9 -3
View File
@@ -6,11 +6,13 @@
#include "svc.h" #include "svc.h"
#define COND_BASE "finit/cond" #define COND_BASE "finit/cond"
#define COND_DEV "dev/"
#define COND_PID "pid/" #define COND_PID "pid/"
#define COND_SYS "sys/" #define COND_SYS "sys/"
#define COND_USR "usr/" #define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/" #define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDDEV _PATH_COND COND_DEV
#define _PATH_CONDPID _PATH_COND COND_PID #define _PATH_CONDPID _PATH_COND COND_PID
#define _PATH_CONDSYS _PATH_COND COND_SYS #define _PATH_CONDSYS _PATH_COND COND_SYS
#define _PATH_CONDUSR _PATH_COND COND_USR #define _PATH_CONDUSR _PATH_COND COND_USR
@@ -31,6 +33,7 @@ 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);
int cond_affects (const char *name, const char *names); int cond_affects (const char *name, const char *names);
void cond_boot_parse (char *arg);
int cond_update (const char *name); int cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new); int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name); void cond_set (const char *name);
@@ -38,14 +41,17 @@ void cond_set_oneshot (const char *name);
void cond_clear (const char *name); void cond_clear (const char *name);
void cond_reload (void); void cond_reload (void);
int cond_set_noupdate (const char *name); int cond_set_noupdate(const char *name);
int cond_set_oneshot_noupdate(const char *name); int cond_set_oneshot_noupdate(const char *name);
int cond_clear_noupdate(const char *name); int cond_clear_noupdate(const char *name);
void cond_reassert (const char *pat); void cond_reassert (const char *pat);
void cond_deassert (const char *pat); void cond_deassert (const char *pat);
int cond_is_available(void);
void cond_init (void); void cond_init (void);
void cond_exit (void);
#endif /* FINIT_COND_H_ */ #endif /* FINIT_COND_H_ */
+381 -93
View File
@@ -1,6 +1,6 @@
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf /* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -40,6 +40,7 @@
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "devmon.h"
#include "iwatch.h" #include "iwatch.h"
#include "private.h" #include "private.h"
#include "service.h" #include "service.h"
@@ -47,9 +48,7 @@
#include "helpers.h" #include "helpers.h"
#include "util.h" #include "util.h"
#define BOOTSTRAP (runlevel == 0) #define BOOTSTRAP (runlevel == INIT_LEVEL)
#define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
int logfile_size_max = 200000; /* 200 kB */ int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5; int logfile_count_max = 5;
@@ -63,6 +62,9 @@ static TAILQ_HEAD(, env_entry) env_list = TAILQ_HEAD_INITIALIZER(env_list);
struct rlimit initial_rlimit[RLIMIT_NLIMITS]; struct rlimit initial_rlimit[RLIMIT_NLIMITS];
struct rlimit global_rlimit[RLIMIT_NLIMITS]; struct rlimit global_rlimit[RLIMIT_NLIMITS];
char *fsck_mode = "";
char *fsck_repair = "-p";
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */ char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
struct conf_change { struct conf_change {
@@ -71,14 +73,18 @@ struct conf_change {
}; };
static struct iwatch iw_conf; static struct iwatch iw_conf;
static int iwatch_fd;
static uev_t etcw; static uev_t etcw;
static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list); static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static char *path;
static char *shell;
static int parse_conf(char *file, int is_rcsd); static int parse_conf(char *file, int is_rcsd);
static void drop_changes(void); static void drop_changes(void);
int get_bool(char *arg, int default_value) static int get_bool(char *arg, int default_value)
{ {
if (!arg) if (!arg)
goto fallback; goto fallback;
@@ -91,7 +97,19 @@ fallback:
return default_value; return default_value;
} }
static int validate_arg(char *arg, const char *opt)
{
if (!arg) {
errx(1, "option %s missing argument, skipping.", opt);
return 1;
}
return 0;
}
/* /*
* finit.cond = foo (=> <boot/foo>)
* finit.config = /path/to/etc/alt-finit.conf
* finit.debug = [on,off] * finit.debug = [on,off]
* finit.fstab = /path/to/etc/fstab.aternative * finit.fstab = /path/to/etc/fstab.aternative
* finit.status = [on,off] (compat finit.show_status) * finit.status = [on,off] (compat finit.show_status)
@@ -105,21 +123,39 @@ static void parse_finit_opts(char *opt)
if (arg) if (arg)
*arg++ = 0; *arg++ = 0;
if (string_compare(opt, "cond")) {
if (validate_arg(arg, "finit.cond"))
return;
cond_boot_parse(arg);
return;
}
if (string_compare(opt, "config")) {
if (validate_arg(arg, "finit.config"))
return;
if (finit_conf)
free(finit_conf);
finit_conf = strdup(arg);
return;
}
if (string_compare(opt, "debug")) { if (string_compare(opt, "debug")) {
debug = get_bool(arg, 1); debug = get_bool(arg, 1);
return; return;
} }
if (string_compare(opt, "fstab")) { if (string_compare(opt, "fstab")) {
fstab = arg; if (validate_arg(arg, "finit.fstab"))
return;
if (fstab)
free(fstab);
fstab = strdup(arg);
return; return;
} }
if (string_compare(opt, "status_style")) { if (string_compare(opt, "status_style")) {
if (!arg) { if (validate_arg(arg, "finit.status_style"))
_e("status_style option requires an argument, skipping.");
return; return;
}
if (string_compare(arg, "old") || string_compare(arg, "classic")) if (string_compare(arg, "old") || string_compare(arg, "classic"))
show_progress(PROGRESS_CLASSIC); show_progress(PROGRESS_CLASSIC);
@@ -134,6 +170,43 @@ static void parse_finit_opts(char *opt)
} }
} }
static void parse_fsck_opts(char *opt)
{
char *arg;
arg = strchr(opt, '=');
if (arg)
*arg++ = 0;
if (string_compare(opt, "mode")) {
if (validate_arg(arg, "fsck.mode"))
return;
if (!strcmp(arg, "skip"))
fsck_mode = NULL;
if (!strcmp(arg, "auto"))
fsck_mode = "";
if (!strcmp(arg, "force"))
fsck_mode = "-f";
return;
}
if (string_compare(opt, "repair")) {
if (validate_arg(arg, "fsck.repair"))
return;
if (!strcmp(arg, "no"))
fsck_repair = "-n";
if (!strcmp(arg, "preen"))
fsck_repair = "-p";
if (!strcmp(arg, "yes"))
fsck_repair = "-y";
return;
}
}
/* /*
* When runlevel (single integer) is given on the command line, * When runlevel (single integer) is given on the command line,
* it overrides the runlevel in finit.conf and the built-in * it overrides the runlevel in finit.conf and the built-in
@@ -154,7 +227,7 @@ static int parse_runlevel(char *arg)
num = strtonum(arg, 1, 9, &err); num = strtonum(arg, 1, 9, &err);
if (err || num == 6) { if (err || num == 6) {
_d("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg); dbg("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg);
return 0; return 0;
} }
@@ -168,6 +241,11 @@ static void parse_arg(char *arg)
return; return;
} }
if (!strncmp(arg, "fsck.", 5)) {
parse_fsck_opts(&arg[5]);
return;
}
if (string_compare(arg, "rescue") || string_compare(arg, "recover")) { if (string_compare(arg, "rescue") || string_compare(arg, "recover")) {
rescue = 1; rescue = 1;
return; return;
@@ -207,7 +285,7 @@ static void parse_kernel_cmdline(void)
} }
cmdline = chomp(line); cmdline = chomp(line);
_d("%s", cmdline); dbg("%s", cmdline);
while ((tok = strtok(cmdline, " \t"))) { while ((tok = strtok(cmdline, " \t"))) {
cmdline = NULL; cmdline = NULL;
@@ -236,7 +314,7 @@ static void parse_kernel_loglevel(void)
fclose(fp); fclose(fp);
ptr = chomp(line); ptr = chomp(line);
_d("%s", ptr); dbg("%s", ptr);
val = atoi(ptr); val = atoi(ptr);
if (val >= 7) if (val >= 7)
kerndebug = 1; kerndebug = 1;
@@ -247,6 +325,13 @@ static void parse_kernel_loglevel(void)
*/ */
void conf_parse_cmdline(int argc, char *argv[]) void conf_parse_cmdline(int argc, char *argv[])
{ {
/* Set up defaults */
fstab = strdup(FINIT_FSTAB);
finit_conf = strdup(FINIT_CONF);
finit_rcsd = strdup(FINIT_RCSD);
path = getenv("PATH");
shell = getenv("SHELL");
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++)
parse_arg(argv[i]); parse_arg(argv[i]);
@@ -271,12 +356,41 @@ void conf_reset_env(void)
free(node); free(node);
} }
setenv("PATH", _PATH_STDPATH, 1); if (path)
setenv("SHELL", _PATH_BSHELL, 1); setenv("PATH", path, 1);
else
setenv("PATH", _PATH_STDPATH, 1);
if (shell)
setenv("SHELL", shell, 1);
else
setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1); setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1); setenv("USER", "root", 1);
} }
/*
* Sourced mainly by initctl and other Finit helper tools
*/
void conf_saverc(void)
{
FILE *fp;
mkpath(_PATH_VARRUN "finit", 0755);
fp = fopen(_PATH_VARRUN "finit/.initrc", "w");
if (!fp) {
err(1, "failed creating .finitrc");
return;
}
fprintf(fp, "FINIT_CONF=%s\n", finit_conf);
fprintf(fp, "FINIT_RCSD=%s\n", finit_rcsd);
fprintf(fp, "FINIT_CGPATH=%s\n", FINIT_CGPATH);
fprintf(fp, "INIT_SOCKET=%s\n", INIT_SOCKET);
fprintf(fp, "INIT_MAGIC=0x%08x\n", INIT_MAGIC);
fclose(fp);
}
/* /*
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line * 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 * from finit.conf, or other .conf file. Note, PATH is always reset in
@@ -339,7 +453,7 @@ static void parse_env(char *line)
node = malloc(sizeof(*node)); node = malloc(sizeof(*node));
if (!node) { if (!node) {
_pe("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);
@@ -372,7 +486,7 @@ static void kmod_load(char *mod)
char module[64] = { 0 }; char module[64] = { 0 };
char cmd[CMD_SIZE]; char cmd[CMD_SIZE];
if (runlevel != 0) if (runlevel != INIT_LEVEL)
return; return;
/* Strip args for progress below and kmod_exists() */ /* Strip args for progress below and kmod_exists() */
@@ -405,15 +519,16 @@ int conf_parse_runlevels(char *runlevels)
break; break;
if ('!' == lvl) { if ('!' == lvl) {
not = 1; not = 1;
bitmask = 0x3FE; bitmask = 0x7FE;
continue; continue;
} }
if ('s' == lvl || 'S' == lvl) if ('s' == lvl || 'S' == lvl)
lvl = '0'; level = INIT_LEVEL;
else
level = lvl - '0';
level = lvl - '0'; if (level > INIT_LEVEL || level < 0)
if (level > 9 || level < 0)
continue; continue;
if (not) if (not)
@@ -429,9 +544,10 @@ void conf_parse_cond(svc_t *svc, char *cond)
{ {
size_t i = 0; size_t i = 0;
char *ptr; char *ptr;
char *c;
if (!svc) { if (!svc) {
_e("Invalid service pointer"); errx(1, "Invalid service pointer");
return; return;
} }
@@ -439,8 +555,10 @@ void conf_parse_cond(svc_t *svc, char *cond)
if (svc_is_daemon(svc)) if (svc_is_daemon(svc))
svc->sighup = 1; svc->sighup = 1;
if (!cond) if (!cond) {
memset(svc->cond, 0, sizeof(svc->cond));
return; return;
}
/* First character must be '!' if SIGHUP is not supported. */ /* First character must be '!' if SIGHUP is not supported. */
ptr = cond; ptr = cond;
@@ -454,16 +572,17 @@ void conf_parse_cond(svc_t *svc, char *cond)
ptr[i] = 0; ptr[i] = 0;
if (i >= sizeof(svc->cond)) { if (i >= sizeof(svc->cond)) {
logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc_ident(svc, NULL, 0), ptr); logit(LOG_WARNING, "%s: too long list of conditions: %s", svc_ident(svc, NULL, 0), ptr);
return; return;
} }
if (!strncmp(ptr, "svc/", 4)) { svc->cond[0] = 0;
logit(LOG_ERR, "Unsupported cond syntax for %s: <%s", svc_ident(svc, NULL, 0), ptr); for (i = 0, c = strtok(ptr, ","); c; c = strtok(NULL, ","), i++) {
return; devmon_add_cond(c);
if (i)
strlcat(svc->cond, ",", sizeof(svc->cond));
strlcat(svc->cond, c, sizeof(svc->cond));
} }
strlcpy(svc->cond, ptr, sizeof(svc->cond));
} }
struct rlimit_name { struct rlimit_name {
@@ -649,18 +768,29 @@ static int parse_static(char *line, int is_rcsd)
return 0; return 0;
} }
if (BOOTSTRAP && MATCH_CMD(line, "rcsd ", x)) {
if (finit_rcsd) free(finit_rcsd);
finit_rcsd = strdup(strip_line(x));
return 0;
}
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) { if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
if (runparts) free(runparts); if (runparts) free(runparts);
runparts = strdup(strip_line(x)); runparts = strdup(strip_line(x));
return 0; return 0;
} }
if (BOOTSTRAP && MATCH_CMD(line, "set ", x)) {
parse_env(x);
return 0;
}
if (MATCH_CMD(line, "include ", x)) { if (MATCH_CMD(line, "include ", x)) {
char *file = strip_line(x); char *file = strip_line(x);
strlcpy(cmd, file, sizeof(cmd)); strlcpy(cmd, file, sizeof(cmd));
if (!fexist(cmd)) { if (!fexist(cmd)) {
_e("Cannot find include file %s, absolute path required!", x); errx(1, "Cannot find include file %s, absolute path required!", x);
return 1; return 1;
} }
@@ -712,6 +842,11 @@ static int parse_static(char *line, int is_rcsd)
return 0; return 0;
} }
if (MATCH_CMD(line, "reboot-delay ", x)) {
syncsec = strtonum(strip_line(x), 0, 60, NULL);
return 0;
}
/* /*
* Periodic check and instability index leveler, seconds * Periodic check and instability index leveler, seconds
*/ */
@@ -790,12 +925,79 @@ static int parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return 1; return 1;
} }
/*
* Very simple and crude implementation, only supports '%i'
*/
static char *instantiate(char *line, char *name)
{
char *ptr, *end = strchr(line, 0);
char *pos = line;
size_t num = 0;
if (!name[0] || !end)
return line;
while ((ptr = strchr(pos, '%'))) {
num++;
pos++;
}
ptr = realloc(line, strlen(line) + num * strlen(name) + 1);
if (!ptr)
return line;
pos = line = ptr;
while ((ptr = strchr(pos, '%'))) {
if (!strncmp(ptr, "%i", 2)) {
char *rest = &ptr[2];
char *next = ptr + strlen(name);
memmove(next, rest, strlen(rest) + 1);
memcpy(ptr, name, strlen(name));
pos += strlen(name);
} else
pos++;
}
return line;
}
static int is_template(const char *file, char *name, size_t len)
{
char *ptr, *nm;
size_t i = 0;
ptr = strchr(file, '@');
if (!ptr)
return 0; /* not a template */
nm = ptr + 1;
ptr = strstr(nm, ".conf");
if (!strcmp(nm, ".conf") || !ptr)
return 1; /* template itself or invalid */
while (nm < ptr && i < len - 1)
name[i++] = *nm++;
name[i] = 0;
return 1; /* instantiated template */
}
static int parse_conf(char *file, int is_rcsd) static int parse_conf(char *file, int is_rcsd)
{ {
struct rlimit rlimit[RLIMIT_NLIMITS]; struct rlimit rlimit[RLIMIT_NLIMITS];
char line[LINE_SIZE] = ""; char name[65] = { 0 };
FILE *fp; FILE *fp;
if (is_template(file, name, sizeof(name))) {
if (!name[0]) {
dbg("*** Skipping template file %s", file);
return 0;
}
dbg("*** instantiating %s from %s ...", name, file);
}
fp = fopen(file, "r"); fp = fopen(file, "r");
if (!fp) if (!fp)
return 1; return 1;
@@ -806,28 +1008,27 @@ static int parse_conf(char *file, int is_rcsd)
cgroup_current[0] = 0; cgroup_current[0] = 0;
} }
_d("*** Parsing %s", file); dbg("*** Parsing %s", file);
while (!feof(fp)) { while (!feof(fp)) {
char *x; char *line;
if (!fgets(line, sizeof(line), fp)) line = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCCOMM);
if (!line)
continue; continue;
chomp(line);
tabstospaces(line); tabstospaces(line);
//DEV _d("%s", line); // dbg("raw: %s", line);
line = instantiate(line, name);
/* Skip comments, i.e. lines beginning with # */ // dbg("ins: %s", line);
if (MATCH_CMD(line, "#", x))
continue;
if (!parse_static(line, is_rcsd)) if (!parse_static(line, is_rcsd))
continue; ;
if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file)) else if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file))
continue; ;
else
parse_env(line);
/* Not static or dynamic conf, check if it is a global env. */ free(line);
parse_env(line);
} }
fclose(fp); fclose(fp);
@@ -835,17 +1036,41 @@ static int parse_conf(char *file, int is_rcsd)
return 0; return 0;
} }
static void glob_append(glob_t *gl, int append, const char *fmt, ...)
{
va_list ap;
size_t len;
char *path;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
path = alloca(++len);
if (!path) {
warn("failed alloca() in glob_append()");
return;
}
va_start(ap, fmt);
vsnprintf(path, len, fmt, ap);
va_end(ap);
dbg("conf_reload(): glob %s ...", path);
glob(path, append ? GLOB_APPEND : 0, NULL, gl);
}
/* /*
* Reload /etc/finit.conf and all *.conf in /etc/finit.d/ * Reload /etc/finit.conf and all *.conf in /etc/finit.d/
*/ */
int conf_reload(void) int conf_reload(void)
{ {
size_t i;
glob_t gl; glob_t gl;
size_t i;
/* Set time according to current time zone */ /* Set time according to current time zone */
tzset(); tzset();
_d("Set time daylight: %d timezone: %ld tzname: %s %s", dbg("Set time daylight: %d timezone: %ld tzname: %s %s",
daylight, timezone, tzname[0], tzname[1]); daylight, timezone, tzname[0], tzname[1]);
/* Mark and sweep */ /* Mark and sweep */
@@ -860,7 +1085,7 @@ int conf_reload(void)
if (rescue) { if (rescue) {
int rc; int rc;
char line[80] = "tty [12345] rescue"; char line[80] = "tty [12345789] rescue";
/* If rescue.conf is missing, fall back to a root shell */ /* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF, 0); rc = parse_conf(RESCUE_CONF, 0);
@@ -872,7 +1097,7 @@ int conf_reload(void)
} }
/* First, read /etc/finit.conf */ /* First, read /etc/finit.conf */
parse_conf(FINIT_CONF, 0); parse_conf(finit_conf, 0);
/* Set global limits */ /* Set global limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) { for (int i = 0; i < RLIMIT_NLIMITS; i++) {
@@ -881,25 +1106,45 @@ int conf_reload(void)
rlim2str(i), lim2str(&global_rlimit[i])); rlim2str(i), lim2str(&global_rlimit[i]));
} }
/* Next, read all *.conf in /etc/finit.d/ */ /*
glob(FINIT_RCSD "/*.conf", 0, NULL, &gl); * Next, read all *.conf in /lib/finit/system and /etc/finit.d/
glob(FINIT_RCSD "/enabled/*.conf", GLOB_APPEND, NULL, &gl); * The system files were previously created at runtime by plugins
* but are now regular files that can be overridden by files in
* /etc/finit.d -- similar to how tmfiles.d(5) work. E.g., add
* an override .conf, or an ignore by symlinking to /dev/null
*/
glob_append(&gl, 0, "%s/*.conf", finit_rcsd);
glob_append(&gl, 1, "%s/*.conf", FINIT_SYSPATH);
glob_append(&gl, 1, "%s/enabled/*.conf", finit_rcsd);
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
char *path = gl.gl_pathv[i]; char *path = gl.gl_pathv[i];
char *rp = NULL; char *rp = NULL;
struct stat st; struct stat st;
size_t len; size_t j, len;
/* check for FINIT_SYSPATH overrides */
for (j = i + 1; j < gl.gl_pathc; j++) {
if (strncmp(path, FINIT_SYSPATH, strlen(FINIT_SYSPATH)))
continue;
if (strcmp(basename(path), basename(gl.gl_pathv[j])))
continue;
path = NULL; /* replacement later in list, skip this */
break;
}
if (!path)
continue; /* skip, override exists */
/* Check that it's an actual file ... beyond any symlinks */ /* Check that it's an actual file ... beyond any symlinks */
if (lstat(path, &st)) { if (lstat(path, &st)) {
_d("Skipping %s, cannot access: %s", path, strerror(errno)); dbg("Skipping %s, cannot access: %s", path, strerror(errno));
continue; continue;
} }
/* Skip directories */ /* Skip directories */
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
_d("Skipping directory %s", path); dbg("Skipping directory %s", path);
continue; continue;
} }
@@ -915,7 +1160,7 @@ int conf_reload(void)
/* Check that file ends with '.conf' */ /* Check that file ends with '.conf' */
len = strlen(path); len = strlen(path);
if (len < 6 || strcmp(&path[len - 5], ".conf")) if (len < 6 || strcmp(&path[len - 5], ".conf"))
_d("Skipping %s, not a Finit .conf file ... ", path); dbg("Skipping %s, not a Finit .conf file ... ", path);
else else
parse_conf(path, 1); parse_conf(path, 1);
@@ -928,6 +1173,9 @@ int conf_reload(void)
/* Mark any reverse deps as chenaged. */ /* Mark any reverse deps as chenaged. */
service_update_rdeps(); service_update_rdeps();
/* Prune according to if:[!]ident or if:<[!]cond> */
service_mark_unavail();
/* Set up top-level cgroups */ /* Set up top-level cgroups */
cgroup_config(); cgroup_config();
done: done:
@@ -981,39 +1229,45 @@ static void drop_changes(void)
drop_change(node); drop_change(node);
} }
static int do_change(char *dir, char *name, uint32_t mask) static int conf_change_act(char *dir, char *name, uint32_t mask)
{ {
char fn[strlen(dir) + strlen(name) + 2]; char fn[strlen(dir) + strlen(name) + 2];
struct conf_change *node; struct conf_change *node;
char *rp = NULL;
paste(fn, sizeof(fn), dir, name); paste(fn, sizeof(fn), dir, name);
_d("path: %s mask: %08x", fn, mask); dbg("path: %s mask: %08x", fn, mask);
node = conf_find(fn); /* Handle disabling/removal of service */
if (mask & (IN_DELETE | IN_MOVED_FROM)) { rp = realpath(fn, NULL);
drop_change(node); if (!rp) {
return 0; if (errno != ENOENT)
goto fail;
rp = strdup(fn);
if (!rp)
goto fail;
} }
node = conf_find(rp);
if (node) { if (node) {
_d("Event already registered for %s ...", name); dbg("event already registered for %s ...", name);
free(rp);
return 0; return 0;
} }
node = malloc(sizeof(*node)); node = malloc(sizeof(*node));
if (!node) if (!node) {
return 1; free(rp);
goto fail;
node->name = strdup(fn);
if (!node->name) {
free(node);
return 1;
} }
_d("Event registered for %s, mask 0x%x", fn, mask); node->name = rp;
TAILQ_INSERT_HEAD(&conf_change_list, node, link); TAILQ_INSERT_HEAD(&conf_change_list, node, link);
dbg("event registered for %s, mask 0x%x", rp, mask);
return 0; return 0;
fail:
warn("failed registering %s event", fn);
return 1;
} }
int conf_any_change(void) int conf_any_change(void)
@@ -1043,18 +1297,16 @@ int conf_changed(char *file)
return rc; return rc;
} }
static void conf_cb(uev_t *w, void *arg, int events) static int conf_iwatch_read(int fd)
{ {
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev; struct inotify_event *ev;
ssize_t sz; ssize_t sz;
size_t off; size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) { if (sz <= 0)
_pe("invalid inotify event"); return -1;
return;
}
ev_buf[sz] = 0; ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) { for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
@@ -1070,30 +1322,48 @@ static void conf_cb(uev_t *w, void *arg, int events)
if (!ev->mask) if (!ev->mask)
continue; continue;
_d("name %s, event: 0x%08x", ev->name, ev->mask); dbg("name %s, event: 0x%08x", ev->name, ev->mask);
/* Find base path for this event */ /* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_conf, ev->wd); iwp = iwatch_find_by_wd(&iw_conf, ev->wd);
if (!iwp) if (!iwp)
continue; continue;
if (do_change(iwp->path, ev->name, ev->mask)) { if (conf_change_act(iwp->path, ev->name, ev->mask)) {
_pe(" Out of memory"); err(1, " Out of memory");
break; break;
} }
} }
#ifdef ENABLE_AUTO_RELOAD return 0;
}
static void conf_cb(uev_t *w, void *arg, int events)
{
if (conf_iwatch_read(w->fd)) {
err(1, "invalid inotify event");
return;
}
#ifdef AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); service_reload_dynamic();
#endif #endif
} }
void conf_flush_events(void)
{
while (!conf_iwatch_read(iwatch_fd))
dbg("emptying inotify queue ...");
}
/* /*
* Set up inotify watcher and load all *.conf in /etc/finit.d/ * Set up inotify watcher and load all *.conf in /etc/finit.d/
*/ */
int conf_monitor(void) int conf_monitor(void)
{ {
char path[strlen(finit_rcsd) + 16];
int rc = 0; int rc = 0;
/* /*
@@ -1102,10 +1372,12 @@ 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);
rc += iwatch_add(&iw_conf, FINIT_RCSD "/available/", IN_ONLYDIR | IN_DONT_FOLLOW); snprintf(path, sizeof(path), "%s/available/", finit_rcsd);
rc += iwatch_add(&iw_conf, FINIT_RCSD "/enabled/", IN_ONLYDIR | IN_DONT_FOLLOW); rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
rc += iwatch_add(&iw_conf, FINIT_CONF, 0); snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd);
rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW);
rc += iwatch_add(&iw_conf, finit_conf, 0);
/* /*
* Systems with /etc/default, /etc/conf.d, or similar, can also * Systems with /etc/default, /etc/conf.d, or similar, can also
@@ -1126,8 +1398,6 @@ int conf_monitor(void)
*/ */
int conf_init(uev_ctx_t *ctx) int conf_init(uev_ctx_t *ctx)
{ {
int fd;
/* default hostname */ /* default hostname */
hostname = strdup(DEFHOST); hostname = strdup(DEFHOST);
@@ -1145,17 +1415,35 @@ int conf_init(uev_ctx_t *ctx)
/* Initialize global rlimits, e.g. for built-in services */ /* Initialize global rlimits, e.g. for built-in services */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit)); memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
/*
* Start built-in watchdogd as soon as possible, if enabled
*/
#ifdef WDT_DEVNODE
if (whichp(FINIT_EXECPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
service_register(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init name:watchdog :finit " FINIT_EXECPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
wdog = svc_find("watchdog", "finit");
}
#endif
/*
* Start kernel event daemon as soon as possible, if enabled
*/
if (whichp(FINIT_EXECPATH_ "/keventd"))
service_register(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init " FINIT_EXECPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
dbg("Allow plugins to register early runlevel 1 run/task/services ...");
plugin_run_hooks(HOOK_SVC_PLUGIN);
/* Read global rlimits and global cgroup setup from /etc/finit.conf */ /* Read global rlimits and global cgroup setup from /etc/finit.conf */
parse_conf(FINIT_CONF, 0); parse_conf(finit_conf, 0);
/* prepare /etc watcher */ /* prepare /etc watcher */
fd = iwatch_init(&iw_conf); iwatch_fd = iwatch_init(&iw_conf);
if (fd < 0) if (iwatch_fd < 0)
return 1; return 1;
if (uev_io_init(ctx, &etcw, conf_cb, NULL, fd, UEV_READ)) { if (uev_io_init(ctx, &etcw, conf_cb, NULL, iwatch_fd, UEV_READ)) {
_pe("Failed setting up I/O callback for /etc watcher"); err(1, "Failed setting up I/O callback for /etc watcher");
close(fd); close(iwatch_fd);
return 1; return 1;
} }
+2 -1
View File
@@ -1,6 +1,6 @@
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf /* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -43,6 +43,7 @@ int conf_changed (char *file);
int conf_monitor (void); int conf_monitor (void);
void conf_reset_env (void); void conf_reset_env (void);
void conf_saverc (void);
void conf_parse_cmdline (int argc, char *argv[]); void conf_parse_cmdline (int argc, char *argv[]);
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);
+275
View File
@@ -0,0 +1,275 @@
/* Device node monitor for the Finit condition engine
*
* Copyright (c) 2022-2023 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.
*/
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "helpers.h"
#include "pid.h"
#include "plugin.h"
#include "service.h"
#include "iwatch.h"
static struct iwatch iw_devmon;
static uev_t devw;
static int fd;
struct dev_node {
TAILQ_ENTRY(dev_node) link;
char *name;
size_t refcnt;
};
static TAILQ_HEAD(, dev_node) dev_node_list = TAILQ_HEAD_INITIALIZER(dev_node_list);
static struct dev_node *find_node(const char *cond)
{
struct dev_node *node, *tmp;
TAILQ_FOREACH_SAFE(node, &dev_node_list, link, tmp) {
if (string_compare(node->name, cond))
return node;
}
return NULL;
}
static void drop_node(struct dev_node *node)
{
if (!node)
return;
node->refcnt--;
if (node->refcnt)
return;
TAILQ_REMOVE(&dev_node_list, node, link);
free(node->name);
free(node);
}
void devmon_add_cond(const char *cond)
{
struct dev_node *node;
if (!cond || strncmp(cond, "dev/", 4)) {
dbg("no match");
return;
}
// dbg("%s", cond);
node = find_node(cond);
if (node) {
node->refcnt++;
return;
}
node = malloc(sizeof(*node));
if (!node) {
fail:
warn("failed allocating node %s", cond);
return;
}
node->name = strdup(cond);
if (!node->name) {
free(node);
goto fail;
}
node->refcnt = 1;
TAILQ_INSERT_TAIL(&dev_node_list, node, link);
}
void devmon_del_cond(const char *cond)
{
if (!cond || strcmp(cond, "dev/"))
return;
drop_node(find_node(cond));
}
static int devmon_add_path(struct iwatch *iw, char *path)
{
char *ptr;
ptr = strstr(path, "dev/");
if (ptr) {
char *slash;
ptr += 4;
slash = strchr(ptr, '/');
if (slash) {
ptr = slash++;
slash = strchr(ptr, '/');
if (slash) {
dbg("Path too deep, skipping.");
return -1;
}
}
}
return iwatch_add1(iw, path, IN_CREATE | IN_DELETE);
}
static void devmon_update_conds(char *dir, char *name, uint32_t mask)
{
char fn[PATH_MAX];
char *cond;
paste(fn, sizeof(fn), dir, name);
cond = &fn[1];
// dbg("path: %s, mask: %08x, cond: %s", fn, mask, cond);
if (!find_node(cond)) {
// dbg("unregistered, skipping %s", cond);
return;
}
if (mask & IN_CREATE)
cond_set(cond);
else if (mask & IN_DELETE)
cond_clear(cond);
}
/* synthesize events in case of new run dirs */
static void devmon_scandir(struct iwatch *iw, char *dir, int len)
{
char path[len + 6];
glob_t gl;
size_t i;
int rc;
snprintf(path, sizeof(path), "%s/*", dir);
rc = glob(path, GLOB_NOSORT, NULL, &gl);
if (rc && rc != GLOB_NOMATCH)
return;
for (i = 0; i < gl.gl_pathc; i++) {
// dbg("scan found %s", gl.gl_pathv[i]);
devmon_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
}
globfree(&gl);
}
/*
* create/remove sub-directory in monitored directory
*/
static void devmon_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
{
char path[strlen(dir) + strlen(name) + 2];
struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name);
dbg("path: %s", path);
iwp = iwatch_find_by_path(iw, path);
if (mask & IN_CREATE) {
if (!iwp) {
if (!devmon_add_path(iw, path))
devmon_scandir(iw, path, sizeof(path));
}
} else if (mask & IN_DELETE) {
if (iwp)
iwatch_del(&iw_devmon, iwp);
}
}
static void devmon_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
err(1, "invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_devmon, ev->wd);
if (!iwp)
continue;
if (ev->mask & IN_ISDIR) {
devmon_handle_dir(&iw_devmon, iwp->path, ev->name, ev->mask);
continue;
}
dbg("Got %s", ev->name);
if (ev->mask & (IN_CREATE | IN_DELETE))
devmon_update_conds(iwp->path, ev->name, ev->mask);
}
}
void devmon_init(void)
{
char dir[MAX_ARG_LEN];
fd = iwatch_init(&iw_devmon);
if (fd < 0)
return;
if (mkpath(pid_runpath(_PATH_CONDDEV, dir, sizeof(dir)), 0755) && errno != EEXIST) {
err(1, "Failed creating %s condition directory, %s", COND_DEV, _PATH_CONDDEV);
return;
}
if (uev_io_init(ctx, &devw, devmon_cb, NULL, fd, UEV_READ)) {
err(1, "Failed setting up I/O callback for /dev watcher");
close(fd);
return;
}
strlcpy(dir, _PATH_DEV, sizeof(dir));
if (devmon_add_path(&iw_devmon, dir))
iwatch_exit(&iw_devmon);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+32
View File
@@ -0,0 +1,32 @@
/* Device node monitor for the Finit condition engine
*
* Copyright (c) 2022-2023 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 FINIT_DEVMON_H_
#define FINIT_DEVMON_H_
void devmon_add_cond(const char *cond);
void devmon_del_cond(const char *cond);
void devmon_init(void);
#endif /* FINIT_DEVMON_H_ */
+47 -24
View File
@@ -1,7 +1,7 @@
/* Functions for exec'ing processes /* Functions for exec'ing processes
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -59,11 +59,11 @@ int complete(char *cmd, int pid)
if (waitpid(pid, &status, 0) == -1) { if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR) if (errno == EINTR)
_e("Caught unblocked signal waiting for %s, aborting", cmd); errx(1, "Caught unblocked signal waiting for %s, aborting", cmd);
else if (errno == ECHILD) else if (errno == ECHILD)
_e("Caught SIGCHLD waiting for %s, aborting", cmd); errx(1, "Caught SIGCHLD waiting for %s, aborting", cmd);
else else
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno)); errx(1, "Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
return -1; return -1;
} }
@@ -131,7 +131,7 @@ int run(char *cmd, char *log)
args[i] = NULL; args[i] = NULL;
if (i == NUM_ARGS && arg) { if (i == NUM_ARGS && arg) {
_e("Command too long: %s", cmd); errx(1, "Command too long: %s", cmd);
free(backup); free(backup);
errno = EOVERFLOW; errno = EOVERFLOW;
return 1; return 1;
@@ -166,7 +166,7 @@ int run(char *cmd, char *log)
_exit(rc); _exit(rc);
} else if (-1 == pid) { } else if (-1 == pid) {
_pe("%s", !log ? args[0] : cmd); err(1, "%s", !log ? args[0] : cmd);
if (backup) if (backup)
free(backup); free(backup);
@@ -182,9 +182,9 @@ int run(char *cmd, char *log)
result = WEXITSTATUS(status); result = WEXITSTATUS(status);
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
_d("Started '%s' and exit without signal, status: %d", cmd, result); dbg("Started '%s' and exit without signal, status: %d", cmd, result);
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
_d("Process '%s' terminated by signal %d", cmd, WTERMSIG(status)); dbg("Process '%s' terminated by signal %d", cmd, WTERMSIG(status));
if (!result) if (!result)
result = 1; /* Must alert callee that the command did complete successfully. result = 1; /* Must alert callee that the command did complete successfully.
* This is necessary since not all programs trap signals and * This is necessary since not all programs trap signals and
@@ -277,7 +277,7 @@ int exec_runtask(char *cmd, char *args[])
strlcat(buf, args[i], sizeof(buf)); strlcat(buf, args[i], sizeof(buf));
} }
logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf); logit(LOG_DEBUG, "Calling %s %s", _PATH_BSHELL, buf);
_d("Calling %s %s", _PATH_BSHELL, buf); dbg("Calling %s %s", _PATH_BSHELL, buf);
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
@@ -329,7 +329,7 @@ static int activate_console(int noclear, int nowait)
static const char clr[] = "\r\e[K"; static const char clr[] = "\r\e[K";
static const char cup[] = "\e[A"; static const char cup[] = "\e[A";
struct termios orig; struct termios orig;
char c; char ch;
int rc; int rc;
if (nowait || rescue) if (nowait || rescue)
@@ -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, &c, 1)) > 0 && c != '\r' && c != '\n') while ((rc = read(STDIN_FILENO, &ch, 1)) > 0 && ch != '\r' && ch != '\n')
continue; continue;
/* On any error (likely EINTR), we avoid starting getty */ /* On any error (likely EINTR), we avoid starting getty */
@@ -419,8 +419,10 @@ int sh(char *tty)
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */ /* Start /bin/sh as a login shell, i.e. with a prefix '-' */
len = strlen(_PATH_BSHELL) + 2; len = strlen(_PATH_BSHELL) + 2;
arg0 = malloc(len); arg0 = malloc(len);
if (!arg0) if (!arg0) {
err(1, "Failed allocating memory"); err(1, "Failed allocating memory");
return 1;
}
snprintf(arg0, len, "-%s", _PATH_BSHELL); snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0; args[0] = arg0;
@@ -441,14 +443,32 @@ pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
return rc; return rc;
} }
int run_parts(char *dir, char *cmd) static void run_env(const char *env[])
{
size_t i = 0;
if (!env)
return;
while (env[i]) {
size_t j = i + 1;
if (!env[j])
break;
setenv(env[i], env[j], 1);
i += 2;
}
}
int run_parts(char *dir, char *cmd, const char *env[], int progress)
{ {
struct dirent **e; struct dirent **e;
int i, num; int i, num;
num = scandir(dir, &e, NULL, alphasort); num = scandir(dir, &e, NULL, alphasort);
if (num < 0) { if (num < 0) {
_d("No files found in %s, skipping ...", dir); dbg("No files found in %s, skipping ...", dir);
return -1; return -1;
} }
@@ -464,23 +484,23 @@ int run_parts(char *dir, char *cmd)
}; };
pid_t pid = 0; pid_t pid = 0;
int status; int status;
int exit_status;
paste(path, sizeof(path), dir, name); paste(path, sizeof(path), dir, name);
if (stat(path, &st)) { if (stat(path, &st)) {
_d("Failed stat(%s): %s", path, strerror(errno)); dbg("Failed stat(%s): %s", path, strerror(errno));
continue; continue;
} }
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) { if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
_d("Skipping %s ...", path); if (strcmp(name, ".") && strcmp(name, ".."))
dbg("Skipping %s ...", path);
continue; continue;
} }
/* If the callee didn't supply a run_parts() argument */ /* If the callee didn't supply a run_parts() argument */
if (!cmd) { if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */ /* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name); // dbg("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1])) if (name[0] == 'S' && isdigit(name[1]))
strlcat(path, " start", sizeof(path)); strlcat(path, " start", sizeof(path));
else if (name[0] == 'K' && isdigit(name[1])) else if (name[0] == 'K' && isdigit(name[1]))
@@ -489,20 +509,23 @@ int run_parts(char *dir, char *cmd)
strlcat(path, cmd, sizeof(path)); strlcat(path, cmd, sizeof(path));
} }
print_desc("Calling ", path); if (progress)
print_desc("Calling ", path);
pid = fork(); pid = fork();
if (!pid) { if (!pid) {
sig_unblock(); sig_unblock();
run_env(env);
return execvp(_PATH_BSHELL, argv); return execvp(_PATH_BSHELL, argv);
} }
status = complete(path, pid); status = complete(path, pid);
exit_status = WEXITSTATUS(status); if (WIFEXITED(status))
if (WIFEXITED(status) && exit_status) warnx("%s exited with status %d", path, WEXITSTATUS(status));
_w("%s exited with status %d", path, exit_status);
else if (WIFSIGNALED(status)) else if (WIFSIGNALED(status))
_w("%s terminated by signad %d", path, WTERMSIG(status)); warnx("%s terminated by signad %d", path, WTERMSIG(status));
print_result(status); if (progress)
print_result(WEXITSTATUS(status));
} }
while (num--) while (num--)
+70 -65
View File
@@ -1,7 +1,7 @@
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins /* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -48,6 +48,7 @@
#include "cgroup.h" #include "cgroup.h"
#include "cond.h" #include "cond.h"
#include "conf.h" #include "conf.h"
#include "devmon.h"
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "plugin.h" #include "plugin.h"
@@ -58,9 +59,8 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
#include "schedule.h" #include "schedule.h"
#include "watchdog.h"
int runlevel = 0; /* Bootstrap 'S' */ int runlevel = INIT_LEVEL; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int cmdlevel = 0; /* runlevel override from cmdline */ int cmdlevel = 0; /* runlevel override from cmdline */
int prevlevel = -1; int prevlevel = -1;
@@ -69,11 +69,13 @@ int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */ int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootrapping (for TTYs) */ int bootstrap = 1; /* set while bootrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */ int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
char *fstab = FINIT_FSTAB; int syncsec = 0; /* reboot delay */
char *finit_conf= NULL;
char *finit_rcsd= NULL;
char *fstab = NULL;
char *sdown = NULL; char *sdown = NULL;
char *network = NULL; char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
char *rcsd = FINIT_RCSD;
char *runparts = NULL; char *runparts = NULL;
char *osheading = NULL; char *osheading = NULL;
@@ -114,14 +116,17 @@ static int sulogin(int do_reboot)
{ {
int rc = EX_OSFILE; int rc = EX_OSFILE;
char *cmd[] = { char *cmd[] = {
_PATH_SULOGIN,
"sulogin", "sulogin",
_PATH_SULOGIN,
}; };
size_t i; size_t i;
for (i = 0; i < NELEMS(cmd); i++) { for (i = 0; i < NELEMS(cmd); i++) {
char *path = which(cmd[i]); char *path = which(cmd[i]);
if (!path)
continue;
if (access(path, X_OK)) { if (access(path, X_OK)) {
free(path); free(path);
continue; continue;
@@ -201,17 +206,17 @@ static int fsck(int pass)
fp = setmntent(fstab, "r"); fp = setmntent(fstab, "r");
if (!fp) { if (!fp) {
_pe("Failed opening fstab: %s", fstab); err(1, "Failed opening fstab: %s", fstab);
sulogin(1); sulogin(1);
} }
_d("Opened %s, pass %d", fstab, pass); dbg("Opened %s, pass %d", fstab, pass);
while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) { while ((mnt = getmntent_r(fp, &mount, buf, sizeof(buf)))) {
int fsck_rc = 0; int fsck_rc = 0;
struct stat st; struct stat st;
char cmd[256]; char cmd[256];
char *dev; char *dev;
_d("got: fsname '%s' dir '%s' type '%s' opts '%s' freq '%d' passno '%d'", dbg("got: fsname '%s' dir '%s' type '%s' opts '%s' freq '%d' passno '%d'",
mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts, mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
mnt->mnt_freq, mnt->mnt_passno); mnt->mnt_freq, mnt->mnt_passno);
@@ -241,22 +246,22 @@ static int fsck(int pass)
} }
if (skip) { if (skip) {
_d("Cannot fsck %s, not a block device: %s", dev, strerror(errno)); dbg("Cannot fsck %s, not a block device: %s", dev, strerror(errno));
continue; continue;
} }
} }
if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) { if (ismnt("/proc/mounts", mnt->mnt_dir, "rw")) {
_d("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir); dbg("Skipping fsck of %s, already mounted rw on %s.", dev, mnt->mnt_dir);
continue; continue;
} }
#ifdef FSCK_FIX #ifdef FSCK_FIX
snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev); snprintf(cmd, sizeof(cmd), "fsck -yf %s", dev);
#else #else
snprintf(cmd, sizeof(cmd), "fsck -a %s", dev); snprintf(cmd, sizeof(cmd), "fsck %s %s %s", fsck_mode, fsck_repair, dev);
#endif #endif
_d("Running pass %d fsck command %s", pass, cmd); dbg("Running pass %d fsck command %s", pass, cmd);
fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev); fsck_rc = run_interactive(cmd, "Checking filesystem %s", dev);
/* /*
* "failure" is defined as exiting with a return code of * "failure" is defined as exiting with a return code of
@@ -281,6 +286,9 @@ static int fsck_all(void)
#ifndef FAST_BOOT #ifndef FAST_BOOT
int pass; int pass;
if (!fsck_mode)
return 0;
for (pass = 1; pass < 10; pass++) { for (pass = 1; pass < 10; pass++) {
rc = fsck(pass); rc = fsck(pass);
if (rc) if (rc)
@@ -299,7 +307,7 @@ static void fs_mount(const char *src, const char *tgt, const char *fstype,
rc = mount(src, tgt, fstype, flags, data); rc = mount(src, tgt, fstype, flags, data);
if (rc && errno != EBUSY) if (rc && errno != EBUSY)
_pe("Failed %s %s on %s", msg, src, tgt); err(1, "Failed %s %s on %s", msg, src, tgt);
} }
#ifndef SYSROOT #ifndef SYSROOT
@@ -453,7 +461,7 @@ static void fs_mount_all(void)
if (!rescue) if (!rescue)
fs_remount_root(fsck_all()); fs_remount_root(fsck_all());
_d("Root FS up, calling hooks ..."); dbg("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP); plugin_run_hooks(HOOK_ROOTFS_UP);
if (fstab && strcmp(fstab, "/etc/fstab")) if (fstab && strcmp(fstab, "/etc/fstab"))
@@ -462,14 +470,17 @@ static void fs_mount_all(void)
if (run_interactive(cmd, "Mounting filesystems from %s", fstab)) if (run_interactive(cmd, "Mounting filesystems from %s", fstab))
plugin_run_hooks(HOOK_MOUNT_ERROR); plugin_run_hooks(HOOK_MOUNT_ERROR);
_d("Calling extra mount hook, after mount -a ..."); dbg("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST); plugin_run_hooks(HOOK_MOUNT_POST);
_d("Enable any swap ..."); dbg("Enable any swap ...");
fs_swapon(cmd, sizeof(cmd)); fs_swapon(cmd, sizeof(cmd));
_d("Finalize, ensure common file systems are available ..."); dbg("Finalize, ensure common file systems are available ...");
fs_finalize(); fs_finalize();
dbg("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
} }
/* /*
@@ -524,11 +535,11 @@ static void fs_init(void)
static void finalize(void *unused) static void finalize(void *unused)
{ {
/* Clean up bootstrap-only tasks/services that never started */ /* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ..."); dbg("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap(); svc_prune_bootstrap();
/* All services/tasks/etc. in configure runlevel have started */ /* All services/tasks/etc. in configure runlevel have started */
_d("Running svc up hooks ..."); dbg("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP); plugin_run_hooks(HOOK_SVC_UP);
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -537,7 +548,7 @@ static void finalize(void *unused)
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL); run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
/* Hooks that should run at the very end */ /* Hooks that should run at the very end */
_d("Calling all system up hooks ..."); dbg("Calling all system up hooks ...");
plugin_run_hooks(HOOK_SYSTEM_UP); plugin_run_hooks(HOOK_SYSTEM_UP);
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -574,42 +585,45 @@ static void bootstrap_worker(void *work)
.cb = finalize, .cb = finalize,
.delay = 10 .delay = 10
}; };
static int cnt = 120; static int cnt = 120 * 10; /* We run with 100ms period */
int level = cfglevel; int level = cfglevel;
_d("Step all services ..."); dbg("Step all services ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
if (cnt-- > 0 && !service_completed()) { if (cnt-- > 0 && !service_completed()) {
_d("Not all bootstrap run/tasks have completed yet ... %d", cnt); dbg("Not all bootstrap run/tasks have completed yet ... %d", cnt);
schedule_work(work); schedule_work(work);
return; return;
} }
if (cnt > 0) if (cnt > 0)
_d("All run/task have completed, resuming bootstrap."); dbg("All run/task have completed, resuming bootstrap.");
else else
_d("Timeout, resuming bootstrap."); dbg("Timeout, resuming bootstrap.");
_d("Starting runlevel change finalize ..."); dbg("Starting runlevel change finalize ...");
schedule_work(&final); schedule_work(&final);
/* /*
* Run startup scripts in the runparts directory, if any. * Run startup scripts in the runparts directory, if any.
*/ */
if (runparts && fisdir(runparts) && !rescue) if (runparts && fisdir(runparts) && !rescue)
run_parts(runparts, NULL); run_parts(runparts, NULL, NULL, 1);
dbg("Flushing pending .conf file events ...");
conf_flush_events();
/* /*
* Start all tasks/services in the configured runlevel, or jump * Start all tasks/services in the configured runlevel, or jump
* into the runlevel selected from the command line. * into the runlevel selected from the command line.
*/ */
if (cmdlevel) { if (cmdlevel) {
_d("Runlevel %d requested from command line, starting all services ...", cmdlevel); dbg("Runlevel %d requested from command line, starting all services ...", cmdlevel);
level = cmdlevel; level = cmdlevel;
} else } else
_d("Change to default runlevel(%d), starting all services ...", cfglevel); dbg("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(level); service_runlevel(level);
} }
@@ -689,12 +703,6 @@ static int telinit(int argc, char *argv[])
return systemf("initctl -b runlevel %c", req); return systemf("initctl -b runlevel %c", req);
} }
/* XXX: add non-pid1 process monitor here
*
* finit -f ~/.config/finit.conf &
*
*/
return usage(1); return usage(1);
} }
@@ -711,8 +719,18 @@ int main(int argc, char *argv[])
uev_ctx_t loop; uev_ctx_t loop;
/* user calling telinit or init */ /* user calling telinit or init */
if (getpid() != 1) if (getpid() != 1) {
return telinit(argc, argv); if (geteuid() == 0)
return telinit(argc, argv);
/*
* Issue #301: add non-pid1 process monitor here.
* Default to read ~/.config/finit.conf with the
* API socket in /run/user/$UID/. Lot of things
* to refactor before then though.
*/
return EX_NOPERM;
}
/* /*
* Need /dev, /proc, and /sys for console=, remount and cgroups * Need /dev, /proc, and /sys for console=, remount and cgroups
@@ -742,7 +760,7 @@ int main(int argc, char *argv[])
conf_reset_env(); conf_reset_env();
if (chdir("/")) if (chdir("/"))
_pe("Failed cd /"); err(1, "Failed cd /");
/* /*
* In case of emergency. * In case of emergency.
@@ -783,7 +801,11 @@ int main(int argc, char *argv[])
*/ */
fs_mount_all(); fs_mount_all();
/* Bootstrap conditions, needed for hooks */ /*
* Base FS up, enable standard SysV init signals and
* Bootstrap conditions, needed for hooks
*/
sig_setup(&loop);
cond_init(); cond_init();
/* /*
@@ -794,29 +816,12 @@ int main(int argc, char *argv[])
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP)); cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
/* /*
* Initialize .conf system and load static /etc/finit.conf. * Initialize .conf system and load static /etc/finit.conf then
* tell the world what we used.
*/ */
devmon_init();
conf_init(&loop); conf_init(&loop);
conf_saverc();
/*
* Start built-in watchdogd as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
wdog = svc_find("watchdog", "finit");
}
/*
* Start kernel event daemon as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/keventd"))
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
/* Base FS up, enable standard SysV init signals */
sig_setup(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
/* /*
* Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and * Set up inotify watcher for /etc/finit.conf, /etc/finit.d, and
@@ -824,13 +829,13 @@ int main(int argc, char *argv[])
*/ */
conf_monitor(); conf_monitor();
_d("Starting initctl API responder ..."); dbg("Starting initctl API responder ...");
api_init(&loop); api_init(&loop);
_d("Starting the big state machine ..."); dbg("Starting the big state machine ...");
schedule_work(&crank_work); schedule_work(&crank_work);
_d("Starting bootstrap finalize timer ..."); dbg("Starting bootstrap finalize timer ...");
schedule_work(&bootstrap_work); schedule_work(&bootstrap_work);
/* /*
@@ -841,7 +846,7 @@ int main(int argc, char *argv[])
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
*/ */
_d("Entering main loop ..."); dbg("Entering main loop ...");
return uev_run(&loop, 0); return uev_run(&loop, 0);
} }
+11 -9
View File
@@ -1,7 +1,7 @@
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins /* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -54,19 +54,20 @@
#define FINIT_CGPATH "/sys/fs/cgroup" #define FINIT_CGPATH "/sys/fs/cgroup"
#endif #endif
/* To use these, include config.h, or define FINIT_LIBPATH_ */ /* To use these, include config.h, or define FINIT_EXECPATH_ */
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit" #define _PATH_LOGIT FINIT_EXECPATH_ "/logit"
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin" #define _PATH_SULOGIN FINIT_EXECPATH_ "/sulogin"
#define _PATH_GETTY FINIT_LIBPATH_ "/getty" #define _PATH_GETTY FINIT_EXECPATH_ "/getty"
#define CMD_SIZE 256 #define CMD_SIZE 256
#define LINE_SIZE 1024 #define LINE_SIZE 1024
#define BUF_SIZE 4096 #define BUF_SIZE 4096
/* We extend the INIT_CMD_ range for the new initctl tool. */
#define INIT_SOCKET _PATH_VARRUN "finit/socket" #define INIT_SOCKET _PATH_VARRUN "finit/socket"
#define INIT_MAGIC 0x03091969 #define INIT_MAGIC 0x03091969
#define INIT_LEVEL 10
/* We extend the INIT_CMD_ range for the new initctl tool. */
#define INIT_CMD_START 0 #define INIT_CMD_START 0
#define INIT_CMD_RUNLVL 1 #define INIT_CMD_RUNLVL 1
#define INIT_CMD_POWERFAIL 2 #define INIT_CMD_POWERFAIL 2
@@ -83,8 +84,8 @@
#define INIT_CMD_STOP_SVC 11 #define INIT_CMD_STOP_SVC 11
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */ #define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */ #define INIT_CMD_RESTART_SVC 13 /* START service, clearing blocks */
#define INIT_CMD_UNUSED2 14 /* Unused, was INIT_CMD_QUERY_INETD */ #define INIT_CMD_GET_PLUGINS 14 /* Fill data[] with loaded plugins */
#define INIT_CMD_UNUSED1 15 /* Unused, was INIT_CMD_EMIT */ #define INIT_CMD_PLUGIN_DEPS 15 /* Fill data[] with plugin deps */
#define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_GET_RUNLEVEL 16
#define INIT_CMD_REBOOT 20 #define INIT_CMD_REBOOT 20
#define INIT_CMD_HALT 21 #define INIT_CMD_HALT 21
@@ -96,6 +97,7 @@
#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
@@ -117,11 +119,11 @@ extern int rescue;
extern int single; extern int single;
extern int bootstrap; extern int bootstrap;
extern int kerndebug; extern int kerndebug;
extern int syncsec;
extern char *fstab; extern char *fstab;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
extern char *hostname; extern char *hostname;
extern char *rcsd;
extern char *runparts; extern char *runparts;
extern char *osheading; extern char *osheading;
extern uev_ctx_t *ctx; extern uev_ctx_t *ctx;
+147 -18
View File
@@ -1,6 +1,6 @@
/* Initialize and serve a login terminal /* Initialize and serve a login terminal
* *
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -34,6 +34,7 @@
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
#include <time.h>
#include <unistd.h> /* sysconf() */ #include <unistd.h> /* sysconf() */
#include "finit.h" #include "finit.h"
@@ -48,7 +49,80 @@
#define LOGIN_NAME_MIN 64 #define LOGIN_NAME_MIN 64
#endif #endif
struct osrel {
char name[32];
char pretty[64];
char id[32];
char version[32];
char version_id[32];
char home_url[128];
char doc_url[128];
char bug_url[128];
char support_url[128];
};
static long logname_len = 32; /* useradd(1) limit at 32 chars */ static long logname_len = 32; /* useradd(1) limit at 32 chars */
static int passenv = 0; /* Set /bin/login -p or not */
static void get_val(char *line, const char *key, char *buf, size_t len)
{
char *val;
if ((val = fgetval(line, key, "="))) {
strlcpy(buf, val, len);
free(val);
}
}
/*
* Parse os-release
*/
static int osrel(struct osrel *rel)
{
char codename[30];
char *line;
FILE *fp;
fp = fopen("/etc/os-release", "r");
if (!fp) {
fp = fopen("/usr/lib/os-release", "r");
if (!fp)
return -1;
}
memset(rel->version, 0, sizeof(rel->version));
memset(codename, 0, sizeof(codename));
while ((line = fparseln(fp, NULL, NULL, NULL, 0))) {
get_val(line, "NAME", rel->name, sizeof(rel->name));
get_val(line, "PRETTY_NAME", rel->pretty, sizeof(rel->pretty));
get_val(line, "ID", rel->id, sizeof(rel->id));
get_val(line, "VERSION", rel->version, sizeof(rel->version));
get_val(line, "VERSION_ID", rel->version_id, sizeof(rel->version_id));
get_val(line, "VERSION_CODENAME", codename, sizeof(codename));
get_val(line, "HOME_URL", rel->home_url, sizeof(rel->home_url));
get_val(line, "DOCUMENTATION_URL", rel->doc_url, sizeof(rel->doc_url));
get_val(line, "SUPPORT_URL", rel->support_url, sizeof(rel->support_url));
get_val(line, "BUG_REPORT_URL", rel->bug_url, sizeof(rel->bug_url));
free(line);
}
fclose(fp);
/*
* Many distros don't set VERSION or VERSION_ID for their
* rolling, or unstable, branches. However, most set the
* version code name, so we use that as VERSION.
*/
if (!rel->version[0] && codename[0]) {
if (codename[0] == '(')
strlcpy(rel->version, codename, sizeof(rel->version));
else
snprintf(rel->version, sizeof(rel->version), "(%s)", codename);
}
return 0;
}
/* /*
* Read one character from stdin. * Read one character from stdin.
@@ -67,10 +141,19 @@ static int readch(void)
/* /*
* Parse and display a line from /etc/issue * Parse and display a line from /etc/issue
*
* Includes Finit-specific extensions to display information from
* /etc/os-release instead of relying on legacy uname. Activated
* only if /etc/os-release or /usr/lib/os-release exist.
*
* https://www.unix.com/man-page/Linux/8/getty/
* https://www.systutorials.com/docs/linux/man/8-mingetty/
*/ */
static void do_parse(char *line, struct utsname *uts, char *tty) static void parseln(char *line, struct utsname *uts, struct osrel *rel, char *tty, int compat)
{ {
char buf[32] = { 0 };
char *s, *s0; char *s, *s0;
time_t now;
s0 = line; s0 = line;
for (s = line; *s != 0; s++) { for (s = line; *s != 0; s++) {
@@ -79,12 +162,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
dprint(STDOUT_FILENO, s0, s - s0); dprint(STDOUT_FILENO, s0, s - s0);
s0 = s + 2; s0 = s + 2;
switch (*++s) { switch (*++s) {
case 'B':
if (!compat)
dprint(STDOUT_FILENO, rel->bug_url, 0);
break;
case 'D':
if (!compat)
dprint(STDOUT_FILENO, rel->doc_url, 0);
break;
case 'H':
if (!compat)
dprint(STDOUT_FILENO, rel->home_url, 0);
break;
case 'I':
if (!compat)
dprint(STDOUT_FILENO, rel->id, 0);
break;
case 'l': case 'l':
dprint(STDOUT_FILENO, tty, 0); dprint(STDOUT_FILENO, tty, 0);
break; break;
case 'm': case 'm':
dprint(STDOUT_FILENO, uts->machine, 0); dprint(STDOUT_FILENO, uts->machine, 0);
break; break;
case 'N':
if (!compat)
dprint(STDOUT_FILENO, rel->name, 0);
break;
case 'n': case 'n':
dprint(STDOUT_FILENO, uts->nodename, 0); dprint(STDOUT_FILENO, uts->nodename, 0);
break; break;
@@ -94,13 +197,31 @@ static void do_parse(char *line, struct utsname *uts, char *tty)
break; break;
#endif #endif
case 'r': case 'r':
dprint(STDOUT_FILENO, uts->release, 0); if (compat)
dprint(STDOUT_FILENO, uts->release, 0);
else
dprint(STDOUT_FILENO, rel->version_id, 0);
break;
case 'S':
if (!compat)
dprint(STDOUT_FILENO, rel->support_url, 0);
break; break;
case 's': case 's':
dprint(STDOUT_FILENO, uts->sysname, 0); if (compat)
dprint(STDOUT_FILENO, uts->sysname, 0);
else
dprint(STDOUT_FILENO, rel->pretty, 0);
break;
case 't':
now = time(NULL);
ctime_r(&now, buf);
dprint(STDOUT_FILENO, chomp(buf), 0);
break; break;
case 'v': case 'v':
dprint(STDOUT_FILENO, uts->version, 0); if (compat)
dprint(STDOUT_FILENO, uts->version, 0);
else
dprint(STDOUT_FILENO, rel->version, 0);
break; break;
case 0: case 0:
goto leave; goto leave;
@@ -118,28 +239,31 @@ leave:
/* /*
* Parse and display /etc/issue * Parse and display /etc/issue
*/ */
static void do_issue(char *tty) static void issue(char *tty)
{ {
FILE *fp;
char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n"; char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n";
struct utsname uts; struct utsname uts;
struct osrel rel;
int compat;
FILE *fp;
/* /*
* Get data about this machine. * Get data about this machine.
*/ */
uname(&uts); uname(&uts);
compat = osrel(&rel);
fp = fopen("/etc/issue", "r"); fp = fopen("/etc/issue", "r");
if (fp) { if (fp) {
while (fgets(buf, sizeof(buf), fp)) while (fgets(buf, sizeof(buf), fp))
do_parse(buf, &uts, tty); parseln(buf, &uts, &rel, tty, compat);
fclose(fp); fclose(fp);
} else { } else {
do_parse(buf, &uts, tty); parseln(buf, &uts, &rel, tty, compat);
} }
do_parse("\\n login: ", &uts, tty); parseln("\\n login: ", &uts, &rel, tty, compat);
} }
/* /*
@@ -156,7 +280,7 @@ static int get_logname(char *tty, char *name, size_t len)
ch = ' '; ch = ' ';
*name = '\0'; *name = '\0';
while (ch != '\n') { while (ch != '\n') {
do_issue(tty); issue(tty);
np = name; np = name;
while ((ch = readch()) != '\n') { while ((ch = readch()) != '\n') {
@@ -177,15 +301,17 @@ static int get_logname(char *tty, char *name, size_t len)
} }
/* /*
* Execute the login(1) command with the current * Start login(1) with the current username as its argument. It replies
* username as its argument. It will reply to the * to the calling user by typing "Password: " ...
* calling user by typing "Password: "...
*/ */
static int do_login(char *name) static int exec_login(char *name)
{ {
struct stat st; struct stat st;
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); if (passenv)
execl(_PATH_LOGIN, _PATH_LOGIN, "-p", name, NULL);
else
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.
@@ -238,7 +364,7 @@ restart:
if (term && term[0]) if (term && term[0])
setenv("TERM", term, 1); setenv("TERM", term, 1);
return do_login(name); return exec_login(name);
} }
static int usage(int rc) static int usage(int rc)
@@ -252,11 +378,14 @@ int main(int argc, char *argv[])
char *tty, *speed = "0", *term = NULL; char *tty, *speed = "0", *term = NULL;
int c; int c;
while ((c = getopt(argc, argv, "h?")) != EOF) { while ((c = getopt(argc, argv, "h?p")) != EOF) {
switch(c) { switch(c) {
case 'h': case 'h':
case '?': case '?':
return usage(0); return usage(0);
case 'p':
passenv = 1;
break;
default: default:
return usage(1); return usage(1);
} }
+18 -8
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions for finit and its plugins /* Misc. utility functions for finit and its plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -42,6 +42,7 @@
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "log.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "util.h" #include "util.h"
@@ -83,6 +84,9 @@ char *console(void)
void console_init(void) void console_init(void)
{ {
/* Enable line wrap, if disabled previously, e.g., qemu */
dprint(STDOUT_FILENO, "\033[?7h", 5);
log_init(); log_init();
} }
@@ -366,7 +370,8 @@ int print_result(int fail)
int getuser(char *username, char **home) int getuser(char *username, char **home)
{ {
#ifdef ENABLE_STATIC #ifdef ENABLE_STATIC
*home = "/"; /* XXX: Fixme */ if (home)
*home = "/"; /* XXX: Fixme */
return fgetint("/etc/passwd", "x:\n", username); return fgetint("/etc/passwd", "x:\n", username);
#else #else
struct passwd *usr; struct passwd *usr;
@@ -443,8 +448,10 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
gid = 0; gid = 0;
rc = makedir(dir, mode); rc = makedir(dir, mode);
if (rc && errno == EEXIST)
rc = chmod(dir, mode);
if (chown(dir, uid, gid)) if (chown(dir, uid, gid))
_pe("Failed chown(%s, %d, %d)", dir, uid, gid); err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
} }
umask(omask); umask(omask);
@@ -482,7 +489,7 @@ void set_hostname(char **hostname)
done: done:
if (*hostname) { if (*hostname) {
if (sethostname(*hostname, strlen(*hostname))) if (sethostname(*hostname, strlen(*hostname)))
_pe("Failed sethostnaem(%s)", *hostname); err(1, "Failed sethostname(%s)", *hostname);
} }
} }
@@ -496,9 +503,9 @@ void networking(int updown)
return; return;
if (updown) if (updown)
_d("Setting up networking ..."); dbg("Setting up networking ...");
else else
_d("Taking down networking ..."); dbg("Taking down networking ...");
/* Run user network start script if enabled */ /* Run user network start script if enabled */
if (updown && network) { if (updown && network) {
@@ -536,7 +543,7 @@ void networking(int updown)
char buf[256]; char buf[256];
while (fgets(buf, sizeof(buf), pp)) while (fgets(buf, sizeof(buf), pp))
logit(LOG_NOTICE, "network: %s", buf); logit(LOG_NOTICE, "network: %s", chomp(buf));
rc = pclose(pp); rc = pclose(pp);
} }
@@ -557,8 +564,11 @@ done:
/* Hooks that rely on loopback, or basic networking being up. */ /* Hooks that rely on loopback, or basic networking being up. */
if (updown) { if (updown) {
_d("Calling all network up hooks ..."); dbg("Calling all network up hooks ...");
plugin_run_hooks(HOOK_NETWORK_UP); plugin_run_hooks(HOOK_NETWORK_UP);
} else {
dbg("Calling all network down hooks ...");
plugin_run_hooks(HOOK_NETWORK_DN);
} }
} }
+95 -5
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions and C-library extensions for finit and its plugins /* Misc. utility functions and C-library extensions for finit and its plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,8 +25,12 @@
#ifndef FINIT_HELPERS_H_ #ifndef FINIT_HELPERS_H_
#define FINIT_HELPERS_H_ #define FINIT_HELPERS_H_
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
@@ -84,18 +88,58 @@ int run_interactive (char *cmd, char *fmt, ...) __attribute__ ((format (prin
int exec_runtask (char *cmd, char *args[]); int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]); pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]); pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
int run_parts (char *dir, char *cmd); int run_parts (char *dir, char *cmd, const char *env[], int progress);
static inline int create(char *path, mode_t mode, uid_t uid, gid_t gid) /*
* 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)
{ {
if (touch(path) || chmod(path, mode) || chown(path, uid, gid)) { int fd, uid, gid;
_w("Failed creating %s properly.", path);
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 -1;
} }
return 0; 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;
@@ -114,6 +158,52 @@ 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)
{
char *ptr, *str, *copy;
size_t len;
str = copy = strdup(line);
if (!copy)
return NULL;
ptr = strsep(&str, sep);
if (ptr && strcmp(ptr, key)) {
fail:
free(copy);
return NULL;
}
/*
* Check if we should strip optional separator, e.g.,
* 'foo = bar' => '= bar' should be stripped to 'bar'
*/
while (str && strchr(sep, str[0])) {
ptr = strsep(&str, sep);
if (!ptr)
goto fail;
}
ptr = str;
if (*ptr == '"' || *ptr == '\'') {
char *end = &ptr[strlen(ptr) - 1];
char q = *ptr;
while (end > ptr && isspace(*end))
*end-- = 0;
if (*end == q) {
*end = 0;
ptr++;
}
}
len = strlen(ptr) + 1;
memmove(copy, ptr, len);
return realloc(copy, len);
}
#endif /* FINIT_HELPERS_H_ */ #endif /* FINIT_HELPERS_H_ */
/** /**
+497 -135
View File
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -1,6 +1,6 @@
/* New client tool, replaces old /dev/initctl API and telinit tool /* New client tool, replaces old /dev/initctl API and telinit tool
* *
* Copyright (c) 2015-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2015-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,10 +29,15 @@
#include "finit.h" #include "finit.h"
#include "util.h" #include "util.h"
extern char *finit_conf;
extern char *finit_rcsd;
extern int icreate; /* initctl -c */ extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */ extern int iforce; /* initctl -f */
extern int ionce; /* initctl -1 */ extern int ionce; /* initctl -1 */
extern int heading; /* initctl -t */ extern int heading; /* initctl -t */
extern int json; /* initctl -j */
extern int noerr; /* initctl -n */
extern int verbose; /* initctl -v */ extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */ extern int plain; /* initctl -p */
extern int quiet; /* initctl -q */ extern int quiet; /* initctl -q */
+16 -11
View File
@@ -1,7 +1,7 @@
/* inotify watcher for files or directories /* inotify watcher for files or directories
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ int iwatch_init(struct iwatch *iw)
iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (iw->fd < 0) { if (iw->fd < 0) {
_pe("Failed creating inotify descriptor"); warn("Failed creating inotify descriptor");
return -1; return -1;
} }
@@ -64,7 +64,7 @@ int iwatch_init(struct iwatch *iw)
len = sizeof(sz); len = sizeof(sz);
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) { if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len)) {
if (setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz))) if (setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)))
_pe("Failed increasing size of inotify socket"); warn("Failed increasing size of inotify socket");
} }
initialized = 1; initialized = 1;
@@ -87,7 +87,7 @@ void iwatch_exit(struct iwatch *iw)
initialized = 0; initialized = 0;
} }
int iwatch_add(struct iwatch *iw, char *file, uint32_t mask) int iwatch_add1(struct iwatch *iw, char *file, uint32_t mask)
{ {
struct iwatch_path *iwp; struct iwatch_path *iwp;
char *path; char *path;
@@ -97,18 +97,18 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
return -1; return -1;
if (!fexist(file)) { if (!fexist(file)) {
_d("skipping %s: no such file or directory", file); dbg("skipping %s: no such file or directory", file);
return 0; return 0;
} }
_d("adding new watcher for path %s", file); dbg("adding new watcher for path %s", file);
path = strdup(file); path = strdup(file);
if (!path) { if (!path) {
_pe("Out of memory"); warn("Out of memory");
return -1; return -1;
} }
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask); wd = inotify_add_watch(iw->fd, path, mask);
if (wd < 0) { if (wd < 0) {
switch (errno) { switch (errno) {
case EEXIST: case EEXIST:
@@ -124,7 +124,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
/* fallthrough */ /* fallthrough */
default: default:
_pe("Failed adding watcher for %s", path); warn("Failed adding watcher for %s", path);
break; break;
} }
@@ -134,7 +134,7 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
iwp = malloc(sizeof(struct iwatch_path)); iwp = malloc(sizeof(struct iwatch_path));
if (!iwp) { if (!iwp) {
_pe("Failed allocating new `struct iwatch_path`"); warn("Failed allocating new `struct iwatch_path`");
inotify_rm_watch(iw->fd, wd); inotify_rm_watch(iw->fd, wd);
free(path); free(path);
return -1; return -1;
@@ -147,12 +147,17 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
return 0; return 0;
} }
int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
{
return iwatch_add1(iw, file, IWATCH_MASK | mask);
}
int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp) int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp)
{ {
if (!initialized) if (!initialized)
return -1; return -1;
_d("Removing watcher for removed path %s", iwp->path); dbg("Removing watcher for removed path %s", iwp->path);
TAILQ_REMOVE(&iw->iwp_list, iwp, link); TAILQ_REMOVE(&iw->iwp_list, iwp, link);
inotify_rm_watch(iw->fd, iwp->wd); inotify_rm_watch(iw->fd, iwp->wd);
+2 -1
View File
@@ -1,7 +1,7 @@
/* inotify watcher for files or directories /* inotify watcher for files or directories
* *
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com> * Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -59,6 +59,7 @@ struct iwatch {
int iwatch_init (struct iwatch *iw); int iwatch_init (struct iwatch *iw);
void iwatch_exit (struct iwatch *iw); void iwatch_exit (struct iwatch *iw);
int iwatch_add1 (struct iwatch *iw, char *file, uint32_t mask);
int iwatch_add (struct iwatch *iw, char *file, uint32_t mask); int iwatch_add (struct iwatch *iw, char *file, uint32_t mask);
int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp); int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp);
+2 -2
View File
@@ -1,6 +1,6 @@
/* Listens to kernel events like AC power status and manages sys/ conditions /* Listens to kernel events like AC power status and manages sys/ conditions
* *
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -152,7 +152,7 @@ static void init(void)
for (i = 0; i < (int)NELEMS(cond_dirs); i++) { for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) { if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS, warn("Failed creating %s condition directory, %s", COND_SYS,
cond_dirs[i]); cond_dirs[i]);
return; return;
} }
+27 -3
View File
@@ -1,6 +1,6 @@
/* Finit daemon log functions /* Finit daemon log functions
* *
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +25,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <time.h>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
#else #else
@@ -99,6 +100,21 @@ void log_debug(void)
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled"); logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
} }
static const char *l2s(int prio)
{
int level = prio & ~LOG_CONSOLE;
switch (level) {
case LOG_ERR: return "ERR";
case LOG_WARNING: return "WRN";
case LOG_NOTICE: return "NOT";
case LOG_INFO: return "NFO";
case LOG_DEBUG: return "DBG";
default: break;
}
return "UNK";
}
/* /*
* 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.
@@ -119,8 +135,16 @@ void logit(int prio, const char *fmt, ...)
goto done; goto done;
if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) { if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
vfprintf(stderr, fmt, ap); static char buf[512];
fputs("\n", stderr); time_t now;
size_t pos;
now = time(NULL);
pos = (int)strftime(buf, sizeof(buf), "%FT%T", localtime(&now));
pos += snprintf(&buf[pos], sizeof(buf) - pos, " [%s]: ", l2s(prio));
vsnprintf(&buf[pos], sizeof(buf) - pos, fmt, ap);
strlcat(buf, "\n", sizeof(buf));
fputs(buf, stderr);
goto done; goto done;
} }
+42 -13
View File
@@ -1,6 +1,6 @@
/* Finit daemon log functions /* Finit daemon log functions
* *
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -31,26 +31,55 @@
#define LOG_CONSOLE (14<<3) #define LOG_CONSOLE (14<<3)
#endif #endif
#ifndef __FINIT__
#include <err.h>
#include <stdarg.h>
#include <stdio.h>
extern int debug;
static __attribute__ ((format (printf, 1, 2))) inline void dbg(char *fmt, ...)
{
va_list ap;
if (!debug)
return;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#define info(fmt, args...) warnx(fmt, ##args)
#define note(fmt, args...) warnx(fmt, ##args)
#else
/* /*
* Developer error and debug messages, otherwise --> use logit() <-- * General log macros, similar to those used by initctl. Initially intended
* ~~~~~~~~~~~ * only for bridging client.c in Finit and initctl.
* All of these prepend the function, so only use for critical warnings */
* errors and debug messages. For all other user messages, see logit() #define dbg(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
* #define info(fmt, args...) logit(LOG_INFO, "%s():" fmt, __func__, ##args)
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages, #define note(fmt, args...) logit(LOG_NOTICE, "%s():" fmt, __func__, ##args)
* use `initctl debug` or add `debug` to the kernel cmdline. #define warnx(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
#define warn(fmt, args...) logit(LOG_WARNING, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
#define errx(rc, fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
#define err(rc, fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
/*
* DEPRECATED developer error and debug messages, please migrate to above!
* Will be removed without further warning in a future Finit release.
*/ */
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args) #define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args) #define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
#define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args) #define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
#define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno)) #define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s", __func__, ##args, strerror(errno))
void log_init (void); void log_init (void);
void log_exit (void); void log_exit (void);
void log_debug (void); void log_debug(void);
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)));
#endif
#endif /* FINIT_LOG_H_ */ #endif /* FINIT_LOG_H_ */
+8 -3
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -208,8 +208,13 @@ int main(int argc, char *argv[])
} }
} }
if (!ident) if (!ident) {
ident = getenv("LOGNAME") ?: getenv("USER"); ident = getenv("LOGNAME");
if (!ident)
ident = getenv("USER");
if (!ident)
ident = PACKAGE_NAME;
}
if (optind < argc) { if (optind < argc) {
size_t pos = 0, len = sizeof(buf); size_t pos = 0, len = sizeof(buf);
+3 -4
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -49,7 +49,6 @@ static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
*/ */
int logrotate(char *file, int num, off_t sz) int logrotate(char *file, int num, off_t sz)
{ {
int cnt;
struct stat st; struct stat st;
if (stat(file, &st)) if (stat(file, &st))
@@ -60,6 +59,7 @@ int logrotate(char *file, int num, off_t sz)
size_t len = strlen(file) + 10 + 1; size_t len = strlen(file) + 10 + 1;
char ofile[len]; char ofile[len];
char nfile[len]; char nfile[len];
int cnt;
/* First age zipped log files */ /* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) { for (cnt = num; cnt > 2; cnt--) {
@@ -97,8 +97,7 @@ int logrotate(char *file, int num, off_t sz)
} else { } else {
fallback: fallback:
if (truncate(file, 0)) if (truncate(file, 0))
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s", syslog(LOG_ERR, "Failed truncating %s: %s", file, strerror(errno));
file, strerror(errno));
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* Handle MD arrays /* Handle MD arrays
* *
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+3 -2
View File
@@ -1,6 +1,6 @@
/* Mount and unmount helpers /* Mount and unmount helpers
* *
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -97,13 +97,14 @@ static int unmount(const char *target)
{ {
int rc; int rc;
dbg("Unmounting %s", target);
rc = umount(target); rc = umount(target);
if (rc) { if (rc) {
if (errno != EBUSY) if (errno != EBUSY)
print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno)); print(2, "Failed unmounting %s, error %d: %s", target, errno, strerror(errno));
return rc; return rc;
} }
print(0, "Unmounting %s", target);
return 0; return 0;
} }
+6 -2
View File
@@ -1,7 +1,7 @@
/* PID and PID file helpers /* PID and PID file helpers
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -149,6 +149,8 @@ int pid_file_create(svc_t *svc)
int pid_file_set(svc_t *svc, char *file, int not) int pid_file_set(svc_t *svc, char *file, int not)
{ {
char buf[sizeof(svc->pidfile)];
if (!file) { if (!file) {
file = pid_file(svc); file = pid_file(svc);
if (!file) if (!file)
@@ -165,7 +167,9 @@ int pid_file_set(svc_t *svc, char *file, int not)
not = 1; not = 1;
} }
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not); de_dotdot(file);
pid_runpath(file, buf, sizeof(buf));
strlcpy(&svc->pidfile[not], buf, sizeof(svc->pidfile) - not);
if (not) if (not)
svc->pidfile[0] = '!'; svc->pidfile[0] = '!';
+6 -6
View File
@@ -1,7 +1,7 @@
/* PID and PID file helpers /* PID and PID file helpers
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -51,6 +51,7 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
{ {
static char *prefix = "/var/run"; static char *prefix = "/var/run";
static int unknown = 1; static int unknown = 1;
char *ptr;
int rc; int rc;
if (unknown) { if (unknown) {
@@ -59,14 +60,13 @@ static inline char *pid_runpath(const char *file, char *path, size_t len)
unknown = 0; unknown = 0;
} }
if (!strncmp(file, "/var/run/", 9)) ptr = strstr(file, "/run/");
file += 9; if (ptr)
else if (!strncmp(file, "/run/", 5)) file = ptr + 5;
file += 5;
rc = paste(path, len, prefix, file); rc = paste(path, len, prefix, file);
if (rc < 0 || (size_t)rc >= len) if (rc < 0 || (size_t)rc >= len)
_e("File path '%s' truncated, should end with '%s'", path, file); err(1, "File path '%s' truncated, should end with '%s'", path, file);
return path; return path;
} }
+144 -27
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit /* Plugin based services architecture for finit
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -42,6 +42,7 @@
#include "plugin.h" #include "plugin.h"
#include "private.h" #include "private.h"
#include "service.h" #include "service.h"
#include "sig.h"
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0) #define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
#define SEARCH_PLUGIN(str) \ #define SEARCH_PLUGIN(str) \
@@ -50,7 +51,8 @@
return p; \ return p; \
} }
static char *plugpath = NULL; /* Set by first load. */ static int plugloaded = 0; /* Set while plugins are loaded */
static char *plugpath = NULL; /* Set by first load. */
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins); static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
#ifndef ENABLE_STATIC #ifndef ENABLE_STATIC
@@ -95,7 +97,7 @@ int plugin_register(plugin_t *plugin)
/* Already registered? */ /* Already registered? */
if (plugin_find(plugin->name)) { if (plugin_find(plugin->name)) {
_d("... %s already loaded", plugin->name); dbg("... %s already loaded", plugin->name);
free(plugin->name); free(plugin->name);
return 0; return 0;
@@ -120,10 +122,10 @@ int plugin_unregister(plugin_t *plugin)
#ifndef ENABLE_STATIC #ifndef ENABLE_STATIC
TAILQ_REMOVE(&plugins, plugin, link); TAILQ_REMOVE(&plugins, plugin, link);
_d("%s exiting ...", plugin->name); dbg("%s exiting ...", plugin->name);
free(plugin->name); free(plugin->name);
#else #else
_d("Finit built statically, cannot unload %s ...", plugin->name); dbg("Finit built statically, cannot unload %s ...", plugin->name);
#endif #endif
return 0; return 0;
@@ -158,8 +160,8 @@ plugin_t *plugin_find(char *name)
SEARCH_PLUGIN(name); SEARCH_PLUGIN(name);
if (plugpath && name[0] != '/') { if (plugpath && name[0] != '/') {
int noext;
char path[CMD_SIZE]; char path[CMD_SIZE];
int noext;
noext = strcmp(name + strlen(name) - 3, ".so"); noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(path, sizeof(path), "%s%s%s%s", plugpath, snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
@@ -195,22 +197,73 @@ int plugin_exists(hook_point_t no)
return 0; return 0;
} }
#if defined(HAVE_HOOK_SCRIPTS_PLUGIN) && defined(PLUGIN_HOOK_SCRIPTS_PATH)
#define CHOOSE(x, y) y
static const char *hscript_paths[] = HOOK_TYPES;
#undef CHOOSE
void plugin_script_run(hook_point_t no)
{
const char *hook_name = hscript_paths[no];
const char *env[] = {
"FINIT_HOOK_NAME", hook_name,
"FINIT_SHUTDOWN", NULL,
NULL,
};
char path[CMD_SIZE] = "";
strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path));
strlcat(path, hook_name + 4, sizeof(path));
if (no >= HOOK_SHUTDOWN) {
switch (halt) {
case SHUT_OFF:
env[3] = "poweroff";
break;
case SHUT_HALT:
env[3] = "halt";
break;
case SHUT_REBOOT:
env[3] = "reboot";
break;
}
} else
env[2] = NULL;
run_parts(path, NULL, env, 0);
}
#else
void plugin_script_run(hook_point_t no)
{
(void)no;
}
#endif
/* Some hooks are called with a fixed argument */ /* Some hooks are called with a fixed argument */
void plugin_run_hook(hook_point_t no, void *arg) void plugin_run_hook(hook_point_t no, void *arg)
{ {
plugin_t *p, *tmp; plugin_t *p, *tmp;
#ifdef HAVE_HOOK_SCRIPTS_PLUGIN
if (!cond_is_available() && !plugloaded) {
dbg("conditions not available, calling script based hooks only!");
plugin_script_run(no);
}
#endif
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) { if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL"); dbg("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL");
p->hook[no].cb(arg ? arg : p->hook[no].arg); p->hook[no].cb(arg ? arg : p->hook[no].arg);
} }
} }
/* Conditions are stored in /run, so don't try to signal /*
* Conditions are stored in /run, so don't try to signal
* conditions for any hooks before filesystems have been * conditions for any hooks before filesystems have been
* mounted. */ * mounted.
if (no >= HOOK_MOUNT_ERROR) */
if (cond_is_available() && no >= HOOK_BASEFS_UP && no <= HOOK_SHUTDOWN)
cond_set_oneshot(hook_cond[no]); cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK); service_step_all(SVC_TYPE_RUNTASK);
@@ -235,7 +288,7 @@ static void generic_io_cb(uev_t *w, void *arg, int events)
/* Stop watcher, callback may close descriptor on us ... */ /* Stop watcher, callback may close descriptor on us ... */
uev_io_stop(w); uev_io_stop(w);
_d("Calling I/O %s from runloop...", basename(p->name)); dbg("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, w->fd, events); p->io.cb(p->io.arg, w->fd, events);
/* Update fd, may be changed by plugin callback, e.g., if FIFO */ /* Update fd, may be changed by plugin callback, e.g., if FIFO */
@@ -248,9 +301,9 @@ int plugin_io_init(plugin_t *p)
if (!is_io_plugin(p)) if (!is_io_plugin(p))
return 0; return 0;
_d("Initializing plugin %s for I/O", basename(p->name)); dbg("Initializing plugin %s for I/O", basename(p->name));
if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) { if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) {
_e("Failed setting up I/O plugin %s", basename(p->name)); warn("Failed setting up I/O plugin %s", basename(p->name));
return 1; return 1;
} }
@@ -260,8 +313,8 @@ int plugin_io_init(plugin_t *p)
/* Setup any I/O callbacks for plugins that use them */ /* Setup any I/O callbacks for plugins that use them */
static int init_plugins(uev_ctx_t *ctx) static int init_plugins(uev_ctx_t *ctx)
{ {
int fail = 0;
plugin_t *p, *tmp; plugin_t *p, *tmp;
int fail = 0;
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (plugin_io_init(p)) if (plugin_io_init(p))
@@ -302,16 +355,18 @@ static int load_one(char *path, char *name)
noext = strcmp(name + strlen(name) - 3, ".so"); noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : ""); snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
_d("Loading plugin %s ...", basename(sofile)); dbg("Loading plugin %s ...", sofile);
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL); handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
if (!handle) { if (!handle) {
_e("Failed loading plugin %s: %s", sofile, dlerror()); char *error = dlerror();
warn("Failed loading plugin %s: %s", sofile, error ? error : "unknown error");
return 1; return 1;
} }
plugin = TAILQ_LAST(&plugins, plugin_head); plugin = TAILQ_LAST(&plugins, plugin_head);
if (!plugin) { if (!plugin) {
_e("Plugin %s failed to register, unloading from memory", sofile); warn("Plugin %s failed to register, unloading from memory", sofile);
dlclose(handle); dlclose(handle);
return 1; return 1;
} }
@@ -338,9 +393,9 @@ static void check_plugin_depends(plugin_t *plugin)
int i; int i;
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) { for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
// _d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]); // dbg("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
if (plugin_find(plugin->depends[i])) { if (plugin_find(plugin->depends[i])) {
// _d("OK plugin %s was already loaded.", plugin->depends[i]); // dbg("OK plugin %s was already loaded.", plugin->depends[i]);
continue; continue;
} }
@@ -350,13 +405,15 @@ static void check_plugin_depends(plugin_t *plugin)
static int load_plugins(char *path) static int load_plugins(char *path)
{ {
struct dirent *entry;
int fail = 0; int fail = 0;
DIR *dp; DIR *dp;
struct dirent *entry;
dp = opendir(path); dp = opendir(path);
if (!dp) { if (!dp) {
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno)); if (errno == ENOENT)
return 0;
warn("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1; return 1;
} }
plugpath = path; plugpath = path;
@@ -381,14 +438,70 @@ static int load_plugins(char *path)
} }
#endif /* ENABLE_STATIC */ #endif /* ENABLE_STATIC */
int plugin_list(char *buf, size_t len)
{
#ifndef ENABLE_STATIC
plugin_t *p, *tmp;
buf[0] = 0;
PLUGIN_ITERATOR(p, tmp) {
if (buf[0])
strlcat(buf, " ", len);
strlcat(buf, p->name, len);
}
#else
buf[0] = 0;
#endif
return 0;
}
int plugin_deps(char *buf, size_t len)
{
#ifndef ENABLE_STATIC
plugin_t *p;
p = plugin_find(buf);
buf[0] = 0;
if (p) {
int i;
for (i = 0; i < PLUGIN_DEP_MAX; i++) {
if (!p->depends[i])
continue;
if (buf[0])
strlcat(buf, " ", len);
strlcat(buf, p->depends[i], len);
}
}
#else
buf[0] = 0;
#endif
return 0;
}
int plugin_init(uev_ctx_t *ctx) int plugin_init(uev_ctx_t *ctx)
{ {
int fail = 1; load_plugins(PLUGIN_PATH);
if (!load_plugins(PLUGIN_PATH)) #ifdef EXTERNAL_PLUGIN_PATH
fail = init_plugins(ctx); char *paths = strdup(EXTERNAL_PLUGIN_PATH);
char *path;
return fail; if (paths) {
dbg("Loading external plugins from %s ...", paths);
path = strtok(paths, ":");
while (path) {
load_plugins(path);
path = strtok(NULL, ":");
}
free(paths);
}
plugloaded = 1;
#endif
return init_plugins(ctx);
} }
void plugin_exit(void) void plugin_exit(void)
@@ -397,9 +510,13 @@ void plugin_exit(void)
plugin_t *p, *tmp; plugin_t *p, *tmp;
PLUGIN_ITERATOR(p, tmp) { PLUGIN_ITERATOR(p, tmp) {
if (dlclose(p->handle)) if (dlclose(p->handle)) {
_e("Failed: unloading plugin %s: %s", p->name, dlerror()); char *error = dlerror();
warn("Failed unloading plugin %s: %s", p->name, error ? error : "unknown error");
}
} }
plugloaded = 0;
#endif #endif
} }
+8 -1
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit /* Plugin based services architecture for finit
* *
* Copyright (c) 2012-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -72,6 +72,7 @@
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \ CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \ CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \ CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
CHOOSE(HOOK_SVC_PLUGIN, "hook/svc/plugin"), \
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \ CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \ CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \ CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
@@ -81,7 +82,10 @@
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \ CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\ \
/* Shutdown hooks, runlevel [06] */ \ /* Shutdown hooks, runlevel [06] */ \
CHOOSE(HOOK_NETWORK_DN, "hook/net/down"), \
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \ CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
CHOOSE(HOOK_SVC_DN, "hook/svc/down"), \
CHOOSE(HOOK_SYS_DN, "hook/sys/down"), \
CHOOSE(HOOK_MAX_NUM, "nop") \ CHOOSE(HOOK_MAX_NUM, "nop") \
} }
@@ -146,6 +150,9 @@ typedef struct plugin {
/* Public plugin API */ /* Public plugin API */
int plugin_io_init (plugin_t *plugin); int plugin_io_init (plugin_t *plugin);
int plugin_list (char *buf, size_t len);
int plugin_deps (char *buf, size_t len);
int plugin_register (plugin_t *plugin); int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin); int plugin_unregister (plugin_t *plugin);
+25 -11
View File
@@ -1,7 +1,7 @@
/* Private header file for main finit daemon, not for plugins /* Private header file for main finit daemon, not for plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,20 +29,34 @@
#include "plugin.h" #include "plugin.h"
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */ #define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
extern int service_interval; #define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
int api_init (uev_ctx_t *ctx); #define IS_RESERVED_RUNLEVEL(l) (l == 0 || l == 6 || l == INIT_LEVEL)
int api_exit (void);
void service_monitor (pid_t lost, int status); extern char *finit_conf;
extern char *finit_rcsd;
extern svc_t *wdog;
extern int service_interval;
extern char *fsck_mode;
extern char *fsck_repair;
const char *plugin_hook_str(hook_point_t no); int api_init (uev_ctx_t *ctx);
int plugin_exists (hook_point_t no); int api_exit (void);
void plugin_run_hook (hook_point_t no, void *arg); void conf_flush_events(void);
void plugin_run_hooks (hook_point_t no);
int plugin_init (uev_ctx_t *ctx); void service_monitor (pid_t lost, int status);
void plugin_exit (void); void service_notify_cb(uev_t *w, void *arg, int events);
const char *plugin_hook_str (hook_point_t no);
int plugin_exists (hook_point_t no);
void plugin_run_hook (hook_point_t no, void *arg);
void plugin_run_hooks (hook_point_t no);
void plugin_script_run(hook_point_t no);
int plugin_init (uev_ctx_t *ctx);
void plugin_exit (void);
#endif /* FINIT_PRIVATE_H_ */ #endif /* FINIT_PRIVATE_H_ */
+22 -10
View File
@@ -1,6 +1,6 @@
/* reboot, halt, poweroff, shutdown, suspend for finit /* reboot, halt, poweroff, shutdown, suspend for finit
* *
* Copyright (c) 2009-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2009-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -43,6 +43,8 @@ static cmd_t cmd = CMD_UNKNOWN;
static char *msg = NULL; static char *msg = NULL;
/* initctl API */ /* initctl API */
extern int timeout;
extern int do_reboot (char *arg); extern int do_reboot (char *arg);
extern int do_halt (char *arg); extern int do_halt (char *arg);
extern int do_poweroff(char *arg); extern int do_poweroff(char *arg);
@@ -103,12 +105,13 @@ static int usage(int rc)
{ {
fprintf(stderr, "Usage: %s [OPTIONS]\n\n" fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
"Options:\n" "Options:\n"
" --help This help text\n" " -?, --help This help text\n"
" -f, --force Force unsafe %s now, do not contact the init system.\n" " -f, --force Force unsafe %s now, do not contact the init system.\n"
" --halt Halt system, regardless of how the command is called.\n" " --halt Halt system, regardless of how the command is called.\n"
" -h Halt or power off after shutdown.\n" " -h Halt or power off after shutdown.\n"
" -P, --poweroff Power-off system, regardless of how the command is called.\n" " -P, --poweroff Power-off system, regardless of how the command is called.\n"
" -r, --reboot Reboot system, regardless of how the command is called.\n" " -r, --reboot Reboot system, regardless of how the command is called.\n"
" -t, --timeout=SEC Force reboot/shutdown after a given timeout\n"
"\n", prognm, msg); "\n", prognm, msg);
return rc; return rc;
@@ -116,20 +119,22 @@ static int usage(int rc)
int reboot_main(int argc, char *argv[]) int reboot_main(int argc, char *argv[])
{ {
int c, force = 0;
struct option long_options[] = { struct option long_options[] = {
{"help", 0, NULL, 'h'}, {"help", 0, NULL, '?'},
{"force", 0, NULL, 'f'}, {"force", 0, NULL, 'f'},
{"halt", 0, NULL, 'H'}, {"halt", 0, NULL, 'H'},
{"poweroff", 0, NULL, 'p'}, {"poweroff", 0, NULL, 'p'},
{"reboot", 0, NULL, 'r'}, {"reboot", 0, NULL, 'r'},
{"timeout", 1, NULL, 't'},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
int c, force = 0;
int tmp;
/* Initial command taken from program name */ /* Initial command taken from program name */
transform(prognm); transform(prognm);
while ((c = getopt_long(argc, argv, "h?fHPpr", long_options, NULL)) != EOF) { while ((c = getopt_long(argc, argv, "h?fHPprt:", long_options, NULL)) != EOF) {
switch(c) { switch(c) {
case '?': case '?':
return usage(0); return usage(0);
@@ -152,6 +157,13 @@ int reboot_main(int argc, char *argv[])
cmd = CMD_REBOOT; cmd = CMD_REBOOT;
break; break;
case 't':
tmp = atoi(optarg);
if (tmp < 1)
errx(1, "Invalid forced timeout value.");
timeout = tmp;
break;
default: default:
return usage(1); return usage(1);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* Work queue helper functions /* Work queue helper functions
* *
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,6 +1,6 @@
/* Work queue helper functions /* Work queue helper functions
* *
* Copyright (c) 2018-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2018-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+113 -55
View File
@@ -1,6 +1,6 @@
/* List and enable/disable service configurations /* List and enable/disable service configurations
* *
* Copyright (c) 2017-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2017-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -66,7 +66,7 @@ static int calc_width(char *arr[], size_t len)
return width; return width;
} }
static void do_list(const char *path) static void do_list(const char *path, char *title, int comma)
{ {
static int once = 0; static int once = 0;
int width, num, prev; int width, num, prev;
@@ -78,6 +78,7 @@ static void do_list(const char *path)
if (!fexist(path)) if (!fexist(path))
return; return;
dir = strdupa(path); dir = strdupa(path);
if (!dir) if (!dir)
return; return;
@@ -88,6 +89,11 @@ static void do_list(const char *path)
else else
ptr = dir; ptr = dir;
if (json) {
printf("%s\"%s\": \"%s\"", comma ? "," :"", title, path);
return;
}
if (heading) if (heading)
print_header("%s%s ", once ? "\n" : "", dir); print_header("%s%s ", once ? "\n" : "", dir);
puts(ptr); puts(ptr);
@@ -101,7 +107,7 @@ static void do_list(const char *path)
if (gl.gl_pathc <= 0) if (gl.gl_pathc <= 0)
goto done; goto done;
if (plain) { if (plain && !json) {
if (heading) if (heading)
print_header("%s%s ", once ? "\n" : "", path); print_header("%s%s ", once ? "\n" : "", path);
once++; once++;
@@ -119,8 +125,10 @@ static void do_list(const char *path)
goto done; goto done;
} }
if (heading) if (heading && !json)
print_header("%s ", path); print_header("%s ", path);
if (json)
printf("%s\"%s\": [", comma ? "," : "", title);
width = calc_width(gl.gl_pathv, gl.gl_pathc); width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0) if (width <= 0)
@@ -132,17 +140,24 @@ static void do_list(const char *path)
prev = 0; prev = 0;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
if (i > 0 && !(i % num)) { if (i > 0 && !(i % num) && !json) {
puts(""); puts("");
prev = 0; prev = 0;
} }
if (prev) if (prev)
printf(" "); printf("%s", json ? "," : " ");
printf("%-*s", width, gl.gl_pathv[i]); if (json)
printf("\"%s\"", gl.gl_pathv[i]);
else
printf("%-*s", width, gl.gl_pathv[i]);
prev++; prev++;
} }
puts("\n");
if (json)
fputs("]", stdout);
else
puts("\n");
done: done:
globfree(&gl); globfree(&gl);
@@ -151,29 +166,40 @@ done:
int serv_list(char *arg) int serv_list(char *arg)
{ {
char path[256]; char path[256];
int pre = 0;
if (arg && arg[0]) { if (arg && arg[0]) {
paste(path, sizeof(path), FINIT_RCSD, arg); paste(path, sizeof(path), finit_rcsd, arg);
if (fisdir(path)) { if (fisdir(path)) {
do_list(path); if (json)
fputs("[", stdout);
do_list(path, NULL, 0);
if (json)
fputs("]\n", stdout);
return 0; return 0;
} }
/* fall back to list all */ /* fall back to list all */
} }
paste(path, sizeof(path), FINIT_RCSD, "available"); if (json)
fputs("{", stdout);
paste(path, sizeof(path), finit_rcsd, "available");
if (fisdir(path)) if (fisdir(path))
do_list(path); do_list(path, "available", pre++);
paste(path, sizeof(path), FINIT_RCSD, "enabled"); paste(path, sizeof(path), finit_rcsd, "enabled");
if (fisdir(path)) if (fisdir(path))
do_list(path); do_list(path, "enabled", pre++);
if (fisdir(FINIT_RCSD)) if (fisdir(finit_rcsd))
do_list(FINIT_RCSD); do_list(finit_rcsd, "static", pre++);
if (fexist(FINIT_CONF)) if (fexist(finit_conf))
do_list(FINIT_CONF); do_list(finit_conf, "finit.conf", pre++);
if (json)
fputs("}\n", stdout);
return 0; return 0;
} }
@@ -197,7 +223,7 @@ static char *conf(char *path, size_t len, char *name, int creat)
char corr[40]; char corr[40];
if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) { if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) {
strlcpy(path, FINIT_CONF, len); strlcpy(path, finit_conf, len);
return path; return path;
} }
@@ -206,85 +232,117 @@ static char *conf(char *path, size_t len, char *name, int creat)
name = corr; name = corr;
} }
if (!fisdir(FINIT_RCSD)) if (!fisdir(finit_rcsd))
return NULL; return NULL;
paste(path, len, FINIT_RCSD, "available/"); paste(path, len, finit_rcsd, "available/");
if (!fisdir(path)) { if (!fisdir(path)) {
if (creat && mkdir(path, 0755) && errno != EEXIST) if (creat && mkdir(path, 0755) && errno != EEXIST)
return NULL; return NULL;
else else
paste(path, len, FINIT_RCSD, name); paste(path, len, finit_rcsd, name);
} else } else
strlcat(path, name, len); strlcat(path, name, len);
/* fall back to static service unless edit/create */ /* fall back to static service unless edit/create */
if (!creat && !fexist(path)) if (!creat && !fexist(path))
paste(path, len, FINIT_RCSD, name); paste(path, len, finit_rcsd, name);
return path; return path;
} }
int serv_enable(char *arg) int serv_enable(char *arg)
{ {
char corr[40]; char *serv, *ptr, *src, *dst;
char path[256]; size_t len;
int ena; int rc;
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
WARNX("missing argument to enable, may be one of:"); WARNX("missing argument to enable, may be one of:");
return serv_list("available"); return serv_list("available");
} }
if (!strstr(arg, ".conf")) { len = strlen(arg);
snprintf(corr, sizeof(corr), "%s.conf", arg); serv = alloca(len + 6);
arg = corr; if (!serv)
} ERR(71, "failed allocating stack");
if (chdir(FINIT_RCSD)) strlcpy(serv, arg, len + 6);
ERR(72, "failed cd %s", FINIT_RCSD); if (len < 6 || strcmp(&serv[len - 5], ".conf"))
strlcat(serv, ".conf", len + 6);
if (chdir(finit_rcsd))
ERR(72, "failed cd %s", finit_rcsd);
if (icreate && mkdir("enabled", 0755) && EEXIST != errno) if (icreate && mkdir("enabled", 0755) && EEXIST != errno)
ERR(73, "failed creating %s/enabled directory", FINIT_RCSD); ERR(73, "failed creating %s/enabled directory", finit_rcsd);
ena = !chdir("enabled"); /* System *may* have enabled/ dir. */
snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg); if (asprintf(&src, "%s/available/%s", finit_rcsd, serv) == -1)
if (!fexist(path)) goto fail;
ERRX(72, "cannot find %s", conf(path, sizeof(path), arg, 0)); if (asprintf(&dst, "%s/enabled/%s", finit_rcsd, serv) == -1) {
free(src);
fail:
ERR(71, "failed allocating memory");
}
if (fexist(arg)) if (fexist(dst)) {
ERRX(1, "%s already enabled", arg); WARNX("%s already enabled", arg);
rc = noerr ? 0 : 1;
goto done;
}
return symlink(path, arg) != 0; /* check for template instantiation */
ptr = strchr(src, '@');
if (ptr)
strcpy(++ptr, ".conf");
if (!fexist(src)) {
WARNX("cannot find %s%s", ptr ? "template ": "", src);
rc = 72;
goto done;
}
rc = symlink(src, dst);
if (rc)
WARN("failed enabling %s", arg);
done:
free(src);
free(dst);
return rc;
} }
int do_disable(char *arg, int check) int do_disable(char *arg, int check)
{ {
struct stat st; struct stat st;
char corr[40]; size_t len;
char *serv;
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
WARNX("missing argument to disable, may be one of:"); WARNX("missing argument to disable, may be one of:");
return serv_list("enabled"); return serv_list("enabled");
} }
if (!strstr(arg, ".conf")) { len = strlen(arg);
snprintf(corr, sizeof(corr), "%s.conf", arg); serv = alloca(len + 6);
arg = corr; if (!serv)
} ERR(71, "failed allocating stack");
if (chdir(FINIT_RCSD)) strlcpy(serv, arg, len + 6);
ERR(72, "failed cd %s", FINIT_RCSD); if (len < 6 || strcmp(&serv[len - 5], ".conf"))
strlcat(serv, ".conf", len + 6);
if (chdir(finit_rcsd))
ERR(72, "failed cd %s", finit_rcsd);
if (chdir("enabled")) /* System *may* have enabled/ dir. */ if (chdir("enabled")) /* System *may* have enabled/ dir. */
dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno)); dbg("Failed changing to %s/enabled/: %s", finit_rcsd, strerror(errno));
if (check && stat(arg, &st)) if (check && stat(serv, &st))
ERRX(6, "%s not (an) enabled (service).", arg); ERRX(noerr ? 0 : 6, "%s not (an) enabled (service).", serv);
if (check && (st.st_mode & S_IFMT) == S_IFLNK) if (check && (st.st_mode & S_IFMT) == S_IFLNK)
ERRX(1, "cannot disable %s, not a symlink.", arg); ERRX(noerr ? 0 : 1, "cannot disable %s, not a symlink.", serv);
return remove(arg) != 0; return remove(serv) != 0;
} }
int serv_disable(char *arg) int serv_disable(char *arg)
@@ -309,7 +367,7 @@ int serv_touch(char *arg)
if (is_builtin(arg)) if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg); ERRX(4, "%s is a built-in service.", arg);
strlcpy(path, FINIT_CONF, sizeof(path)); strlcpy(path, finit_conf, sizeof(path));
fn = path; fn = path;
} }
@@ -391,7 +449,7 @@ static int do_edit(char *arg, int creat)
int serv_edit(char *arg) int serv_edit(char *arg)
{ {
if (!arg || !arg[0]) { if (!arg || !arg[0]) {
if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF)) if (!yorn("Do you want to edit %s (y/N)? ", finit_conf))
return 0; return 0;
arg = ""; arg = "";
} }
@@ -449,7 +507,7 @@ int serv_delete(char *arg)
if (!fn) { if (!fn) {
if (is_builtin(arg)) if (is_builtin(arg))
ERRX(4, "%s is a built-in service.", arg); ERRX(4, "%s is a built-in service.", arg);
ERRX(72, FINIT_RCSD " missing on system."); ERRX(72, "%s missing on system.", finit_rcsd);
} }
if (!fexist(fn)) if (!fexist(fn))
+1 -1
View File
@@ -1,6 +1,6 @@
/* List and enable/disable service configurations /* List and enable/disable service configurations
* *
* Copyright (c) 2017-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2017-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+687 -182
View File
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -1,7 +1,7 @@
/* Finit service monitor, task starter and generic API for managing svc_t /* Finit service monitor, task starter and generic API for managing svc_t
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -34,17 +34,23 @@ void service_unregister (svc_t *svc);
void service_runtask_clean (void); void service_runtask_clean (void);
void service_reload_dynamic (void); void service_reload_dynamic (void);
void service_update_rdeps (void); void service_update_rdeps (void);
void service_mark_unavail (void);
void service_ready_script (svc_t *svc); /* XXX: only for pidfile plugin before notify framework */
int service_timeout_after (svc_t *svc, int timeout, void (*cb)(svc_t *svc)); int service_timeout_after (svc_t *svc, int timeout, void (*cb)(svc_t *svc));
int service_timeout_cancel (svc_t *svc); int service_timeout_cancel (svc_t *svc);
void service_forked (svc_t *svc); void service_forked (svc_t *svc);
void service_ready (svc_t *svc);
int service_stop (svc_t *svc);
int service_step (svc_t *svc); int service_step (svc_t *svc);
void service_step_all (int types); void service_step_all (int types);
void service_worker (void *unused); void service_worker (void *unused);
int service_completed (void); int service_completed (void);
void service_notify_reconf (void);
void service_init (void); void service_init (void);
+70 -26
View File
@@ -1,7 +1,7 @@
/* Signal management - Be conservative with what finit responds to! /* Signal management - Be conservative with what finit responds to!
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -97,8 +97,6 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
extern svc_t *wdog;
/* /*
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT", * Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to * "=POWERDOWN", or "" to cancel shutdown, before requesting change to
@@ -231,12 +229,12 @@ void do_iterate_proc(int (*cb)(int, void *), void *data)
if (fgets(file, sizeof(file), fp)) { if (fgets(file, sizeof(file), fp)) {
if (strstr(file, "gdbserver")) if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file); dbg("Skipping %s ...", file);
else if (file[0] == '@') else if (file[0] == '@')
_d("Skipping %s ...", &file[1]); dbg("Skipping %s ...", &file[1]);
else else
if (cb(pid, data)) { if (cb(pid, data)) {
_d("PID %d is still alive (%s)", pid, file); dbg("PID %d is still alive (%s)", pid, file);
fclose(fp); fclose(fp);
break; break;
} }
@@ -302,6 +300,7 @@ void do_shutdown(shutop_t op)
sched_setscheduler(1, SCHED_RR, &sched_param); sched_setscheduler(1, SCHED_RR, &sched_param);
} }
halt = op;
if (sdown) if (sdown)
run_interactive(sdown, "Calling shutdown hook: %s", sdown); run_interactive(sdown, "Calling shutdown hook: %s", sdown);
@@ -318,14 +317,19 @@ void do_shutdown(shutop_t op)
do_iterate_proc(kill_cb, &signo); do_iterate_proc(kill_cb, &signo);
} }
/* Exit plugins and API gracefully */ /* Exit plugins gracefully */
plugin_exit(); plugin_exit();
api_exit();
/* We can no longer act on conditions, activate script runner */
cond_exit();
/* Reap 'em */ /* Reap 'em */
while (waitpid(-1, NULL, WNOHANG) > 0) while (waitpid(-1, NULL, WNOHANG) > 0)
; ;
/* All services and (non-critical) processes have stopped. */
plugin_script_run(HOOK_SVC_DN);
if (in_cont) { if (in_cont) {
if (osheading) if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading); logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading);
@@ -351,6 +355,7 @@ void do_shutdown(shutop_t op)
} }
/* Unmount any tmpfs before unmounting swap ... */ /* Unmount any tmpfs before unmounting swap ... */
print(0, "Unmounting filesystems ...");
unmount_tmpfs(); unmount_tmpfs();
fs_swapoff(); fs_swapoff();
@@ -371,8 +376,27 @@ void do_shutdown(shutop_t op)
/* Call mdadm to mark any RAID array(s) as clean before halting. */ /* Call mdadm to mark any RAID array(s) as clean before halting. */
mdadm_wait(); mdadm_wait();
/* Last chance to run scripts (all .so plugins have exited already) */
print(0, "Calling hook/svc/down scripts ...");
plugin_script_run(HOOK_SYS_DN);
/*
* Optional reboot-delay, needed on systems running ubifs, jffs,
* or similar. From fileutils-4.0, sync(8); "On Linux, sync is
* only guaranteed to schedule the dirty blocks for writing; it
* can actually take a short time before all the blocks are
* finally written." -- https://linux.die.net/man/8/sync
*/
sync();
if (syncsec > 0) {
print(-1, "Reboot delay, waiting for filesystem sync, %d sec", syncsec);
do_sleep(syncsec);
print(0, NULL);
}
/* Reboot via watchdog or kernel, or shutdown? */ /* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) { if (op == SHUT_REBOOT) {
print(0, "Rebooting ...");
if (wdog && wdog->pid > 1) { if (wdog && wdog->pid > 1) {
int timeout = 10; int timeout = 10;
@@ -382,15 +406,15 @@ void do_shutdown(shutop_t op)
do_sleep(1); do_sleep(1);
} }
_d("Rebooting ..."); dbg("Rebooting ...");
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) { } else if (op == SHUT_OFF) {
_d("Powering down ..."); print(0, "Powering down ...");
reboot(RB_POWER_OFF); reboot(RB_POWER_OFF);
} }
/* Also fallback if any of the other two fails */ /* Also fallback if any of the other two fails */
_d("Halting ..."); print(0, "Halting ...");
reboot(RB_HALT_SYSTEM); reboot(RB_HALT_SYSTEM);
} }
@@ -399,9 +423,14 @@ 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)
{ {
_d("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return;
}
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("SIGHUP ignored in runlevel S and 6.");
return; return;
} }
@@ -420,9 +449,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)
{ {
_d("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return; return;
} }
@@ -436,9 +465,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)
{ {
_d("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return; return;
} }
@@ -450,9 +479,14 @@ 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)
{ {
_d("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return;
}
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("SIGUSR1 ignored in runlevel S and 6.");
return; return;
} }
@@ -466,9 +500,14 @@ 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)
{ {
_d("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return;
}
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("SIGUSR2 ignored in runlevel S and 6.");
return; return;
} }
@@ -481,9 +520,14 @@ 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)
{ {
_d("..."); dbg("...");
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return;
}
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("SIGTERM ignored in runlevel S and 6.");
return; return;
} }
@@ -500,7 +544,7 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
pid_t pid; pid_t pid;
if (UEV_ERROR == events) { if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher"); errx(1, "Unrecoverable error in signal watcher");
return; return;
} }
@@ -513,7 +557,7 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
break; break;
} }
_d("Collected child %d", pid); dbg("Collected child PID %d, status: %d", pid, status);
service_monitor(pid, status); service_monitor(pid, status);
} }
} }
@@ -603,7 +647,7 @@ void sig_setup(uev_ctx_t *ctx)
{ {
struct sigaction sa; struct sigaction sa;
_d("Setup signals"); dbg("Setup signals");
/* /*
* Standard SysV init calls ctrl-alt-delete handler * Standard SysV init calls ctrl-alt-delete handler
+1 -1
View File
@@ -1,7 +1,7 @@
/* Signal management - Be conservative with what finit responds to! /* Signal management - Be conservative with what finit responds to!
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+28 -17
View File
@@ -77,6 +77,13 @@ static char *sm_status(sm_state_t state)
} }
} }
static char sm_runlevel(int lvl)
{
if (lvl == INIT_LEVEL)
return 'S';
return lvl + '0';
}
/* /*
* Disable login in single user mode and shutdown/reboot * Disable login in single user mode and shutdown/reboot
* *
@@ -86,10 +93,10 @@ static char *sm_status(sm_state_t state)
*/ */
static void nologin(void) static void nologin(void)
{ {
if (runlevel == 1 || runlevel == 0 || runlevel == 6) if (runlevel == 1 || IS_RESERVED_RUNLEVEL(runlevel))
touch(FINIT_NOLOGIN_PATH); touch(FINIT_NOLOGIN_PATH);
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6) if (prevlevel == 1 || IS_RESERVED_RUNLEVEL(prevlevel))
erase(FINIT_NOLOGIN_PATH); erase(FINIT_NOLOGIN_PATH);
} }
@@ -116,12 +123,12 @@ void sm_step(sm_t *sm)
restart: restart:
old_state = sm->state; old_state = sm->state;
_d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d", dbg("state: %s, runlevel: %c, newlevel: %d, teardown: %d, reload: %d",
sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload); sm_status(sm->state), sm_runlevel(runlevel), sm->newlevel, sm->in_teardown, sm->reload);
switch (sm->state) { switch (sm->state) {
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
_d("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);
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
break; break;
@@ -150,15 +157,16 @@ restart:
/* 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) {
api_exit();
log_exit(); log_exit();
plugin_run_hooks(HOOK_SHUTDOWN); plugin_run_hooks(HOOK_SHUTDOWN);
} }
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); dbg("Setting new runlevel --> %c <-- previous %c", sm_runlevel(runlevel), sm_runlevel(prevlevel));
if (osheading) if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %d", osheading, runlevel); logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %c", osheading, sm_runlevel(runlevel));
else else
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %d", runlevel); logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %c", sm_runlevel(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 */
@@ -173,7 +181,7 @@ restart:
/* Reset once flag of runtasks */ /* Reset once flag of runtasks */
service_runtask_clean(); service_runtask_clean();
_d("Stopping services not allowed in new runlevel ..."); dbg("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1; sm->in_teardown = 1;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -187,15 +195,15 @@ restart:
*/ */
svc = svc_stop_completed(); svc = svc_stop_completed();
if (svc) { if (svc) {
_d("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid); dbg("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break; break;
} }
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */ /* Prev runlevel services stopped, call hooks before starting new runlevel ... */
_d("All services have been stopped, calling runlevel change hooks ..."); dbg("All services have been stopped, calling runlevel change hooks ...");
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
_d("Starting services new to this runlevel ..."); dbg("Starting services new to this runlevel ...");
sm->in_teardown = 0; sm->in_teardown = 0;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -225,7 +233,7 @@ restart:
* Then, mark all affected service conditions as in-flux and * Then, mark all affected service conditions as in-flux and
* let all affected services move to WAITING/HALTED * let all affected services move to WAITING/HALTED
*/ */
_d("Stopping services not allowed after reconf ..."); dbg("Stopping services not allowed after reconf ...");
sm->in_teardown = 1; sm->in_teardown = 1;
cond_reload(); cond_reload();
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
@@ -240,23 +248,26 @@ restart:
*/ */
svc = svc_stop_completed(); svc = svc_stop_completed();
if (svc) { if (svc) {
_d("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid); dbg("Waiting to collect %s, cmd %s(%d) ...", svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
break; break;
} }
sm->in_teardown = 0; sm->in_teardown = 0;
_d("Starting services after reconf ..."); dbg("Starting services after reconf ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
/* Cleanup stale services */ /* Cleanup stale services */
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
_d("Calling reconf hooks ..."); dbg("Calling reconf hooks ...");
plugin_run_hooks(HOOK_SVC_RECONF); plugin_run_hooks(HOOK_SVC_RECONF);
dbg("Update configuration generation of unmodified non-native services ...");
service_notify_reconf();
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
_d("Reconfiguration done"); dbg("Reconfiguration done");
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
break; break;
+2 -2
View File
@@ -1,6 +1,6 @@
/* Reset terminal line settings (stty sane) /* Reset terminal line settings (stty sane)
* *
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -132,7 +132,7 @@ void stty(int fd, speed_t speed)
dprint(fd, "\033[?25h", 6); dprint(fd, "\033[?25h", 6);
/* Enable line wrap, if disabled previously */ /* Enable line wrap, if disabled previously */
dprint(fd, "\033[7h", 4); dprint(fd, "\033[?7h", 5);
} }
/** /**
+1 -1
View File
@@ -1,6 +1,6 @@
/* poor man's sulogin, may be statically linked /* poor man's sulogin, may be statically linked
* *
* Copyright (c) 2021-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
+164 -15
View File
@@ -1,7 +1,7 @@
/* Low-level service primitives and generic API for managing svc_t structures /* Low-level service primitives and generic API for managing svc_t structures
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -70,12 +70,12 @@ static void maybe_clear_cond(svc_t *svc)
if (!string_compare(cond, c)) if (!string_compare(cond, c))
continue; continue;
_d("Not clearing cond %s from gc svc %s, provided by new active service %s", dbg("Not clearing cond %s from gc svc %s, provided by new active service %s",
cond, ident, svc_ident(s, NULL, 0)); cond, ident, svc_ident(s, NULL, 0));
return; return;
} }
_d("Cleaning out %s, clearing any conditions ...", svc->name); dbg("Cleaning out %s, clearing any conditions ...", svc->name);
cond_clear(mkcond(svc, cond, sizeof(cond))); cond_clear(mkcond(svc, cond, sizeof(cond)));
} }
@@ -372,7 +372,33 @@ svc_t *svc_find(char *name, char *id)
} }
/** /**
* svc_find_by_pid - Find an service object by its PID * svc_find_by_str - Find a service object by name:id
* @str: name[:id]
*
* Like svc_find() but takes 'name[:id]' argument
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_str(const char *str)
{
char *name, *id;
if (!str)
return NULL;
name = strdupa(str);
id = strchr(name, ':');
if (id)
*id++ = 0;
else
id = "";
return svc_find(name, id);
}
/**
* svc_find_by_pid - Find a service object by its PID
* @pid: Process ID to match * @pid: Process ID to match
* *
* Returns: * Returns:
@@ -391,7 +417,29 @@ svc_t *svc_find_by_pid(pid_t pid)
} }
/** /**
* svc_find_by_jobid - Find an service object by its JOB:ID * svc_find_by_cond - Find a service object by its pid/foo condition
* @cond: Full "pid/foo" condition
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_cond(const char *cond)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char buf[MAX_COND_LEN];
mkcond(svc, buf, sizeof(buf));
if (string_compare(buf, cond))
return svc;
}
return NULL;
}
/**
* svc_find_by_jobid - Find a service object by its JOB:ID
* @job: Job n:o * @job: Job n:o
* @id: Optional instance id * @id: Optional instance id
* *
@@ -478,6 +526,14 @@ svc_t *svc_find_by_pidfile(char *fn)
return NULL; return NULL;
} }
void svc_mark(svc_t *svc)
{
if (!svc || svc->protect)
return;
*((int *)&svc->removed) = 1;
}
/** /**
* svc_mark_dynamic - Mark dynamically loaded services for deletion. * svc_mark_dynamic - Mark dynamically loaded services for deletion.
* *
@@ -492,12 +548,8 @@ void svc_mark_dynamic(void)
{ {
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->protect) svc_mark(svc);
continue;
*((int *)&svc->removed) = 1;
}
} }
void svc_mark_dirty(svc_t *svc) void svc_mark_dirty(svc_t *svc)
@@ -542,7 +594,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
*/ */
int svc_clean_bootstrap(svc_t *svc) int svc_clean_bootstrap(svc_t *svc)
{ {
if (!ISOTHER(svc->runlevels, 0)) { if (!ISOTHER(svc->runlevels, INIT_LEVEL)) {
svc->pid = 0; svc->pid = 0;
svc_del(svc); svc_del(svc);
return 1; return 1;
@@ -597,6 +649,103 @@ int svc_enabled(svc_t *svc)
return 1; return 1;
} }
/**
* svc_conflicts - Check if svc conflicts with any other running svc
* @svc:
*
* Returns:
* %TRUE(1) or %FALSE(0)
*/
int svc_conflicts(svc_t *svc)
{
char *ptr, *nm;
int rc = 0;
if (!svc->conflict[0])
return 0;
ptr = strdup(svc->conflict);
if (!ptr)
return 0;
nm = strtok(ptr, ",");
while (nm) {
svc_t *s;
s = svc_find_by_str(nm);
if (s) {
if (s->state >= SVC_STOPPING_STATE)
rc = 1;
break;
}
nm = strtok(NULL, ",");
}
free(ptr);
return rc;
}
/**
* svc_ifthen - Check if: statement in declaration
* @ident: svc_t identififcation (name:id)
* @stmt: if: statement from .conf file
*
* Returns:
* %TRUE(1) yes, use this svc_t, %FALSE(0) prune
*/
int svc_ifthen(const char *ident, char *stmt)
{
int not = 0;
svc_t *svc;
if (!stmt || !stmt[0])
return 1;
if (stmt[0] == '<') {
enum cond_state cond;
char *ptr = ++stmt;
int i = 0;
if (stmt[0] == '!') {
stmt++;
not++;
}
while (ptr[i] != '>' && ptr[i] != 0)
i++;
ptr[i] = 0;
cond = cond_get(stmt);
if (not && cond == COND_ON) {
logit(LOG_NOTICE, "skipping %s, cond %s is on.", ident, stmt);
return 0;
}
if (!not && cond == COND_OFF) {
logit(LOG_NOTICE, "skipping %s, cond %s is off.", ident, stmt);
return 0;
}
return 1;
}
if (stmt[0] == '!') {
stmt++;
not++;
}
svc = svc_find_by_str(stmt);
if (not && svc) {
logit(LOG_NOTICE, "skipping %s, %s already loaded.", ident, svc_ident(svc, NULL, 0));
return 0;
}
if (!not && !svc) {
logit(LOG_NOTICE, "skipping %s, %s not available.", ident, stmt);
return 0;
}
return 1;
}
/* break up "job:id job:id job:id ..." into several "job:id" tokens */ /* break up "job:id job:id job:id ..." into several "job:id" tokens */
static char *tokstr(char *str, size_t len) static char *tokstr(char *str, size_t len)
{ {
@@ -635,17 +784,17 @@ int svc_parse_jobstr(char *str, size_t len, void *user_data, int (*found)(svc_t
char *input, *token; char *input, *token;
int result = 0; int result = 0;
_d("Got str:'%s'", str); dbg("Got str:'%s'", str);
input = tokstr(str, len); input = tokstr(str, len);
while (input) { while (input) {
char *id = NULL; char *id = NULL;
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
char *ptr; char *ptr;
_d("Got token:'%s'", input); dbg("Got token:'%s'", input);
token = sanitize(input, strlen(input) + 1); token = sanitize(input, strlen(input) + 1);
if (!token) { if (!token) {
_d("Sanitation of token:'%s' failed", input); dbg("Sanitation of token:'%s' failed", input);
goto next; goto next;
} }
ptr = strchr(token, ':'); ptr = strchr(token, ':');
+44 -12
View File
@@ -1,7 +1,7 @@
/* Low-level service primitives and generic API for managing svc_t structures /* Low-level service primitives and generic API for managing svc_t structures
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -61,8 +61,9 @@ typedef enum {
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_CLEANUP_STATE, /* Running post: script */
SVC_SETUP_STATE, /* Running pre: script */ SVC_SETUP_STATE, /* Running pre: script */
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */ SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */
SVC_READY_STATE, /* Enabled but condition not satisfied */ SVC_WAITING_STATE, /* Enabled but condition not satisfied */
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
SVC_RUNNING_STATE, /* Process running */ SVC_RUNNING_STATE, /* Process running */
} svc_state_t; } svc_state_t;
@@ -73,11 +74,13 @@ typedef enum {
SVC_BLOCK_USER, SVC_BLOCK_USER,
SVC_BLOCK_BUSY, SVC_BLOCK_BUSY,
SVC_BLOCK_RESTARTING, SVC_BLOCK_RESTARTING,
SVC_BLOCK_CONFLICT,
} svc_block_t; } svc_block_t;
typedef enum { typedef enum {
SVC_ONCRASH_IGNORE = 0, SVC_ONCRASH_IGNORE = 0,
SVC_ONCRASH_REBOOT = 1, SVC_ONCRASH_REBOOT,
SVC_ONCRASH_SCRIPT,
} svc_oncrash_action_t; } svc_oncrash_action_t;
#define MAX_ID_LEN 16 #define MAX_ID_LEN 16
@@ -121,7 +124,9 @@ typedef struct svc {
int status; /* From waitpid() when process is collected */ int status; /* From waitpid() when process is collected */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */ const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, ... */ svc_type_t type; /* Service, run, task, ... */
int protect; /* Services like dbus-daemon & udev by Finit */ char protect; /* Services like dbus-daemon & udev by Finit */
char manual; /* run/task that require `initctl start foo` */
char nowarn; /* Skip or log warning if cmd missing or conflicts */
const int dirty; /* 0: unmodified, 1: modified */ const int dirty; /* 0: unmodified, 1: modified */
const int removed; const int removed;
int starting; /* ... waiting for pidfile to be re-asserted */ int starting; /* ... waiting for pidfile to be re-asserted */
@@ -135,12 +140,14 @@ typedef struct svc {
int job; /* For intenal use only, canonical ref is NAME:ID */ int job; /* For intenal 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];
/* Counters */ /* Counters */
char once; /* run/task, (at least) once per runlevel */ char once; /* run/task, (at least) once per runlevel */
unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */ unsigned int restart_tot; /* Total restarts ever, summarized, including `initctl restart` */
unsigned char restart_max; /* Maximum number of restarts allowed */ int restart_max; /* Maximum number of restarts allowed */
int restart_tmo; /* Time required for the service to start. */ int restart_saved; /* INTERNAL, saved copy of .conf value */
int restart_tmo; /* Time before restarting a crashing service */
unsigned char oncrash_action; /* Action to perform in crashed state. */ unsigned char oncrash_action; /* Action to perform in crashed state. */
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */ char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
const char restart_cnt; /* Incremented for each restart by service monitor. */ const char restart_cnt; /* Incremented for each restart by service monitor. */
@@ -177,10 +184,12 @@ typedef struct svc {
char cmd[MAX_ARG_LEN]; char cmd[MAX_ARG_LEN];
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN]; char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
int args_dirty; int args_dirty;
char conflict[MAX_ARG_LEN];
char desc[MAX_STR_LEN]; char desc[MAX_STR_LEN];
char env[MAX_ARG_LEN]; char env[MAX_ARG_LEN];
char pre_script[MAX_ARG_LEN]; char pre_script[MAX_ARG_LEN];
char post_script[MAX_ARG_LEN]; char post_script[MAX_ARG_LEN];
char ready_script[MAX_ARG_LEN];
/* /*
* Used to forcefully kill services that won't shutdown on * Used to forcefully kill services that won't shutdown on
@@ -189,6 +198,12 @@ typedef struct svc {
uev_t timer; uev_t timer;
void (*timer_cb)(struct svc *svc); void (*timer_cb)(struct svc *svc);
/*
* Readiness notification socket: systemd, s6
*/
int notify; /* 0: none, 1: systemd, 2: s6 */
uev_t notify_watcher; /* i/o watcher */
/* time at svc_del(), used by gc timer */ /* time at svc_del(), used by gc timer */
struct timespec gc; struct timespec gc;
} svc_t; } svc_t;
@@ -198,7 +213,9 @@ int svc_del (svc_t *svc);
void svc_validate (svc_t *svc); void svc_validate (svc_t *svc);
svc_t *svc_find (char *name, char *id); svc_t *svc_find (char *name, char *id);
svc_t *svc_find_by_str (const char *str);
svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_by_cond (const char *cond);
svc_t *svc_find_by_jobid (int job, char *id); svc_t *svc_find_by_jobid (int job, char *id);
svc_t *svc_find_by_tty (char *dev); svc_t *svc_find_by_tty (char *dev);
svc_t *svc_find_by_pidfile (char *fn); svc_t *svc_find_by_pidfile (char *fn);
@@ -212,6 +229,7 @@ void svc_foreach_type (int types, int (*cb)(svc_t *));
svc_t *svc_stop_completed (void); svc_t *svc_stop_completed (void);
void svc_mark (svc_t *svc);
void svc_mark_dynamic (void); void svc_mark_dynamic (void);
void svc_mark_dirty (svc_t *svc); void svc_mark_dirty (svc_t *svc);
void svc_mark_clean (svc_t *svc); void svc_mark_clean (svc_t *svc);
@@ -221,6 +239,8 @@ void svc_prune_bootstrap (void);
void svc_enable (svc_t *svc); void svc_enable (svc_t *svc);
int svc_enabled (svc_t *svc); int svc_enabled (svc_t *svc);
int svc_conflicts (svc_t *svc);
int svc_ifthen (const char *ident, char *ifstm);
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 *));
@@ -229,12 +249,14 @@ static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV
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_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); } static inline int svc_nohup (svc_t *svc) { return svc && (0 == svc->sighup || 0 != svc->args_dirty); }
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 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; }
@@ -248,6 +270,9 @@ static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; } static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; } static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; } static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
static inline int svc_is_crashing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_CRASHING; }
static inline int svc_is_restart (svc_t *svc) { return svc && svc->block == SVC_BLOCK_RESTARTING; }
static inline int svc_is_conflict (svc_t *svc) { return svc && svc->block == SVC_BLOCK_CONFLICT; }
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; } static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
#define svc_start(svc) svc_unblock(svc) #define svc_start(svc) svc_unblock(svc)
@@ -256,6 +281,7 @@ static inline void svc_busy (svc_t *svc) { if (svc) svc->block = SVC_BLOC
static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; } static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_MISSING; }
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; } static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; } static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
static inline void svc_conflict (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CONFLICT; }
/* Has condition in configuration and cond is allowed? */ /* Has condition in configuration and cond is allowed? */
static inline int svc_has_cond(svc_t *svc) static inline int svc_has_cond(svc_t *svc)
@@ -328,6 +354,9 @@ static inline char *svc_status(svc_t *svc)
case SVC_BLOCK_RESTARTING: case SVC_BLOCK_RESTARTING:
return "restart"; return "restart";
case SVC_BLOCK_CONFLICT:
return "conflict";
} }
return "unknown"; return "unknown";
@@ -347,17 +376,20 @@ static inline char *svc_status(svc_t *svc)
return "stopping"; return "stopping";
} }
case SVC_WAITING_STATE:
return "waiting";
case SVC_CLEANUP_STATE: case SVC_CLEANUP_STATE:
return "cleanup"; return "cleanup";
case SVC_SETUP_STATE: case SVC_SETUP_STATE:
return "setup"; return "setup";
case SVC_READY_STATE: case SVC_PAUSED_STATE:
return "ready"; return "paused";
case SVC_WAITING_STATE:
return "waiting";
case SVC_STARTING_STATE:
return "starting";
case SVC_RUNNING_STATE: case SVC_RUNNING_STATE:
return "running"; return "running";
+530
View File
@@ -0,0 +1,530 @@
/* Limited tmpfiles.d implementation
*
* Copyright (c) 2023 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.
*/
#include "config.h" /* Generated by configure script */
#include <sys/sysmacros.h>
#include "finit.h"
#include "helpers.h"
#include "tmpfiles.h"
#include "util.h"
static int glob_do(const char *path, int (*cb)(const char *))
{
int rc = 0;
glob_t gl;
rc = glob(path, GLOB_NOESCAPE, NULL, &gl);
if (rc) {
if (rc == GLOB_NOMATCH) {
errno = ENOENT;
return -1;
}
return 0;
}
for (size_t i = 0; i < gl.gl_pathc; i++)
rc += cb(gl.gl_pathv[i]);
return rc;
}
static int parse_mm(char *arg, int *major, int *minor)
{
char *ptr;
if (!arg) {
inval:
errno = EINVAL;
return -1;
}
ptr = strchr(arg, ':');
if (!ptr)
goto inval;
*ptr++ = 0;
*major = atoi(arg);
*minor = atoi(ptr);
if (!*major || !*minor)
return -1;
return 0;
}
static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw)
{
if (ftw->level == 0)
return 1;
if (remove(fpath))
warn("Failed removing condition %s", fpath);
return 0;
}
static int rmrf(const char *path)
{
if (!fisdir(path))
return 0;
nftw(path, do_delete, 20, FTW_DEPTH);
if (remove(path) && errno != ENOENT)
warn("Failed removing path %s", path);
return 0;
}
static void mkparent(char *path, mode_t mode)
{
mkpath(dirname(strdupa(path)), mode);
}
static char *write_hex(FILE *fp, char *p)
{
unsigned char val;
char num[4], *ptr;
int i, len = 0;
for (i = 0; p[i] && len < 2; i++) {
char c = p[i];
if ((c >= '0' && c <= '7') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'))
num[len++] = c;
else
break;
}
num[len] = 0;
if (len == 0) {
fputs("x", fp);
return p;
}
errno = 0;
val = strtoul(num, &ptr, 16);
if (errno || ptr == num)
goto end;
fputc(val & 0xff, fp);
end:
return &p[i];
}
static char *write_num(FILE *fp, char *p)
{
unsigned char val;
char num[4], *ptr;
int i, len = 0;
for (i = 0; p[i] && len < 3; i++) {
char c = p[i];
if (c >= '0' && c <= '7')
num[len++] = c;
else
break;
}
num[len] = 0;
if (len == 0)
return p;
errno = 0;
val = strtoul(num, &ptr, 8);
if (errno || ptr == num)
goto end;
fputc(val & 0xff, fp);
end:
return &p[i];
}
static void write_arg(FILE *fp, char *arg)
{
char *p;
if (!arg)
return;
while (*arg && (p = strchr(arg, '\\'))) {
fwrite(arg, sizeof(char), p - arg, fp);
*p++ = 0;
switch (*p) {
case '\\':
fputc('\\', fp);
arg = p + 1;
break;
case '\'':
fputc('\'', fp);
arg = p + 1;
break;
case '"':
fputc('"', fp);
arg = p + 1;
break;
case 'a':
fputc('\a', fp);
arg = p + 1;
break;
case 'b':
fputc('\b', fp);
arg = p + 1;
break;
case 'e':
fputc('\e', fp);
arg = p + 1;
break;
case 'n':
fputc('\n', fp);
arg = p + 1;
break;
case 't':
fputc('\t', fp);
arg = p + 1;
break;
case 'x':
arg = write_hex(fp, &p[1]);
break;
case '0' ... '7':
arg = write_num(fp, p);
break;
default:
fputc(*p, fp);
arg = p + 1;
break;
}
}
fputs(arg, fp);
}
/*
* The configuration format is one line per path, containing type, path,
* mode, ownership, age, and argument fields. The lines are separated by
* newlines, the fields by whitespace:
*
* #Type Path Mode User Group Age Argument…
* d /run/user 0755 root root 10d -
* L /tmp/foobar - - - - /dev/null
*
* https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
*/
static void tmpfiles(char *line)
{
char *type, *path, *token, *user, *group, *age, *arg;
char *dst = NULL, *opts = "";
struct stat st, ast;
int major, minor;
int strc, rc = 0;
mode_t mode = 0;
FILE *fp = NULL;
char buf[1024];
glob_t gl;
type = strtok(line, "\t ");
if (!type)
return;
path = strtok(NULL, "\t ");
if (!path)
return;
if (strchr(path, '%')) {
errx(1, "Path name specifiers unsupported, skipping.");
return;
}
token = strtok(NULL, "\t ");
if (token) {
errno = 0;
mode = strtoul(token, &arg, 8);
if (errno || arg == token)
mode = 0;
}
user = strtok(NULL, "\t ");
if (!user || !strcmp(user, "-"))
user = "root";
group = strtok(NULL, "\t ");
if (!group || !strcmp(group, "-"))
group = "root";
age = strtok(NULL, "\t ");
(void)age; /* unused atm. */
arg = strtok(NULL, "\n");
strc = stat(path, &st);
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 (!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;
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 '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]);
run(buf, "restorecon");
}
break;
default:
errx(1, "Unsupported tmpfiles command '%s'", type);
return;
}
if (dst)
free(dst);
if (rc)
warn("Failed %s operation on path %s", type, path);
}
/*
* 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)
{
/* in priority order */
char *dir[] = {
FINIT_TMPFILES "/*.conf",
TMPFILES_PATH "/*.conf",
"/usr/lib/tmpfiles.d/*.conf",
"/run/tmpfiles.d/*.conf",
"/etc/tmpfiles.d/*.conf", /* local admin overrides */
};
int flags = GLOB_NOESCAPE;
glob_t gl;
size_t i;
for (i = 0; i < NELEMS(dir); i++) {
glob(dir[i], flags, NULL, &gl);
flags |= GLOB_APPEND;
}
for (i = 0; i < gl.gl_pathc; i++) {
char *fn = gl.gl_pathv[i];
size_t j;
FILE *fp;
/* check for overrides */
for (j = i + 1; j < gl.gl_pathc; j++) {
if (strcmp(basename(fn), basename(gl.gl_pathv[j])))
continue;
fn = NULL;
break;
}
if (!fn)
continue; /* skip, override exists */
fp = fopen(fn, "r");
if (!fp)
continue;
// info("Parsing %s ...", fn);
while (!feof(fp)) {
char *line;
line = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCCOMM);
if (!line)
continue;
tmpfiles(line);
}
fclose(fp);
}
globfree(&gl);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+42
View File
@@ -0,0 +1,42 @@
/* Limited tmpfiles.d implementation
*
* Copyright (c) 2023 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_ */
+27 -15
View File
@@ -1,7 +1,7 @@
/* Finit TTY handling /* Finit TTY handling
* *
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com> * Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2013-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -60,7 +60,7 @@ char *tty_canonicalize(char *dev)
if (stat(path, &st)) { if (stat(path, &st)) {
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) { if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
unavailable: unavailable:
_d("TTY %s not available at the moment, registering anyway.", path); dbg("TTY %s not available at the moment, registering anyway.", path);
return path; return path;
} }
@@ -99,14 +99,14 @@ char *tty_atcon(void)
fp = fopen("/sys/class/tty/console/active", "r"); fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) { if (!fp) {
_e("Cannot find system console, is sysfs not mounted?"); errx(1, "Cannot find system console, is sysfs not mounted?");
errno = ENOENT; errno = ENOENT;
return NULL; return NULL;
} }
buf = malloc(512); buf = malloc(512);
if (!buf) { if (!buf) {
_pe("Failed allocating memory for @console"); err(1, "Failed allocating memory for @console");
fclose(fp); fclose(fp);
return NULL; return NULL;
} }
@@ -118,7 +118,7 @@ char *tty_atcon(void)
fclose(fp); fclose(fp);
ptr = chomp(buf); ptr = chomp(buf);
_d("consoles: %s", ptr); dbg("consoles: %s", ptr);
} }
dev = strtok_r(ptr, " \t", &save); dev = strtok_r(ptr, " \t", &save);
@@ -133,7 +133,9 @@ char *tty_atcon(void)
/** /**
* tty_parse_args - Parse cmdline args for a tty * tty_parse_args - Parse cmdline args for a tty
* @cmd: command or tty * @tty: service tty
* @cmd: command or tty
* @args: args from strtok_r()
* *
* A Finit tty line can use the internal getty implementation or an * A Finit tty line can use the internal getty implementation or an
* external one, like the BusyBox getty for instance. This function * external one, like the BusyBox getty for instance. This function
@@ -151,9 +153,10 @@ char *tty_atcon(void)
* Different getty implementations prefer the TTY device argument in * Different getty implementations prefer the TTY device argument in
* different order, so take care to investigate this first. * different order, so take care to investigate this first.
*/ */
int tty_parse_args(char *cmd, struct tty *tty) int tty_parse_args(struct tty *tty, char *cmd, char **args)
{ {
char *dev = NULL; char *dev = NULL;
int passenv = 0;
size_t i; size_t i;
do { do {
@@ -165,6 +168,8 @@ int tty_parse_args(char *cmd, struct tty *tty)
tty->nologin = 1; tty->nologin = 1;
else if (!strcmp(cmd, "notty")) else if (!strcmp(cmd, "notty"))
tty->notty = 1; /* for board bringup */ tty->notty = 1; /* for board bringup */
else if (!strcmp(cmd, "passenv"))
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 (whichp(cmd)) /* in $PATH? */
@@ -172,7 +177,7 @@ int tty_parse_args(char *cmd, struct tty *tty)
else else
tty->args[tty->num++] = cmd; tty->args[tty->num++] = cmd;
cmd = strtok(NULL, " "); cmd = strtok_r(NULL, " ", args);
} while (cmd && tty->num < NELEMS(tty->args)); } while (cmd && tty->num < NELEMS(tty->args));
/* rescue shells are always notty */ /* rescue shells are always notty */
@@ -220,16 +225,23 @@ int tty_parse_args(char *cmd, struct tty *tty)
tty->dev = tty_canonicalize(dev); tty->dev = tty_canonicalize(dev);
if (!tty->dev) { if (!tty->dev) {
_e("Incomplete or non-existing TTY device given, cannot register."); errx(1, "Incomplete or non-existing TTY device given, cannot register.");
return errno = EINVAL; return errno = EINVAL;
} }
_d("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->cmd ? "external" : "built-in",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A"); 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) {
if (passenv && tty->num < NELEMS(tty->args)) {
for (i = tty->num; i > 0; i--)
tty->args[i] = tty->args[i - 1];
tty->args[0] = "-p";
tty->num++;
}
tty->cmd = _PATH_GETTY; tty->cmd = _PATH_GETTY;
}
return 0; return 0;
} }
@@ -281,21 +293,21 @@ int tty_exec(svc_t *svc)
dev = tty_canonicalize(svc->dev); dev = tty_canonicalize(svc->dev);
if (!dev) { if (!dev) {
_d("%s: Cannot find TTY device: %s", svc->dev, strerror(errno)); dbg("%s: Cannot find TTY device: %s", svc->dev, strerror(errno));
return EX_CONFIG; return EX_CONFIG;
} }
if (!tty_exists(dev)) { if (!tty_exists(dev)) {
_d("%s: Not a valid TTY: %s", dev, strerror(errno)); dbg("%s: Not a valid TTY: %s", dev, strerror(errno));
return EX_OSFILE; return EX_OSFILE;
} }
if (svc->nologin) { if (svc->nologin) {
_d("%s: Starting /bin/sh ...", dev); dbg("%s: Starting /bin/sh ...", dev);
return run_sh(dev, svc->noclear, svc->nowait, svc->rlimit); return run_sh(dev, svc->noclear, svc->nowait, svc->rlimit);
} }
_d("%s: Starting %s ...", dev, svc->cmd); dbg("%s: Starting %s ...", dev, svc->cmd);
for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) { for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) {
if (!svc->args[i][0]) if (!svc->args[i][0])
break; break;
+2 -2
View File
@@ -1,7 +1,7 @@
/* Finit TTY handling /* Finit TTY handling
* *
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com> * Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
* Copyright (c) 2013-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2013-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -48,7 +48,7 @@ char *tty_canonicalize (char *dev);
int tty_isatcon (char *dev); int tty_isatcon (char *dev);
char *tty_atcon (void); char *tty_atcon (void);
int tty_parse_args (char *cmdline, struct tty *tty); int tty_parse_args (struct tty *tty, char *cmd, char **args);
int tty_exists (char *dev); int tty_exists (char *dev);
int tty_exec (svc_t *tty); int tty_exec (svc_t *tty);
+45 -9
View File
@@ -1,6 +1,6 @@
/* Misc. shared utility functions for initctl, reboot and finit /* Misc. shared utility functions for initctl, reboot and finit
* *
* Copyright (c) 2016-2022 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2016-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -30,6 +30,7 @@
#endif #endif
#include <regex.h> #include <regex.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
# include <termios.h> # include <termios.h>
@@ -303,13 +304,10 @@ int strtobytes(char *arg)
char *sig2str(int signo) char *sig2str(int signo)
{ {
static char signame[8];
if (signo < 1 || signo >= (int)NELEMS(signames)) if (signo < 1 || signo >= (int)NELEMS(signames))
return ""; return "";
snprintf(signame, sizeof(signame), "/%s", signames[signo]); return signames[signo];
return signame;
} }
/** /**
@@ -336,13 +334,10 @@ int str2sig(char *sig)
char *code2str(int code) char *code2str(int code)
{ {
static char codename[20];
if (code < 0 || code >= (int)NELEMS(exitcodes)) if (code < 0 || code >= (int)NELEMS(exitcodes))
return ""; return "";
snprintf(codename, sizeof(codename), "/%s", exitcodes[code]); return exitcodes[code];
return codename;
} }
@@ -472,6 +467,47 @@ char *sanitize(char *arg, size_t len)
return arg; return arg;
} }
void de_dotdot(char *file)
{
char *cp, *cp2;
int l;
/* Remove leading ./ and any /./ sequences. */
while (strncmp(file, "./", 2) == 0)
memmove(file, file + 2, strlen(file) - 1);
while ((cp = strstr(file, "/./")))
memmove(cp, cp + 2, strlen(cp) - 1);
/* Alternate between removing leading ../ and removing xxx/../ */
for (;;) {
while (strncmp(file, "../", 3) == 0)
memmove(file, file + 3, strlen(file) - 2);
cp = strstr(file, "/../");
if (!cp)
break;
for (cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2)
continue;
memmove(cp2 + 1, cp + 4, strlen(cp + 3));
}
/* Also elide any xxx/.. at the end. */
while ((l = strlen(file)) > 3 && strcmp((cp = file + l - 3), "/..") == 0) {
for (cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2)
continue;
if (cp2 < file)
break;
*cp2 = '\0';
}
/* Collapse any multiple / sequences. */
while ((cp = strstr(file, "//"))) {
cp2 = cp + 1;
memmove(cp, cp2, strlen(cp2) + 1);
}
}
static int hasopt(char *opts, char *opt) static int hasopt(char *opts, char *opt)
{ {
char buf[strlen(opts) + 1]; char buf[strlen(opts) + 1];

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