Compare commits

...
200 Commits
Author SHA1 Message Date
Joachim Wiberg 58e31dbd22 .github: try running with older Ubuntu version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-06 19:57:46 +02:00
Joachim Wiberg 85058b54a8 plugins: bootmisc: use relative symlink for /var/run
In an upside world, much like the Finit test cases, the root may be
relocated.  This make /var/run relative to /var, instead of /.  Which
hopefully is safer and covers more use-cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 17:46:43 +02:00
Joachim Wiberg f16df142d7 Minor shellcheck fixes to debug code
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 17:46:20 +02:00
Joachim Wiberg f9f52a46a6 Debug sys plugin
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 07:27:22 +02:00
Joachim Wiberg ef1bfb869f test: HUP Finit from inside container instead ...
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 06:49:52 +02:00
Joachim Wiberg 5002c74876 test: force enable debug logs and HUP Finit before test
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-05 06:39:23 +02:00
Joachim Wiberg a0bc23c48f test: enable finit debug to see why API socket isn't created
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:52:08 +02:00
Joachim Wiberg 350cf6e578 test: dump contents of /var/run/finit/ to check for socket
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:46:48 +02:00
Joachim Wiberg b96a8d8766 test: dump correct root dir and add more PID 1 debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:31:13 +02:00
Joachim Wiberg a419828dea .github: no ~/.cache, check rest
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:22:05 +02:00
Joachim Wiberg 702b22d4a3 .github: check if ~/.cache exists, dump test root files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 20:19:13 +02:00
Joachim Wiberg aeb48ef04b test: add support for caching busybox-x86_64 binary in ~/.cache
While without a working Internet connection today I ran into the issue
of not being able to run the tests.  This adds a basic caching mechanism
to setup-root.sh which saves busybox-x86_64 in ~/.cache, if available.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-04 08:20:48 +02:00
Joachim Wiberg b8b200ad75 doc: move state machine to inside its section
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 12:13:42 +02:00
Joachim Wiberg c148d8d783 doc: document supported types of forking/non-forking daemon's
Issue #223

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 12:03:21 +02:00
Joachim Wiberg 27ba3b27f5 test: extend start-stop-serv test w/ all supported combinations
serv: add support for foregrounding, running with and without PID file,
      including custom PID filename.

test: verify Finit can start & monitor services that:
      1) Fork and creates a PID file in a known location
      2) Don't fork and don't create a PID file, but Finit does
      3) Don't fork but create a PID file
      4) Don't fork and create custom named PID file

Note: Finit cannot support a service that forks and doesn't create a PID
      file.  This combination is impossible to support without tracking
      all processes created in /proc -- which Finit does not do atm.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 11:56:42 +02:00
Joachim Wiberg b4af86255d test: rename fserv -> serv, more features to be added
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-03 11:55:11 +02:00
Joachim Wiberg 01a007b388 .github: detect Finit version and figure out build dir on failure
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:38:44 +02:00
Joachim Wiberg 0e28741dcb test: fserv is built in another directory
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:38:20 +02:00
Joachim Wiberg c993feeb44 .github: dump start-stop-fserv.log on make check error
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:21:01 +02:00
Joachim Wiberg ec17f0aa23 test: add new test to dist, fixes CI build
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:14:32 +02:00
Joachim Wiberg 6baf2990e8 test: new test to verify support for forking services
Issue #223

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:08:10 +02:00
Joachim Wiberg 6416e2a1a4 test: minor refactor in build system
Add Makefile.am in tenv and common for EXTRA_DIST.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 12:07:34 +02:00
Joachim Wiberg 7c42986800 Simplify shutdown, no need for workarounds to remount / read-only
Calling sync is not needed, remount does this for us.

Remount with 'dummydev' causes warnings and is not needed.  It appears
sysvinit used this to try and fix a sparc related bug.  Instead, use the
rootfs keyword to ensure we don't accidentally remount a bind mounted /.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 11:09:29 +02:00
Joachim Wiberg 6f802a7ff1 test: whitespace changes only
Coding style for shell scripts:

  - Tabs for indent (change in Emacs needed)
  - Braces on their own line, like C functions

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 10:45:30 +02:00
Joachim Wiberg 879721839e test: slight formatting change in log() output
Encase (stripped) test name in [] and use decreased intensity ANSI coloring.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-02 10:26:07 +02:00
Joachim Wiberg 20290a4166 Clear any outstanding restart timer when svc reaches running state
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:32:42 +02:00
Joachim Wiberg ba77e4f217 Ensure we clear the 'starting' flag when cleaning up an svc_t
Prevent crashing/restarting tasks from winding up in the wrong state.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:31:19 +02:00
Joachim Wiberg e7b18feca5 Don't block PID 1 waiting for start/stop scripts, fork and forget
No point waiting for SysV start/stop scripts to finish, PID 1 should not
risk getting blocked forever by broken scripts. Instead we fork them off
and forget about them until they terminate. If they are buggy and don't
finish, it's the problem of the admin.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:27:46 +02:00
Joachim Wiberg 4a90a3e8ef test: drop plugins we don't need in test
E.g., a failing urandom.so does no good for ppl reading test results.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 16:22:20 +02:00
Joachim Wiberg 6d14ff84d3 test: add missing script helpers to dist archive
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 15:41:24 +02:00
Joachim Wiberg 89cc6017df .github: distcheck runs in another subdir, fix test logs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:51:36 +02:00
Joachim Wiberg f6fafdbd23 .github: show individual test logs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:28:51 +02:00
Joachim Wiberg 3480e42687 .github: 2nd test-suite.log debug attempt
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:23:02 +02:00
Joachim Wiberg 053c1643a7 .github: debug test-suite.log
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 14:09:22 +02:00
Joachim Wiberg 2018cac050 test: at first run, oldpid doesn't exist
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 13:47:11 +02:00
Joachim Wiberg f9090f535d test: drop cat $tty, seems to not be needed
I'll have to ask Jacques what this was for, because it doesn't seem to
be needed to run and monitor the test.  Also, it lingers at shutdown,
causing some unintended side effects in Finit.

Commenting out for now, as a reminder to myself.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-27 13:46:54 +02:00
Joachim Wiberg 10577f3e48 Allow matching of svc_t and pid file, prefer PID over file lookup
When a SysV init script starts a daemon, Finit knows nothing of the PID
it should monitor.  The PID is written, by start-stop-daemon or the
daemon itself, to the PID file.  Finit monitors for new PID files and
can match the PID in such files with an svc_t.

For the regular use-case, we prefer first looking up the matching svc_t
based on the PID -- assuming we start and monitor the service.  As a
fallback we resort to mathching the svc_t's declared PID file with the
new file we just discovered.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:53:59 +01:00
Joachim Wiberg 07f7caef0d Allow setting custom pid: file for SysV init scripts
We want to find the PID of the daemon the init script starts, so we need
a way to declare this to Finit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:52:03 +01:00
Joachim Wiberg 093b3ba467 Fix problem with restarting stopped SysV init scripts
SysV init scripts should not go to "done" state but "halted" so we can
do: `initctl stop foo; initctl start foo`, like we do for our regular
monitored services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:50:40 +01:00
Joachim Wiberg c63279630b Fix starting and stopping of SysV init scripts
Due to lack of tests the SysV start/stop script stopped working.
Unclear when.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-25 06:45:55 +01:00
Joachim Wiberg 4755e5206b test: new test, verify SysV start/stop daemon support
Install start-stop-daemon in test root.  Add S01-service.sh, which uses
start-stop-daemon to start service.sh.  Modify service.sh to respect
signals, and not exit immediately when sleep exits due to SIGTERM.
Remove PID file in signal callback and make sure to exit OK

The test itself is basically a copy of the start-stop-service.sh test.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-24 22:11:24 +01:00
Joachim Wiberg 2002157032 Simplify shutdown in container, call _exit()
Calling _exit(2) lets libc do most of the cleanup we want when shutting
down while in a container.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-24 21:56:47 +01:00
Joachim Wiberg 094e00d405 test: update test to use kill(1) replacement slay.sh
The slay script checks for common errors and gives some logs and status
of Finit when something goes wrong.  Helps detect issue #226 when the
start-kill-service.sh test runs at 100000 laps.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 07:02:10 +01:00
Joachim Wiberg e01836c506 Clarify debug messages for respawning daemons/ttys
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:36:46 +01:00
Joachim Wiberg 685f332c6e logit(): avoid extra \n when logging to stderr
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:33:21 +01:00
Joachim Wiberg ae09272bcc logit(): detect running in container to use stderr for logging
When running in a container we still want to use any syslog daemon
available for our logging needs.  However, the time between the first
logit() in Finit and any such daemon having started can be long.  In a
normal (non-containerized) setup we log to the kernel ring buffer, but
that's not available in a container scenario.  At least not for
unprivileged containers.  So we need to detect all these cases and be
prepared to fall back to log to the console, either using these LOG_CONS
flag to openlog(), or by simply calling vfprintf() to stderr.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:29:40 +01:00
Joachim Wiberg d16f7e0c88 test: help finit know its under test
This trick can also be used by others who want to run Finit in an
unshare.  Set the container environment variable to 'unshare',
like lxc and docker do for their products.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:27:33 +01:00
Joachim Wiberg 063d645d95 in_container(): cache positive results
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-23 06:25:43 +01:00
Joachim Wiberg f87e9d8880 Follow-up to 6fa3aa4, log cmd not args[0] which may be NULL
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-22 22:30:59 +01:00
Joachim Wiberg e8099f1bfa The kill(2) syscall returns ENOENT, not ESRCH on missing PIDs
Turns out the kill(2) syscall returns ENOENT, not ESRCH, in our test
suite.  Don't know why, the man page never mentions ENOENT, only the
ESRCH code.  Let's check for both, either way ithe PID is not there.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-22 07:22:15 +01:00
Joachim Wiberg 7dc7f9a968 Fix #227: set svc state late when stopping services, nay be dead
A service may have unexpectedly died, and we never got the signal, so
when stopping services we must set the new state after we've tried to
stop the service.  Otherwise the svc_set_state() function starts a
background timer for the SIGKILL job, which may block a reboot.

The kill() syscall tells us if the service was there or not, if not we
must clean up and go to HALTED state.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-22 07:18:37 +01:00
Joachim Wiberg 996ab8c6c6 Minor, fix local var shadowing outer var
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-21 22:04:09 +01:00
Joachim Wiberg 16c2198304 .github: dump test-suite.log on failure
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-20 15:45:12 +01:00
Joachim Wiberg ecca01dea0 Fix GitHub action status links
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-20 15:37:29 +01:00
Joachim Wiberg bda3cdd3ad Increase inotify socket receive buffer size
At startup (and reconf) of systems with lots of services there is a risk
of losing inotify events, e.g., PID file creation/delete events.  This
patch increase the receive buffer (doubles it).

On Linux the getsockopt() for SO_RCVBUF returns double the set size, due
to housekeeping in the kernel.  So we don't have to do any adjustments
when setting it.

Issue #226

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-20 15:33:56 +01:00
Joachim Wiberg 4811f76294 test: refactor service kill/respawn test
- Allow service to be respawned immediately (hidden option)
 - Stress test by killing 1000 times

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:50:23 +01:00
Joachim Wiberg 0aba704312 test: install awk, grep, and tail busybox tools as well
We may need them in test scripts.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:49:45 +01:00
Joachim Wiberg 57329832d0 initctl: skip cgroup in status output pid_cgruop() fails
In some (error) cases the PID known to Finit may no longer exist, or may
not have been added to a cgroup (yet).  Handle this case by skipping the
output of cgroup info in such conditions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:48:15 +01:00
Joachim Wiberg 078a1d0c95 initctl: if the system doesn't have logs, skip it in status output
On, e.g., a container based system logs may not be available so check
also that the messages fallback exists instead of causing confusing
error in the status output for the service.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 15:43:58 +01:00
Joachim Wiberg 0e695aa0c7 Only run swapon/swapoff if they exist
To silence warnings at startup/shutdown, check for existance of swapon
and swapoff before calling them.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 14:01:25 +01:00
Joachim Wiberg f7a1514131 test: update with info on running a single/few tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-07 11:26:17 +01:00
Joachim Wiberg 11bc87ecc3 Merge pull request #233 from liuming50/fix-a-return-value-of-initctl-status
initctl: fix return status of run tasks

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-05 11:59:57 +01:00
Ming Liu e1c59a2515 Fix some wrong return values of run tasks
When a run task is started with svc->started = 1, it should be
considered started successfully or failed on the other hand.

Signed-off-by: Sergio Morlans <sergio.morlans@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-03-05 11:53:25 +01:00
Joachim Wiberg 2faefd44a2 Simplify shutdown/reboot when running in a container
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 6fa3aa41df run(): add support for logging/redirect
This patch adds support for optional logging of output from all run()
commands.  For run_interactive() we've opted to log instead of just
redirect, meaning output on error is till on console but also in log.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg ef9e9ec786 networking: log output from 'ifup -a', run 'ifdown -a' interactively
We've been discussing, over the years, that we'd like to have an easier
way to debug bringup with Finit.  Network bringup is one such case where
it's hard to debug if/what you've misspelled in /etc/network/interfaces

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg d77e3a6113 networking: when ifup is missing, fall back to bring up loopback
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 0b1258b44c Add very basic in_container() function, from sysklogd project
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 62dad82ba8 Bring networking down also in runlevel 6, reboot
This fixes a seemingly long-running bug; we only brought down networking
in shutdown/halt and for runlevel 1, single-user mode -- not reboot.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 20:46:01 +01:00
Joachim Wiberg 5423b5b0a2 Merge pull request #232 from liuming50/tweak-done-conditions
service.c: create conditions according to runtask exit status

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-03-01 16:27:52 +01:00
Ming Liu 6b10174bcb service.c: create conditions according to runtask exit status
In commit f0f358a13:
[ service.c: set/clear condition 'done' for run tasks ]

a runtask done condition would be set/cleared when entering DONE/HALTED
states, but it did not cover all the user cases, for instance, sometimes
an end user may want to know if a runtask has finished sucessfully or
to decide what to do on its failures.

So we now change the conditions to: tsktype/tskname/success and
tsktype/tskname/failure.

And this change not only applies to run/task types, but also applies to
sysv type, in case it fails, a sysv/tskname/failure condition would be
set.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-26 14:05:49 +01:00
Joachim Wiberg ec94f4d321 Fix sig.c build warning
Refactor new kill/shutdown implementation from 3e0063e to fix the
regression in compiler output:

sig.c: In function ‘kill_callback’:
sig.c:222:12: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  222 |  kill(pid, (int)context);
      |            ^

Also, some minor renames and simplificactions to match project style.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:52:42 +01:00
Joachim Wiberg 69800537f2 Minor, rename local variable for consistency
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:18:54 +01:00
Joachim Wiberg 61faedb0b5 Minor refactor
- Reversed christmas tree
  - Avoid assignments inside if/while
  - Use established nomenclature

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:16:19 +01:00
Joachim Wiberg 61014431aa Minor refactor
- Simplify, no need for additional local variable
  - Use established nomenclature

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:15:43 +01:00
Joachim Wiberg 49bd63994c Minor refactor
- Check all pointers
  - Declaratons always at top of func/scope
  - Use established variable nomenclature
  - Skip useless if() stmt

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:15:03 +01:00
Joachim Wiberg 85b29d9407 Minor whitespace and comment cleanup
- Comments preferably at beginning of func/sect
  - Reorder code slightly, add whitespace for readability
  - Drop useless comment

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-25 07:15:03 +01:00
Joachim Wiberg b83fdea49b Merge pull request #229 from jorgensigvardsson/issue/255/initctl-signal
Fix #225: add support for initctl signal

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-20 09:50:21 +01:00
Jörgen Sigvardsson beec0ee590 Test case for initctl signal
This commit implements a test for the signal feature of initctl. The
existing service used in tests (`common/service.sh`) has been extended
with a signal trap handler. When SIGUSR1 is received, it will write the
string `'USR1'` to `/tmp/usr1.log`. The test will run `initctl signal
service.sh SIGUSR1` and assert that the contents of the file
`/tmp/usr1.log` really is `USR1`.
2022-02-15 12:43:07 +01:00
Jörgen Sigvardsson 4121113688 Added implementation for the new command INIT_CMD_SIGNAL.
The implementation looks up the named service by using
`svc_parse_jobstr`. The callbacks for `svc_parse_jobstr` has been
augmented to accept a user data parameter. For this use case,
a carrier for the actual signal was needed. The address of the
signal parameter is taken and passed on as a `void *`. The
callback then simply deferences it as an int - the signal number.
2022-02-15 12:29:40 +01:00
Jörgen Sigvardsson 62da379472 initctl signal command parsing: done 2022-02-15 12:29:40 +01:00
Joachim Wiberg 3c9053b055 Merge pull request #231 from jorgensigvardsson/fix/absolute-var-lock-symlink-breaks-tests
Remove test code that may break tests

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-15 10:47:47 +01:00
Jörgen Sigvardsson 71b07ce2bd Remove test code that may break tests
In the test framework for finit, a set of shell scripts are used to
setup a test harness, run tests, and finalize tests. In the setup, a
file system is created for the virtualized/containerized environment in
which all tests are run, located at `$FINIT_SRC/test/tenv-root`. In the
tear down phase of each test, the file `/var/lock` is made readable
using `chmod +r`. It is not clear _why_ the teardown code does this, as
there are no references in the test framework to this path anywhere
else. It is conceivable that the teardown phase attempted to "reset the
state" for next test.

The code that that this commit removes does not always work. When
`/var/lock` is a symlink, and resolves to an absolute path, the test
framework does not function properly. The teardown code is run in the
context of the host computer, and touching files outside of the
virtualized environment is not ok.

The removal of the offending code does not seem to affect tests: all
tests pass without it, so its existence is questionable.

Signed-off-by: Jörgen Sigvardsson <jorgen.sigvardsson@gmail.com>
2022-02-15 10:30:04 +01:00
Joachim Wiberg ebea3d273e test: mention need for proper configure first
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg f3a76b8a63 Clarify debug message slightly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg ba93791077 Revert 5cdbd57, causes prolonged delays at shutdown/reboot
Blocking SIGTERM means Finit will wait another two seconds before it
sends SIGKILL, which casues a delayed [WARN] Killing ... before we can
proceed to shutdown/reboot -- not OK.  Don't know what I was smoking
back in 2017, but it must've been good.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg b994858907 initctl: exit immediately on signal (from Finit)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg 74ad34a0c5 Another case for when we need to silently disable cgroups
When running in a container we might not have the necessary privileges
to mount cgroups.  Don't cause error in this case, just log it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg 1b5b37c06b plugin: allow modprobe and tty plugins to be disabled
This patch allows disabling (enabled by default) the modprobe and tty
plugins.  They are not particularly useful in container use-cases.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-13 22:45:47 +01:00
Joachim Wiberg c8e1bd0e9a test: add new kill test to dist
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 13:37:55 +01:00
Joachim Wiberg e9c8d3d740 test: verify finit restarts crashing services & registers new PID
We extend the service.sh to emulate a well behaving service that creates
and removes its own PID file.

The new test emulates a crash by sending SIGKILL to service.sh.  We then
verify that Finit restarts it, and eventually registers the new PID when
the services recreates the PID file.

Issue #226

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 13:30:05 +01:00
Joachim Wiberg f567b866a0 test: drop plugins we don't want in tests
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 13:29:27 +01:00
Joachim Wiberg 3471d3ee2d test: add service descriptions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:51:28 +01:00
Joachim Wiberg 6e197f5751 test: add /var and /run to rootfs, add empty fstab
- We need a working /var and /run for some features, e.g. PID
   monitoring and status files for urandom.so
 - Add empty fstab to silence mount errors from finit

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:49:34 +01:00
Joachim Wiberg b568c18346 initctl: fix usage text for -V,--version
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:32:53 +01:00
Joachim Wiberg 1b29876ba8 initctl: adjust indentation in usage
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-12 12:32:44 +01:00
Joachim Wiberg 4c6f3d2d69 Minor markdown syntax fixes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-11 17:30:09 +01:00
Joachim Wiberg 8d3f7eded2 contrib/patches: publish inotify IN_MASK_CREATE patch for Linux 4.9
Issue #196.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-11 17:30:09 +01:00
Joachim Wiberg 0222feeda1 Prepare ChangeLog for 4.3 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-08 09:22:00 +01:00
Joachim Wiberg f10cf1a9a0 Merge pull request #220 from liuming50/fix-a-regression
service.c: fix a regression
2022-02-07 12:18:41 +01:00
Ming Liu 85b2089fd3 initctl.c: fix a incosistent coding sytle
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-07 11:37:56 +01:00
Ming Liu 885808c64c service.c: fix a regression
A regression was introduced by commit f0f358a1:
[ service.c: set/clear condition 'done' for run tasks ]

svc->type is a integer but mistakenly being used as string, which will
cause crash.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-07 11:15:47 +01:00
Joachim Wiberg 78069b42e7 Merge pull request #219 from liuming50/fix-filesystems-not-unmounted-at-shutdown
Fix filesystems not unmounted at shutdown
2022-02-07 10:27:45 +01:00
Ming Liu 3e0063e874 Fix filesystems not unmounted at shutdown
Instead of unconditionally waiting 2 seconds for processes to die,
check continuously for remaining processes, and break the loop when
none remain.

Turn PID 1 to a RT process with highest priority 99 during shutdown,
this ensures it would not be preempted by other RT processes.

Signed-off-by: Robert Andersson <robert.m.andersson@atlascopco.com>
Signed-off-by: Mathias Thore <mathias.thore@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-07 10:06:36 +01:00
Joachim Wiberg 6ec1ab3d4f Merge pull request #218 from liuming50/fix-some-string-comparing-issues
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 07:04:10 +01:00
Joachim Wiberg 33b4540ad9 Merge pull request #207 from liuming50/set-condition-done-for-oneshot-tasks
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:59:28 +01:00
Joachim Wiberg a434b51653 Follow-up to #189: change SERVICE_TYPE, add SERVICE_SCRIPT_TYPE
- SERVICE_TYPE for svc->type
 - SERVICE_SCRIPT_TYPE for pre/post

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:53:34 +01:00
Joachim Wiberg 222d8d3575 New helper function, resolves svc_t type to string
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:51:20 +01:00
Joachim Wiberg 8e2d006048 Fix #189: add type/env/exist envs to pre/post scripts
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:29:05 +01:00
Joachim Wiberg fd9f204b6d Minor refactor, split out pre/post envs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-07 06:12:37 +01:00
Ming Liu f0f358a138 service.c: set/clear condition 'done' for run tasks
We like to know when a runtask (run/task/sysv)is done, so as to start
the services that depending on it, set a oneshot condition 'done' for
that case and clear it when runtask services enter HALTED state.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-05 18:18:53 +01:00
Ming Liu cfffcd4513 svc.c: change strncmp to string_compare
strncmp can not handle the case when name is a substring of cmd.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-05 13:54:54 +01:00
Ming Liu e51587c66e initctl.c: change string_match to string_compare
It should not use relaxed comparison in some places, otherwise it mess
up with scenarios like:
```
$ initctl status ntpdpre
$ initctl status ntpd
```

the output might be same for both of the above commands without this
fix.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-02-05 13:54:15 +01:00
Joachim Wiberg f930953e6a Fix #214: change WARN color in progress to yellow
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-02-01 08:54:12 +01:00
Joachim Wiberg ebdc10d1c5 Bump version for 4.3 development cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-30 11:03:10 +01:00
Joachim Wiberg d12370568f initctl: add -v,--version command line option
This patch adds the -v,--version option to initctl, which acts as an
alias to the 'version' command.  For now they end up in the same old
function, printing the program version.  However, in time this will be
changed so that the 'version' command asks a running Finit for its
version instead of assuming the daemon is of the same version.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-30 11:02:38 +01:00
Joachim Wiberg 176125deca Strip module args from progress output
A 'module foo args ...' line in finit.conf can be looooooong.  This
patch drops the module args from the progress output for brevity.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-30 10:37:14 +01:00
Joachim Wiberg cb934ca39f Fix issue with "reboot doesn't work on 32-bit targets"
With the introduction of GLIBC v2.34, all 32-bit devices running Linux
get the option of having a 64-bit time_t to survice the UNIX 2038
apocalypse.

Support for 64-bit time_t was added to libuev in v2.4.0.  The build
flags for linking against libuev are defined in the library's .pc
files, which basically gives you -D_TIME_BITS=64 for your CPPFLAGS.

However, it turns out the $(uev_CFLAGS) from pkg-config were only used
when building the main finit binary -- not for the plugins, causing
some really wacky behavior as a result.  All plugins and Finit itself
had some very different opinions on what `struct plugin` looked like,
including offsets and size.  This led to all I/O plugins initializing
their sockets at the wrong offset -- little-endian 32-bit targets set
the HOOK_SHUTDOWN callback hook to address 0x1 -- which the reboot
code then tried to execute, of course leading to a segfault.

This is quite possibly the root cause of issue #216

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-27 18:50:46 +01:00
Joachim Wiberg 4fd95fdcbe Fix #215: disable cgroup support when cpu controller is missing
Previous patches only checked if cgroups v2 support was available in
the kernel.  If mounting failed cgroups support was disabled.

However, much of the core logic to cgroups in Finit revolve around the
CPU controller.  This patch adds checks to ensure that the kernel has
basic controller support as well.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-27 18:50:46 +01:00
Joachim Wiberg cb62349054 plugins/bootmisc: only ignore EEXIST errors in ln()
This patch prevents bogus error messages from Finit for when
directories or symlinks already exist.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-27 18:45:00 +01:00
Joachim Wiberg 1b72119673 Merge pull request #217 from liuming50/fix-a-iwatcher-initializing-issue
cgroup.c: fix a iwatcher initializing issue
2022-01-27 11:02:08 +01:00
Ming Liu add474e825 cgroup.c: fix a iwatcher initializing issue
iwatcher of cgroup needs to be initialized earlier before any
cgroup_add functions being called.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2022-01-27 10:16:14 +01:00
Joachim Wiberg f32e4d4b30 Update changelog and bump version for v4.2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-16 05:01:03 +01:00
Joachim Wiberg eff87b3dc5 .github: add basic release general
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-16 05:00:31 +01:00
Joachim Wiberg ea4821fd18 plugins/urandom: fix resource leak, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-16 04:56:34 +01:00
Joachim Wiberg 6285668dcd .github: action for running coverity scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-16 04:47:51 +01:00
Joachim Wiberg 449bc7d12b Follow-up to 10b72f1: periodically age service crash counter
Traditionally we've reset the service restart counter on two occasions:

  1) when a service reaches its max crash count (default 10), and
  2) when a service somehow "stabilizes"

In the second case the service is restored to normal running state and
we "forget" its bad previous behavior.  Meaning we cannot catch daemons
that act in an unstable manner outside the "rage quit" scenario.

This patch allows for slowing aging (decrementing) the restart counter
once every five minutes.  Meaning we still catch rage quitters but now
are also able catch other types of misbehavior.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-13 23:13:26 +01:00
Joachim Wiberg ee867e7f3b Update ChangeLog
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-13 12:03:12 +01:00
Joachim Wiberg b4c7ae9c8c plugins/bootmisc: check return value from symlink() log on error
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-11 17:20:22 +01:00
Joachim Wiberg 95995a45cf Add total restart counter for service & sysv daemons
This patch adds a recent feature request to keep track of the total
number of restarts (including crashes and initctl restart) of both
service and sysv daemons.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-11 17:18:59 +01:00
Joachim Wiberg 823bb82b8c initctl: display number of restarts and max restarts in job status
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-11 16:19:25 +01:00
Joachim Wiberg 9a5bb46f78 Fix #212: service PID file lost after initctl reload
Clearing of the svc->pidfile was introduced in e1b87d70 for a
restriction that has now been removed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-11 15:54:53 +01:00
Joachim Wiberg 950ca953f5 Fix #211: drop hard-coded getty limit of 32 characters for usernames
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-11 13:40:07 +01:00
Joachim Wiberg 495c8c9eac Update changelog and bump version for v4.2-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 21:49:24 +01:00
Joachim Wiberg 2887795586 initctl: minor, coding style cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 21:49:01 +01:00
Joachim Wiberg 6a893f50ae initctl: refactor has_utmp() capability check to use new cmd conds
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 21:46:22 +01:00
Joachim Wiberg 5d08353284 initctl: minor, fix shadowing of global variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 21:38:19 +01:00
Joachim Wiberg a37037ded4 Fix #188: support without cgroups v2 support
This patch adds support for detecting if the Linux kernel Fint runs on
has support for cgroups v2.  The probe is done very early, in runlevel
S, when attempting to mount /sys/fs/cgroup.  Meaning, the log message
may be lost on systems that do not run sysklogd[1].

Here's the relevant logs from a system running myLinux on a kernel
that has cgroups v2 support disabled:

    Jan 10 20:19:40 anarchy finit[1]: myLinux 2021.11-696-g8fddaf36ed, entering runlevel S
    Jan 10 20:19:40 anarchy finit[1]: Kernel does not support cgroups v2, disabling.
    Jan 10 20:19:41 anarchy finit[1]: myLinux 2021.11-696-g8fddaf36ed, entering runlevel 2

All code paths that attempt to set or query kernel cgroups config has
been short circuited when this happens.  However, this does not apply
to any cgroup related config in the Finit .conf files -- which may of
course be omitted.  Yet, if configuration directives exist, they will
be parsed and may cause error.

Also, the initctl tool checks if /sys/fs/cgroup is mounted at runtime
and disables all status output and commands that rely on the feature.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 21:21:35 +01:00
Joachim Wiberg d331b72dfd Relocate ismnt() and fismnt() to util.c, for sharing with initctl
initctl cannot link with helpers.c, so let's relocate these helper
functions to util.c instead.  Need them to probe for cgroup support.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 20:26:37 +01:00
Joachim Wiberg 3b21d35f23 Fix arrow in service desciption ASCII
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-10 13:48:46 +01:00
Joachim Wiberg 62a694b8b7 Prepare ChangeLog for upcoming v4.2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 08:22:15 +01:00
Joachim Wiberg eeba2960f9 Document new per-service options controlling automatic respawn
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 07:35:33 +01:00
Joachim Wiberg 91550c926b Follow-up to ec2900e: use restart_sec:SEC as canonical option name
A timeout is not the same as a restart delay, which this option really
controls, but the name restart_delay is a bit too long.  Hence, this
obvious name so there's no ambiguity what unit we're dealing with.

Keeping restarttmo:SEC as a compatibility setting for the benefit of
the original authors, for now.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 07:13:06 +01:00
Joachim Wiberg 184525687c Follow-up to ec2900e: minor cleanup + signed vs unsigned comparison
Minor coding style cleanup.  Also, clang complained of a signed vs
unsigned comparison in service_retry(), max(timeout, svc->restart_tmo)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 06:52:44 +01:00
Joachim Wiberg cdede817fd initctl: new command 'ident [NAME]' shows ids of all/named services
As suggested in issue #197, this new command: 'initctl id [NAME]'
lists identities of all, or named, services.  E.g,

    # initctl id telnetd
    telnetd:23
    telnetd:2323

The NAME argument is optional, when omitted it lists all enabled
run/task/sysv/service in the system, with their full name, and any
optional identitty.  E.g.

    # initctl id
    dropbear
    tty:console
    mdnsd
    mini-snmpd
    smcrouted
    sntpd
    ssdpd
    sysklogd
    watchdogd
    httpd
    telnetd:23
    telnetd:2323

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 06:14:42 +01:00
Joachim Wiberg df42c2f6fe Fix #197: initctl status foo should show overview of foo:1, foo:2 ..
When the system has multiple services 'foo' the initctl command
'status foo' should give a focused overview of all matching foo:*,
whereas a 'status foo:2' should show the detailed view of that
particular instance.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 06:11:57 +01:00
Joachim Wiberg b53f2f4a60 Cleanup, drop now unused internal function svc_is_unique()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 05:34:10 +01:00
Joachim Wiberg 5ff27c4fe2 Allow kernel log messages to console with loglevel=7
Since Finit v4.0 kernel console output has been silenced just prior to
the banner being printed, unless finit.debug mode was enabled.  This
patch makes sure to check the kernel loglevel, if it's >= 7, finit no
longer disables kernel console ouput.

Consequently, you now need 'quiet' on your kernel command line, or a
loglevel < 7 for a quiet boot, at least until a syslogd takes over.

This means that you now can debug the kernel and finit independently,
which should be a great comfort to anyone doing bringup or any form of
kernel debugging.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 01:34:49 +01:00
Joachim Wiberg e5fff5117d Minor, simplify internal API
Cleanup of old cruft.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 01:34:19 +01:00
Joachim Wiberg 615cd5aac3 plugins/urandom: use RNDADDENTROPY ioctl to seed kernel rng
This patch switches from "cp /var/lib/misc/random-seed /dev/urandom"
to use the kernel RNDADDENTROPY ioctl, which actually increments the
entropy count.

Also, the amount of random data saved at reboot is increased from the
fairly low 512 to 32768 bytes.

All in all, this should greatly improve the stability of most systems
during, or close to, bootstrap.  In particular embedded systems with
limited or no HWRNG.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-02 01:04:07 +01:00
Joachim Wiberg 621d95a89e Minor, fix clang warning 'bootstrap shadows local variable'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-01 22:55:07 +01:00
Joachim Wiberg cd383167f9 plugins/rtc: instead of bailing, reset invalid RTC to kernel default
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-01 22:51:45 +01:00
Joachim Wiberg cb2969002c Fix #210: simplify escape code to clear line
Since we just want to clear the complete line, and are already a the
beginning of the line, we can use the simpler \e[K which also seems
to fix the display resize bug in #210.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-01 18:20:47 +01:00
Joachim Wiberg 1cf204e23d Fix #187: fix typos, including slight clean up
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-01-01 16:37:06 +01:00
Joachim Wiberg 393834b253 Follow-up to 4ca8256: ordering mishap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-12-07 00:13:54 +01:00
Joachim Wiberg 4ca8256e18 Follow-up to 280d91b, fix conio.h namespace
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-12-07 00:05:47 +01:00
Joachim Wiberg 280d91b9bf Add support for new libite (-lite) header namespace
Due to an unfortunate name clash with the DirectFB project LiTE, the
libite (-lite) project had to change its header namespace from

   lite/*.h -> libite/*.h

This patch adds support for the new namepace in Finit, triggered by the
define _LIBITE_LITE, from the .pc file read by pkg-config.  This should
only be needed on systems that install libite without the compatibility
symlink lite -> libite/ in the staging include directory.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-12-06 21:38:20 +01:00
Joachim Wiberg 980152fd53 Add support for non-root users to use initctl, e.g. group wheel
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-11-30 11:44:22 +01:00
Joachim Wiberg d994420d4d plugins: minor coding style cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-11-10 10:18:02 +01:00
Joachim Wiberg 38f3ecf01f .github: run tests from distcheck
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 12:44:31 +02:00
Joachim Wiberg 5cd0d05b31 .github: install libite and libuev to /usr
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 12:27:33 +02:00
Joachim Wiberg 62519c3638 .github: avoid running tests in parallel
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 10:01:45 +02:00
Joachim Wiberg 27bb2deb1a .github: sudo do what I mean
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:56:19 +02:00
Joachim Wiberg a2c8e71f14 .github: update dynamic linker cache after installing library deps
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:53:26 +02:00
Joachim Wiberg d1f56e7c2e .github: adjust path for showing finit & intictl usage text
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:46:19 +02:00
Joachim Wiberg a9fd0cbf02 .github: simplify and fix install path
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:32:38 +02:00
Joachim Wiberg be8c89ec00 Bump version for v4.2 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:28:44 +02:00
Joachim Wiberg 4f53752693 .github: install support libs to /usr/local
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:26:54 +02:00
Joachim Wiberg 393673222d Replace Travis-CI with GitHub Actions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-23 09:05:38 +02:00
Joachim Wiberg a88f4524d5 Merge pull request #208 from liuming50/support-more-specific-control-of-services
service.c: add more parameters when starting services
2021-09-23 08:46:42 +02:00
Joachim Wiberg 3a02066a66 Merge pull request #206 from liuming50/dont-hard-code-dbus-user-pidfile
dbus: dont hard-code daemon user/pidfile
2021-09-23 08:31:19 +02:00
Joachim Wiberg 7e3981d1a3 Merge pull request #205 from liuming50/fix-a-wrong-header-include
Fix a header including syntax
2021-09-23 08:30:20 +02:00
Robert Andersson ec2900e4dd service.c: add more parameters when starting services
It would be good if we can control the services more specifically with
the following parameters like:

restart:N   - how many times shall the service to be restarted on
              failures (<10), the default max is 10.
restart_tmo - the timeout of the restarting.
norestart   - dont restart on failures.
oncrash     - once all retrying also fail, how to deal with it,
              rebooting or ignoring the failures.

Signed-off-by: Robert Andersson <robert.m.andersson@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-09-21 10:55:29 +02:00
Ming Liu d718a27abf dbus: dont hard-code daemon user/pidfile
Both DBUS user and pidfile is configurable in DBUS compilation, by
passing "--with-dbus-user=" and "--with-system-pid-file=", let's avoid
hard-coding them here, use macros instead.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-09-21 10:48:25 +02:00
Ming Liu db8027911c Fix a header including syntax
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-09-21 10:47:15 +02:00
Joachim Wiberg aef3b4117d Merge pull request #200 from liuming50/dont-hard-code-nologins-file
Dont hard-code nologin file
2021-09-18 09:48:43 +02:00
Ming Liu 6728b77aca Dont hard-code nologin file
It's configurable in /etc/login.defs as NOLOGINS_FILE, avoid to hard
coding it here, introduce a macro FINIT_NOLOGIN_PATH instead which is
configurable from compiler flags, it's not defined, then back to
_PATH_NOLOGIN from /usr/include/paths.h.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-09-17 13:10:01 +02:00
Ming Liu a7c0b42965 configure.ac: add fastboo/fsckfix to config summary output
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-09-10 11:35:45 +02:00
Joachim Wiberg 667b6ecbbd Merge branch 'liuming50-introduce-two-more-config-options' 2021-09-09 09:10:46 +02:00
Joachim Wiberg b852878a4a Follow-up, coding style fixes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-09 09:10:16 +02:00
Ming Liu 940f1f7486 Introduce two more configure options
fastboot: once enabled, fsck would be skipped on filesystems listed in
          /etc/fstab.
fsckfix: once enabled, 'fsck -ys' would be run instead of 'fsck -a',
         and check the return value larger than 1 as a failure and goes
         to login console.

They are both disabled by default, no functional changes unless an end
user choose to enable them.

The idea derives from Sysvinit's "FASTBOOT" and "FSCKFIX".

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-09-09 09:08:14 +02:00
Joachim Wiberg 49c705b5ad Merge pull request #201 from liuming50/fix-a-memleak
Fix a memleak.
2021-09-09 09:04:42 +02:00
Joachim Wiberg 5163e8a433 Merge pull request #198 from timgates42/bugfix_typos
docs: Fix a few typos
2021-09-09 08:56:07 +02:00
Joachim Wiberg 71e9603e4e Merge pull request #199 from liuming50/drop-c++-key-word
Avoid using C++ key words
2021-09-09 08:55:39 +02:00
Joachim Wiberg c71102f44e Merge branch 'liuming50-fix-a-unmount-issue' 2021-09-09 08:51:41 +02:00
Joachim Wiberg 9b9158a765 Follow-up, coding style and drop extra print()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-09-09 08:51:26 +02:00
Robert Andersson 595e2b4e81 Ensure all filesystems listed in /proc/mounts are unmounted
The iterator function getmntent() is not stable. It may skip
entries if the contents of the iterated file changes, which
is the case with /proc/mounts when filesystems are unmounted.
As a result, some filesystems were never unmounted.

To fix this, iteratation is now restarted after every
sucessful unmount.

Signed-off-by: Robert Andersson <robert.m.andersson@atlascopco.com>
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-08-30 10:16:00 +02:00
Ming Liu ae8e205343 Fix a memleak.
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-08-29 14:02:04 +02:00
Ming Liu 00b33fbd42 Avoid using C++ key words
"protected" is a C++ key word, replace it with "is_protected" avoid
compiling failures with C++ compiler.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2021-08-29 10:35:08 +02:00
Tim Gates 841b06df37 docs: Fix a few typos
There are small typos in:
- doc/conditions.md
- src/mount.c
- src/svc.h

Fixes:
- Should read `satisfied` rather than `satsifed`.
- Should read `process` rather than `prorcess`.
- Should read `measure` rather than `meausre`.
2021-08-27 06:23:04 +10:00
Joachim Wiberg 522a06f864 Merge pull request #181 from yangfl/typo
Fix typo
2021-06-25 07:43:08 +02:00
yangfl fa14ed1649 Fix typo
with the love from codespell
2021-06-25 13:08:14 +08:00
Joachim Wiberg 64e41e066b Change default loglevel to LOG_INFO from LOG_NOTICE
We've had several logit() messages of LOG_INFO level that never
really made it to syslog because we classified it as "debug".

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-22 17:20:17 +02:00
Joachim Wiberg 2a71f35005 Reduce log level of watchdog handover, this is not an error condition
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-22 17:20:17 +02:00
Joachim Wiberg 961a337a5e Minor, we no longer know if it's an external or bundled getty
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-22 17:20:17 +02:00
Joachim Wiberg f0622a692e Minor, format style change of comment block
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-22 17:20:17 +02:00
Joachim Wiberg ef7042dd9d finit: early log, "entering runlevel S", just after printing banner
This patch adds "the missing" first runlevel log entry when Finit has
printed the console banner and begins its bootstrap phase.

At this point we know syslogd hasn't been started yet, so we rely on
logit() to redirect this to /dev/kmsg in the meantime.  Later syslogd
will pick this up, see that it's not from the kernel, and log it to
the proper log file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-22 17:20:17 +02:00
Joachim Wiberg 046ecfa329 Fix #180: user managed services started by initctl reload
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-14 13:16:14 +02:00
Joachim Wiberg 47fbe49861 Wrap all mount(2) calls so we at least get an error message
If any of these mount() calls fail, not counting EBUSY because that
would mean sth is already mounted, there really isn't much we can do.
So the least we should do is warn on console if it happens.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-14 11:20:08 +02:00
Joachim Wiberg 2f34315634 fnread(): make sure to NUL terminate buffer
Note, this is dead code, currently unsused in Finit.  Possibly an
external plugin makes use of it, but even that is highly unlikely.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-14 11:19:57 +02:00
104 changed files with 2826 additions and 828 deletions
+70
View File
@@ -0,0 +1,70 @@
name: Bob the Builder
# Run on all branches, including all pull requests, except the 'dev'
# branch since that's where we run Coverity Scan (limited tokens/day)
on:
push:
branches:
- '**'
- '!dev'
pull_request:
branches:
- '**'
jobs:
build:
# Verify we can build on latest Ubuntu with both gcc and clang
name: ${{ matrix.compiler }}
runs-on: ubuntu-18.04
strategy:
matrix:
compiler: [gcc, clang]
fail-fast: false
env:
MAKEFLAGS: -j3
CC: ${{ matrix.compiler }}
steps:
- name: Install 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
- uses: actions/checkout@v2
- name: Static Finit
run: |
./autogen.sh
./configure --prefix= --enable-static
make V=1
- name: Regular Finit
run: |
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
make clean
make V=1
- name: Install to ~/tmp and Inspect
run: |
DESTDIR=/tmp make install-strip
tree /tmp
ldd /tmp/sbin/finit
size /tmp/sbin/finit
ldd /tmp/sbin/initctl
size /tmp/sbin/initctl
ldd /tmp/sbin/reboot
size /tmp/sbin/reboot
/tmp/sbin/finit -h
/tmp/sbin/initctl -h
- name: Run Unit Tests
run: |
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
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
uses: actions/upload-artifact@v2
with:
name: finit-test-${{ matrix.compiler }}
path: test/*
+63
View File
@@ -0,0 +1,63 @@
name: Coverity Scan
on:
push:
branches:
- 'dev'
env:
PROJECT_NAME: finit
CONTACT_EMAIL: troglobit@gmail.com
COVERITY_NAME: troglobit-finit
COVERITY_PROJ: troglobit%2Ffinit
jobs:
coverity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
id: coverity-toolchain-cache
with:
path: cov-analysis-linux64
key: ${{ runner.os }}-coverity
- name: Download Coverity Scan
if: steps.coverity-toolchain-cache.outputs.cache-hit != 'true'
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: |
wget -q https://scan.coverity.com/download/cxx/linux64 \
--post-data "token=$TOKEN&project=${COVERITY_PROJ}" \
-O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
- name: Install 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: Build
run: |
./autogen.sh
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
cov-build --dir cov-int make
- name: Submit result to Coverity Scan
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
run: |
tar czvf ${PROJECT_NAME}.tgz cov-int
curl \
--form project=${COVERITY_NAME} \
--form token=$TOKEN \
--form email=${CONTACT_EMAIL} \
--form file=@${PROJECT_NAME}.tgz \
--form version=trunk \
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
+69
View File
@@ -0,0 +1,69 @@
name: Release General
on:
push:
tags:
- '[0-9]+.[0-9]+'
jobs:
release:
name: Create GitHub release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
steps:
- uses: actions/checkout@v2
- name: Extract ChangeLog entry ...
# Hack to extract latest entry for body_path below
run: |
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|head -n -1 > release.md
cat release.md
- name: Create release ...
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Finit v${{ github.ref }}
body_path: release.md
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/*
+3
View File
@@ -45,3 +45,6 @@ GTAGS
/.autoconf /.autoconf
/.automake /.automake
/.built /.built
# VS Code user settings
.vscode
-62
View File
@@ -1,62 +0,0 @@
# Travis CI integration -- https://travis-ci.org/troglobit/finit
# Defaults to GNU GCC and autotools: ./configure && make && make test
language: c
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false
# Test build with both GCC and Clang (LLVM)
compiler:
- gcc
- clang
# The secure: declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
env:
global:
- PKG_CONFIG_PATH=/tmp/lib/pkgconfig
- CFLAGS="-W -Wall -Wextra"
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
addons:
apt:
packages:
- tree
coverity_scan:
project:
name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --prefix= --disable-doc --disable-contrib --with-watchdog"
build_command: "make"
branch_pattern: dev
install:
- 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=/tmp && make && make install-strip)
- (cd libite-2.3.1 && ./configure --prefix=/tmp && make && make install-strip)
# Custom build script for the time being, no "make test" yet
script:
- ./autogen.sh
- ./configure --prefix= --enable-static
- make clean
- make V=1 -j5
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
- cat config.log
- make clean
- make V=1 -j5
- make install-strip DESTDIR=/tmp/finit
- tree /tmp/finit
- ldd /tmp/finit/sbin/finit
- size /tmp/finit/sbin/finit
- size /tmp/finit/sbin/initctl
- size /tmp/finit/sbin/reboot
- LD_LIBRARY_PATH=/tmp/lib /tmp/finit/sbin/initctl -h
- unshare -V
- nsenter -V
- chroot --version
- 'LD_LIBRARY_PATH=/tmp/lib make distcheck || (cat finit-4.0-beta1/_build/sub/test/test-suite.log; exit 1)'
+97 -7
View File
@@ -4,6 +4,94 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.3][UNRELEASED]
--------------------
Critical bug fix release. If you run a 32-bit target with GLIBC 2.34
you *need* to upgrade!
### Changes
* Loading `module`s no longer shows arguments in progress output
* Warning messages in progress output now in yellow, not red, issue 214
* `initctl`, new command line option `-v,--version` for ease of use
* Extended environment variables for pre/post scripts, issue #189
* New condition `done` for run task, issue #207 by Ming Liu, Atlas Copco
* Refactor parts of shutdown and reboot sequence for PREEMPT-RT kernels,
by Robert Andersson, Mathias Thore, and Ming Liu, Atlas Copco
### Fixes
* Fix nasty 32/64-bit alignment issue between finit and its plugins,
applicable to 32-bit targets with GLIBC 2.34 and later. External
plugins must make sure to use, at least: `-D_TIME_BITS=64`
* Fix #215: disable cgroup support at runtime if kernel lacks support
or does not have the required controllers (cpu)
* Fix #217: iwatcher initialization issue, by Ming Liu, Atlas Copco
* Fix #218: initctl matches too many services, by Ming Liu, Atlas Copco
[4.2][] - 2022-01-16
--------------------
### Changes
* Support for non-root users to use `initctl`, e.g. group wheel
* Support for new libite (-lite) header namespace
* RTC plugin now reset an invalid RTC time to the kernel default
time, 2000-01-01 00:00, prevents errors and is less crazy than
some systems coming with with <= Jan 1, 1970
* urandom plugin now use RNDADDENTROPY ioctl to seed kernel rng,
incrementing entropy count. Also, 32 kiB instead of 512 bytes are
now saved (and restored) on reboot. This should greatly improve
reliability of systems with none or poor HWRNG
* Kernel logging to console (loglevel >= 7, debug, when quiet mode is
not used) is now honored by Finit, regardless of the finit.debug
command line option
* Reduced default log level from `LOG_NOTICE` to `LOG_INFO`
* Wrapped all calls to mount(2) to add logging in case of failure
* New configure options to control fastboot (no fsck) and fsck fix
options, by Ming Liu, Atlas Copco
* Support for overriding default `/etc/nologin` file with an external
`#define`, by Ming Liu, Atlas Copco
* Support for overriding default `/var/run/dbus/pid` file with an
external `#define`, by Ming Liu, Atlas Copco
* Support for more service options to control respawn behavior of
crashing services, by Robert Andersson and Ming Liu, Atlas Copco
* Support for `initctl ident [NAME]` which lists all instances of
`NAME`, or all enabled system run/tasks and services
* Show number of total restarts and current respawn count for a
service in `initctl status foo`
* Crashing services no longer have the crash/restart counter reset as
soon as they have stabilized. Instead, a background timer will
slowly (every 300 sec) age (decrement) the counter. This will still
catch services that "rage quit", but also those that crash after a
longer period of activity
### Fixes
* Fix #180: user managed (`manual:yes`) services accidentally started
by `initctl reload`, regression introduced in Finit v4.0
* Fix #181: lots of typos all over the tree, by David Yang, Debian
* Fix #187: fix typos, incl. small cleanup, in doc/bootstrap.md
* Fix #188: support running on kernels that do not have cgroups v2.
When this is detect, all functions related to cgroups support in
Finit are disabled, *except* the .conf file parser. Hence, you
may get parse error if you have invalid cgroup configuration in
your Finit .conf files
* Fix #197: `initctl status foo` now shows a focused overview of all
matching instances; foo:1, foo:2 -- if only one instance matches the
command line argument, or if onle one instance exists, the detailed
view is shown, as before
* Fix #198: a few typos found by Tim Gates
* Fix #199: avoid using C++ reserved keywords
* Fix #201: memory leak in usr condition plugin, by Ming Liu, Atlas Copco
* Fix #203: ensure all filesystems listed in /proc/mounts are properly
unumounted on shutdown/reboot, by Robert Andersson, Atlas Copco
* Fix #210: resizing terminal (smaller) after boot causes empty lines
to be inserted between boot progress
* Fix #211: drop hard-coded 32 character limit in getty, now reads
`_SC_LOGIN_NAME_MAX` from `sysconf(3)`
* Fix #212: service PID file lost after inictl reload, visible from
the output from `initctl status foo`
[4.1][] - 2021-06-06 [4.1][] - 2021-06-06
-------------------- --------------------
@@ -184,7 +272,7 @@ make sure to read the whole change log when upgrading.
- As long as a stepped service changes state we queue another step all - As long as a stepped service changes state we queue another step all
event, because services may depend on each other event, because services may depend on each other
* Require new libuEv API: `uev_init1()` to reduce event cache so that * Require new libuEv API: `uev_init1()` to reduce event cache so that
the kernel can invalidate deleted events before enqueing to userspace the kernel can invalidate deleted events before enqueuing to userspace
* Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone * Rename `hwclock.so` plugin to `rtc.so` since it now is stand-alone
from the `hwclock` tool. Note: the kernel can also be set to load from the `hwclock` tool. Note: the kernel can also be set to load
and store RTC to/from system clock at boot/halt as well, issue #110 and store RTC to/from system clock at boot/halt as well, issue #110
@@ -506,7 +594,7 @@ Bug fix release.
* Add support for status/show service by `name:id` * Add support for status/show service by `name:id`
* Enforce terse mode after boot, if verbose mode is disabled * Enforce terse mode after boot, if verbose mode is disabled
* Reenable verbose mode at reboot, if disabled at boot * Re-enable verbose mode at reboot, if disabled at boot
* Update section mentioning BusyBox getty * Update section mentioning BusyBox getty
* Update debugging documentation * Update debugging documentation
* Allow debug to override terse mode * Allow debug to override terse mode
@@ -561,7 +649,7 @@ handling and reconfiguration at runtime.
* Clarify how to select different plugins with the configure script * Clarify how to select different plugins with the configure script
* Improve urandom plugin for embedded systems w/o random seed * Improve urandom plugin for embedded systems w/o random seed
* Add `--debug` flag to `initctl` * Add `--debug` flag to `initctl`
* The runlevels listed for services in `initctl show` now hightlight the * The runlevels listed for services in `initctl show` now highlight the
active runlevel. active runlevel.
* Clarify in the README and in `initctl help` that the GW event to * Clarify in the README and in `initctl help` that the GW event to
listen for in service declarations is `GW:UP` listen for in service declarations is `GW:UP`
@@ -593,7 +681,7 @@ handling and reconfiguration at runtime.
* Do not use `-Os` use `-O2` as default optimization level. Many cross * Do not use `-Os` use `-O2` as default optimization level. Many cross
compiler toolchains are known to have problems with `-Os` compiler toolchains are known to have problems with `-Os`
* Do not allow build VERSION to be overloaded by an environment variable * Do not allow build VERSION to be overloaded by an environment variable
* Fix too small MAX arguments and too few argments in `svc_t` for * Fix too small MAX arguments and too few arguments in `svc_t` for
reading currently running services with `initctl show` reading currently running services with `initctl show`
* Unblock blocked signals after forking off a child * Unblock blocked signals after forking off a child
@@ -711,7 +799,7 @@ The inetd release.
turned out to not work so well after all. For instance, launching turned out to not work so well after all. For instance, launching
TTYs in a background process completely blocked inetd services from TTYs in a background process completely blocked inetd services from
even starting up listening sockets ... proper fork seems to work fine even starting up listening sockets ... proper fork seems to work fine
though. This is the casue for *yanking* the [1.11] release, below. though. This is the cause for *yanking* the [1.11] release, below.
* Trap segfaults caused by external plugins/callbacks in a sub-process. * Trap segfaults caused by external plugins/callbacks in a sub-process.
This prevents a single programming mistake in by a 3rd party developer This prevents a single programming mistake in by a 3rd party developer
from taking down the entire system. from taking down the entire system.
@@ -807,7 +895,7 @@ Major bug fix release.
### Changes ### Changes
* Show `__FILE__` in `_d()` debug messages, useful for plugins with * Show `__FILE__` in `_d()` debug messages, useful for plugins with
similarily named callbacks. Also, only in debug mode anyway similarly named callbacks. Also, only in debug mode anyway
* Make sure to cleanup recorded PID when a service is lost. Required by * Make sure to cleanup recorded PID when a service is lost. Required by
service plugins for their callbacks to work. service plugins for their callbacks to work.
* Only clear screen when in verbose mode. Maybe this should be removed * Only clear screen when in verbose mode. Maybe this should be removed
@@ -975,7 +1063,9 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.0...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD
[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.1]: https://github.com/troglobit/finit/compare/4.0...4.1 [4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0 [4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1 [3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
+19 -19
View File
@@ -1,4 +1,4 @@
[![License Badge][]][License] [![Travis Status]][Travis] [![Coverity Status]][Coverity Scan] [![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
<img align="right" src="img/finit3.png" alt="Finit: Fast Init"> <img align="right" src="img/finit3.png" alt="Finit: Fast Init">
* [Introduction](#introduction) * [Introduction](#introduction)
@@ -120,9 +120,9 @@ some of the most common components needed to start a UNIX daemon:
``` ```
service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon service [LVLS] <COND> log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
| | | | | | | `-- Optional description | | | | | | | `---------- Optional description
| | | | | | `----------- Daemon arguments | | | | | | `------------------ Daemon arguments
| | | | | `-------------------------- Path to daemon | | | | | `------------------------- Path to daemon
| | | | `---------------------------------------------------- Optional env. file | | | | `---------------------------------------------------- Optional env. file
| | | `-------------------------------------------------------- Redirect output to log | | | `-------------------------------------------------------- Redirect output to log
| | `--------------------------------------------------------------- Optional conditions | | `--------------------------------------------------------------- Optional conditions
@@ -140,7 +140,7 @@ Dependencies are handled using [conditions](doc/conditions.md). One of
the most common conditions is to wait for basic networking to become the most common conditions is to wait for basic networking to become
available: available:
service <net/route/default> nginx -- High performace HTTP server service <net/route/default> nginx -- High performance HTTP server
Here is another example where we instruct Finit to not start BusyBox Here is another example where we instruct Finit to not start BusyBox
`ntpd` until `syslogd` has started properly. Finit waits for `syslogd` `ntpd` until `syslogd` has started properly. Finit waits for `syslogd`
@@ -571,34 +571,34 @@ This project is based on the [original finit][] by [Claudio Matsuoka][]
which was reverse engineered from syscalls of the [EeePC fastinit][] — which was reverse engineered from syscalls of the [EeePC fastinit][] —
"gaps filled with frog DNA …" "gaps filled with frog DNA …"
Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][]. Finit is developed and maintained by [Joachim Wiberg][] at [GitHub][6].
Please file bug reports, clone it, or send pull requests for bug fixes Please file bug reports, clone it, or send pull requests for bug fixes
and proposed extensions. and proposed extensions.
[1]: https://en.wikipedia.org/wiki/Process_supervision [1]: https://en.wikipedia.org/wiki/Process_supervision
[2]: http://cr.yp.to/daemontools.html [2]: https://cr.yp.to/daemontools.html
[3]: http://smarden.org/runit/ [3]: https://smarden.org/runit/
[4]: http://en.wikipedia.org/wiki/Init [4]: https://en.wikipedia.org/wiki/Init
[5]: http://en.wikipedia.org/wiki/Runlevel [5]: https://en.wikipedia.org/wiki/Runlevel
[6]: https://github.com/troglobit/finit
[RFC862]: https://tools.ietf.org/html/rfc862 [RFC862]: https://tools.ietf.org/html/rfc862
[RFC863]: https://tools.ietf.org/html/rfc863 [RFC863]: https://tools.ietf.org/html/rfc863
[RFC864]: https://tools.ietf.org/html/rfc864 [RFC864]: https://tools.ietf.org/html/rfc864
[RFC867]: https://tools.ietf.org/html/rfc867 [RFC867]: https://tools.ietf.org/html/rfc867
[RFC868]: https://tools.ietf.org/html/rfc868 [RFC868]: https://tools.ietf.org/html/rfc868
[init]: http://en.wikipedia.org/wiki/Init [init]: https://en.wikipedia.org/wiki/Init
[upstart]: http://upstart.ubuntu.com/ [upstart]: https://upstart.ubuntu.com/
[systemd]: http://www.freedesktop.org/wiki/Software/systemd/ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/
[openrc]: http://www.gentoo.org/proj/en/base/openrc/ [openrc]: https://www.gentoo.org/proj/en/base/openrc/
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts [run-parts(8)]: https://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[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]: http://troglobit.com [Joachim Wiberg]: https://troglobit.com
[GitHub]: https://github.com/troglobit/finit
[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
[Travis]: https://travis-ci.org/troglobit/finit [GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master [GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
[Coverity Scan]: https://scan.coverity.com/projects/3545 [Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg [Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+31 -3
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html) AC_INIT(Finit, 4.3-dev, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
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])
@@ -14,8 +14,8 @@ AC_CONFIG_FILES([Makefile
man/Makefile man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
test/test.env test/test.env test/Makefile
test/Makefile]) test/tenv/Makefile test/common/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])
@@ -57,6 +57,14 @@ AC_ARG_ENABLE(kernel_cmdline,
AS_HELP_STRING([--enable-kernel-cmdline], [Parse init args from /proc/cmdline (don't use!)]),,[ AS_HELP_STRING([--enable-kernel-cmdline], [Parse init args from /proc/cmdline (don't use!)]),,[
enable_kernel_cmdline=no]) enable_kernel_cmdline=no])
AC_ARG_ENABLE(fastboot,
AS_HELP_STRING([--enable-fastboot], [Skip fsck check on filesystems listed in /etc/fstab]),,[
enable_fastboot=no])
AC_ARG_ENABLE(fsckfix,
AS_HELP_STRING([--enable-fsckfix], [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab]),,[
enable_fsckfix=no])
AC_ARG_ENABLE(redirect, AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[ AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
enable_redirect=yes]) enable_redirect=yes])
@@ -78,11 +86,13 @@ 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])
AC_PLUGIN([modprobe], [yes], [Coldplug modules using modalias magic])
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf]) AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)]) AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.]) AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon]) 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([urandom], [yes], [Setup and save random seed at boot/shutdown]) AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
# Check for extra arguments or packages # Check for extra arguments or packages
@@ -106,6 +116,10 @@ AC_ARG_WITH(sysconfig,
AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]), AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]),
[sysconfig=$withval], [sysconfig=default]) [sysconfig=$withval], [sysconfig=default])
AC_ARG_WITH(group,
AS_HELP_STRING([--with-group=NAME], [Group for /run/finit/socket (initctl), default: root]),
[group=$withval], [group=root])
AC_ARG_WITH(hostname, AC_ARG_WITH(hostname,
AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]), AS_HELP_STRING([--with-hostname=NAME], [If /etc/hostname is missing, default: noname]),
[hostname=$withval], [hostname=yes]) [hostname=$withval], [hostname=yes])
@@ -134,6 +148,12 @@ AS_IF([test "x$enable_auto_reload" = "xyes"], [
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [ AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])]) AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])])
AS_IF([test "x$enable_fastboot" = "xyes"], [
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
AS_IF([test "x$enable_fsckfix" = "xyes"], [
AC_DEFINE(FSCK_FIX, 1, [Run fsck fix mode (options: -yf) on filesystems listed in /etc/fstab])])
AS_IF([test "x$enable_redirect" = "xyes"], [ AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])]) AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
@@ -174,6 +194,11 @@ AS_IF([test "x$with_heading" != "xno"], [
AS_IF([test "x$heading" = "xyes"], [heading=""]) AS_IF([test "x$heading" = "xyes"], [heading=""])
AC_DEFINE_UNQUOTED(INIT_OSHEADING, "$heading", [Displayed by Finit at boot, defaults to /etc/os-release PRETTY_NAME])]) AC_DEFINE_UNQUOTED(INIT_OSHEADING, "$heading", [Displayed by Finit at boot, defaults to /etc/os-release PRETTY_NAME])])
AS_IF([test "x$with_group" != "xno"], [
AS_IF([test "x$group" = "xyes"], [
group=root])])
AC_DEFINE_UNQUOTED(DEFGROUP, "$group", [For /run/finit/socket])
AS_IF([test "x$with_hostname" != "xno"], [ AS_IF([test "x$with_hostname" != "xno"], [
AS_IF([test "x$hostname" = "xyes"], [ AS_IF([test "x$hostname" = "xyes"], [
hostname=noname])]) hostname=noname])])
@@ -255,8 +280,11 @@ Optional features:
Built-in keventd......: $with_keventd Built-in keventd......: $with_keventd
Built-in watchdogd....: $with_watchdog $watchdog Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Skip fsck check.......: $enable_fastboot
Run fsck fix mode.....: $enable_fsckfix
Redirect output.......: $enable_redirect Redirect output.......: $enable_redirect
Default hostname......: $hostname Default hostname......: $hostname
Default group.........: $group
Default runlevel......: $runlevel Default runlevel......: $runlevel
------------- Compiler version -------------- ------------- Compiler version --------------
+1 -1
View File
@@ -4,5 +4,5 @@
# #
# Each line is run in sequence. # Each line is run in sequence.
# #
# NOTE: Remeber to set the executable bit! # NOTE: Remember to set the executable bit!
# #
@@ -0,0 +1,75 @@
In Finit issue #196 we were reminded [1] of a missing inotify flag in
Linux older kernels, IN_MASK_CREATE. Finit makes use of this internally
for monitoring PID conditions, and more. This is a central mechanism to
synchronize services at boot and reload at runtime.
The following patch for Linux 4.9.0, was provided by Andy Savage for
other people that may want to run Finit on slightly older kernels.
[1]: https://github.com/troglobit/finit/issues/196
Index: fs/notify/inotify/inotify_user.c
===================================================================
--- fs.orig/notify/inotify/inotify_user.c
+++ fs/notify/inotify/inotify_user.c
@@ -522,6 +522,7 @@ static int inotify_update_existing_watch
__u32 old_mask, new_mask;
__u32 mask;
int add = (arg & IN_MASK_ADD);
+ int create = (arg & IN_MASK_CREATE);
int ret;
mask = inotify_arg_to_mask(arg);
@@ -529,6 +530,8 @@ static int inotify_update_existing_watch
fsn_mark = fsnotify_find_inode_mark(group, inode);
if (!fsn_mark)
return -ENOENT;
+ else if (create)
+ return -EEXIST;
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
@@ -726,6 +729,10 @@ SYSCALL_DEFINE3(inotify_add_watch, int,
if (unlikely(!f.file))
return -EBADF;
+ /* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
+ if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE)))
+ return -EINVAL;
+
/* verify that this is indeed an inotify instance */
if (unlikely(f.file->f_op != &inotify_fops)) {
ret = -EINVAL;
@@ -814,7 +821,7 @@ static int __init inotify_user_setup(voi
BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
- BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
+ BUG_ON(hweight32(ALL_INOTIFY_BITS) != 22);
inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC);
Index: include/linux/inotify.h
===================================================================
--- include.orig/linux/inotify.h
+++ include/linux/inotify.h
@@ -17,6 +17,6 @@ extern struct ctl_table inotify_table[];
IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | \
IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | \
IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \
- IN_ISDIR | IN_ONESHOT)
+ IN_MASK_CREATE | IN_ISDIR | IN_ONESHOT)
#endif /* _LINUX_INOTIFY_H */
Index: include/uapi/linux/inotify.h
===================================================================
--- include.orig/uapi/linux/inotify.h
+++ include/uapi/linux/inotify.h
@@ -52,6 +52,7 @@ struct inotify_event {
#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */
#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */
#define IN_EXCL_UNLINK 0x04000000 /* exclude events on unlinked objects */
+#define IN_MASK_CREATE 0x10000000 /* only create watches */
#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */
#define IN_ISDIR 0x40000000 /* event occurred against dir */
#define IN_ONESHOT 0x80000000 /* only send event once */
+1 -1
View File
@@ -4,4 +4,4 @@
# #
# Each line is run in sequence. # Each line is run in sequence.
# #
# NOTE: Remeber to set the executable bit! # NOTE: Remember to set the executable bit!
+9 -10
View File
@@ -16,7 +16,9 @@ Bootstrap
11. Load all `/etc/finit.d/*.conf` files and set hostname 11. Load all `/etc/finit.d/*.conf` files and set hostname
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro` 12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
13. Call 1st level hooks, `HOOK_ROOTFS_UP` 13. Call 1st level hooks, `HOOK_ROOTFS_UP`
14. Mount all file systems listed in `/etc/fstab` and swap, if available 14. Mount all file systems listed in `/etc/fstab` and swap, if available.
On mount error `HOOK_MOUNT_ERROR` is called. After mount, regardless
of error, `HOOK_MOUNT_POST` is called
15. Enable SysV init signals 15. Enable SysV init signals
16. Call 2nd level hooks, `HOOK_BASEFS_UP` 16. Call 2nd level hooks, `HOOK_BASEFS_UP`
17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin 17. Cleanup stale files from `/tmp/*` et al, handled by `bootmisc` plugin
@@ -38,16 +40,13 @@ Bootstrap
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP` 26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs 27. Start all configured TTYs
In (19) and (22) tasks and services defined in `/etc/finit.conf` and In (19) and (23) tasks and services defined in `/etc/finit.conf` and
`/etc/sysctl.d/*.conf` are started. Remember, all `service` and `task` `/etc/finit.d/*.conf` are started.
stanzas are started in parallel and `run` in sequence, and in the order
listed. Hence, to emulate a SysV `/etc/init.d/rcS` one could write a
long `finit.conf` with only `run` statements.
Notice the five hook points that are called at various point in the Notice the seven hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any way bootstrap process. This is where plugins can extend the boot in any
they please. There are other hook points, e.g. `HOOK_MOUNT_ERROR`, for way they please. There are other hook points available, for more on
more on this see [plugins.md](plugins.md). this, see [plugins.md](plugins.md).
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
USB stick, convert/copy its contents to the system's `/etc/` directory, USB stick, convert/copy its contents to the system's `/etc/` directory,
+1 -1
View File
@@ -27,7 +27,7 @@ optional plugins to enable. It depends on two external libraries:
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
The configure script checks for all dependencies, including the correct The configure script checks for all dependencies, including the correct
version of the above mentioned libraries. Currently requried versions: version of the above mentioned libraries. Currently required versions:
- libite v2.2.0 - libite v2.2.0
- libuEv v2.2.0 - libuEv v2.2.0
+1 -1
View File
@@ -44,7 +44,7 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
`[1]` will be started, so a `tty [1] @console nologin` configuration `[1]` will be started, so a `tty [1] @console nologin` configuration
presents you with a root console without login. presents you with a root console without login.
For more on kernel boot paramaters, see the man page [bootparam(7)][]. For more on kernel boot parameters, see the man page [bootparam(7)][].
[config.md][]: config.md#rescue-mode [config.md][]: config.md#rescue-mode
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html [bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
+2 -2
View File
@@ -90,7 +90,7 @@ Built-in Conditions
Finit comes with a set of plugins for conditions: `pidfile`, `netlink`, Finit comes with a set of plugins for conditions: `pidfile`, `netlink`,
`sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/` `sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
(recursively) for PID files created by the monitored services, and sets (recursively) for PID files created by the monitored services, and sets
a corresponding condition in the `pid/` namespace. Similarily, the a corresponding condition in the `pid/` namespace. Similarly, the
`netlink` plugin provides basic conditions for when an interface is `netlink` plugin provides basic conditions for when an interface is
brought up/down and when a default route (gateway) is set, in the `net/` brought up/down and when a default route (gateway) is set, in the `net/`
namespace. The `sys` and `usr` plugins monitor are passive condition namespace. The `sys` and `usr` plugins monitor are passive condition
@@ -180,7 +180,7 @@ PID IDENT STATUS CONDITION (+ ON, ~ FLUX, - OFF)
Here we can see that `netd` is allowed to run since both its conditions Here we can see that `netd` is allowed to run since both its conditions
are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however
is not allowed to run since `net/vlan1/exist` condition is not satsifed. is not allowed to run since `net/vlan1/exist` condition is not satisfied.
As indicated by the `-`-prefix. As indicated by the `-`-prefix.
To fake interface `vlan1` suddenly appearing, and test what happens to To fake interface `vlan1` suddenly appearing, and test what happens to
+21 -3
View File
@@ -59,7 +59,7 @@ 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:
- If a service's `.conf` file has been removed, or its conditions are no - If a service's `.conf` file has been removed, or its conditions are no
longer satisifed, the service is stopped. longer satisfied, the service is stopped.
- If the file is modified, or a service it depends on has been reloaded, - If the file is modified, or a service it depends on has been reloaded,
the service is reloaded (stopped and started). the service is reloaded (stopped and started).
- If a new service is added it is automatically started — respecting - If a new service is added it is automatically started — respecting
@@ -332,8 +332,11 @@ by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`.
**Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description` **Syntax:** `service [LVLS] <COND> /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die 10 times exits prematurely. Finit tries to restart services that die, by
before giving up, then you have to `initctl restart NAME` it manually. default 10 times before giving up and marking them as *crashed*.
After which they have to be manually restarted with `initctl restart
NAME`. The limits controling this are configurable, see the options
below.
For daemons that support it, we recommend appending `--foreground`, For daemons that support it, we recommend appending `--foreground`,
`--no-background`, `-n`, `-F`, or similar command line argument to `--no-background`, `-n`, `-F`, or similar command line argument to
@@ -390,6 +393,21 @@ optional `name` argument:
name:<service-name> name:<service-name>
As mentioned previously, services are automatically restarted, this is
configurable with the following options:
* `restart:NUM` -- number of times Finit tries to restart a crashing
service, default: 10. When this limit is reached the service is
marked *crashed* and must be restarted manually with `initctl`
* `restart_sec:SEC` -- number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
* `norestart` -- dont restart on failures, same as `restart:0`
* `oncrash:reboot` -- when all retries have failed, and the service
has *crashed*, if this option is set the system is rebooted.
Note, future releases may include other `oncrash:` actions
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
allow the process to shut down gracefully. If the process has not allow the process to shut down gracefully. If the process has not
+45 -1
View File
@@ -1,7 +1,49 @@
Finit Services Finit Services
============== ==============
![The service state machine](svc-machine.png "The service state machine") Starting & Monitoring
---------------------
Finit can start and monitor the following types of daemons:
* Forks to background, creates a PID file
* Runs in foreground:
- creates a PID file
- does not create a PID file -- Finit can create it for you (optional)
Finit can *not* start and monitor a daemon that:
* Forks to background and does *not* create a PID file
| | Forking | Creates PID File | Finit creates PID File |
|---|---------|------------------|------------------------|
| ✔ | Yes | Yes | No |
| ✔ | No | Yes | No |
| ✔ | No | No | Yes, optionally |
| ✘ | Yes | No | No |
> **Note:** PID files is one mechanism used to assert conditions to
> synchronize the start and stop of other, dependent, services.
### Forks to Background w/ PID File
service pid:!/run/serv.pid serv -- Forking service
### Runs in Foreground w/ PID File
service serv -n -p -- Foreground service w/ PID file
### Runs in Foreground w/o PID File
Same as previous, but we tell Finit to create the PID file, because we
need it to synchronize start/stop of a dependent service.
service pid:/run/serv.pid serv -n -- Foreground service w/o PID file
### Runs in Foreground w/ Custom PID File
service pid:/run/servy.pid serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file
State Machine State Machine
------------- -------------
@@ -11,6 +53,8 @@ For `run`s and `task`s there is an additional end state called `DONE`,
not shown in the diagram for brevity. Services start in the `HALTED` not shown in the diagram for brevity. Services start in the `HALTED`
state. state.
![The service state machine](svc-machine.png "The service state machine")
The current state depends on the two following conditions: The current state depends on the two following conditions:
* `E`: Service enabled. In order for `E` to be satisfied, the service * `E`: Service enabled. In order for `E` to be satisfied, the service
+1 -1
View File
@@ -107,7 +107,7 @@ Finit waits for the PID file of service A to be created/updated before
it starts service B. Conversely, if service A is stopped and its PID it starts service B. Conversely, if service A is stopped and its PID
file removed, service B is also stopped. file removed, service B is also stopped.
.Pp .Pp
The following condition families are availble today: The following condition families are available today:
.Pp .Pp
.Bl -tag -width pid -offset indent .Bl -tag -width pid -offset indent
.It Cm net .It Cm net
+37 -4
View File
@@ -174,9 +174,13 @@ the default behavior for the
command modifier. command modifier.
.It Cm service Oo LVLS Oc Ao COND Ac Ar /path/to/daemon ARGS Op -- Optional description .It Cm service Oo LVLS Oc Ao COND Ac Ar /path/to/daemon ARGS Op -- Optional description
Service, or daemon, to be monitored and automatically restarted if it Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die 10 times exits prematurely. Finit tries to restart services that die, by
before giving up, then you have to restart it manually: default 10 times, before giving up and marking them as
.Cm initctl restart NAME .Em crashed .
After which they have to be restarted manually using
.Cm initctl restart NAME .
The number of restarts, the delay between each restart, and more is
configurable, see the options below.
.Pp .Pp
For daemons that support it, we recommend appending For daemons that support it, we recommend appending
.Cm --foreground , --no-background , -n , -F , .Cm --foreground , --no-background , -n , -F ,
@@ -200,6 +204,7 @@ flag (daemonize) to prevent it from forking to the background:
.Bd -unfilled -offset indent .Bd -unfilled -offset indent
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
.Ed .Ed
.Pp
.Cm [2345] .Cm [2345]
denote the runlevels denote the runlevels
.Cm ospfd .Cm ospfd
@@ -264,6 +269,34 @@ changed with the
.Cm name:foo .Cm name:foo
command modifier. command modifier.
.Pp .Pp
As mentioned previously, services are automatically restarted should
they crash, this is configurable with the following options:
.Bl -tag -width 1n
.It Cm restart:NUM
number of times Finit tries to restart a crashing
service, default: 10. When this limit is reached the service is
marked
.Em crashed
and must be restarted manually with
.Xr initctl 8 .
.It Cm restart_sec:SEC
number of seconds before Finit tries to restart
a crashing service, default: 2 seconds for the first five retries,
then back-off to 5 seconds. The maximum of this configured value
and the above (2 and 5) will be used
.It Cm norestart
dont restart on failures, same as
.Cm restart:0
.It Cm oncrash:reboot
when all retries have failed, and the service
has
.Em crashed ,
if this option is set the system is rebooted. Note, future releases
may include other
.Cm oncrash:
actions.
.El
.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
moving to another runlevel, Finit starts by sending SIGTERM, to allow moving to another runlevel, Finit starts by sending SIGTERM, to allow
the process to shut down gracefully. If the process has not been the process to shut down gracefully. If the process has not been
@@ -301,7 +334,7 @@ services named
may be may be
.Cm foo:1 , .Cm foo:1 ,
or any unique identifier specified in the .conf file. The or any unique identifier specified in the .conf file. The
.cm post:script .Cm post:script
is called with an additional set of environment variables: is called with an additional set of environment variables:
.Bl -tag -offset indent -width 1n .Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal] .It Cm EXIT_CODE=[exited,signal]
+23 -4
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 bcfhlpqtv .Op Fl bcfhlpqtvV
.Op COMMAND .Op COMMAND
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@@ -41,16 +41,20 @@ Silent, only return status of command.
Skip table headings. Skip table headings.
.It Fl v, -verbose .It Fl v, -verbose
Verbose output, where applicable. Verbose output, where applicable.
.It Fl V, -version
Show program version.
.El .El
.Sh COMMANDS .Sh COMMANDS
.Bl -tag -width Ds .Bl -tag -width Ds
.It Nm Ar debug .It Nm Ar debug
Toggle finit (daemon) debug to Toggle
.Nm finit
(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 Finit (daemon) version Show program version
.It Nm Ar ls | list .It Nm Ar ls | list
List all List all
.Cm *.conf .Cm *.conf
@@ -118,6 +122,13 @@ Show condition status, default
command. command.
.It Nm Ar cond dump .It Nm Ar cond dump
Dump all conditions and their status Dump all conditions and their status
.It Nm Ar ident Op Cm NAME
Display indentities of all run/task/services, or only instances
matching
.Cm NAME .
A partial string, e.g.,
.Cm NAM ,
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 ,
@@ -129,10 +140,18 @@ Start service by name, with optional ID, e.g.,
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 by name (SIGHUP or restart) Reload service by name (SIGHUP or restart)
.It Nm Ar signal Cm NAME:[ID] S
Send signal S to service, by name.
.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).
.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 Show service status, by name. If only
.Cm NAME
is given and multiple instances exits, a summary of all matching
instances are shown. Only an exact match displays the detailed status
for a particular instance
.It Nm Ar status .It Nm Ar status
Show status of all services, default command Show status of all services, default command
.It Nm Ar cgroup .It Nm Ar cgroup
+19 -3
View File
@@ -3,11 +3,11 @@ 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 -U_FORTIFY_SOURCE
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) AM_CPPFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c pidfile.c procps.c sys.c tty.c usr.c libplug_la_SOURCES = bootmisc.c pidfile.c procps.c sys.c usr.c
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c libplug_la_SOURCES += alsa-utils.c
@@ -25,6 +25,10 @@ if BUILD_MODULES_LOAD_PLUGIN
libplug_la_SOURCES += modules-load.c libplug_la_SOURCES += modules-load.c
endif endif
if BUILD_MODPROBE_PLUGIN
libplug_la_SOURCES += modprobe.c
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
libplug_la_SOURCES += netlink.c libplug_la_SOURCES += netlink.c
endif endif
@@ -37,6 +41,10 @@ if BUILD_RTC_PLUGIN
libplug_la_SOURCES += rtc.c libplug_la_SOURCES += rtc.c
endif endif
if BUILD_TTY_PLUGIN
libplug_la_SOURCES += tty.c
endif
if BUILD_URANDOM_PLUGIN if BUILD_URANDOM_PLUGIN
libplug_la_SOURCES += urandom.c libplug_la_SOURCES += urandom.c
endif endif
@@ -46,7 +54,7 @@ libplug_la_SOURCES += x11-common.c
endif endif
else else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la pidfile.la procps.la sys.la tty.la usr.la pkglib_LTLIBRARIES = bootmisc.la pidfile.la procps.la sys.la usr.la
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la pkglib_LTLIBRARIES += alsa-utils.la
@@ -64,6 +72,10 @@ if BUILD_MODULES_LOAD_PLUGIN
pkglib_LTLIBRARIES += modules-load.la pkglib_LTLIBRARIES += modules-load.la
endif endif
if BUILD_MODPROBE_PLUGIN
pkglib_LTLIBRARIES += modprobe.la
endif
if BUILD_NETLINK_PLUGIN if BUILD_NETLINK_PLUGIN
pkglib_LTLIBRARIES += netlink.la pkglib_LTLIBRARIES += netlink.la
endif endif
@@ -76,6 +88,10 @@ if BUILD_RTC_PLUGIN
pkglib_LTLIBRARIES += rtc.la pkglib_LTLIBRARIES += rtc.la
endif endif
if BUILD_TTY_PLUGIN
pkglib_LTLIBRARIES += tty.la
endif
if BUILD_URANDOM_PLUGIN if BUILD_URANDOM_PLUGIN
pkglib_LTLIBRARIES += urandom.la pkglib_LTLIBRARIES += urandom.la
endif endif
+6 -1
View File
@@ -21,7 +21,12 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
+16 -6
View File
@@ -24,7 +24,11 @@
#include <ftw.h> #include <ftw.h>
#include <mntent.h> #include <mntent.h>
#include <string.h> #include <string.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
@@ -100,6 +104,12 @@ static void clean(void *arg)
} }
} }
static void ln(const char *target, const char *linkpath)
{
if (symlink(target, linkpath) && errno != EEXIST)
_pe("Failed creating %s -> %s symlink", target, linkpath);
}
/* /*
* Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP * Setup standard FHS 2.3 structure in /var, and write runlevel to UTMP
*/ */
@@ -121,11 +131,11 @@ static void setup(void *arg)
_d("System with new /run tmpfs ..."); _d("System with new /run tmpfs ...");
if (!fisdir("/run/lock")) if (!fisdir("/run/lock"))
makedir("/run/lock", 1777); makedir("/run/lock", 1777);
symlink("/run/lock", "/var/lock"); ln("/run/lock", "/var/lock");
symlink("/dev/shm", "/run/shm"); ln("/dev/shm", "/run/shm");
/* compat only, should really be set up by OS/dist */ /* compat only, should really be set up by OS/dist */
symlink("/run", "/var/run"); ln("../run", "/var/run");
} else { } else {
makedir("/var/lock", 1777); makedir("/var/lock", 1777);
makedir("/var/run", 0755); makedir("/var/run", 0755);
@@ -179,7 +189,7 @@ static void setup(void *arg)
makedir("/var/run/sshd", 01755); /* OpenSSH */ makedir("/var/run/sshd", 01755); /* OpenSSH */
if (!fexist("/etc/mtab")) if (!fexist("/etc/mtab"))
symlink("../proc/self/mounts", "/etc/mtab"); ln("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */ /* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd"); mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
@@ -188,7 +198,7 @@ static void setup(void *arg)
mksubsys("/var/run/sudo", 0711, "root", "root"); mksubsys("/var/run/sudo", 0711, "root", "root");
mksubsys("/var/run/sudo/ts", 0700, "root", "root"); mksubsys("/var/run/sudo/ts", 0700, "root", "root");
if (whichp("restorecon")) if (whichp("restorecon"))
run("restorecon /var/run/sudo /var/run/sudo/ts"); run("restorecon /var/run/sudo /var/run/sudo/ts", "restorecon");
umask(prev); umask(prev);
} }
+14 -4
View File
@@ -22,7 +22,11 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "config.h" #include "config.h"
@@ -35,7 +39,13 @@
#define ARGS "--nofork --system --syslog-only" #define ARGS "--nofork --system --syslog-only"
#define DESC "D-Bus message bus daemon" #define DESC "D-Bus message bus daemon"
#ifndef DAEMONUSER
#define DAEMONUSER "messagebus" #define DAEMONUSER "messagebus"
#endif
#ifndef DAEMONPIDFILE
#define DAEMONPIDFILE "/var/run/dbus/pid"
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
@@ -61,11 +71,11 @@ static void setup(void *arg)
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 */ /* Clean up from any previous pre-bootstrap run */
remove("/var/run/dbus/pid"); remove(DAEMONPIDFILE);
/* Register service with Finit */ /* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] cgroup.system @%s:%s %s %s -- %s", snprintf(line, sizeof(line), "[S12345789] cgroup.system pid:!%s @%s:%s %s %s -- %s",
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC); 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); _pe("Failed registering %s", DAEMON);
free(cmd); free(cmd);
+6 -2
View File
@@ -40,11 +40,15 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */ #include <sys/time.h> /* gettimeofday() */
#include <sys/types.h> #include <sys/types.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "util.h"
#include "plugin.h" #include "plugin.h"
struct module { struct module {
+23 -13
View File
@@ -21,10 +21,14 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -40,36 +44,42 @@
static void load(void *arg) static void load(void *arg)
{ {
FILE *fp; struct dirent *d;
DIR *dirp;
struct dirent *direntp;
char line[256]; char line[256];
int index = 1; int index = 1;
FILE *fp;
DIR *dir;
_d("Scanning " MODULES_LOAD_PATH " for config files ..."); _d("Scanning " MODULES_LOAD_PATH " for config files ...");
dirp = opendir(MODULES_LOAD_PATH); dir = opendir(MODULES_LOAD_PATH);
if (!dirp) return; if (!dir)
return;
while ((direntp = readdir(dirp))) while ((d = readdir(dir))) {
{
char module_path[PATH_MAX]; char module_path[PATH_MAX];
strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path)); strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path));
strlcat(module_path, direntp->d_name, sizeof(module_path)); strlcat(module_path, d->d_name, sizeof(module_path));
fp = fopen(module_path, "r"); fp = fopen(module_path, "r");
if (!fp) continue; if (!fp)
continue;
while (fgets(line, sizeof(line), fp)) { while (fgets(line, sizeof(line), fp)) {
char cmd[CMD_SIZE], *mod, *args; char cmd[CMD_SIZE], *mod, *args;
mod = strip_line(line); mod = strip_line(line);
if (!*mod) continue; if (!*mod)
continue;
mod = chomp(mod); mod = chomp(mod);
if (!mod || !*mod) continue; if (!mod || !*mod)
continue;
mod = strtok_r(mod, " ", &args); mod = strtok_r(mod, " ", &args);
if (!mod)
continue;
snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod); snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL); service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
@@ -80,7 +90,7 @@ static void load(void *arg)
fclose(fp); fclose(fp);
} }
closedir(dirp); closedir(dir);
} }
static plugin_t plugin = { static plugin_t plugin = {
+1 -1
View File
@@ -355,7 +355,7 @@ static void nl_resync(int all)
if (all) { if (all) {
_d("============================ RESYNC ================================="); _d("============================ RESYNC =================================");
/* this doesn't update condtions, and thus does not stop services */ /* this doesn't update conditions, and thus does not stop services */
cond_deassert("net/"); cond_deassert("net/");
nl_resync_ifaces(sd, seq++); nl_resync_ifaces(sd, seq++);
+3 -1
View File
@@ -84,8 +84,10 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
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); svc_started(svc);
if (!svc_has_pidfile(svc)) if (!svc_has_pidfile(svc)) {
_d("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;
+11 -5
View File
@@ -22,7 +22,11 @@
*/ */
#include <glob.h> #include <glob.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -30,9 +34,7 @@
static void setup(void *arg) static void setup(void *arg)
{ {
size_t i;
glob_t gl; glob_t gl;
char buf[160];
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
@@ -47,9 +49,13 @@ static void setup(void *arg)
glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl); glob("/mnt/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl); glob("/etc/sysctl.conf", GLOB_APPEND, NULL, &gl);
if (gl.gl_pathc > 0) { if (gl.gl_pathc > 0) {
size_t i;
for (i = 0; i < gl.gl_pathc; i++) { for (i = 0; i < gl.gl_pathc; i++) {
snprintf(buf, sizeof(buf), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]); char cmd[160];
run(buf);
snprintf(cmd, sizeof(cmd), "/sbin/sysctl -e -p %s >/dev/null", gl.gl_pathv[i]);
run(cmd, "sysctl");
} }
globfree(&gl); globfree(&gl);
} }
+5 -1
View File
@@ -21,7 +21,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
+16 -2
View File
@@ -24,7 +24,11 @@
#include <time.h> #include <time.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -152,8 +156,18 @@ static void rtc_restore(void *arg)
logit(LOG_ERR, "RTC has no previously saved (valid) time."); logit(LOG_ERR, "RTC has no previously saved (valid) time.");
else else
logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno)); logit(LOG_ERR, "RTC error code %d: %s", errno, strerror(errno));
rc = 2;
/* Been here already? */
if (rc)
goto out; goto out;
/* Attempt to set RTC to a sane value ... */
tv.tv_sec = RTC_TIMESTAMP_BEGIN_2000;
if (!gmtime_r(&tv.tv_sec, &tm))
goto out;
logit(LOG_NOTICE, "Resetting RTC to kernel default, 2000-01-01 00:00.");
rc = 2;
} }
print_desc(NULL, "Restoring system clock (UTC) from RTC"); print_desc(NULL, "Restoring system clock (UTC) from RTC");
+5 -1
View File
@@ -1,4 +1,4 @@
/* System condtion event monitor for the Finit condition engine /* System condition event monitor for the Finit condition engine
* *
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* *
@@ -160,19 +160,23 @@ static void sys_init(void *arg)
char sysdir[MAX_ARG_LEN]; char sysdir[MAX_ARG_LEN];
char *path; char *path;
_d("Calling mkpath() on sys conds");
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); _pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
return; return;
} }
_d("Calling realpth() on condsys");
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); _pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
return; return;
} }
_d("Calling iwatch_add()");
if (iwatch_add(&iw_sys, path, IN_ONLYDIR)) if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
iwatch_exit(&iw_sys); iwatch_exit(&iw_sys);
_d("Done.");
} }
static plugin_t plugin = { static plugin_t plugin = {
+5 -1
View File
@@ -27,7 +27,11 @@
#include <unistd.h> /* read() */ #include <unistd.h> /* read() */
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
+70 -3
View File
@@ -22,19 +22,34 @@
*/ */
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> /* gettimeofday() */ #include <sys/time.h> /* gettimeofday() */
#include <sys/types.h> #include <sys/types.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <linux/random.h>
#include "config.h" #include "config.h"
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#ifndef RANDOM_BYTES
#define RANDOM_BYTES (32*1024)
#endif
static void setup(void *arg) static void setup(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
struct rand_pool_info *rpi;
ssize_t len = 0;
int rc = -1;
int fd, err;
if (rescue) { if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__); _d("Skipping %s plugin in rescue mode.", __FILE__);
return; return;
@@ -67,7 +82,59 @@ static void setup(void *arg)
} }
print_desc("Initializing random number generator", NULL); print_desc("Initializing random number generator", NULL);
print_result(512 != copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
/*
* Simply copying our saved entropy to /dev/urandom doesn't
* increment the kernel "entropy count", instead we must use
* the ioctl below.
*/
fd = open(RANDOMSEED, O_RDONLY);
if (fd < 0)
goto fallback;
rpi = malloc(sizeof(*rpi) + RANDOM_BYTES);
if (!rpi) {
close(fd);
goto fallback;
}
do {
ssize_t num;
num = read(fd, &rpi->buf[len], RANDOM_BYTES - len);
if (num <= 0) {
if (num == -1 && errno == EINTR)
continue;
if (len > 0)
break;
close(fd);
free(rpi);
goto fallback;
}
len += num;
} while (len < RANDOM_BYTES);
close(fd);
fd = open("/dev/urandom", O_WRONLY);
if (fd < 0) {
free(rpi);
goto fallback;
}
rpi->buf_size = len;
rpi->entropy_count = len * 8;
rc = ioctl(fd, RNDADDENTROPY, rpi);
err = errno;
close(fd);
free(rpi);
if (rc < 0)
logit(LOG_ERR, "Failed adding entropy to kernel random pool: %s", strerror(err));
print_result(rc < 0);
return;
fallback:
print_result(512 > copyfile(RANDOMSEED, "/dev/urandom", 0, 0));
#endif #endif
} }
@@ -84,7 +151,7 @@ static void save(void *arg)
prev = umask(077); prev = umask(077);
print_desc("Saving random seed", NULL); print_desc("Saving random seed", NULL);
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0)); print_result(RANDOM_BYTES != copyfile("/dev/urandom", RANDOMSEED, RANDOM_BYTES, 0));
umask(prev); umask(prev);
#endif #endif
+3 -1
View File
@@ -1,4 +1,4 @@
/* User-defined condtion event monitor for the Finit condition engine /* User-defined condition event monitor for the Finit condition engine
* *
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
* *
@@ -97,6 +97,8 @@ static void usr_init(void *arg)
if (iwatch_add(&iw_usr, path, IN_ONLYDIR)) if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
iwatch_exit(&iw_usr); iwatch_exit(&iw_usr);
free(path);
} }
static plugin_t plugin = { static plugin_t plugin = {
+7 -3
View File
@@ -24,7 +24,11 @@
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -54,7 +58,7 @@ static void setup(void *arg)
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) { if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username)); write(fd, username, strlen(username));
close(fd); close(fd);
run("pam_console_apply"); run("pam_console_apply", "pam-console");
} }
#endif #endif
@@ -62,7 +66,7 @@ static void setup(void *arg)
makedir("/tmp/.ICE-unix", 01777); makedir("/tmp/.ICE-unix", 01777);
if (whichp("restorecon")) if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix"); run("restorecon /tmp/.ICE-unix /tmp/.X11-unix", "restorecon");
umask(prev); umask(prev);
} }
+54 -16
View File
@@ -29,7 +29,11 @@
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <uev/uev.h> #include <uev/uev.h>
#include "config.h" #include "config.h"
@@ -47,12 +51,12 @@
extern svc_t *wdog; extern svc_t *wdog;
static uev_t api_watcher; static uev_t api_watcher;
static int call(int (*action)(svc_t *), char *buf, size_t len) static int call(int (*action)(svc_t *, void *), char *buf, size_t len)
{ {
return svc_parse_jobstr(buf, len, action, NULL); return svc_parse_jobstr(buf, len, NULL, action, NULL);
} }
static int stop(svc_t *svc) static int stop(svc_t *svc, void *user_data)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -63,7 +67,7 @@ static int stop(svc_t *svc)
return 0; return 0;
} }
static int start(svc_t *svc) static int start(svc_t *svc, void *user_data)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -79,7 +83,7 @@ static int start(svc_t *svc)
* responsibility of initctl to do. Otherwise we'd block PID 1, * responsibility of initctl to do. Otherwise we'd block PID 1,
* or introduce some nasty race conditions. * or introduce some nasty race conditions.
*/ */
static int restart(svc_t *svc) static int restart(svc_t *svc, void *user_data)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -91,7 +95,7 @@ static int restart(svc_t *svc)
return 0; return 0;
} }
static int reload(svc_t *svc) static int reload(svc_t *svc, void *user_data)
{ {
if (!svc) if (!svc)
return 1; return 1;
@@ -110,8 +114,28 @@ static int do_start (char *buf, size_t len) { return call(start, buf, len); }
static int do_restart(char *buf, size_t len) { return call(restart, buf, len); } static int do_restart(char *buf, size_t len) { return call(restart, buf, len); }
static int do_reload (char *buf, size_t len) { return call(reload, buf, len); } static int do_reload (char *buf, size_t len) { return call(reload, buf, len); }
static int do_signal_svc(svc_t *svc, void *user_data)
{
int signo;
if (!svc || !user_data || !svc_is_running(svc))
return 1;
signo = *(int *)user_data;
return !!kill(svc->pid, signo);
}
static int do_signal(char *buf, size_t len, int sig)
{
/* Sanity check: do we know this signal? */
if (!*sig2str(sig))
return 1;
return svc_parse_jobstr(buf, len, &sig, do_signal_svc, NULL);
}
static char query_buf[368]; static char query_buf[368];
static int missing(char *job, char *id) static int missing(char *job, char *id, void *user_data)
{ {
char buf[20]; char buf[20];
@@ -129,7 +153,7 @@ static int missing(char *job, char *id)
static int do_query(char *buf, size_t len) static int do_query(char *buf, size_t len)
{ {
query_buf[0] = 0; query_buf[0] = 0;
if (svc_parse_jobstr(buf, len, NULL, missing)) { if (svc_parse_jobstr(buf, len, NULL, NULL, missing)) {
memcpy(buf, query_buf, len); memcpy(buf, query_buf, len);
return 1; return 1;
} }
@@ -387,7 +411,7 @@ static void api_cb(uev_t *w, void *arg, int events)
break; break;
} }
_e("Request to hand-over wdog ... to PID %d", rq.runlevel); _d("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);
@@ -403,7 +427,7 @@ static void api_cb(uev_t *w, void *arg, int events)
if (wdog->protect) { if (wdog->protect) {
logit(LOG_NOTICE, "Stopping and deleting built-in watchdog."); logit(LOG_NOTICE, "Stopping and deleting built-in watchdog.");
stop(wdog); stop(wdog, NULL);
svc_del(wdog); svc_del(wdog);
} }
} }
@@ -413,7 +437,7 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_SVC_ITER: case INIT_CMD_SVC_ITER:
// _d("svc iter, first: %d", rq.runlevel); // _d("svc iter, first: %d", rq.runlevel);
/* /*
* XXX: This severly limits the number of * XXX: This severely limits the number of
* simultaneous client connections, but will * simultaneous client connections, but will
* have to do for now. * have to do for now.
*/ */
@@ -439,6 +463,13 @@ static void api_cb(uev_t *w, void *arg, int events)
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:
/* runlevel is reused for signal */
_d("svc signal %d: %s", rq.runlevel, rq.data);
strterm(rq.data, sizeof(rq.data));
result = do_signal(rq.data, sizeof(rq.data), rq.runlevel);
break;
default: default:
_d("Unsupported cmd: %d", rq.cmd); _d("Unsupported cmd: %d", rq.cmd);
break; break;
@@ -466,12 +497,13 @@ error:
int api_init(uev_ctx_t *ctx) int api_init(uev_ctx_t *ctx)
{ {
int sd;
mode_t oldmask;
struct sockaddr_un sun = { struct sockaddr_un sun = {
.sun_family = AF_UNIX, .sun_family = AF_UNIX,
.sun_path = INIT_SOCKET, .sun_path = INIT_SOCKET,
}; };
mode_t oldmask;
int uid, gid;
int sd;
_d("Setting up external API socket ..."); _d("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); sd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
@@ -480,20 +512,26 @@ int api_init(uev_ctx_t *ctx)
return 1; return 1;
} }
uid = geteuid();
gid = getgroup(DEFGROUP);
erase(INIT_SOCKET); erase(INIT_SOCKET);
oldmask = umask(0077); oldmask = umask(0117);
if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun))) if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
goto error; goto error;
if (-1 == listen(sd, 10)) if (-1 == listen(sd, 10))
goto error; goto error;
if (chown(INIT_SOCKET, uid, gid))
_pe("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 intializing API socket"); _pe("Failed initializing API socket");
umask(oldmask); umask(oldmask);
close(sd); close(sd);
return 1; return 1;
+65 -13
View File
@@ -24,8 +24,13 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
#include <lite/queue.h> # include <libite/lite.h>
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/lite.h>
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */ #include <sys/sysinfo.h> /* get_nprocs_conf() */
@@ -42,7 +47,7 @@ struct cg {
char *cfg; /* kernel settings */ char *cfg; /* kernel settings */
int active; /* for mark & sweep */ int active; /* for mark & sweep */
int protected; /* for init/, user/, & system/ */ int is_protected; /* for init/, user/, & system/ */
}; };
static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups); static TAILQ_HEAD(, cg) cgroups = TAILQ_HEAD_INITIALIZER(cgroups);
@@ -51,6 +56,7 @@ static char controllers[256];
static struct iwatch iw_cgroup; static struct iwatch iw_cgroup;
static uev_t cgw; static uev_t cgw;
static int avail;
static void cgset(const char *path, char *ctrl, char *prop) static void cgset(const char *path, char *ctrl, char *prop)
@@ -159,6 +165,9 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
int cgroup_user(char *name, int pid) int cgroup_user(char *name, int pid)
{ {
if (!avail)
return 0;
return cgroup_leaf_init("user", name, pid, NULL); return cgroup_leaf_init("user", name, pid, NULL);
} }
@@ -166,6 +175,9 @@ int cgroup_service(char *name, int pid, struct cgroup *cg)
{ {
char *group = "system"; char *group = "system";
if (!avail)
return 0;
if (cg && cg->name[0]) { if (cg && cg->name[0]) {
char path[256]; char path[256];
@@ -302,8 +314,11 @@ void cgroup_mark_all(void)
{ {
struct cg *cg; struct cg *cg;
if (!avail)
return;
TAILQ_FOREACH(cg, &cgroups, link) { TAILQ_FOREACH(cg, &cgroups, link) {
if (cg->protected) if (cg->is_protected)
continue; continue;
cg->active = 0; cg->active = 0;
@@ -318,6 +333,9 @@ void cgroup_cleanup(void)
struct cg *cg, *tmp; struct cg *cg, *tmp;
char path[256]; char path[256];
if (!avail)
return;
TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) { TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) {
if (cg->active) if (cg->active)
continue; continue;
@@ -330,10 +348,13 @@ void cgroup_cleanup(void)
/* /*
* Add, or update, settings for top-level cgroup * Add, or update, settings for top-level cgroup
*/ */
int cgroup_add(char *name, char *cfg, int protected) int cgroup_add(char *name, char *cfg, int is_protected)
{ {
struct cg *cg; struct cg *cg;
if (!avail)
return 0;
if (!name) if (!name)
return -1; return -1;
if (!cfg) if (!cfg)
@@ -364,7 +385,7 @@ int cgroup_add(char *name, char *cfg, int protected)
free(cg); free(cg);
return -1; return -1;
} }
cg->protected = protected; cg->is_protected = is_protected;
cg->active = 1; cg->active = 1;
return 0; return 0;
@@ -378,6 +399,9 @@ int cgroup_del(char *dir)
struct cg *cg; struct cg *cg;
char path[256]; char path[256];
if (!avail)
return 0;
TAILQ_FOREACH(cg, &cgroups, link) { TAILQ_FOREACH(cg, &cgroups, link) {
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name); snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
if (strcmp(path, dir)) if (strcmp(path, dir))
@@ -409,6 +433,9 @@ void cgroup_config(void)
{ {
struct cg *cg; struct cg *cg;
if (!avail)
return;
TAILQ_FOREACH(cg, &cgroups, link) { TAILQ_FOREACH(cg, &cgroups, link) {
char path[256]; char path[256];
int leaf = 0; int leaf = 0;
@@ -437,14 +464,24 @@ void cgroup_init(uev_ctx_t *ctx)
int fd; int fd;
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
if (errno == ENOENT)
logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling.");
else if (errno == EPERM) /* Probably inside an unpriviliged container */
logit(LOG_NOTICE, "Not allowed to mount cgroups v2, disabling.");
else
_pe("Failed mounting cgroup v2"); _pe("Failed mounting cgroup v2");
avail = 0;
return; return;
} }
avail = 1;
/* 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"); _pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers");
abort:
umount(FINIT_CGPATH);
avail = 0;
return; return;
} }
@@ -460,9 +497,26 @@ void cgroup_init(uev_ctx_t *ctx)
fclose(fp); fclose(fp);
/* Check for cpu controller, abort if missing */
if (!strstr(controllers, "+cpu")) {
logit(LOG_NOTICE, "Missing CPU controller, disabling cgroup support.");
goto abort;
}
/* 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"); _pe("Failed enabling %s for %s", controllers, FINIT_CGPATH "/cgroup.subtree_control");
goto abort;
}
/* prepare cgroup.events watcher */
fd = iwatch_init(&iw_cgroup);
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
_pe("Failed setting up cgroup.events watcher");
iwatch_exit(&iw_cgroup);
close(fd);
goto abort;
}
/* Default (protected) groups, PID 1, services, and user/login processes */ /* Default (protected) groups, PID 1, services, and user/login processes */
cgroup_add("init", "cpu.weight:100", 1); cgroup_add("init", "cpu.weight:100", 1);
@@ -471,14 +525,12 @@ void cgroup_init(uev_ctx_t *ctx)
cgroup_config(); cgroup_config();
/* 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 ", FINIT_CGPATH "/init"); _pe("Failed moving PID 1 to cgroup ", FINIT_CGPATH "/init");
uev_io_stop(&cgw);
/* prepare cgroup.events watcher */ iwatch_exit(&iw_cgroup);
fd = iwatch_init(&iw_cgroup);
if (uev_io_init(ctx, &cgw, cgroup_events_cb, NULL, fd, UEV_READ)) {
_pe("Failed setting up cgroup.events watcher");
close(fd); close(fd);
goto abort;
} }
} }
+1 -1
View File
@@ -34,7 +34,7 @@ struct cgroup {
void cgroup_mark_all(void); void cgroup_mark_all(void);
void cgroup_cleanup (void); void cgroup_cleanup (void);
int cgroup_add (char *name, char *cfg, int protected); int cgroup_add (char *name, char *cfg, int is_protected);
int cgroup_del (char *dir); int cgroup_del (char *dir);
void cgroup_config (void); void cgroup_config (void);
+9 -1
View File
@@ -30,7 +30,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <search.h> #include <search.h>
#include <inttypes.h> #include <inttypes.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <sys/sysinfo.h> /* sysinfo() */ #include <sys/sysinfo.h> /* sysinfo() */
#include "cgutil.h" #include "cgutil.h"
@@ -49,6 +53,10 @@ uint64_t total_ram; /* From sysinfo() */
struct cg dummy; /* empty result "NULL" */ struct cg dummy; /* empty result "NULL" */
struct cg *list; struct cg *list;
int cgroup_avail(void)
{
return fismnt(FINIT_CGPATH);
}
char *pid_cmdline(int pid, char *buf, size_t len) char *pid_cmdline(int pid, char *buf, size_t len)
{ {
+2
View File
@@ -52,6 +52,8 @@ struct cg {
} cg_cpu; } cg_cpu;
}; };
int cgroup_avail(void);
char *pid_cmdline (int pid, char *buf, size_t len); char *pid_cmdline (int pid, char *buf, size_t len);
char *pid_comm (int pid, char *buf, size_t len); char *pid_comm (int pid, char *buf, size_t len);
char *pid_cgroup (int pid, char *buf, size_t len); char *pid_cgroup (int pid, char *buf, size_t len);
+5 -3
View File
@@ -86,11 +86,13 @@ int client_send(struct init_request *rq, ssize_t len)
} }
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLIN; pfd.events = POLLIN | POLLERR | POLLHUP;
if ((rc = poll(&pfd, 1, 2000)) <= 0) { if ((rc = poll(&pfd, 1, 2000)) <= 0) {
if (rc) if (rc) {
if (errno == EINTR) /* shutdown/reboot */
goto exit;
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; goto exit;
} }
+6 -2
View File
@@ -23,8 +23,12 @@
#include <ftw.h> #include <ftw.h>
#include <libgen.h> #include <libgen.h>
#include <lite/lite.h>
#include <stdio.h> #include <stdio.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
@@ -273,7 +277,7 @@ 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 condtion path %s, cannot %sassert", fpath, set ? "re" : "de"); _e("Incorrect condition path %s, cannot %sassert", fpath, set ? "re" : "de");
return 1; return 1;
} }
+5 -1
View File
@@ -21,8 +21,12 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h>
#include <stdio.h> #include <stdio.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
+2 -1
View File
@@ -2,7 +2,8 @@
#define FINIT_COND_H_ #define FINIT_COND_H_
#include <paths.h> #include <paths.h>
#include <svc.h>
#include "svc.h"
#define COND_BASE "finit/cond" #define COND_BASE "finit/cond"
#define COND_PID "pid/" #define COND_PID "pid/"
+70 -21
View File
@@ -28,14 +28,20 @@
#include <string.h> #include <string.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
#include <lite/queue.h> /* BSD sys/queue.h API */ # include <libite/lite.h>
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/lite.h>
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include <time.h> #include <time.h>
#include <glob.h> #include <glob.h>
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
#include "iwatch.h" #include "iwatch.h"
#include "private.h"
#include "service.h" #include "service.h"
#include "tty.h" #include "tty.h"
#include "helpers.h" #include "helpers.h"
@@ -82,6 +88,7 @@ fallback:
/* /*
* finit.debug = [on,off] * finit.debug = [on,off]
* finit.show_status = [on,off] * finit.show_status = [on,off]
* finit.status_style = [old,classic,modern]
*/ */
static void parse_finit_opts(char *opt) static void parse_finit_opts(char *opt)
{ {
@@ -165,39 +172,57 @@ static void parse_kernel_cmdline(void)
#define parse_kernel_cmdline() #define parse_kernel_cmdline()
#endif #endif
static void parse_kernel_loglevel(void)
{
char line[LINE_SIZE], *ptr;
FILE *fp;
int val;
fp = fopen("/proc/sys/kernel/printk", "r");
if (!fp)
return;
if (!fgets(line, sizeof(line), fp)) {
fclose(fp);
return;
}
fclose(fp);
ptr = chomp(line);
_d("%s", ptr);
val = atoi(ptr);
if (val >= 7)
kerndebug = 1;
}
/* /*
* Kernel gives us all non-kernel options on our cmdline * Kernel gives us all non-kernel options on our cmdline
*/ */
void conf_parse_cmdline(int argc, char *argv[]) void conf_parse_cmdline(int argc, char *argv[])
{ {
int dbg = 0;
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++)
parse_arg(argv[i]); parse_arg(argv[i]);
parse_kernel_cmdline(); parse_kernel_cmdline();
parse_kernel_loglevel();
log_init(dbg); log_init();
} }
static int kmod_exists(char *mod) static int kmod_exists(char *mod)
{ {
char buf[256];
int found = 0; int found = 0;
FILE *fp; FILE *fp;
char buf[256];
size_t len;
for (len = 0; mod[len]; len++) {
if (mod[len] == ' ' || mod[len] == '\t')
break;
}
fp = fopen("/proc/modules", "r"); fp = fopen("/proc/modules", "r");
if (!fp) if (!fp)
return 0; return 0;
while (!found && (fgets(buf, sizeof(buf), fp))) { while (!found && fgets(buf, sizeof(buf), fp)) {
if (!strncmp(buf, mod, len)) char *kmod = strtok(buf, " \t");
if (kmod && !strcmp(kmod, mod))
found = 1; found = 1;
} }
fclose(fp); fclose(fp);
@@ -205,22 +230,26 @@ static int kmod_exists(char *mod)
return found; return found;
} }
static void kmod_load(char *line) static void kmod_load(char *mod)
{ {
char *mod; char module[64] = { 0 };
char cmd[CMD_SIZE]; char cmd[CMD_SIZE];
if (runlevel != 0) if (runlevel != 0)
return; return;
mod = strip_line(line); /* Strip args for progress below and kmod_exists() */
if (kmod_exists(mod)) strlcpy(module, mod, sizeof(module));
if (!strtok(module, " \t"))
return;
if (kmod_exists(module))
return; return;
strcpy(cmd, "modprobe "); strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd)); strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod); run_interactive(cmd, "Loading kernel module %s", module);
} }
/* Convert optional "[!123456789S]" string into a bitmask */ /* Convert optional "[!123456789S]" string into a bitmask */
@@ -473,7 +502,7 @@ static void parse_static(char *line, int is_rcsd)
/* Kernel module to load */ /* Kernel module to load */
if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) { if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) {
kmod_load(x); kmod_load(strip_line(x));
return; return;
} }
@@ -544,6 +573,26 @@ static void parse_static(char *line, int is_rcsd)
cfglevel = 2; /* Fallback */ cfglevel = 2; /* Fallback */
return; return;
} }
/*
* Periodic check and instability index leveler, seconds
*/
if (MATCH_CMD(line, "service-interval ", x)) {
char *token = strip_line(x);
const char *err = NULL;
int val;
/* 0 min to 1 day, should check at least daily */
val = strtonum(token, 0, 1440, &err);
if (!err) {
int disabled = !service_interval;
service_interval = val * 1000; /* to milliseconds */
if (disabled)
service_init();
}
return;
}
} }
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file) static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
@@ -580,7 +629,7 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return; return;
} }
/* Read contrl group limits */ /* Read control group limits */
if (MATCH_CMD(line, "cgroup ", x)) { if (MATCH_CMD(line, "cgroup ", x)) {
conf_parse_cgroup(x); conf_parse_cgroup(x);
return; return;
+63 -21
View File
@@ -28,12 +28,17 @@
#include <dirent.h> #include <dirent.h>
#include <err.h> #include <err.h>
#include <stdarg.h> #include <stdarg.h>
#include <sysexits.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/wait.h> #include <sys/wait.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 <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <linux/vt.h> #include <linux/vt.h>
#include "finit.h" #include "finit.h"
@@ -66,16 +71,42 @@ int complete(char *cmd, int pid)
return status; return status;
} }
int run(char *cmd) static int do_redirect(void)
{
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
return fclose(fp);
}
return -1;
}
/*
* Run 'cmd' and wait for completion. If 'log' is not NULL any output
* from the command is logged with the given string as prefix, use "" to
* skip. If 'log' is NULL all output is redirected to /dev/null, which
* is what this function originally did.
*/
int run(char *cmd, char *log)
{ {
int status, result, i = 0; int status, result, i = 0;
char *args[NUM_ARGS + 1], *arg, *backup; char *args[NUM_ARGS + 1], *arg;
char *backup = NULL;
pid_t pid; pid_t pid;
if (!log) {
/* We must create a copy that is possible to modify. */ /* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd); backup = arg = strdup(cmd);
if (!arg) if (!arg)
return 1; /* Failed allocating a string to be modified. */ return 1;
/* Split command line into tokens of an argv[] array. */ /* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t "); args[i++] = strsep(&arg, "\t ");
@@ -103,29 +134,38 @@ int run(char *cmd)
errno = EOVERFLOW; errno = EOVERFLOW;
return 1; return 1;
} }
}
pid = fork(); pid = fork();
if (0 == pid) { if (0 == pid) {
FILE *fp; int rc = EX_OSERR;
setsid(); setsid();
sig_unblock();
if (!log) {
do_redirect();
execvp(args[0], args);
} else {
char *pfx = *log ? ": " : "";
FILE *pp;
/* Always redirect stdio for run() */ pp = popen(cmd, "r");
fp = fopen("/dev/null", "w"); if (pp) {
if (fp) { char buf[256];
int fd = fileno(fp);
dup2(fd, STDIN_FILENO); while (fgets(buf, sizeof(buf), pp)) {
dup2(fd, STDOUT_FILENO); chomp(buf);
dup2(fd, STDERR_FILENO); logit(LOG_NOTICE, "%s%s%s", log, pfx, buf);
} }
sig_unblock(); rc = pclose(pp);
execvp(args[0], args); }
}
_exit(1); /* Only if execv() fails. */ _exit(rc);
} else if (-1 == pid) { } else if (-1 == pid) {
_pe("%s", args[0]); _pe("%s", args[0]);
if (backup)
free(backup); free(backup);
return -1; return -1;
@@ -133,21 +173,23 @@ int run(char *cmd)
status = complete(args[0], pid); status = complete(args[0], pid);
if (-1 == status) { if (-1 == status) {
if (backup)
free(backup); free(backup);
return 1; return 1;
} }
result = WEXITSTATUS(status); result = WEXITSTATUS(status);
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
_d("Started %s and ended OK: %d", args[0], result); _d("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", args[0], WTERMSIG(status)); _d("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
* change their return code accordingly. --Jocke */ * change their return code accordingly. --Jocke */
} }
if (backup)
free(backup); free(backup);
return result; return result;
@@ -182,7 +224,7 @@ int run_interactive(char *cmd, char *fmt, ...)
} }
/* Run cmd ... */ /* Run cmd ... */
status = run(cmd); status = run(cmd, "");
/* Restore stderr/stdout */ /* Restore stderr/stdout */
if (fp && !debug) { if (fp && !debug) {
@@ -218,7 +260,6 @@ int run_interactive(char *cmd, char *fmt, ...)
int exec_runtask(char *cmd, char *args[]) int exec_runtask(char *cmd, char *args[])
{ {
size_t i;
char buf[1024] = ""; char buf[1024] = "";
char *argv[4] = { char *argv[4] = {
"sh", "sh",
@@ -226,6 +267,7 @@ int exec_runtask(char *cmd, char *args[])
buf, buf,
NULL NULL
}; };
size_t i;
strlcat(buf, cmd, sizeof(buf)); strlcat(buf, cmd, sizeof(buf));
for (i = 1; args[i]; i++) { for (i = 1; args[i]; i++) {
@@ -282,7 +324,7 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
static int activate_console(int noclear, int nowait) static int activate_console(int noclear, int nowait)
{ {
static const char msg[] = "\nPlease press Enter to activate this console."; static const char msg[] = "\nPlease press Enter to activate this console.";
static const char clr[] = "\r\e[2K"; 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 c;
@@ -351,7 +393,7 @@ pid_t run_getty(char *tty, char *cmd, char *args[], int noclear, int nowait, str
/* Dunno speed, tell stty() to not mess with it */ /* Dunno speed, tell stty() to not mess with it */
prepare_tty(tty, B0, "getty", rlimit); prepare_tty(tty, B0, "getty", rlimit);
if (activate_console(noclear, nowait)) { if (activate_console(noclear, nowait)) {
logit(LOG_INFO, "Starting external getty on %s", tty); logit(LOG_INFO, "Starting getty on %s", tty);
rc = execv(cmd, args); rc = execv(cmd, args);
} }
+83 -30
View File
@@ -36,7 +36,11 @@
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/stat.h> /* umask(), mkdir() */ #include <sys/stat.h> /* umask(), mkdir() */
#include <sys/wait.h> #include <sys/wait.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "cgroup.h" #include "cgroup.h"
@@ -61,6 +65,7 @@ int debug = 0; /* debug mode from kernel cmdline */
int rescue = 0; /* rescue mode from kernel cmdline */ 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 */
char *sdown = NULL; char *sdown = NULL;
char *network = NULL; char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
@@ -82,7 +87,7 @@ static void banner(void)
* similar enabled to start emptying /dev/kmsg, but for * similar enabled to start emptying /dev/kmsg, but for
* our progress we want to own the console. * our progress we want to own the console.
*/ */
if (!debug) if (!debug && !kerndebug)
klogctl(6, NULL, 0); klogctl(6, NULL, 0);
/* /*
@@ -117,6 +122,7 @@ static int fsck(int pass)
while ((fs = getfsent())) { while ((fs = getfsent())) {
char cmd[80]; char cmd[80];
struct stat st; struct stat st;
int fsck_rc = 0;
if (fs->fs_passno != pass) if (fs->fs_passno != pass)
continue; continue;
@@ -134,8 +140,33 @@ static int fsck(int pass)
continue; continue;
} }
#ifdef FSCK_FIX
snprintf(cmd, sizeof(cmd), "fsck -yf %s", fs->fs_spec);
#else
snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec); snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
rc += run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec); #endif
fsck_rc = run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
/*
* "failure" is defined as exiting with a return code of
* 2 or larger. A return code of 1 indicates that filesystem
* errors were corrected but that the boot may proceed.
*/
if (fsck_rc > 1) {
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
break;
}
do_shutdown(SHUT_REBOOT);
}
rc += fsck_rc;
} }
endfsent(); endfsent();
@@ -145,17 +176,31 @@ static int fsck(int pass)
static int fsck_all(void) static int fsck_all(void)
{ {
int pass, rc = 0; int rc = 0;
#ifndef FAST_BOOT
int pass;
for (pass = 1; pass < 10; pass++) { for (pass = 1; pass < 10; pass++) {
rc = fsck(pass); rc = fsck(pass);
if (rc) if (rc)
break; break;
} }
#endif
return rc; return rc;
} }
/* Wrapper for mount(2), logs any errors to stderr */
static void fs_mount(const char *src, const char *tgt, const char *fstype,
unsigned long flags, const void *data)
{
const char *msg = !fstype ? "MS_MOVE" : "mounting";
int rc;
rc = mount(src, tgt, fstype, flags, data);
if (rc && errno != EBUSY)
_pe("Failed %s %s on %s", msg, src, tgt);
}
#ifndef SYSROOT #ifndef SYSROOT
/* If / is not listed in fstab, or listed as 'ro', leave it alone */ /* If / is not listed in fstab, or listed as 'ro', leave it alone */
static int fs_readonly_root(struct fstab *fs) static int fs_readonly_root(struct fstab *fs)
@@ -207,8 +252,7 @@ static void fs_remount_root(int fsckerr)
* XXX: Untested, in the initramfs age we should * XXX: Untested, in the initramfs age we should
* probably use switch_root instead. * probably use switch_root instead.
*/ */
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL)) fs_mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
_pe("Failed %s / MS_MOVE");
} }
#endif /* SYSROOT */ #endif /* SYSROOT */
@@ -232,7 +276,7 @@ static void fs_finalize(void)
*/ */
if (!fismnt("/dev/shm")) { if (!fismnt("/dev/shm")) {
makedir("/dev/shm", 0777); makedir("/dev/shm", 0777);
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777"); fs_mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
} }
/* Modern systems use /dev/pts */ /* Modern systems use /dev/pts */
@@ -250,7 +294,7 @@ static void fs_finalize(void)
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode); snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
makedir("/dev/pts", 0755); makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts); fs_mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
} }
/* /*
@@ -263,19 +307,19 @@ static void fs_finalize(void)
* for /run (and optionally /run/lock). * for /run (and optionally /run/lock).
*/ */
if (fisdir("/run") && !fismnt("/run")) { if (fisdir("/run") && !fismnt("/run")) {
mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%"); fs_mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */ /* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
makedir("/run/lock", 1777); makedir("/run/lock", 1777);
mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880"); fs_mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
} }
/* Modern systems use tmpfs for /tmp */ /* Modern systems use tmpfs for /tmp */
if (!fismnt("/tmp")) if (!fismnt("/tmp"))
mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777"); fs_mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
} }
static void fs_mount(void) static void fs_mount_all(void)
{ {
if (!rescue) if (!rescue)
fs_remount_root(fsck_all()); fs_remount_root(fsck_all());
@@ -289,7 +333,8 @@ static void fs_mount(void)
_d("Calling extra mount hook, after mount -a ..."); _d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST); plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea"); if (whichp("swapon"))
run("swapon -ea", "swapon");
_d("Finalize, ensure common file systems are available ..."); _d("Finalize, ensure common file systems are available ...");
fs_finalize(); fs_finalize();
@@ -299,9 +344,9 @@ static void fs_mount(void)
* We need /proc for rs_remount_root() and conf_parse_cmdline(), /dev * We need /proc for rs_remount_root() and conf_parse_cmdline(), /dev
* for early multi-console, and /sys for the cgroups support. Any * for early multi-console, and /sys for the cgroups support. Any
* occurrence of these file systems in /etc/fstab will replace these * occurrence of these file systems in /etc/fstab will replace these
* mounts later in fs_mount() * mounts later in fs_mount_all()
* *
* Ignore any mount errors with EBUSY, kernel likely alread mounted * Ignore any mount errors with EBUSY, kernel likely already mounted
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT. * the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
*/ */
static void fs_init(void) static void fs_init(void)
@@ -319,8 +364,6 @@ static void fs_init(void)
umask(022); umask(022);
for (i = 0; i < NELEMS(fs); i++) { for (i = 0; i < NELEMS(fs); i++) {
int rc;
/* /*
* Check if already mounted, we may be running in a * Check if already mounted, we may be running in a
* container, or an initramfs ran before us. The * container, or an initramfs ran before us. The
@@ -330,9 +373,7 @@ static void fs_init(void)
if (fismnt(fs[i].file)) if (fismnt(fs[i].file))
continue; continue;
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL); fs_mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
if (rc && errno != EBUSY)
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
} }
} }
@@ -382,7 +423,7 @@ static void finalize(void *unused)
static void crank_worker(void *unused) static void crank_worker(void *unused)
{ {
/* /*
* Initalize state machine and start all bootstrap tasks * Initialize state machine and start all bootstrap tasks
* NOTE: no network available! * NOTE: no network available!
*/ */
sm_init(&sm); sm_init(&sm);
@@ -519,11 +560,11 @@ static int telinit(int argc, char *argv[])
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct wq crank = { struct wq crank_work = {
.cb = crank_worker, .cb = crank_worker,
.delay = 10 .delay = 10
}; };
struct wq bootstrap = { struct wq bootstrap_work = {
.cb = bootstrap_worker, .cb = bootstrap_worker,
.delay = 100 .delay = 100
}; };
@@ -550,7 +591,7 @@ int main(int argc, char *argv[])
console_init(); console_init();
/* /*
* Initalize event context. * Initialize event context.
*/ */
uev_init1(&loop, 1); uev_init1(&loop, 1);
ctx = &loop; ctx = &loop;
@@ -597,6 +638,11 @@ int main(int argc, char *argv[])
enable_progress(1); /* Allow progress, if enabled */ enable_progress(1); /* Allow progress, if enabled */
banner(); banner();
if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel S", osheading);
else
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel S");
/* /*
* Initial setup of signals, ignore all until we're up. * Initial setup of signals, ignore all until we're up.
*/ */
@@ -608,13 +654,15 @@ int main(int argc, char *argv[])
cgroup_init(&loop); cgroup_init(&loop);
/* Check and mount filesystems. */ /* Check and mount filesystems. */
fs_mount(); fs_mount_all();
/* Bootstrap conditions, needed for hooks */ /* Bootstrap conditions, needed for hooks */
cond_init(); cond_init();
/* Emit conditions for early hooks that ran before the /*
* condition system was initialized in case anyone . */ * Emit conditions for early hooks that ran before the condition
* system was initialized in case anyone.
*/
cond_set_oneshot(plugin_hook_str(HOOK_BANNER)); cond_set_oneshot(plugin_hook_str(HOOK_BANNER));
cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP)); cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP));
@@ -653,10 +701,15 @@ int main(int argc, char *argv[])
api_init(&loop); api_init(&loop);
_d("Starting the big state machine ..."); _d("Starting the big state machine ...");
schedule_work(&crank); schedule_work(&crank_work);
_d("Starting bootstrap finalize timer ..."); _d("Starting bootstrap finalize timer ...");
schedule_work(&bootstrap); schedule_work(&bootstrap_work);
/*
* Background service tasks
*/
service_init();
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
+7 -1
View File
@@ -34,7 +34,11 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <uev/uev.h> #include <uev/uev.h>
/* just in case */ /* just in case */
@@ -91,6 +95,7 @@
#define INIT_CMD_SVC_QUERY 130 #define INIT_CMD_SVC_QUERY 130
#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_NACK 254 #define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255 #define INIT_CMD_ACK 255
@@ -110,6 +115,7 @@ extern int debug;
extern int rescue; extern int rescue;
extern int single; extern int single;
extern int bootstrap; extern int bootstrap;
extern int kerndebug;
extern char *rcsd; extern char *rcsd;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
+16 -2
View File
@@ -25,6 +25,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <limits.h> /* LOGIN_NAME_MAX */
#include <paths.h> #include <paths.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@@ -33,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 <unistd.h> /* sysconf() */
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -42,6 +44,12 @@
#define _PATH_LOGIN "/bin/login" #define _PATH_LOGIN "/bin/login"
#endif #endif
#ifndef LOGIN_NAME_MIN
#define LOGIN_NAME_MIN 64
#endif
static long logname_len = 32; /* useradd(1) limit at 32 chars */
/* /*
* Read one character from stdin. * Read one character from stdin.
*/ */
@@ -196,8 +204,8 @@ static int do_login(char *name)
static int getty(char *tty, speed_t speed, char *term, char *user) static int getty(char *tty, speed_t speed, char *term, char *user)
{ {
const char cln[] = "\r\e[2K\n"; const char cln[] = "\r\e[K\n";
char name[33]; /* useradd(1) limit at 32 chars */ char name[logname_len + 1]; /* +1 for NUL termination */
pid_t sid; pid_t sid;
/* /*
@@ -263,6 +271,12 @@ int main(int argc, char *argv[])
if (optind < argc) if (optind < argc)
term = argv[optind++]; term = argv[optind++];
logname_len = sysconf(_SC_LOGIN_NAME_MAX);
if (logname_len == -1i)
logname_len = LOGIN_NAME_MAX;
if (logname_len < LOGIN_NAME_MIN)
logname_len = LOGIN_NAME_MIN;
return getty(tty, atoi(speed), term, NULL); return getty(tty, atoi(speed), term, NULL);
} }
+75 -61
View File
@@ -30,12 +30,18 @@
#include <net/if.h> #include <net/if.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdarg.h> #include <stdarg.h>
#include <sysexits.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "sig.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
@@ -53,7 +59,7 @@ static pstyle_t progress_style = PROGRESS_DEFAULT;
#define STATUS_CLASS { \ #define STATUS_CLASS { \
CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \ CHOOSE(" OK ", " OK ", "\e[1;32m"), /* Green */ \
CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \ CHOOSE("FAIL", "FAIL", "\e[1;31m"), /* Red */ \
CHOOSE("WARN", "WARN", "\e[1;31m"), /* Red */ \ CHOOSE("WARN", "WARN", "\e[1;33m"), /* Yellow */ \
CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \ CHOOSE(" \\/ ", " ⋯ ", "\e[1;33m"), /* Yellow */ \
} }
@@ -223,7 +229,7 @@ void print_banner(const char *heading)
return; return;
buf[0] = 0; buf[0] = 0;
strlcat(buf, "\r\e[2K", sizeof(buf)); strlcat(buf, "\r\e[K", sizeof(buf));
if (progress_style == PROGRESS_CLASSIC) { if (progress_style == PROGRESS_CLASSIC) {
strlcat(buf, "\e[1m", sizeof(buf)); strlcat(buf, "\e[1m", sizeof(buf));
@@ -308,7 +314,7 @@ void printv(const char *fmt, va_list ap)
if (progress_style == PROGRESS_CLASSIC) if (progress_style == PROGRESS_CLASSIC)
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf))); cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
else else
cprintf("\r\e[2K%s%s", status(3), buf); cprintf("\r\e[K%s%s", status(3), buf);
} }
void print(int rc, const char *fmt, ...) void print(int rc, const char *fmt, ...)
@@ -489,27 +495,46 @@ void networking(int updown)
/* Debian/Ubuntu/Busybox/RH/Suse */ /* Debian/Ubuntu/Busybox/RH/Suse */
if (!whichp("ifup")) if (!whichp("ifup"))
goto done; goto fallback;
if (fexist("/etc/network/interfaces")) { if (fexist("/etc/network/interfaces")) {
pid_t pid; pid_t pid;
if (!updown) {
run_interactive("ifdown -a", "Stopping networking");
goto done;
}
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
if (updown) int rc = EX_OSERR;
run("ifup -a"); FILE *pp;
else
run("ifdown -a"); setsid();
_exit(0); sig_unblock();
pp = popen("ifup -a 2>&1", "r");
if (pp) {
char buf[256];
while (fgets(buf, sizeof(buf), pp))
logit(LOG_NOTICE, "network: %s", buf);
rc = pclose(pp);
}
_exit(rc);
} }
cgroup_service("network", pid, NULL); cgroup_service("network", pid, NULL);
print(pid > 0 ? 0 : 1, "Bringing up network interfaces ...");
print(pid > 0 ? 0 : 1, "%s networking", updown ? "Starting" : "Stopping"); goto done;
} }
done: fallback:
/* Fall back to bring up at least loopback */ /* Fall back to bring up at least loopback */
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown); ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
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) {
@@ -518,6 +543,44 @@ done:
} }
} }
/*
* Very basic, and incomplete, check if we're running in a container.
*/
int in_container(void)
{
const char *containers[] = {
"lxc",
"docker",
"kubepod",
"unshare"
};
const char *files[] = {
"/run/.containerenv",
"/.dockerenv"
};
static int incont = -1;
size_t i;
char *c;
if (incont > 0)
return incont;
c = getenv("container");
if (c) {
for (i = 0; i < NELEMS(containers); i++) {
if (!strcmp(containers[i], c))
return incont = 1;
}
}
for (i = 0; i < NELEMS(files); i++) {
if (!access(files[i], F_OK))
return incont = 1;
}
return 0;
}
#ifndef HAVE_GETFSENT #ifndef HAVE_GETFSENT
static lfile_t *fstab = NULL; static lfile_t *fstab = NULL;
@@ -560,55 +623,6 @@ void endfsent(void)
} }
#endif /* HAVE_GETFSENT */ #endif /* HAVE_GETFSENT */
static int hasopt(char *opts, char *opt)
{
char buf[strlen(opts) + 1];
char *ptr;
memcpy(buf, opts, sizeof(buf));
ptr = strtok(buf, ",");
while (ptr) {
if (!strcmp(ptr, opt))
return 1;
ptr = strtok(NULL, ",");
}
return 0;
}
int ismnt(char *file, char *dir, char *mode)
{
struct mntent *mnt;
int found = 0;
FILE *fp;
fp = setmntent(file, "r");
if (!fp)
return 0; /* Dunno, maybe not */
while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, dir)) {
if (mode) {
if (hasopt(mnt->mnt_opts, mode))
found = 1;
} else
found = 1;
break;
}
}
endmntent(fp);
return found;
}
/* Requires /proc to be mounted */
int fismnt(char *dir)
{
return ismnt("/proc/mounts", dir, NULL);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+7 -5
View File
@@ -32,7 +32,11 @@
#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>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "log.h" #include "log.h"
#define PROGRESS_DEFAULT PROGRESS_MODERN #define PROGRESS_DEFAULT PROGRESS_MODERN
@@ -89,9 +93,10 @@ int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname); void set_hostname (char **hostname);
void networking (int updown); void networking (int updown);
int in_container (void);
int complete (char *cmd, int pid); int complete (char *cmd, int pid);
int run (char *cmd); int run (char *cmd, char *log);
int run_interactive (char *cmd, char *fmt, ...); int run_interactive (char *cmd, char *fmt, ...);
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[]);
@@ -126,9 +131,6 @@ static inline int dprint(int fd, const char *s, size_t len)
return rc; return rc;
} }
int ismnt (char *file, char *dir, char *mode);
int fismnt (char *dir);
#endif /* FINIT_HELPERS_H_ */ #endif /* FINIT_HELPERS_H_ */
/** /**
+229 -85
View File
@@ -33,7 +33,11 @@
#include <time.h> #include <time.h>
#include <utmp.h> #include <utmp.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "client.h" #include "client.h"
#include "cond.h" #include "cond.h"
@@ -47,6 +51,8 @@ struct cmd {
char *cmd; char *cmd;
struct cmd *ctx; struct cmd *ctx;
int (*cb)(char *arg); int (*cb)(char *arg);
int *cond;
int (*cb_multiarg)(int argc, char **argv);
}; };
int icreate = 0; int icreate = 0;
@@ -58,6 +64,9 @@ int verbose = 0;
int plain = 0; int plain = 0;
int quiet = 0; int quiet = 0;
int runlevel = 0; int runlevel = 0;
int cgrp = 0;
int utmp = 0;
int iw, pw; int iw, pw;
extern int reboot_main(int argc, char *argv[]); extern int reboot_main(int argc, char *argv[]);
@@ -152,8 +161,11 @@ static int do_log(char *svc)
if (!svc || !svc[0]) if (!svc || !svc[0])
svc = "finit"; svc = "finit";
if (!fexist(logfile)) if (!fexist(logfile)) {
logfile = "/var/log/messages"; logfile = "/var/log/messages";
if (!fexist(logfile))
return 0; /* bail out, maybe in container */
}
return systemf("cat %s | grep %s | tail -10", logfile, svc); return systemf("cat %s | grep %s | tail -10", logfile, svc);
} }
@@ -167,11 +179,11 @@ static int do_runlevel(char *arg)
if (!arg) { if (!arg) {
int prevlevel = 0; int prevlevel = 0;
int runlevel; int currlevel;
char prev; char prev;
runlevel = runlevel_get(&prevlevel); currlevel = runlevel_get(&prevlevel);
if (255 == runlevel) { if (255 == currlevel) {
printf("unknown\n"); printf("unknown\n");
return 0; return 0;
} }
@@ -180,7 +192,7 @@ static int do_runlevel(char *arg)
if (prev <= '0' || prev > '9') if (prev <= '0' || prev > '9')
prev = 'N'; prev = 'N';
printf("%c %d\n", prev , runlevel); printf("%c %d\n", prev , currlevel);
return 0; return 0;
} }
@@ -267,6 +279,61 @@ static int do_restart(char *arg)
return do_startstop(INIT_CMD_RESTART_SVC, arg); return do_startstop(INIT_CMD_RESTART_SVC, arg);
} }
/**
* do_signal - Ask finit to send a signal to a service.
* @argv: must point to an array of strings, containing a service
* and signal name, in that order.
* @argc: must be 2.
*
* A signal can be a complete signal name such as "SIGHUP", or
* it can be the shortest unique name, such as "HUP" (no SIG prefix).
* It can also be a raw signal number, such as "9" (SIGKILL).
*/
int do_signal(int argc, char *argv[])
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_QUERY
};
int signo;
if (argc != 2)
errx(1, "invalid number of arguments to signal");
/* Validate service name */
strlcpy(rq.data, argv[0], sizeof(rq.data));
if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such task or service(s): %s\n\n", argv[0]);
goto show_usage;
}
/* Validate signal name (or number) */
signo = str2sig(argv[1]);
if (signo == -1) {
/* Not a signal name, is it an actual signal number? */
errno = 0;
signo = (int)strtol(argv[1], NULL, 10);
/* Was it a number? Was it a signo that finit recognizes? */
if (errno || !*(sig2str(signo))) {
fprintf(stderr, "Not a valid signal (or signo): %s\n", argv[1]);
goto show_usage;
}
}
/* Reuse runlevel for signal number. */
rq.magic = INIT_MAGIC;
rq.cmd = INIT_CMD_SIGNAL;
rq.runlevel = signo;
strlcpy(rq.data, argv[0], sizeof(rq.data));
return client_send(&rq, sizeof(rq));
show_usage:
fprintf(stderr, "Usage: initctl signal <NAME>[:ID] <S>\n");
return 1;
}
static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{ {
const char *cond, *asserted; const char *cond, *asserted;
@@ -284,7 +351,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
if (strncmp("pid/", cond, 4) == 0) { if (strncmp("pid/", cond, 4) == 0) {
svc_t *svc; svc_t *svc;
svc= client_svc_find_by_cond(cond); svc = client_svc_find_by_cond(cond);
if (!svc) { if (!svc) {
nm = "unknown"; nm = "unknown";
pid = 0; pid = 0;
@@ -468,15 +535,12 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
int utmp_show(char *file) int utmp_show(char *file)
{ {
static int once = 0;
struct tm *sectm;
struct utmp *ut; struct utmp *ut;
time_t sec; time_t sec;
int pid;
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
if (heading) { if (heading) {
static int once = 0;
print_header("%s%s ", once ? "\n" : "", file); print_header("%s%s ", once ? "\n" : "", file);
once++; once++;
} }
@@ -484,7 +548,12 @@ int utmp_show(char *file)
setutent(); setutent();
while ((ut = getutent())) { while ((ut = getutent())) {
char user[sizeof(ut->ut_user) + 1];
char id[sizeof(ut->ut_id) + 1];
struct tm *sectm;
char when[80];
char addr[64]; char addr[64];
int pid;
strlcpy(id, ut->ut_id, sizeof(id)); strlcpy(id, ut->ut_id, sizeof(id));
strlcpy(user, ut->ut_user, sizeof(user)); strlcpy(user, ut->ut_user, sizeof(user));
@@ -509,7 +578,7 @@ int utmp_show(char *file)
static int do_utmp(char *file) static int do_utmp(char *file)
{ {
if (!has_utmp()) if (!utmp)
return 1; return 1;
if (fexist(file)) if (fexist(file))
@@ -538,7 +607,7 @@ static int show_version(char *arg)
* Pointer to string on the form [2345], may be up to 12 chars long, * Pointer to string on the form [2345], may be up to 12 chars long,
* plus escape sequences for highlighting current runlevel. * plus escape sequences for highlighting current runlevel.
*/ */
char *runlevel_string(int runlevel, int levels) char *runlevel_string(int currlevel, int levels)
{ {
int i, pos = 1; int i, pos = 1;
static char lvl[21]; static char lvl[21];
@@ -548,7 +617,7 @@ char *runlevel_string(int runlevel, int levels)
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
if (ISSET(levels, i)) { if (ISSET(levels, i)) {
if (!plain && runlevel == i) if (!plain && currlevel == i)
pos = strlcat(lvl, "\e[1m", sizeof(lvl)); pos = strlcat(lvl, "\e[1m", sizeof(lvl));
if (i == 0) if (i == 0)
@@ -556,7 +625,7 @@ char *runlevel_string(int runlevel, int levels)
else else
lvl[pos++] = '0' + i; lvl[pos++] = '0' + i;
if (!plain && runlevel == i) if (!plain && currlevel == i)
pos = strlcat(lvl, "\e[0m", sizeof(lvl)); pos = strlcat(lvl, "\e[0m", sizeof(lvl));
} else { } else {
lvl[pos++] = '-'; lvl[pos++] = '-';
@@ -699,28 +768,47 @@ static int show_status(char *arg)
{ {
char ident[MAX_IDENT_LEN]; char ident[MAX_IDENT_LEN];
char buf[512]; char buf[512];
int num = 0;
svc_t *svc; svc_t *svc;
runlevel = runlevel_get(NULL); runlevel = runlevel_get(NULL);
if (arg && arg[0]) { while (arg && arg[0]) {
long now = jiffies(); long now = jiffies();
char uptm[42] = "N/A"; char uptm[42] = "N/A";
char grbuf[128]; char *pidfn = NULL;
char path[256]; int exact = 0;
struct cg *cg;
char *group; for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
svc_ident(svc, ident, sizeof(ident));
if (string_compare(ident, arg))
num++;
if (string_case_compare(ident, arg))
exact++;
}
if (num > 1 && !exact)
break;
svc = client_svc_find(arg); svc = client_svc_find(arg);
if (!svc) if (!svc)
return 255; return 255;
if (quiet) if (quiet) {
if (svc_is_runtask(svc))
if (svc->started)
return 0;
else
return 1;
else
return svc->state != SVC_RUNNING_STATE; return svc->state != SVC_RUNNING_STATE;
}
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf)); pidfn = svc->pidfile;
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group); if (pidfn[0] == '!')
cg = cg_conf(path); pidfn++;
else if (pidfn[0] == 0)
pidfn = "none";
printf(" Status : %s\n", status(svc, 1)); printf(" Status : %s\n", status(svc, 1));
printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident))); printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
@@ -729,19 +817,32 @@ static int show_status(char *arg)
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf))); printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf))); printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf))); printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf)));
printf(" PID file : %s\n", svc->pidfile); printf(" PID file : %s\n", pidfn);
printf(" PID : %d\n", svc->pid); printf(" PID : %d\n", svc->pid);
printf(" User : %s\n", svc->username); printf(" User : %s\n", svc->username);
printf(" Group : %s\n", svc->group); printf(" Group : %s\n", svc->group);
printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm); printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
if (svc->pid > 1) { if (cgrp && svc->pid > 1) {
char grbuf[128];
char path[256];
struct cg *cg;
char *group;
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf));
if (!group)
goto no_cgroup; /* ... or PID doesn't exist (anymore) */
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
cg = cg_conf(path);
printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm))); printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n", printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n",
group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max, group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max,
cg->cg_mem.min, cg->cg_mem.max); cg->cg_mem.min, cg->cg_mem.max);
show_cgroup_tree(group, " "); show_cgroup_tree(group, " ");
} }
no_cgroup:
printf("\n"); printf("\n");
return do_log(svc->cmd); return do_log(svc->cmd);
@@ -764,9 +865,11 @@ static int show_status(char *arg)
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char *lvls; char *lvls;
printf("%-*d ", pw, svc->pid);
svc_ident(svc, ident, sizeof(ident)); svc_ident(svc, ident, sizeof(ident));
if (num && !string_compare(ident, arg))
continue;
printf("%-*d ", pw, svc->pid);
printf("%-*s %s ", iw, ident, status(svc, 0)); printf("%-*s %s ", iw, ident, status(svc, 0));
lvls = runlevel_string(runlevel, svc->runlevels); lvls = runlevel_string(runlevel, svc->runlevels);
@@ -784,6 +887,30 @@ static int show_status(char *arg)
return 0; return 0;
} }
static int show_ident(char *arg)
{
svc_t *svc;
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char ident[MAX_IDENT_LEN];
size_t len;
char *pos;
svc_ident(svc, ident, sizeof(ident));
pos = strchr(ident, ':');
if (pos)
len = pos - ident;
else
len = strlen(ident);
if (arg && arg[0] && strncasecmp(ident, arg, len))
continue;
puts(ident);
}
return 0;
}
static int transform(char *nm) static int transform(char *nm)
{ {
char *names[] = { char *names[] = {
@@ -833,12 +960,13 @@ static int usage(int rc)
" -q, --quiet Silent, only return status of command\n" " -q, --quiet Silent, only return status of command\n"
" -t, --no-heading Skip table headings\n" " -t, --no-heading Skip table headings\n"
" -v, --verbose Verbose output\n" " -v, --verbose Verbose output\n"
" -V, --version Show program version\n"
" -h, --help This help text\n" " -h, --help This help text\n"
"\n" "\n"
"Commands:\n" "Commands:\n"
" debug Toggle Finit (daemon) debug\n" " debug Toggle Finit (daemon) debug\n"
" help This help text\n" " help This help text\n"
" version Show Finit version\n" " version Show program version\n"
"\n", prognm); "\n", prognm);
if (has_rcsd) if (has_rcsd)
@@ -880,12 +1008,17 @@ static int usage(int rc)
" stop <NAME>[:ID] Stop/Pause a running service by name\n" " stop <NAME>[:ID] Stop/Pause a running service by name\n"
" reload <NAME>[:ID] Reload service by name (SIGHUP or restart)\n" " reload <NAME>[:ID] Reload service by name (SIGHUP or restart)\n"
" restart <NAME>[:ID] Restart (stop/start) service by name\n" " restart <NAME>[:ID] Restart (stop/start) service by name\n"
" signal <NAME>[:ID] <S> Send signal S to service by name, with optional ID\n"
" ident [NAME] Show matching identities for NAME, or all\n"
" status <NAME>[:ID] Show service status, by name\n" " status <NAME>[:ID] Show service status, by name\n"
" status Show status of services, default command\n" " status Show status of services, default command\n");
if (cgrp)
fprintf(stderr,
"\n" "\n"
" cgroup List cgroup config overview\n" " cgroup List cgroup config overview\n"
" ps List processes based on cgroups\n" " ps List processes based on cgroups\n"
" top Show top-like listing based on cgroups\n" " top Show top-like listing based on cgroups\n");
fprintf(stderr,
"\n" "\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n" " runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n" " reboot Reboot system\n"
@@ -893,12 +1026,10 @@ static int usage(int rc)
" poweroff Halt and power off system\n" " poweroff Halt and power off system\n"
" suspend Suspend system\n"); " suspend Suspend system\n");
if (has_utmp()) if (utmp)
fprintf(stderr, fprintf(stderr,
"\n" "\n"
" utmp show Raw dump of UTMP/WTMP db\n" " utmp show Raw dump of UTMP/WTMP db\n");
"\n");
else
fprintf(stderr, "\n"); fprintf(stderr, "\n");
return rc; return rc;
@@ -918,17 +1049,31 @@ static int do_help(char *arg)
return usage(0); return usage(0);
} }
static int cmd_cond(struct cmd *cmd)
{
if (!cmd || !cmd->cond)
return 1;
return *cmd->cond;
}
static int cmd_parse(int argc, char *argv[], struct cmd *command) static int cmd_parse(int argc, char *argv[], struct cmd *command)
{ {
int i, j; int i, j;
for (i = 0; argc > 0 && command[i].cmd; i++) { for (i = 0; argc > 0 && command[i].cmd; i++) {
if (!string_match(command[i].cmd, argv[0])) if (!cmd_cond(&command[i]))
continue;
if (!string_compare(command[i].cmd, argv[0]))
continue; continue;
if (command[i].ctx) if (command[i].ctx)
return cmd_parse(argc - 1, &argv[1], command[i].ctx); return cmd_parse(argc - 1, &argv[1], command[i].ctx);
if (command[i].cb_multiarg)
return command[i].cb_multiarg(argc - 1, argv + 1);
if (command[i].cb) { if (command[i].cb) {
int rc = 0; int rc = 0;
@@ -961,61 +1106,68 @@ int main(int argc, char *argv[])
{ "quiet", 0, NULL, 'q' }, { "quiet", 0, NULL, 'q' },
{ "no-heading", 0, NULL, 't' }, { "no-heading", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' }, { "verbose", 0, NULL, 'v' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
struct cmd cond[] = { struct cmd cond[] = {
{ "status", NULL, do_cond_show }, /* default cmd */ { "status", NULL, do_cond_show, NULL, NULL }, /* default cmd */
{ "dump", NULL, do_cond_dump }, { "dump", NULL, do_cond_dump, NULL, NULL },
{ "set", NULL, do_cond_set }, { "set", NULL, do_cond_set, NULL, NULL },
{ "clr", NULL, do_cond_clr }, { "clr", NULL, do_cond_clr, NULL, NULL },
{ "clear", NULL, do_cond_clr }, { "clear", NULL, do_cond_clr, NULL, NULL },
{ NULL, NULL, NULL } { NULL, NULL, NULL, NULL, NULL }
}; };
struct cmd command[] = { struct cmd command[] = {
{ "status", NULL, show_status }, /* default cmd */ { "status", NULL, show_status, NULL, NULL }, /* default cmd */
{ "ident", NULL, show_ident, NULL, NULL },
{ "debug", NULL, toggle_debug }, { "debug", NULL, toggle_debug, NULL, NULL },
{ "devel", NULL, do_devel }, { "devel", NULL, do_devel, NULL, NULL },
{ "help", NULL, do_help }, { "help", NULL, do_help, NULL, NULL },
{ "version", NULL, show_version }, { "version", NULL, show_version, NULL, NULL },
{ "list", NULL, serv_list }, { "list", NULL, serv_list, NULL, NULL },
{ "ls", NULL, serv_list }, { "ls", NULL, serv_list, NULL, NULL },
{ "enable", NULL, serv_enable }, { "enable", NULL, serv_enable, NULL, NULL },
{ "disable", NULL, serv_disable }, { "disable", NULL, serv_disable, NULL, NULL },
{ "touch", NULL, serv_touch }, { "touch", NULL, serv_touch, NULL, NULL },
{ "show", NULL, serv_show }, { "show", NULL, serv_show, NULL, NULL },
{ "edit", NULL, serv_edit }, { "edit", NULL, serv_edit, NULL, NULL },
{ "create", NULL, serv_creat }, { "create", NULL, serv_creat, NULL, NULL },
{ "delete", NULL, serv_delete }, { "delete", NULL, serv_delete, NULL, NULL },
{ "reload", NULL, do_reload }, { "reload", NULL, do_reload, NULL, NULL },
{ "cond", cond, NULL }, { "cond", cond, NULL, NULL, NULL },
{ "log", NULL, do_log }, { "log", NULL, do_log, NULL, NULL },
{ "start", NULL, do_start }, { "start", NULL, do_start, NULL, NULL },
{ "stop", NULL, do_stop }, { "stop", NULL, do_stop, NULL, NULL },
{ "restart", NULL, do_restart }, { "restart", NULL, do_restart, NULL, NULL },
{ "signal", NULL, NULL, NULL, do_signal },
{ "cgroup", NULL, show_cgroup }, { "cgroup", NULL, show_cgroup, &cgrp, NULL },
{ "ps", NULL, show_cgps }, { "ps", NULL, show_cgps, &cgrp, NULL },
{ "top", NULL, show_cgtop }, { "top", NULL, show_cgtop, &cgrp, NULL },
{ "runlevel", NULL, do_runlevel }, { "runlevel", NULL, do_runlevel, NULL, NULL },
{ "reboot", NULL, do_reboot }, { "reboot", NULL, do_reboot, NULL, NULL },
{ "halt", NULL, do_halt }, { "halt", NULL, do_halt, NULL, NULL },
{ "poweroff", NULL, do_poweroff }, { "poweroff", NULL, do_poweroff, NULL, NULL },
{ "suspend", NULL, do_suspend }, { "suspend", NULL, do_suspend, NULL, NULL },
{ "utmp", NULL, do_utmp }, { "utmp", NULL, do_utmp, &utmp, NULL },
{ NULL, NULL, NULL } { NULL, NULL, NULL, NULL, NULL }
}; };
int interactive = 1, c; int interactive = 1, c;
if (transform(progname(argv[0]))) if (transform(progname(argv[0])))
return reboot_main(argc, argv); return reboot_main(argc, argv);
while ((c = getopt_long(argc, argv, "1bcdfh?pqtv", long_options, NULL)) != EOF) { /* Enable functionality depending on system capabilities */
cgrp = cgroup_avail();
utmp = has_utmp();
while ((c = getopt_long(argc, argv, "1bcdfh?pqtvV", long_options, NULL)) != EOF) {
switch(c) { switch(c) {
case '1': case '1':
ionce = 1; ionce = 1;
@@ -1056,23 +1208,15 @@ int main(int argc, char *argv[])
case 'v': case 'v':
verbose = 1; verbose = 1;
break; break;
case 'V':
return show_version(NULL);
} }
} }
if (interactive) if (interactive)
ttinit(); ttinit();
if (!has_utmp()) {
/* system w/o utmp support, disable 'utmp' command */
for (c = 0; command[c].cmd; c++) {
if (!string_compare(command[c].cmd, "utmp"))
continue;
command[c].cb = NULL;
break;
}
}
return cmd_parse(argc - optind, &argv[optind], command); return cmd_parse(argc - optind, &argv[optind], command);
} }
+14
View File
@@ -23,6 +23,7 @@
*/ */
#include <errno.h> #include <errno.h>
#include <sys/socket.h>
#include "finit.h" #include "finit.h"
#include "iwatch.h" #include "iwatch.h"
@@ -39,6 +40,9 @@ static int initialized = 0;
int iwatch_init(struct iwatch *iw) int iwatch_init(struct iwatch *iw)
{ {
socklen_t len;
int sz;
if (!iw) { if (!iw) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
@@ -51,6 +55,16 @@ int iwatch_init(struct iwatch *iw)
_pe("Failed creating inotify descriptor"); _pe("Failed creating inotify descriptor");
return -1; return -1;
} }
/*
* Double the size of the socket's receive buffer to address
* issues with lost events in some cases, e.g. bootstrap and
* reconf of systems with lots of services.
*/
len = sizeof(sz);
if (!getsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, &len))
setsockopt(iw->fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz));
initialized = 1; initialized = 1;
return iw->fd; return iw->fd;
+5 -1
View File
@@ -26,7 +26,11 @@
#define FINIT_IWATCH_H_ #define FINIT_IWATCH_H_
#include <stdint.h> #include <stdint.h>
#include <lite/queue.h> #ifdef _LIBITE_LITE
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include <sys/inotify.h> #include <sys/inotify.h>
#ifndef IN_MASK_CREATE #ifndef IN_MASK_CREATE
+5 -1
View File
@@ -37,7 +37,11 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include "lite/lite.h" #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "cond.h" #include "cond.h"
#include "pid.h" #include "pid.h"
+25 -17
View File
@@ -24,7 +24,11 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "helpers.h" #include "helpers.h"
@@ -32,14 +36,14 @@
#include "util.h" #include "util.h"
static int up = 0; static int up = 0;
static int loglevel = LOG_NOTICE; static int loglevel = LOG_INFO;
void log_init(int dbg) void log_init(void)
{ {
if (debug) if (debug)
loglevel = LOG_DEBUG; loglevel = LOG_DEBUG;
else else
loglevel = LOG_NOTICE; loglevel = LOG_INFO;
} }
/* If we enabled terse mode at boot, restore to previous setting at shutdown */ /* If we enabled terse mode at boot, restore to previous setting at shutdown */
@@ -55,18 +59,24 @@ void log_exit(void)
enable_progress(1); enable_progress(1);
} }
static void log_open(void) static int log_open(void)
{ {
int opts = LOG_PID; int opts;
if (up)
return 1;
if (access("/dev/log", W_OK))
return 0;
opts = LOG_CONS | LOG_PID;
if (debug)
opts |= LOG_PERROR;
closelog(); closelog();
if (debug)
opts |= LOG_PERROR; /* LOG_CONS | */
openlog("finit", opts, LOG_DAEMON); openlog("finit", opts, LOG_DAEMON);
setlogmask(LOG_UPTO(loglevel)); setlogmask(LOG_UPTO(loglevel));
up = 1; return up = 1;
} }
/* Toggle debug mode */ /* Toggle debug mode */
@@ -91,15 +101,12 @@ void log_debug(void)
*/ */
void logit(int prio, const char *fmt, ...) void logit(int prio, const char *fmt, ...)
{ {
FILE *fp;
va_list ap; va_list ap;
FILE *fp;
va_start(ap, fmt); va_start(ap, fmt);
if (up || fexist("/dev/log")) { if (up || log_open()) {
if (!up)
log_open();
vsyslog(prio, fmt, ap); vsyslog(prio, fmt, ap);
goto done; goto done;
} }
@@ -107,9 +114,9 @@ void logit(int prio, const char *fmt, ...)
if (LOG_PRI(prio) > loglevel) if (LOG_PRI(prio) > loglevel)
goto done; goto done;
fp = fopen("/dev/kmsg", "w"); if (in_container() || !(fp = fopen("/dev/kmsg", "w"))) {
if (!fp) {
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
goto done; goto done;
} }
@@ -121,6 +128,7 @@ void logit(int prio, const char *fmt, ...)
va_end(ap); va_end(ap);
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
} }
done: done:
+5 -5
View File
@@ -40,12 +40,12 @@
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages, * The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
* use `initctl debug` or add `debug` to the kernel cmdline. * use `initctl debug` or add `debug` to the kernel cmdline.
*/ */
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args) #define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt, __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args) #define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt, __func__, ##args)
#define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt "\n", __func__, ##args) #define _e(fmt, args...) logit(LOG_ERR, "%s():" fmt, __func__, ##args)
#define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %s\n", __func__, ##args, strerror(errno)) #define _pe(fmt, args...) logit(LOG_ERR, "%s():" fmt ": %n", __func__, ##args, strerror(errno))
void log_init (int dbg); void log_init (void);
void log_exit (void); void log_exit (void);
void log_debug (void); void log_debug (void);
+5 -1
View File
@@ -30,7 +30,11 @@
#include <syslog.h> #include <syslog.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION; static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
extern int logrotate(char *file, int num, off_t sz); extern int logrotate(char *file, int num, off_t sz);
+5 -1
View File
@@ -20,12 +20,16 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <lite/lite.h>
#include <stdio.h> #include <stdio.h>
#include <syslog.h> #include <syslog.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid) static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
{ {
+24 -18
View File
@@ -28,7 +28,7 @@
/* /*
* SysV init on Debian/Ubuntu skips these protected mount points * SysV init on Debian/Ubuntu skips these protected mount points
* *
* It also skips anything below /proc, /sys and /run for good meausre so * It also skips anything below /proc, /sys and /run for good measure so
* we do the same here. What we want is a list of non-protected tmpfs * we do the same here. What we want is a list of non-protected tmpfs
* and regular filesystems that can be safely unmounted before we do * and regular filesystems that can be safely unmounted before we do
* swapoff, and remount / as read-only, respectively. * swapoff, and remount / as read-only, respectively.
@@ -36,7 +36,7 @@
static int is_protected(char *dir) static int is_protected(char *dir)
{ {
size_t i; size_t i;
char *protected[] = { char *protected_mnts[] = {
"/sys", "/proc", "/sys", "/proc",
"/.dev", "/dev", "/dev/pts", "/dev/shm", "dev/.static/dev", "/dev/vcs", "/.dev", "/dev", "/dev/pts", "/dev/shm", "dev/.static/dev", "/dev/vcs",
"/run", "/var/run", "/run", "/var/run",
@@ -46,8 +46,8 @@ static int is_protected(char *dir)
"/proc/", "/sys/", "/run/", NULL "/proc/", "/sys/", "/run/", NULL
}; };
for (i = 0; protected[i]; i++) { for (i = 0; protected_mnts[i]; i++) {
if (!strcmp(dir, protected[i])) if (!strcmp(dir, protected_mnts[i]))
return 1; return 1;
} }
@@ -59,46 +59,52 @@ static int is_protected(char *dir)
return 0; return 0;
} }
static struct mntent *iterator(char *fstab) static void iterator_end(FILE **fp)
{
endmntent(*fp);
*fp = NULL;
}
static struct mntent *iterator(char *fstab, FILE **fp)
{ {
static FILE *fp = NULL;
static struct mntent *mnt; static struct mntent *mnt;
if (!fp && fstab) { if (!*fp && fstab) {
fp = setmntent(fstab, "r"); *fp = setmntent(fstab, "r");
if (!fp) if (!*fp)
return NULL; return NULL;
} }
while ((mnt = getmntent(fp))) { while ((mnt = getmntent(*fp))) {
if (is_protected(mnt->mnt_dir)) if (is_protected(mnt->mnt_dir))
continue; continue;
return mnt; return mnt;
} }
endmntent(fp); iterator_end(fp);
fp = NULL;
return NULL; return NULL;
} }
void unmount_tmpfs(void) void unmount_tmpfs(void)
{ {
struct mntent *mnt; struct mntent *mnt;
FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts"))) { while ((mnt = iterator("/proc/mounts", &fp))) {
if (!strcmp("tmpfs", mnt->mnt_fsname)) if (!strcmp("tmpfs", mnt->mnt_fsname) && !umount(mnt->mnt_dir))
umount(mnt->mnt_dir); iterator_end(&fp); /* Restart iteration */
} }
} }
void unmount_regular(void) void unmount_regular(void)
{ {
struct mntent *mnt; struct mntent *mnt;
FILE *fp = NULL;
while ((mnt = iterator("/proc/mounts"))) { while ((mnt = iterator("/proc/mounts", &fp))) {
umount(mnt->mnt_dir); if (!umount(mnt->mnt_dir))
iterator_end(&fp); /* Restart iteration */
} }
} }
+6 -2
View File
@@ -27,7 +27,11 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "pid.h" #include "pid.h"
#include "svc.h" #include "svc.h"
@@ -166,7 +170,7 @@ int pid_file_set(svc_t *svc, char *file, int not)
* The logic is explained below. Please note that using the first form * The logic is explained below. Please note that using the first form
* of the syntax not only creates and removes the PID file, but it also * of the syntax not only creates and removes the PID file, but it also
* calls touch to update the mtime on service_restart(), only applicable * calls touch to update the mtime on service_restart(), only applicable
* to processes that accept SIGHUP. This to ensure dependant services * to processes that accept SIGHUP. This to ensure dependent services
* are sent SIGCONT properly on reload, otherwise their condition would * are sent SIGCONT properly on reload, otherwise their condition would
* never be asserted again after `initctl reload`. * never be asserted again after `initctl reload`.
* *
+8 -3
View File
@@ -26,8 +26,13 @@
#include <dirent.h> /* readdir() et al */ #include <dirent.h> /* readdir() et al */
#include <poll.h> #include <poll.h>
#include <string.h> #include <string.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
#include <lite/queue.h> /* BSD sys/queue.h API */ # include <libite/lite.h>
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/lite.h>
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include "config.h" #include "config.h"
#include "cond.h" #include "cond.h"
@@ -274,7 +279,7 @@ static int init_plugins(uev_ctx_t *ctx)
* Loads a plugin from @path/@name[.so]. Note, if ".so" is missing from * Loads a plugin from @path/@name[.so]. Note, if ".so" is missing from
* the plugin @name it is added before attempting to load. * the plugin @name it is added before attempting to load.
* *
* It is up to the plugin itself ot register itself as a "ctor" with the * It is up to the plugin itself or register itself as a "ctor" with the
* %PLUGIN_INIT macro so that plugin_register() is called automatically. * %PLUGIN_INIT macro so that plugin_register() is called automatically.
* *
* Returns: * Returns:
+5 -1
View File
@@ -25,7 +25,11 @@
#define FINIT_PLUGIN_H_ #define FINIT_PLUGIN_H_
#include <uev/uev.h> #include <uev/uev.h>
#include <lite/queue.h> /* BSD sys/queue.h API */ #ifdef _LIBITE_LITE
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include "svc.h" #include "svc.h"
+3
View File
@@ -28,6 +28,9 @@
#include "svc.h" #include "svc.h"
#include "plugin.h" #include "plugin.h"
#define SERVICE_INTERVAL_DEFAULT 300000 /* 5 mins */
extern int service_interval;
int api_init (uev_ctx_t *ctx); int api_init (uev_ctx_t *ctx);
int api_exit (void); int api_exit (void);
-8
View File
@@ -98,11 +98,6 @@ static void do_kill(int signo, char *msg)
do_sleep(5); do_sleep(5);
} }
static void sig(int signo)
{
(void)signo; /* NOP */
}
static int usage(int rc) static int usage(int rc)
{ {
fprintf(stderr, "Usage: %s [OPTIONS]\n\n" fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
@@ -164,9 +159,6 @@ int reboot_main(int argc, char *argv[])
/* Check for any overrides */ /* Check for any overrides */
transform(NULL); transform(NULL);
/* Catch and ignore SIGTERM on halt/poweroff */
signal(SIGTERM, sig);
if (force) { if (force) {
switch (cmd) { switch (cmd) {
case CMD_REBOOT: case CMD_REBOOT:
+1 -1
View File
@@ -12,7 +12,7 @@
# runlevel. Finit does not restart them when they exit. # runlevel. Finit does not restart them when they exit.
# #
# - run : wait for completion before continuing with next stanza # - run : wait for completion before continuing with next stanza
# - task : like run but started in background, parallell with other stanzas # - task : like run but started in background, parallel with other stanzas
# - service : supervised daemon, run in foreground, restarted if it crashes # - service : supervised daemon, run in foreground, restarted if it crashes
# #
# The env: is an optional path to a file with environment variables to # The env: is an optional path to a file with environment variables to
+5 -1
View File
@@ -28,7 +28,11 @@
#include <glob.h> #include <glob.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "initctl.h" #include "initctl.h"
+185 -29
View File
@@ -26,11 +26,16 @@
#include <ctype.h> /* isblank() */ #include <ctype.h> /* isblank() */
#include <string.h> #include <string.h>
#include <sys/reboot.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <net/if.h> #include <net/if.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include <wordexp.h> #include <wordexp.h>
#include "cgroup.h" #include "cgroup.h"
@@ -51,6 +56,7 @@
static struct wq work = { static struct wq work = {
.cb = service_worker, .cb = service_worker,
}; };
int service_interval = SERVICE_INTERVAL_DEFAULT;
static void svc_set_state(svc_t *svc, svc_state_t new); static void svc_set_state(svc_t *svc, svc_state_t new);
@@ -491,6 +497,12 @@ static int service_start(svc_t *svc)
/* Declare we're waiting for svc to create its pidfile */ /* Declare we're waiting for svc to create its pidfile */
svc_starting(svc); svc_starting(svc);
/* Increment total restarts, unless first time or non-service */
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
if (svc->restart_cnt || svc->restart_tot)
svc->restart_tot++;
}
/* Block SIGCHLD while forking. */ /* Block SIGCHLD while forking. */
sigemptyset(&nmask); sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD); sigaddset(&nmask, SIGCHLD);
@@ -559,6 +571,7 @@ static int service_start(svc_t *svc)
} else { } else {
i = 0; i = 0;
args[i++] = svc->cmd; args[i++] = svc->cmd;
args[i++] = svc->cmd;
args[i++] = "start"; args[i++] = "start";
} }
args[i] = NULL; args[i] = NULL;
@@ -616,10 +629,13 @@ static int service_start(svc_t *svc)
switch (svc->type) { switch (svc->type) {
case SVC_TYPE_RUN: case SVC_TYPE_RUN:
svc->status = complete(svc->cmd, pid); svc->status = complete(svc->cmd, pid);
if (WIFEXITED(svc->status) && !WEXITSTATUS(svc->status)) if (WIFEXITED(svc->status) && !WEXITSTATUS(svc->status)) {
svc->started = 1;
result = 0; result = 0;
else } else {
svc->started = 0;
result = 1; result = 1;
}
svc->start_time = svc->pid = 0; svc->start_time = svc->pid = 0;
svc->once++; svc->once++;
svc_set_state(svc, SVC_STOPPING_STATE); svc_set_state(svc, SVC_STOPPING_STATE);
@@ -689,7 +705,7 @@ static void service_cleanup(svc_t *svc)
utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */ utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */
svc->oldpid = svc->pid; svc->oldpid = svc->pid;
svc->start_time = svc->pid = 0; svc->starting = svc->start_time = svc->pid = 0;
} }
/** /**
@@ -731,8 +747,6 @@ static int service_stop(svc_t *svc)
*/ */
svc_started(svc); svc_started(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
if (!svc->desc[0]) if (!svc->desc[0])
do_progress = 0; do_progress = 0;
@@ -743,12 +757,17 @@ static int service_stop(svc_t *svc)
if (svc->pid > 1) { if (svc->pid > 1) {
/* Kill all children in the same proess group, e.g. logit */ /* Kill all children in the same proess group, e.g. logit */
rc = kill(-svc->pid, svc->sighalt); rc = kill(-svc->pid, svc->sighalt);
_d("kill(-%d, %d) => rc %d", svc->pid, svc->sighalt, rc); _d("kill(-%d, %d) => rc %d, errno %d", svc->pid, svc->sighalt, rc, errno);
/* PID lost or forking process never really started */ /* PID lost or forking process never really started */
if (rc == -1 && ESRCH == errno) if (rc == -1 && (errno == ESRCH || errno == ENOENT)) {
service_cleanup(svc); service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE);
} else } else
svc_set_state(svc, SVC_STOPPING_STATE);
} else {
service_cleanup(svc); service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE);
}
} else { } else {
char *args[] = { svc->cmd, "stop", NULL }; char *args[] = { svc->cmd, "stop", NULL };
pid_t pid; pid_t pid;
@@ -766,7 +785,7 @@ static int service_stop(svc_t *svc)
rc = 1; rc = 1;
break; break;
default: default:
rc = WEXITSTATUS(complete(svc->cmd, pid)); svc_set_state(svc, SVC_STOPPING_STATE);
break; break;
} }
} }
@@ -817,7 +836,7 @@ static int service_restart(svc_t *svc)
logit(LOG_CONSOLE | LOG_NOTICE, "Restarting %s[%d], sending SIGHUP ...", logit(LOG_CONSOLE | LOG_NOTICE, "Restarting %s[%d], sending SIGHUP ...",
svc_ident(svc, NULL, 0), svc->pid); svc_ident(svc, NULL, 0), svc->pid);
rc = kill(svc->pid, SIGHUP); rc = kill(svc->pid, SIGHUP);
if (rc == -1 && errno == ESRCH) { if (rc == -1 && (errno == ESRCH || errno == ENOENT)) {
/* nobody home, reset internal state machine */ /* nobody home, reset internal state machine */
lost = svc->pid; lost = svc->pid;
} else { } else {
@@ -843,7 +862,7 @@ static int service_restart(svc_t *svc)
/** /**
* service_reload_dynamic - Called on SIGHUP, 'init q' or 'initctl reload' * service_reload_dynamic - Called on SIGHUP, 'init q' or 'initctl reload'
* *
* This function is called when Finit has recieved SIGHUP to reload * This function is called when Finit has received SIGHUP to reload
* .conf files in /etc/finit.d. It is responsible for starting, * .conf files in /etc/finit.d. It is responsible for starting,
* stopping and reloading (forwarding SIGHUP) to processes affected. * stopping and reloading (forwarding SIGHUP) to processes affected.
*/ */
@@ -868,7 +887,7 @@ void service_runlevel(int newlevel)
sm_set_runlevel(&sm, newlevel); sm_set_runlevel(&sm, newlevel);
sm_step(&sm); sm_step(&sm);
if (!rescue && runlevel <= 1) if (!rescue && (runlevel <= 1 || runlevel == 6))
networking(0); networking(0);
} }
@@ -1079,7 +1098,7 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* This function is used to register commands to be run on different * This function is used to register commands to be run on different
* system runlevels with optional username. The @type argument details * system runlevels with optional username. The @type argument details
* if it's service to bo monitored/respawned (daemon), a one-shot task * if it's service to bo monitored/respawned (daemon), a one-shot task
* or a command that must run in sequence and not in parallell, like * or a command that must run in sequence and not in parallel, like
* service and task commands do. * service and task commands do.
* *
* The @line can optionally start with a username, denoted by an @ * The @line can optionally start with a username, denoted by an @
@@ -1119,7 +1138,7 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* *
* Without the :ID syntax, Finit replaces the first service line with * Without the :ID syntax, Finit replaces the first service line with
* the contents of the second. The :ID can be any string value and * the contents of the second. The :ID can be any string value and
* defaults to "" (emtpy string). * defaults to "" (empty string).
* *
* Returns: * Returns:
* POSIX OK(0) on success, or non-zero errno exit status on failure. * POSIX OK(0) on success, or non-zero errno exit status on failure.
@@ -1136,6 +1155,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
int respawn = 0; int respawn = 0;
int levels = 0; int levels = 0;
int manual = 0; int manual = 0;
int restart_max = SVC_RESPAWN_MAX;
int restart_tmo = 0;
unsigned oncrash_action = SVC_ONCRASH_IGNORE;
char *line; char *line;
svc_t *svc; svc_t *svc;
@@ -1193,6 +1215,18 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
name = cmd; name = cmd;
else if (!strncasecmp(cmd, "manual:yes", 10)) else if (!strncasecmp(cmd, "manual:yes", 10))
manual = 1; manual = 1;
else if (!strncasecmp(cmd, "restart:", 8))
restart_max = atoi(&cmd[8]);
else if (!strncasecmp(cmd, "restarttmo:", 11)) /* compat alias */
restart_tmo = atoi(&cmd[11]) * 1000;
else if (!strncasecmp(cmd, "restart_sec:", 12))
restart_tmo = atoi(&cmd[12]) * 1000;
else if (!strncasecmp(cmd, "norestart", 9))
restart_max = 0;
else if (!strncasecmp(cmd, "oncrash:", 8)) {
if (!strncasecmp(&cmd[8], "reboot", 6))
oncrash_action = SVC_ONCRASH_REBOOT;
}
else if (!strncasecmp(cmd, "respawn", 7)) else if (!strncasecmp(cmd, "respawn", 7))
respawn = 1; respawn = 1;
else if (!strncasecmp(cmd, "halt:", 5)) else if (!strncasecmp(cmd, "halt:", 5))
@@ -1277,18 +1311,21 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (type == SVC_TYPE_SERVICE && manual) if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc); svc_stop(svc);
svc->restart_max = restart_max;
svc->restart_tmo = restart_tmo;
svc->oncrash_action = oncrash_action;
} else { } else {
/* update type, may have changed from service -> task */ /* update type, may have changed from service -> task */
svc->type = type; svc->type = type;
/* e.g., if missing cmd or env before */ /* e.g., if missing cmd or env before */
if (!manual)
svc_unblock(svc); svc_unblock(svc);
} }
/* Always clear svc PID file, for now. See TODO */
svc->pidfile[0] = 0;
/* Decode any optional pid:/optional/path/to/file.pid */ /* Decode any optional pid:/optional/path/to/file.pid */
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid)) if (pid && (svc_is_daemon(svc) || svc_is_sysv(svc)) && pid_file_parse(svc, pid))
_e("Invalid 'pid' argument to service: %s", pid); _e("Invalid 'pid' argument to service: %s", pid);
if (username) { if (username) {
@@ -1327,7 +1364,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
/* TTYs cannot be redirected */ /* TTYs cannot be redirected */
log = NULL; log = NULL;
/* Create name:id tuple for identiy, e.g., tty:S0 */ /* Create name:id tuple for identity, e.g., tty:S0 */
ptr = strrchr(svc->dev, '/'); ptr = strrchr(svc->dev, '/');
if (ptr) if (ptr)
ptr++; ptr++;
@@ -1518,6 +1555,34 @@ static void service_kill_script(svc_t *svc)
kill(-svc->pid, SIGKILL); kill(-svc->pid, SIGKILL);
} }
/*
* Shared env vars for both pre: and post: scripts
*/
static void set_pre_post_envs(svc_t *svc, const char *type)
{
char buf[25 + 256] = "/etc/default:/etc/conf.d";
const char *fn = svc_getenv(svc);
const char *exist = "0";
setenv("SERVICE_TYPE", svc_typestr(svc), 1);
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1);
setenv("SERVICE_SCRIPT_TYPE", type, 1);
if (fn) {
setenv("SERVICE_ENV_FILE", fn, 1);
if (!access(fn, R_OK))
exist = "1";
}
setenv("SERVICE_ENV_FILE", fn, 1);
setenv("SERVICE_ENV_FILE_EXIST", exist, 1);
#ifdef FINIT_SYSCONFIG
strlcat(buf, ":", sizeof(buf));
strlcat(buf, FINIT_SYSCONFIG, sizeof(buf));
#endif
setenv("SERVICE_CONF_DIR", buf, 1);
}
static void service_pre_script(svc_t *svc) static void service_pre_script(svc_t *svc)
{ {
svc->pid = service_fork(svc); svc->pid = service_fork(svc);
@@ -1534,7 +1599,7 @@ static void service_pre_script(svc_t *svc)
NULL NULL
}; };
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1); set_pre_post_envs(svc, "pre");
execvp(_PATH_BSHELL, argv); execvp(_PATH_BSHELL, argv);
_exit(EX_OSERR); _exit(EX_OSERR);
} }
@@ -1563,7 +1628,7 @@ static void service_post_script(svc_t *svc)
rc = WEXITSTATUS(svc->status); rc = WEXITSTATUS(svc->status);
sig = WTERMSIG(svc->status); sig = WTERMSIG(svc->status);
setenv("SERVICE_IDENT", svc_ident(svc, NULL, 0), 1); set_pre_post_envs(svc, "post");
if (WIFEXITED(svc->status)) { if (WIFEXITED(svc->status)) {
char val[4]; char val[4];
@@ -1586,12 +1651,12 @@ static void service_post_script(svc_t *svc)
static void service_retry(svc_t *svc) static void service_retry(svc_t *svc)
{ {
int timeout;
char *restart_cnt = (char *)&svc->restart_cnt; char *restart_cnt = (char *)&svc->restart_cnt;
int timeout;
service_timeout_cancel(svc); service_timeout_cancel(svc);
if (svc->respawn) { if (svc->respawn) {
_d("%s crashed, respawning ...", svc->cmd); _d("%s crashed/exited, respawning ...", svc->cmd);
svc_unblock(svc); svc_unblock(svc);
service_step(svc); service_step(svc);
return; return;
@@ -1599,15 +1664,22 @@ static void service_retry(svc_t *svc)
if (svc->state != SVC_HALTED_STATE || if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) { svc->block != SVC_BLOCK_RESTARTING) {
*restart_cnt = 0; logit(LOG_CONSOLE | LOG_NOTICE, "Successfully restarted crashing service %s.",
svc_ident(svc, NULL, 0));
return; return;
} }
if (*restart_cnt >= SVC_RESPAWN_MAX) { /* Peak instability index */
if (*restart_cnt >= svc->restart_max) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.", logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0)); svc_ident(svc, NULL, 0));
svc_crashing(svc); svc_crashing(svc);
*restart_cnt = 0; *restart_cnt = 0;
if (svc->oncrash_action == SVC_ONCRASH_REBOOT) {
logit(LOG_ERR, "%s issuing reboot", svc->cmd);
sync();
kill(1, SIGTERM);
}
service_step(svc); service_step(svc);
return; return;
} }
@@ -1616,12 +1688,14 @@ static void service_retry(svc_t *svc)
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt); _d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)", logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX); svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, svc->restart_max);
svc_unblock(svc); svc_unblock(svc);
service_step(svc); service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */ /* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000; timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000;
/* If a longer timeout was specified in the conf, use that instead. */
timeout = max(svc->restart_tmo, timeout);
service_timeout_after(svc, timeout, service_retry); service_timeout_after(svc, timeout, service_retry);
} }
@@ -1638,6 +1712,27 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
service_timeout_cancel(svc); service_timeout_cancel(svc);
service_timeout_after(svc, svc->killdelay, service_kill); service_timeout_after(svc, svc->killdelay, service_kill);
} }
if (svc_is_runtask(svc)) {
char success[MAX_COND_LEN], failure[MAX_COND_LEN];
snprintf(success, sizeof(success), "%s/%s/success", svc_typestr(svc), svc->name);
snprintf(failure, sizeof(failure), "%s/%s/failure", svc_typestr(svc), svc->name);
/* create success/failure condition when entering SVC_DONE_STATE. */
if (*state == SVC_DONE_STATE) {
if (svc->started)
cond_set_oneshot(success);
else
cond_set_oneshot(failure);
}
/* clear all conditions when entering SVC_HALTED_STATE. */
if (*state == SVC_HALTED_STATE) {
cond_clear(success);
cond_clear(failure);
}
}
} }
/* /*
@@ -1680,14 +1775,15 @@ restart:
case SVC_STOPPING_STATE: case SVC_STOPPING_STATE:
if (!svc->pid) { if (!svc->pid) {
char cond[MAX_COND_LEN]; char condstr[MAX_COND_LEN];
_d("%s: stopped, cleaning up timers and conditions ...", svc->cmd); _d("%s: stopped, cleaning up timers and conditions ...", svc->cmd);
service_timeout_cancel(svc); service_timeout_cancel(svc);
cond_clear(mkcond(svc, cond, sizeof(cond))); cond_clear(mkcond(svc, condstr, sizeof(condstr)));
switch (svc->type) { switch (svc->type) {
case SVC_TYPE_SERVICE: case SVC_TYPE_SERVICE:
case SVC_TYPE_SYSV:
case SVC_TYPE_TTY: case SVC_TYPE_TTY:
if (svc_has_post(svc)) { if (svc_has_post(svc)) {
svc_set_state(svc, SVC_CLEANUP_STATE); svc_set_state(svc, SVC_CLEANUP_STATE);
@@ -1698,7 +1794,6 @@ restart:
case SVC_TYPE_TASK: case SVC_TYPE_TASK:
case SVC_TYPE_RUN: case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
svc_set_state(svc, SVC_DONE_STATE); svc_set_state(svc, SVC_DONE_STATE);
break; break;
@@ -1758,6 +1853,7 @@ restart:
* Restart directly after the first crash, * Restart directly after the first crash,
* then retry after 2 sec * then retry after 2 sec
*/ */
if (!svc->respawn)
_d("delayed restart of %s", svc->cmd); _d("delayed restart of %s", svc->cmd);
service_timeout_after(svc, 1, service_retry); service_timeout_after(svc, 1, service_retry);
break; break;
@@ -1774,6 +1870,7 @@ restart:
break; break;
} }
} }
service_timeout_cancel(svc);
cond = cond_get_agg(svc->cond); cond = cond_get_agg(svc->cond);
switch (cond) { switch (cond) {
@@ -1932,6 +2029,65 @@ int service_completed(void)
return 1; return 1;
} }
/*
* Every five¹ minutes we sweep over all services, skipping crashed or
* otherwise no longer running ones. Decrement non-zero crash counters
* to allow services that have started after an initial crash to slowly
* prove themselves again as stable services. Previously this counter
* was reset as soon as such services had stopped crashing at least once
* per second. This new scheme allows us to catch those that rage-quit
* immediately when we try to start them, but now also those that are
* only slightly buggy -- when they reach their restart_max, they too
* are marked 'crashed'.
*
* This does not affect the restart_tot counter, which you can see in
* the output from 'initctl status foo', along with this instability
* "index" in parethesis: total (cnt/max)
*/
static void service_interval_cb(uev_t *w, void *arg, int events)
{
svc_t *svc, *iter = NULL;
(void)arg;
if (UEV_ERROR == events) {
uev_timer_start(w);
return;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc_is_daemon(svc) || svc_is_sysv(svc)) {
char *restart_cnt = (char *)&svc->restart_cnt;
if (!svc_is_running(svc))
continue;
if (*restart_cnt > 0) {
logit(LOG_CONSOLE | LOG_DEBUG, "Aging %s instability index (%d/%d)",
svc_ident(svc, NULL, 0), svc->restart_cnt, svc->restart_max);
(*restart_cnt)--;
}
}
}
service_init();
}
/*
* The service_interval may change (conf) between invocations, so we
* periodically reset the one-shot timer instead of using a periodic.
*/
void service_init(void)
{
static int initialized = 0;
static uev_t watcher;
if (!initialized) {
uev_timer_init(ctx, &watcher, service_interval_cb, NULL, service_interval, 0);
initialized = 1;
} else
uev_timer_set(&watcher, service_interval, 0);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+2
View File
@@ -41,6 +41,8 @@ void service_worker (void *unused);
int service_completed (void); int service_completed (void);
void service_init (void);
#endif /* FINIT_SERVICE_H_ */ #endif /* FINIT_SERVICE_H_ */
/** /**
+82 -13
View File
@@ -71,9 +71,14 @@
#include <dirent.h> #include <dirent.h>
#include <string.h> /* strerror() */ #include <string.h> /* strerror() */
#include <sched.h>
#include <sys/reboot.h> #include <sys/reboot.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "finit.h" #include "finit.h"
#include "cond.h" #include "cond.h"
@@ -168,7 +173,7 @@ void unmount_regular(void);
* *
* https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/ * https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
*/ */
void do_kill(int signo) void do_iterate_proc(int (*cb)(int, void *), void *data)
{ {
DIR *dirp; DIR *dirp;
@@ -187,6 +192,8 @@ void do_kill(int signo)
pid = atoi(d->d_name); pid = atoi(d->d_name);
if (!pid) if (!pid)
continue; continue;
if (pid == 1)
continue;
snprintf(file, sizeof(file), "/proc/%s/cmdline", d->d_name); snprintf(file, sizeof(file), "/proc/%s/cmdline", d->d_name);
fp = fopen(file, "r"); fp = fopen(file, "r");
@@ -199,7 +206,10 @@ void do_kill(int signo)
else if (file[0] == '@') else if (file[0] == '@')
_d("Skipping %s ...", &file[1]); _d("Skipping %s ...", &file[1]);
else else
kill(pid, signo); if (cb(pid, data)) {
print(0, "PID %d is still alive (%s)", pid, file);
break;
}
} }
fclose(fp); fclose(fp);
} }
@@ -207,8 +217,61 @@ void do_kill(int signo)
} }
} }
static int kill_cb(int pid, void *data)
{
int signo = *(int *)data;
kill(pid, signo);
return 0;
}
static int status_cb(int pid, void *data)
{
int *has_proc = (int *)data;
*has_proc = 1;
return 1;
}
/* return value:
* 1 - at least one process remaining
* 0 - no processes remaining
*/
static int do_wait(int secs)
{
const int delay = 250000;
int iterations = secs * 1000 * 1000 / delay;
int has_proc;
do {
do_usleep(delay);
while (waitpid(-1, NULL, WNOHANG) > 0)
;
has_proc = 0;
iterations--;
do_iterate_proc(status_cb, &has_proc);
}
while (has_proc && iterations > 0);
return has_proc;
}
void do_shutdown(shutop_t op) void do_shutdown(shutop_t op)
{ {
struct sched_param sched_param = { .sched_priority = 99 };
int in_cont = in_container();
int signo = SIGTERM;
if (!in_cont) {
/*
* On a PREEMPT-RT system, Finit must run as the highest prioritized
* RT process to ensure it completes the shutdown sequence.
*/
sched_setscheduler(1, SCHED_RR, &sched_param);
}
if (sdown) if (sdown)
run_interactive(sdown, "Calling shutdown hook: %s", sdown); run_interactive(sdown, "Calling shutdown hook: %s", sdown);
@@ -219,9 +282,11 @@ void do_shutdown(shutop_t op)
* Tell remaining non-monitored processes to exit, give them * Tell remaining non-monitored processes to exit, give them
* time to exit gracefully, 2 sec was customary, we go for 1. * time to exit gracefully, 2 sec was customary, we go for 1.
*/ */
do_kill(SIGTERM); do_iterate_proc(kill_cb, &signo);
do_sleep(1); if (do_wait(1)) {
do_kill(SIGKILL); signo = SIGKILL;
do_iterate_proc(kill_cb, &signo);
}
/* Exit plugins and API gracefully */ /* Exit plugins and API gracefully */
plugin_exit(); plugin_exit();
@@ -231,6 +296,12 @@ void do_shutdown(shutop_t op)
while (waitpid(-1, NULL, WNOHANG) > 0) while (waitpid(-1, NULL, WNOHANG) > 0)
; ;
if (in_cont) {
if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading);
_exit(0);
}
/* Close all local non-console descriptors */ /* Close all local non-console descriptors */
for (int fd = 3; fd < 128; fd++) for (int fd = 3; fd < 128; fd++)
close(fd); close(fd);
@@ -251,16 +322,14 @@ void do_shutdown(shutop_t op)
/* Unmount any tmpfs before unmounting swap ... */ /* Unmount any tmpfs before unmounting swap ... */
unmount_tmpfs(); unmount_tmpfs();
run("swapoff -e -a"); if (whichp("swapoff"))
run("swapoff -e -a", "swapoff");
/* ... unmount remaining regular file systems. */ /* ... unmount remaining regular file systems. */
unmount_regular(); unmount_regular();
/* We sit on / so we must remount it ro, try all the things! */ /* We sit on / so we must remount it read-only */
sync(); run("mount -n -o remount,ro -t none rootfs /", "mount");
run("mount -n -o remount,ro -t dummytype dummydev /");
run("mount -n -o remount,ro dummydev /");
run("mount -n -o remount,ro /");
/* 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();
@@ -445,7 +514,7 @@ const char *sig_name(int signo)
} }
/* /*
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding * Initial signal setup - ignore everything but SIGCHLD until we're capable of responding
*/ */
static void chld_handler(int sig, siginfo_t *info, void *ctx) static void chld_handler(int sig, siginfo_t *info, void *ctx)
{ {
+10 -5
View File
@@ -23,6 +23,7 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <paths.h>
#include <sys/types.h> #include <sys/types.h>
#include "finit.h" #include "finit.h"
@@ -38,6 +39,10 @@
sm_t sm; sm_t sm;
#ifndef FINIT_NOLOGIN_PATH
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
#endif
void sm_init(sm_t *sm) void sm_init(sm_t *sm)
{ {
sm->state = SM_BOOTSTRAP_STATE; sm->state = SM_BOOTSTRAP_STATE;
@@ -76,16 +81,16 @@ static char *sm_status(sm_state_t state)
* Disable login in single user mode and shutdown/reboot * Disable login in single user mode and shutdown/reboot
* *
* Re-enable only when going from these runlevels, this way a user can * Re-enable only when going from these runlevels, this way a user can
* manage /etc/nologin manually within the other runlevels without us * manage FINIT_NOLOGIN_PATH manually within the other runlevels without
* pulling the rug from under their feet. * us pulling the rug from under their feet.
*/ */
static void nologin(void) static void nologin(void)
{ {
if (runlevel == 1 || runlevel == 0 || runlevel == 6) if (runlevel == 1 || runlevel == 0 || runlevel == 6)
touch("/etc/nologin"); touch(FINIT_NOLOGIN_PATH);
if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6) if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6)
erase("/etc/nologin"); erase(FINIT_NOLOGIN_PATH);
} }
void sm_set_runlevel(sm_t *sm, int newlevel) void sm_set_runlevel(sm_t *sm, int newlevel)
@@ -187,7 +192,7 @@ restart:
} }
/* 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 stoppped, calling runlevel change hooks ..."); _d("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 ..."); _d("Starting services new to this runlevel ...");
+2 -2
View File
@@ -27,9 +27,9 @@
typedef enum { typedef enum {
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */ SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
SM_RUNNING_STATE, /* Normal state, services running */ SM_RUNNING_STATE, /* Normal state, services running */
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occured */ SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */ SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
SM_RELOAD_CHANGE_STATE, /* A reload event has occured */ SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */ SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */
} sm_state_t; } sm_state_t;
+26 -43
View File
@@ -29,7 +29,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <strings.h> #include <strings.h>
#include <sys/time.h> #include <sys/time.h>
#include <lite/queue.h> /* BSD sys/queue.h API */ #ifdef _LIBITE_LITE
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include "finit.h" #include "finit.h"
#include "svc.h" #include "svc.h"
@@ -252,7 +256,7 @@ svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
char *name = svc->name; char *name = svc->name;
if (!strncmp(name, cmd, strlen(name))) if (string_compare(name, cmd))
return svc; return svc;
} }
@@ -469,7 +473,17 @@ svc_t *svc_find_by_pidfile(char *fn)
pid_t pid; pid_t pid;
pid = pid_file_read(fn); pid = pid_file_read(fn);
if (pid <= 0) { if (pid > 1) {
svc = svc_find_by_pid(pid);
if (svc)
return svc;
}
/*
* No PID in file, or a sysv service. So it has probably forked
* off a chiled that we don't know about. See if we can match
* the PID file to a service instead.
*/
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
int v = 0; int v = 0;
@@ -481,16 +495,6 @@ svc_t *svc_find_by_pidfile(char *fn)
return svc; return svc;
} }
return NULL;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid != pid)
continue;
return svc;
}
return NULL; return NULL;
} }
@@ -613,27 +617,6 @@ int svc_enabled(svc_t *svc)
return 1; return 1;
} }
int svc_is_unique(svc_t *svc)
{
svc_t *s, *iter = NULL;
int unique = 1;
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
if (svc->type == SVC_TYPE_FREE)
continue;
if (s == svc)
continue;
if (s->job == svc->job) {
unique = 0;
break;
}
}
return unique;
}
/* 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)
{ {
@@ -667,7 +650,7 @@ static char *tokstr(char *str, size_t len)
/* /*
* Used by api.c (to start/stop/restart) and initctl.c (for input validation) * Used by api.c (to start/stop/restart) and initctl.c (for input validation)
*/ */
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *)) int svc_parse_jobstr(char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *))
{ {
char *input, *token; char *input, *token;
int result = 0; int result = 0;
@@ -703,11 +686,11 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
if (!ptr) { if (!ptr) {
svc = svc_job_iterator(&iter, 1, job); svc = svc_job_iterator(&iter, 1, job);
if (!svc && not_found) if (!svc && not_found)
result += not_found(NULL, token); result += not_found(NULL, token, user_data);
while (svc) { while (svc) {
if (found) if (found)
result += found(svc); result += found(svc, user_data);
svc = svc_job_iterator(&iter, 0, job); svc = svc_job_iterator(&iter, 0, job);
} }
} else { } else {
@@ -716,19 +699,19 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
svc = svc_find_by_jobid(job, id); svc = svc_find_by_jobid(job, id);
if (!svc && not_found) if (!svc && not_found)
result += not_found(token, id); result += not_found(token, id, user_data);
else if (found) else if (found)
result += found(svc); result += found(svc, user_data);
} }
} else { } else {
if (!ptr) { if (!ptr) {
svc = svc_named_iterator(&iter, 1, token); svc = svc_named_iterator(&iter, 1, token);
if (!svc && not_found) if (!svc && not_found)
result += not_found(token, id); result += not_found(token, id, user_data);
while (svc) { while (svc) {
if (found) if (found)
result += found(svc); result += found(svc, user_data);
svc = svc_named_iterator(&iter, 0, token); svc = svc_named_iterator(&iter, 0, token);
} }
} else { } else {
@@ -737,9 +720,9 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
svc = svc_find_by_nameid(token, id); svc = svc_find_by_nameid(token, id);
if (!svc && not_found) if (!svc && not_found)
result += not_found(token, id); result += not_found(token, id, user_data);
else if (found) else if (found)
result += found(svc); result += found(svc, user_data);
} }
} }
+47 -6
View File
@@ -28,8 +28,13 @@
#include <sys/ipc.h> /* IPC_CREAT */ #include <sys/ipc.h> /* IPC_CREAT */
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/types.h> /* pid_t */ #include <sys/types.h> /* pid_t */
#include <lite/lite.h> #ifdef _LIBITE_LITE
#include <lite/queue.h> /* BSD sys/queue.h API */ # include <libite/lite.h>
# include <libite/queue.h> /* BSD sys/queue.h API */
#else
# include <lite/lite.h>
# include <lite/queue.h> /* BSD sys/queue.h API */
#endif
#include <uev/uev.h> #include <uev/uev.h>
#include "cgroup.h" #include "cgroup.h"
@@ -40,7 +45,7 @@ typedef int svc_cmd_t;
typedef enum { typedef enum {
SVC_TYPE_FREE = 0, /* Free to allocate */ SVC_TYPE_FREE = 0, /* Free to allocate */
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */ SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */ SVC_TYPE_TASK = 2, /* One-shot, runs in parallel */
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */ SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
SVC_TYPE_TTY = 8, /* Like service, but dedicated to TTYs */ SVC_TYPE_TTY = 8, /* Like service, but dedicated to TTYs */
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */ SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
@@ -70,6 +75,11 @@ typedef enum {
SVC_BLOCK_RESTARTING, SVC_BLOCK_RESTARTING,
} svc_block_t; } svc_block_t;
typedef enum {
SVC_ONCRASH_IGNORE = 0,
SVC_ONCRASH_REBOOT = 1,
} svc_oncrash_action_t;
#define MAX_ID_LEN 16 #define MAX_ID_LEN 16
#define MAX_ARG_LEN 64 #define MAX_ARG_LEN 64
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1) #define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
@@ -102,7 +112,7 @@ typedef struct svc {
struct cgroup cgroup; struct cgroup cgroup;
/* Service details */ /* Service details */
int sighalt; /* Signal to stop prorcess, default: SIGTERM */ int sighalt; /* Signal to stop process, default: SIGTERM */
int killdelay; /* Delay in msec before sending SIGKILL */ int killdelay; /* Delay in msec before sending SIGKILL */
pid_t oldpid, pid; pid_t oldpid, pid;
char pidfile[256]; char pidfile[256];
@@ -127,6 +137,10 @@ typedef struct svc {
/* 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 char restart_max; /* Maximum number of restarts allowed */
int restart_tmo; /* Time required for the service to start. */
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. */
@@ -207,9 +221,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_is_unique (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *)); int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; } static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; } static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
@@ -265,6 +278,31 @@ static inline int svc_has_cond(svc_t *svc)
return 0; return 0;
} }
static inline const char *svc_typestr(svc_t *svc)
{
switch (svc->type) {
case SVC_TYPE_FREE:
return "free";
case SVC_TYPE_SERVICE:
return "service";
case SVC_TYPE_TASK:
return "task";
case SVC_TYPE_RUN:
return "run";
case SVC_TYPE_TTY:
return "tty";
case SVC_TYPE_SYSV:
return "sysv";
}
return "unknown";
}
static inline char *svc_status(svc_t *svc) static inline char *svc_status(svc_t *svc)
{ {
if (!svc) if (!svc)
@@ -294,7 +332,10 @@ static inline char *svc_status(svc_t *svc)
return "unknown"; return "unknown";
case SVC_DONE_STATE: case SVC_DONE_STATE:
if (svc->started)
return "done"; return "done";
else
return "failed";
case SVC_STOPPING_STATE: case SVC_STOPPING_STATE:
switch (svc->type) { switch (svc->type) {
+5 -1
View File
@@ -29,7 +29,11 @@
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include "finit.h" #include "finit.h"
+97 -6
View File
@@ -24,6 +24,7 @@
#include "config.h" #include "config.h"
#include <ctype.h> /* isprint() */ #include <ctype.h> /* isprint() */
#include <errno.h> #include <errno.h>
#include <mntent.h>
#include <regex.h> #include <regex.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@@ -37,7 +38,11 @@
# include <sys/ioctl.h> # include <sys/ioctl.h>
#endif #endif
#include <sys/sysinfo.h> /* sysinfo() */ #include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */ #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "util.h" #include "util.h"
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
@@ -177,7 +182,8 @@ int fnread(char *buf, size_t len, char *fmt, ...)
if (!fp) if (!fp)
return -1; return -1;
len = fread(buf, sizeof(char), len, fp); len = fread(buf, sizeof(char), len - 1, fp);
buf[len] = 0;
fclose(fp); fclose(fp);
return (int)len; return (int)len;
@@ -216,7 +222,7 @@ int fnwrite(char *value, char *fmt, ...)
int fngetint(char *path, int *val) int fngetint(char *path, int *val)
{ {
char buf[10]; char buf[64];
if (fnread(buf, sizeof(buf), path) <= 0) if (fnread(buf, sizeof(buf), path) <= 0)
return -1; return -1;
@@ -258,17 +264,39 @@ int strtobytes(char *arg)
return bytes; return bytes;
} }
char *sig2str(int sig) char *sig2str(int signo)
{ {
static char signame[8]; static char signame[8];
if (sig < 1 || sig >= (int)NELEMS(signames)) if (signo < 1 || signo >= (int)NELEMS(signames))
return ""; return "";
snprintf(signame, sizeof(signame), "/%s", signames[sig]); snprintf(signame, sizeof(signame), "/%s", signames[signo]);
return signame; return signame;
} }
/**
* str2sig - Translate signal name to the corresponding signal number.
* @sig: The name of the signal
*
* A signal can be a complete signal name such as "SIGHUP", or
* it can be the shortest unique name, such as "HUP" (no SIG prefix).
*/
int str2sig(char *sig)
{
size_t i;
if (!strncasecmp(sig, "SIG", 3))
sig += 3;
for (i = 1; i < NELEMS(signames); ++i) {
if (strcasecmp(sig, signames[i]) == 0)
return i;
}
return -1;
}
char *code2str(int code) char *code2str(int code)
{ {
static char codename[20]; static char codename[20];
@@ -291,6 +319,20 @@ void do_sleep(unsigned int sec)
; ;
} }
void do_usleep(unsigned int usec)
{
struct timespec deadline;
clock_gettime(CLOCK_MONOTONIC, &deadline);
deadline.tv_nsec += usec * 1000;
deadline.tv_sec += deadline.tv_nsec / 1000000000;
deadline.tv_nsec = deadline.tv_nsec % 1000000000;
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0 && errno == EINTR)
;
}
/* Seconds since boot, from sysinfo() */ /* Seconds since boot, from sysinfo() */
long jiffies(void) long jiffies(void)
{ {
@@ -393,6 +435,55 @@ char *sanitize(char *arg, size_t len)
return arg; return arg;
} }
static int hasopt(char *opts, char *opt)
{
char buf[strlen(opts) + 1];
char *ptr;
memcpy(buf, opts, sizeof(buf));
ptr = strtok(buf, ",");
while (ptr) {
if (!strcmp(ptr, opt))
return 1;
ptr = strtok(NULL, ",");
}
return 0;
}
int ismnt(char *file, char *dir, char *mode)
{
struct mntent *mnt;
int found = 0;
FILE *fp;
fp = setmntent(file, "r");
if (!fp)
return 0; /* Dunno, maybe not */
while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, dir)) {
if (mode) {
if (hasopt(mnt->mnt_opts, mode))
found = 1;
} else
found = 1;
break;
}
}
endmntent(fp);
return found;
}
/* Requires /proc to be mounted */
int fismnt(char *dir)
{
return ismnt("/proc/mounts", dir, NULL);
}
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
/* /*
* Called by initctl, and by finit at boot and shutdown, to * Called by initctl, and by finit at boot and shutdown, to
+12 -2
View File
@@ -32,7 +32,6 @@
#include <sys/reboot.h> #include <sys/reboot.h>
#include <termios.h> #include <termios.h>
#endif #endif
#include <lite/lite.h>
#ifndef RB_SW_SUSPEND /* Since Linux 2.5.18, but not in GLIBC until 2.19 */ #ifndef RB_SW_SUSPEND /* Since Linux 2.5.18, but not in GLIBC until 2.19 */
#define RB_SW_SUSPEND 0xd000fce2 #define RB_SW_SUSPEND 0xd000fce2
@@ -45,7 +44,13 @@ extern char *prognm;
/* Define for printheader() from conio.h, used in initctl */ /* Define for printheader() from conio.h, used in initctl */
#define SCREEN_WIDTH ttcols #define SCREEN_WIDTH ttcols
#include <lite/conio.h> #ifdef _LIBITE_LITE
# include <libite/conio.h>
# include <libite/lite.h>
#else
# include <lite/conio.h>
# include <lite/lite.h>
#endif
char *progname (char *arg0); char *progname (char *arg0);
@@ -56,15 +61,20 @@ int fngetint (char *path, int *val);
int strtobytes (char *arg); int strtobytes (char *arg);
char *sig2str (int sig); char *sig2str (int sig);
int str2sig (char *sig);
char *code2str (int code); char *code2str (int code);
void do_sleep (unsigned int sec); void do_sleep (unsigned int sec);
void do_usleep (unsigned int usec);
long jiffies (void); long jiffies (void);
char *uptime (long secs, char *buf, size_t len); char *uptime (long secs, char *buf, size_t len);
char *memsz (uint64_t sz, char *buf, size_t len); char *memsz (uint64_t sz, char *buf, size_t len);
char *sanitize (char *arg, size_t len); char *sanitize (char *arg, size_t len);
int ismnt (char *file, char *dir, char *mode);
int fismnt (char *dir);
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
int ttinit (void); int ttinit (void);
int ttraw (void); int ttraw (void);
+5 -1
View File
@@ -26,7 +26,11 @@
#include <time.h> #include <time.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <lite/lite.h> #ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
# include <lite/lite.h>
#endif
#include "helpers.h" #include "helpers.h"
#include "util.h" #include "util.h"
+11 -5
View File
@@ -1,12 +1,14 @@
EXTRA_DIST = tenv/lib.sh tenv/exec.sh tenv/start.sh SUBDIRS = common tenv
EXTRA_DIST += tenv/root.mk tenv/busybox-x86_64.md5
EXTRA_DIST += tenv/chrootsetup.sh EXTRA_DIST = setup-root.sh
EXTRA_DIST += setup-root.sh
EXTRA_DIST += common/service.conf common/service.sh
EXTRA_DIST += add-remove-dynamic-service.sh EXTRA_DIST += add-remove-dynamic-service.sh
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
EXTRA_DIST += start-stop-service.sh EXTRA_DIST += start-stop-service.sh
EXTRA_DIST += start-stop-service-sub-config.sh EXTRA_DIST += start-stop-service-sub-config.sh
EXTRA_DIST += start-kill-service.sh
EXTRA_DIST += start-stop-sysv.sh
EXTRA_DIST += start-stop-serv.sh
EXTRA_DIST += signal-service.sh
AM_TESTS_ENVIRONMENT = TENV_ROOT='$(abs_builddir)/tenv-root/'; AM_TESTS_ENVIRONMENT = TENV_ROOT='$(abs_builddir)/tenv-root/';
AM_TESTS_ENVIRONMENT += export TENV_ROOT; AM_TESTS_ENVIRONMENT += export TENV_ROOT;
@@ -26,6 +28,10 @@ TESTS += add-remove-dynamic-service.sh
TESTS += add-remove-dynamic-service-sub-config.sh TESTS += add-remove-dynamic-service-sub-config.sh
TESTS += start-stop-service.sh TESTS += start-stop-service.sh
TESTS += start-stop-service-sub-config.sh TESTS += start-stop-service-sub-config.sh
TESTS += start-kill-service.sh
TESTS += start-stop-sysv.sh
TESTS += start-stop-serv.sh
TESTS += signal-service.sh
clean-local: clean-local:
-rm -rf $(builddir)/tenv-root/ -rm -rf $(builddir)/tenv-root/
+14 -3
View File
@@ -1,5 +1,5 @@
Tests Finit Test Suite
===== ================
Finit comes with a set of tests and a small framework for running them. Finit comes with a set of tests and a small framework for running them.
Contributors are encouraged to write new tests when implementing features, or Contributors are encouraged to write new tests when implementing features, or
@@ -10,10 +10,16 @@ be able to be launched as PID 1. Since it's also running with it's own root
directory it will be able to function properly without having any super user directory it will be able to function properly without having any super user
privileges in the host environment. privileges in the host environment.
Running tests Running tests
------------- -------------
To run the test suite, first [build](../doc/build.md) Finit - then simply run: To run the test suite, first [build](../doc/build.md) Finit, e.g:
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var
make -j9 clean all
Then run (parallel does not work atm):
make check make check
@@ -24,3 +30,8 @@ test suite, which is handy when developing new tests or debugging existing
test. To execute an individual test, simply invoke the script containing it: test. To execute an individual test, simply invoke the script containing it:
./test/name-of-the-test.sh ./test/name-of-the-test.sh
Another way to run a single (or more) test(s) is to define the `TESTS`
environment variable:
TESTS="start-kill-service" make check
@@ -7,7 +7,8 @@ TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh" . "$TEST_DIR/tenv/lib.sh"
test_teardown() { test_teardown()
{
say "Test done $(date)" say "Test done $(date)"
say "Running test teardown." say "Running test teardown."
@@ -20,7 +21,7 @@ say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say "Add a dynamic service in $FINIT_RCSD/service.conf" say "Add a dynamic service in $FINIT_RCSD/service.conf"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh -- Dyn serv' > $FINIT_RCSD/service.conf"
say 'Reload Finit' say 'Reload Finit'
texec sh -c "initctl reload" texec sh -c "initctl reload"
+9 -2
View File
@@ -3,11 +3,13 @@
set -eu set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
export DEBUG=1
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh" . "$TEST_DIR/tenv/lib.sh"
test_teardown() { test_teardown()
{
say "Test done $(date)" say "Test done $(date)"
say "Running test teardown." say "Running test teardown."
@@ -15,12 +17,17 @@ test_teardown() {
texec rm -f /test_assets/service.sh texec rm -f /test_assets/service.sh
} }
say "Hi, this is /run et al"
texec sh -c "echo 'ROOT:/bin'; ls -l /bin/; echo 'ROOT:/sbin'; ls -l /sbin/; echo 'ROOT:/run'; ls -l /run/; echo 'ROOT:/run/finit/'; ls -l /run/finit/; echo 'ROOT:/dev'; ls -l /dev/; ps; ls -l /proc/1/fd/"
say "HUPing PID 1 ..."
texec sh -c "kill -HUP 1"
say "Test start $(date)" say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say "Add a dynamic service in $FINIT_CONF" say "Add a dynamic service in $FINIT_CONF"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh -- Dyn service' > $FINIT_CONF"
say 'Reload Finit' say 'Reload Finit'
texec sh -c "initctl reload" texec sh -c "initctl reload"
+5
View File
@@ -0,0 +1,5 @@
/Makefile
/Makefile.in
/.deps/
/serv
+4
View File
@@ -0,0 +1,4 @@
EXTRA_DIST = service.conf service.sh S01-service.sh slay.sh
noinst_PROGRAMS = serv
serv_SOURCES = serv.c
+51
View File
@@ -0,0 +1,51 @@
#!/bin/sh
# SysV style start script for service.sh
DAEMON="/test_assets/service.sh"
PIDFILE="/var/run/service.pid"
start()
{
printf 'Starting %s: ' "$DAEMON"
start-stop-daemon -S -b -q -p "$PIDFILE" -x "$DAEMON"
status=$?
if [ "$status" -eq 0 ]; then
echo "OK"
else
echo "FAIL"
fi
return "$status"
}
stop()
{
printf 'Stopping %s: ' "$DAEMON"
start-stop-daemon -K -q -p "$PIDFILE"
status=$?
if [ "$status" -eq 0 ]; then
echo "OK"
else
echo "FAIL"
fi
return "$status"
}
restart()
{
stop
sleep 1
start
}
case "$1" in
start|stop|restart)
"$1"
;;
*)
echo "Usage: $0 {start|stop|restart}, got $0 $1"
exit 1
esac
+110
View File
@@ -0,0 +1,110 @@
/*
* Basic UNIX daemon
*
* Options to run in foreground and to create a PID file. When running
* in foregrund it does not create a PID file by default.
*/
#include <err.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PROGNM "serv"
static void sig(int signo)
{
warnx("We got signal %d ...", signo);
exit(0);
}
static void pidfile(char *pidfn)
{
char fn[80];
pid_t pid;
FILE *fp;
if (!pidfn) {
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, PROGNM);
pidfn = fn;
}
pid = getpid();
warnx("Creating PID file %s with %d", pidfn, pid);
fp = fopen(pidfn, "w");
if (!fp)
exit(1);
fprintf(fp, "%d\n", pid);
fclose(fp);
}
static int usage(int rc)
{
FILE *fp = rc ? stderr : stdout;
fprintf(fp,
"%s [-nhp] [-P FILE]\n"
"\n"
" -n Run in foreground\n"
" -h Show help text (this)\n"
" -p Create PID file despite running in foreground\n"
" -P FILE Create PID file using FILE\n"
"\n"
"By default this program daemonizes itself to the background, and,\n"
"when it's done setting up its signal handler(s), creates a PID file\n"
"to let the rest of the system know it's done. When the program runs\n"
"in the foreground it does not create a PID file by default.\n",
PROGNM);
return rc;
}
int main(int argc, char *argv[])
{
int do_background = 1;
int do_pidfile = 1;
char *pidfn = NULL;
int c;
while ((c = getopt(argc, argv, "hnpP:")) != EOF) {
switch (c) {
case 'h':
return usage(0);
case 'n':
do_background = 0;
do_pidfile--;
break;
case 'p':
do_pidfile++;
break;
case 'P':
pidfn = optarg;
break;
default:
return usage(1);
}
}
/* Daemonize, fork to background etc. */
if (do_background) {
if (daemon(0, 1))
return 1;
}
/* Signal handlers first *then* PID file */
signal(SIGTERM, sig);
/* Tell world where we are, but not if bg w/o pid file */
if (do_pidfile == 1)
pidfile(pidfn);
warnx("Entering while(1) loop");
while (1)
sleep(1);
warnx("Leaving ...");
return 0;
}
+19 -2
View File
@@ -1,7 +1,24 @@
#!/bin/sh #!/bin/sh
set -eu cleanup()
{
echo "Got signal, stopping ..."
rm -f /run/service.pid
exit 0
}
# Hook SIGUSR1 and dump trace to file system
# shellcheck disable=SC2172
trap 'echo USR1 > /tmp/usr1.log' USR1
trap cleanup INT
trap cleanup TERM
trap cleanup QUIT
trap cleanup EXIT
echo $$ > /run/service.pid
# sleep may exit on known signal, so
# we cannot use 'set -e'
while true; do while true; do
sleep 5 sleep 1
done done
+47
View File
@@ -0,0 +1,47 @@
#!/bin/sh
nm=$1
# shellcheck disable=SC2046
getpid()
{
initctl status "$1" |awk '/PID :/{ print $3; }'
}
pid=$(getpid "$nm")
if [ -f oldpid ]; then
oldpid=$(cat oldpid)
if [ "$oldpid" = "$pid" ]; then
echo "Looks bad, wait for it ..."
sleep 3
pid=$(getpid "$nm")
if [ "$oldpid" = "$pid" ]; then
echo "Finit did not deregister old PID $oldpid vs $pid"
initctl status "$nm"
ps
echo "Reloading finit ..."
initctl reload
sleep 1
initctl status "$nm"
exit 1
fi
fi
fi
if [ "$pid" -le 1 ]; then
sleep 3
pid=$(getpid "$nm")
if [ "$pid" -le 1 ]; then
echo "Got a bad PID: $pid, aborting ..."
ps
sleep 1
initctl status "$nm"
ps
exit 1
fi
fi
echo "$pid" > oldpid
#echo "PID $pid, kill -9 ..."
kill -9 "$pid"
+16
View File
@@ -4,5 +4,21 @@ set -eu
# shellcheck disable=SC2154 # shellcheck disable=SC2154
make -C "$top_builddir" DESTDIR="$TENV_ROOT" install make -C "$top_builddir" DESTDIR="$TENV_ROOT" install
# shellcheck disable=SC2154 # shellcheck disable=SC2154
FINITBIN="$(pwd)/$top_builddir/src/finit" DEST="$TENV_ROOT" make -f "$srcdir/tenv/root.mk" FINITBIN="$(pwd)/$top_builddir/src/finit" DEST="$TENV_ROOT" make -f "$srcdir/tenv/root.mk"
# Drop plugins we don't need in test, only causes confusing FAIL in logs.
for plugin in tty.so urandom.so rtc.so modprobe.so hotplug.so; do
find "$TENV_ROOT" -name $plugin -delete
done
echo "$TENV_ROOT:"
ls -l "$TENV_ROOT"
echo "$TENV_ROOT:/bin"
ls -l "$TENV_ROOT"/bin
echo "$TENV_ROOT:/sbin"
ls -l "$TENV_ROOT"/sbin
echo "$TENV_ROOT:/var"
ls -l "$TENV_ROOT"/var/
+37
View File
@@ -0,0 +1,37 @@
#!/bin/sh
# Verifies initctl can send arbitrary signals (USR1 in this case) to finit managed services
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF"
texec rm -f /test_assets/service.sh
}
say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say 'Ensure file system is cleared'
texec rm -f /tmp/usr1.log
say "Add service stanza in $FINIT_CONF"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh -- Test service' > $FINIT_CONF"
say 'Reload Finit'
texec sh -c "initctl reload"
say 'Send SIGUSR1 to service...'
texec sh -c "initctl signal service.sh SIGUSR1"
# shellcheck disable=SC2016
retry 'assert "service.sh received SIGUSR" "$(texec cat /tmp/usr1.log)" = "USR1"' 10 1
+47
View File
@@ -0,0 +1,47 @@
#!/bin/sh
# Verifies Finit restarts crashing services and registers their new PID
# from their (default) PID file.
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF"
texec rm -f /test_assets/service.sh
}
say "Test start $(date)"
cp "$TEST_DIR"/common/slay.sh "$TENV_ROOT"/test_assets/
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
rm -f "$TENV_ROOT"/oldpid
say "Add service stanza in $FINIT_CONF"
texec sh -c "echo 'service [2345] respawn log /test_assets/service.sh -- Test service' > $FINIT_CONF"
say 'Reload Finit'
texec sh -c "initctl reload"
#texec sh -c "initctl status"
retry 'assert_num_children 1 service.sh'
texec sh -c "initctl status service.sh"
say 'Simulate service crash (kill -9 ..)'
i=0
laps=1000
while [ $i -lt $laps ]; do
i=$((i + 1))
say "Lap $i/$laps, killing service ..." # we have this, no sleep needed
texec sh -c "/test_assets/slay.sh service.sh"
done
retry 'assert_new_pid service.sh /run/service.pid'
retry 'assert_num_children 1 service.sh'
+67
View File
@@ -0,0 +1,67 @@
#!/bin/sh
set -eu
TEST_DIR=$(dirname "$0")
test_setup()
{
say "Test start $(date)"
cp "$TENV_ROOT"/../common/serv "$TENV_ROOT"/test_assets/
}
test_teardown()
{
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF"
texec rm -f /test_assets/serv
}
test_one()
{
pidfn=$1
service=$2
say "Add service stanza '$service' to $FINIT_CONF ..."
texec sh -c "echo '$service' > $FINIT_CONF"
say 'Reload Finit'
#texec sh -c "initctl debug"
texec sh -c "initctl reload"
#texec sh -c "initctl status"
#texec sh -c "ps"
#texec sh -c "initctl status"
#texec sh -c "ps"
#texec sh -c "initctl status serv"
retry 'assert_num_children 1 serv'
retry "assert_pidfile $pidfn" 1
say 'Stop the service'
texec sh -c "initctl stop serv"
retry 'assert_num_children 0 serv'
say 'Start the service again'
texec sh -c "initctl start serv"
retry 'assert_num_children 1 serv'
say "Done, drop service from $FINIT_CONF ..."
texec sh -c "rm $FINIT_CONF"
texec sh -c "initctl reload"
}
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_one "/run/serv.pid" "service pid:!/run/serv.pid /test_assets/serv -- Forking service"
# This one could never be started by and monitored by Finit: it forks to
# background and does not create a PID file. Essentially it's lost to
# Finit, and any other sane process monitor.
#test_one "/run/serv.pid" "service pid:/run/serv.pid /test_assets/serv -p -- Forking service w/o PID file"
test_one "/run/serv.pid" "service pid:/run/serv.pid /test_assets/serv -n -- Foreground service w/o PID file"
test_one "/run/serv.pid" "service /test_assets/serv -n -p -- Foreground service w/ PID file"
test_one "/run/servy.pid" "service pid:/run/servy.pid /test_assets/serv -n -p -P /run/servy.pid -- Foreground service w/ custom PID file"
+3 -2
View File
@@ -7,7 +7,8 @@ TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh" . "$TEST_DIR/tenv/lib.sh"
test_teardown() { test_teardown()
{
say "Test done $(date)" say "Test done $(date)"
say "Running test teardown." say "Running test teardown."
@@ -20,7 +21,7 @@ say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say "Add service stanza in $FINIT_RCSD/service.conf" say "Add service stanza in $FINIT_RCSD/service.conf"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_RCSD/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh -- Subserv' > $FINIT_RCSD/service.conf"
say 'Reload Finit' say 'Reload Finit'
texec sh -c "initctl reload" texec sh -c "initctl reload"
+3 -2
View File
@@ -7,7 +7,8 @@ TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh" . "$TEST_DIR/tenv/lib.sh"
test_teardown() { test_teardown()
{
say "Test done $(date)" say "Test done $(date)"
say "Running test teardown." say "Running test teardown."
@@ -20,7 +21,7 @@ say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/ cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
say "Add service stanza in $FINIT_CONF" say "Add service stanza in $FINIT_CONF"
texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh -- Test service' > $FINIT_CONF"
say 'Reload Finit' say 'Reload Finit'
texec sh -c "initctl reload" texec sh -c "initctl reload"
+42
View File
@@ -0,0 +1,42 @@
#!/bin/sh
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/tenv/lib.sh"
test_teardown() {
say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF"
texec rm -f /test_assets/service.sh
}
say "Test start $(date)"
cp "$TEST_DIR"/common/service.sh "$TENV_ROOT"/test_assets/
cp "$TEST_DIR"/common/S01-service.sh "$TENV_ROOT"/test_assets/
chmod +x "$TENV_ROOT"/test_assets/*.sh
say "Add sysv stanza in $FINIT_CONF"
texec sh -c "echo 'sysv [2345] pid:!/run/service.pid name:service.sh /test_assets/S01-service.sh -- SysV test service' > $FINIT_CONF"
say 'Reload Finit'
texec sh -c "initctl reload"
retry 'assert_num_children 1 service.sh'
say 'Stop the sysv service'
texec sh -c "initctl stop service.sh"
retry 'assert_num_children 0 service.sh'
say 'Start the sysv service again'
texec sh -c "initctl start service.sh"
retry 'assert_num_children 1 service.sh'
return 0
+2
View File
@@ -0,0 +1,2 @@
/Makefile
/Makefile.in
+2
View File
@@ -0,0 +1,2 @@
EXTRA_DIST = lib.sh exec.sh start.sh root.mk busybox-x86_64.md5
EXTRA_DIST += chrootsetup.sh
+1 -1
View File
@@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# Execute a given command from withing the test environment. # Execute a given command from within the test environment.
# #
# Copyright (c) 2021 Jacques de Laval <jacques@de-laval.se> # Copyright (c) 2021 Jacques de Laval <jacques@de-laval.se>
# #
+48 -18
View File
@@ -21,21 +21,37 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
assert_num_children() { assert_num_children()
{
assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1"
} }
texec() { # shellcheck disable=SC2086
# shellcheck disable=SC2154 assert_new_pid()
{
assert "Finit has registered new PID" "$(texec initctl |grep $1 |awk '{print $1}')" -eq "$(texec cat $2)"
}
# shellcheck disable=SC2086
assert_pidfile()
{
assert "Process has PID file" "$(texec find $1 2>/dev/null)"
}
# shellcheck disable=SC2154
texec()
{
"$TEST_DIR/tenv/exec.sh" "$finit_pid" "$@" "$TEST_DIR/tenv/exec.sh" "$finit_pid" "$@"
} }
pause() { pause()
{
echo "Press any key to continue... " echo "Press any key to continue... "
read -r REPLY read -r REPLY
} }
toggle_finit_debug() { toggle_finit_debug()
{
say 'Toggle finit debug' say 'Toggle finit debug'
texec initctl debug texec initctl debug
sleep 0.5 sleep 0.5
@@ -45,23 +61,30 @@ color_reset='\e[0m'
fg_red='\e[1;31m' fg_red='\e[1;31m'
fg_green='\e[1;32m' fg_green='\e[1;32m'
fg_yellow='\e[1;33m' fg_yellow='\e[1;33m'
log() { log()
printf "< %s > %b%b%b %s\n" "$0" "$1" "$2" "$color_reset" "$3" {
test=$(basename "$0" ".sh")
printf "\e[2m[%s]\e[0m %b%b%b %s\n" "$test" "$1" "$2" "$color_reset" "$3"
} }
assert() { assert()
{
__assert_msg=$1 __assert_msg=$1
shift shift
if [ ! "$@" ]; then if [ ! "$@" ]; then
log "$fg_red" ✘ "$__assert_msg ($*)" log "$fg_red" ✘ "$__assert_msg ($*)"
return 1 return 1
fi fi
log "$fg_green" ✔ "$__assert_msg" log "$fg_green" ✔ "$__assert_msg"
} }
retry() { retry()
{
__retry_cmd=$1 __retry_cmd=$1
shift shift
case "$#" in case "$#" in
2) 2)
__retry_n=$1 __retry_n=$1
@@ -83,16 +106,20 @@ retry() {
echo "$__retry_cmd_out" && \ echo "$__retry_cmd_out" && \
return 0 return 0
done done
__retry_cmd_exit="$?" __retry_cmd_exit="$?"
echo "$__retry_cmd_out" echo "$__retry_cmd_out"
return "$__retry_cmd_exit" return "$__retry_cmd_exit"
} }
say() { say()
{
log "$fg_yellow" • "$@" log "$fg_yellow" • "$@"
} }
teardown() { teardown()
{
test_status="$?" test_status="$?"
if type test_teardown > /dev/null 2>&1 ; then if type test_teardown > /dev/null 2>&1 ; then
@@ -111,9 +138,6 @@ teardown() {
wait wait
if [ -d "$TENV_ROOT/var/lock" ]; then
chmod +r "$TENV_ROOT/var/lock"
fi
rm -f "$TENV_ROOT"/running_test.pid rm -f "$TENV_ROOT"/running_test.pid
} }
@@ -130,15 +154,21 @@ if [ -n "${DEBUG:-}" ]; then
FINIT_ARGS="${FINIT_ARGS:-} finit.debug=on" FINIT_ARGS="${FINIT_ARGS:-} finit.debug=on"
fi fi
# shellcheck disable=2086 # shellcheck disable=2086
"$TEST_DIR/tenv/start.sh" finit ${FINIT_ARGS:-} & "$TEST_DIR/tenv/start.sh" finit ${FINIT_ARGS:-} finit.debug=on &
finit_ppid=$! finit_ppid=$!
echo "$finit_ppid" > "$TENV_ROOT"/running_test.pid echo "$finit_ppid" > "$TENV_ROOT"/running_test.pid
>&2 echo "Finit runs as PID $finit_ppid outside of container."
>&2 echo "Hint: Execute 'TENV_ROOT=$TENV_ROOT $TEST_DIR/tenv/enter.sh' to enter the test namespace" >&2 echo "Hint: Execute 'TENV_ROOT=$TENV_ROOT $TEST_DIR/tenv/enter.sh' to enter the test namespace"
log "$color_reset" 'Setup of test environment done' '' log "$color_reset" 'Setup of test environment done' ''
finit_pid=$(retry "pgrep -P $finit_ppid") finit_pid=$(retry "pgrep -P $finit_ppid")
tty=/dev/$(texec cat /sys/class/tty/console/active) if type test_setup > /dev/null 2>&1 ; then
texec cat "$tty" & test_setup
sleep 1 fi
#tty=/dev/$(texec cat /sys/class/tty/console/active)
#texec cat "$tty" &
sleep 5

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