Compare commits

..
122 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
58 changed files with 1573 additions and 434 deletions
+4 -2
View File
@@ -15,7 +15,7 @@ jobs:
build: build:
# Verify we can build on latest Ubuntu with both gcc and clang # Verify we can build on latest Ubuntu with both gcc and clang
name: ${{ matrix.compiler }} name: ${{ matrix.compiler }}
runs-on: ubuntu-latest runs-on: ubuntu-18.04
strategy: strategy:
matrix: matrix:
compiler: [gcc, clang] compiler: [gcc, clang]
@@ -60,7 +60,9 @@ jobs:
/tmp/sbin/initctl -h /tmp/sbin/initctl -h
- name: Run Unit Tests - name: Run Unit Tests
run: | run: |
make -j1 distcheck 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 - name: Upload Test Results
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
+3
View File
@@ -45,3 +45,6 @@ GTAGS
/.autoconf /.autoconf
/.automake /.automake
/.built /.built
# VS Code user settings
.vscode
+26
View File
@@ -4,6 +4,31 @@ 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 [4.2][] - 2022-01-16
-------------------- --------------------
@@ -1039,6 +1064,7 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.2...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.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.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 -3
View File
@@ -1,4 +1,4 @@
[![License Badge][]][License] [![GitHub Status]][GitHub] [![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)
@@ -598,7 +598,7 @@ and proposed extensions.
[Joachim Wiberg]: https://troglobit.com [Joachim Wiberg]: https://troglobit.com
[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
[GitHub]: https://travis-ci.org/troglobit/finit [GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
[GitHub 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
+5 -3
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.2, 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])
@@ -86,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
@@ -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 */
+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
+11 -3
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
@@ -136,6 +140,10 @@ 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]
+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
+7 -7
View File
@@ -104,9 +104,9 @@ static void clean(void *arg)
} }
} }
static void ln(const char *target, const char *linkpath, int ignerr) static void ln(const char *target, const char *linkpath)
{ {
if (symlink(target, linkpath) && !ignerr) if (symlink(target, linkpath) && errno != EEXIST)
_pe("Failed creating %s -> %s symlink", target, linkpath); _pe("Failed creating %s -> %s symlink", target, linkpath);
} }
@@ -131,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);
ln("/run/lock", "/var/lock", 0); ln("/run/lock", "/var/lock");
ln("/dev/shm", "/run/shm", 0); 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 */
ln("/run", "/var/run", 1); ln("../run", "/var/run");
} else { } else {
makedir("/var/lock", 1777); makedir("/var/lock", 1777);
makedir("/var/run", 0755); makedir("/var/run", 0755);
@@ -189,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"))
ln("../proc/self/mounts", "/etc/mtab", 1); 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");
@@ -198,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);
} }
+6 -4
View File
@@ -34,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__);
@@ -51,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);
} }
+4
View File
@@ -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 = {
+2 -2
View File
@@ -58,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
@@ -66,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);
} }
+36 -9
View File
@@ -51,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;
@@ -67,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;
@@ -83,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;
@@ -95,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;
@@ -114,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];
@@ -133,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;
} }
@@ -407,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);
} }
} }
@@ -443,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;
+26 -8
View File
@@ -466,8 +466,10 @@ void cgroup_init(uev_ctx_t *ctx)
if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) {
if (errno == ENOENT) if (errno == ENOENT)
logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling."); 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 else
_pe("Failed mounting cgroup v2, disabling."); _pe("Failed mounting cgroup v2");
avail = 0; avail = 0;
return; return;
} }
@@ -477,6 +479,7 @@ void cgroup_init(uev_ctx_t *ctx)
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); umount(FINIT_CGPATH);
avail = 0; avail = 0;
return; return;
@@ -494,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);
@@ -505,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;
} }
} }
+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;
} }
+15 -15
View File
@@ -211,22 +211,18 @@ void conf_parse_cmdline(int argc, char *argv[])
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);
@@ -234,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 */
@@ -502,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;
} }
+85 -47
View File
@@ -28,6 +28,7 @@
#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>
@@ -70,89 +71,126 @@ 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;
/* We must create a copy that is possible to modify. */ if (!log) {
backup = arg = strdup(cmd); /* We must create a copy that is possible to modify. */
if (!arg) backup = arg = strdup(cmd);
return 1; /* Failed allocating a string to be modified. */ if (!arg)
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 ");
while (arg && i < NUM_ARGS) { while (arg && i < NUM_ARGS) {
/* Handle run("su -c \"dbus-daemon --system\" messagebus"); /* Handle run("su -c \"dbus-daemon --system\" messagebus");
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */ * => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
if (*arg == '\'' || *arg == '"') { if (*arg == '\'' || *arg == '"') {
char *p, delim[2] = " "; char *p, delim[2] = " ";
delim[0] = arg[0]; delim[0] = arg[0];
args[i++] = arg++; args[i++] = arg++;
strsep(&arg, delim); strsep(&arg, delim);
p = arg - 1; p = arg - 1;
*p = *delim; *p = *delim;
*arg++ = 0; *arg++ = 0;
} else { } else {
args[i++] = strsep(&arg, "\t "); args[i++] = strsep(&arg, "\t ");
}
} }
} args[i] = NULL;
args[i] = NULL;
if (i == NUM_ARGS && arg) { if (i == NUM_ARGS && arg) {
_e("Command too long: %s", cmd); _e("Command too long: %s", cmd);
free(backup); free(backup);
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);
}
rc = pclose(pp);
}
} }
sig_unblock(); _exit(rc);
execvp(args[0], args);
_exit(1); /* Only if execv() fails. */
} else if (-1 == pid) { } else if (-1 == pid) {
_pe("%s", args[0]); _pe("%s", args[0]);
free(backup); if (backup)
free(backup);
return -1; return -1;
} }
status = complete(args[0], pid); status = complete(args[0], pid);
if (-1 == status) { if (-1 == status) {
free(backup); if (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 */
} }
free(backup); if (backup)
free(backup);
return result; return result;
} }
@@ -186,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) {
@@ -222,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",
@@ -230,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++) {
+2 -1
View File
@@ -333,7 +333,8 @@ static void fs_mount_all(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();
+1
View File
@@ -95,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
+68 -9
View File
@@ -30,6 +30,7 @@
#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>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
# include <libite/lite.h> # include <libite/lite.h>
@@ -40,6 +41,7 @@
#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"
@@ -57,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 */ \
} }
@@ -493,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) {
@@ -522,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;
+2 -1
View File
@@ -93,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[]);
+128 -48
View File
@@ -52,6 +52,7 @@ struct cmd {
struct cmd *ctx; struct cmd *ctx;
int (*cb)(char *arg); int (*cb)(char *arg);
int *cond; int *cond;
int (*cb_multiarg)(int argc, char **argv);
}; };
int icreate = 0; int icreate = 0;
@@ -160,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);
} }
@@ -275,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;
@@ -292,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;
@@ -722,7 +781,7 @@ 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)) {
svc_ident(svc, ident, sizeof(ident)); svc_ident(svc, ident, sizeof(ident));
if (string_match(ident, arg)) if (string_compare(ident, arg))
num++; num++;
if (string_case_compare(ident, arg)) if (string_case_compare(ident, arg))
exact++; exact++;
@@ -735,8 +794,15 @@ static int show_status(char *arg)
if (!svc) if (!svc)
return 255; return 255;
if (quiet) if (quiet) {
return svc->state != SVC_RUNNING_STATE; if (svc_is_runtask(svc))
if (svc->started)
return 0;
else
return 1;
else
return svc->state != SVC_RUNNING_STATE;
}
pidfn = svc->pidfile; pidfn = svc->pidfile;
if (pidfn[0] == '!') if (pidfn[0] == '!')
@@ -765,6 +831,8 @@ static int show_status(char *arg)
char *group; char *group;
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf)); 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); snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
cg = cg_conf(path); cg = cg_conf(path);
@@ -774,6 +842,7 @@ static int show_status(char *arg)
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);
@@ -797,7 +866,7 @@ static int show_status(char *arg)
char *lvls; char *lvls;
svc_ident(svc, ident, sizeof(ident)); svc_ident(svc, ident, sizeof(ident));
if (num && !string_match(ident, arg)) if (num && !string_compare(ident, arg))
continue; continue;
printf("%-*d ", pw, svc->pid); printf("%-*d ", pw, svc->pid);
@@ -891,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)
@@ -920,10 +990,10 @@ static int usage(int rc)
" disable <CONF> Disable .conf in %s\n", ena); " disable <CONF> Disable .conf in %s\n", ena);
if (has_rcsd) if (has_rcsd)
fprintf(stderr, fprintf(stderr,
" reload Reload *.conf in " FINIT_RCSD " (activate changes)\n"); " reload Reload *.conf in " FINIT_RCSD " (activate changes)\n");
else else
fprintf(stderr, fprintf(stderr,
" reload Reload " FINIT_CONF " (activate changes)\n"); " reload Reload " FINIT_CONF " (activate changes)\n");
fprintf(stderr, fprintf(stderr,
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n" // " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
@@ -938,6 +1008,7 @@ 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" " 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");
@@ -994,12 +1065,15 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
if (!cmd_cond(&command[i])) if (!cmd_cond(&command[i]))
continue; continue;
if (!string_match(command[i].cmd, argv[0])) 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;
@@ -1018,6 +1092,7 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command)
return command[0].cb(NULL); /* default cmd */ return command[0].cb(NULL); /* default cmd */
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct option long_options[] = { struct option long_options[] = {
@@ -1031,55 +1106,57 @@ 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, NULL }, /* default cmd */ { "status", NULL, do_cond_show, NULL, NULL }, /* default cmd */
{ "dump", NULL, do_cond_dump, NULL }, { "dump", NULL, do_cond_dump, NULL, NULL },
{ "set", NULL, do_cond_set, NULL }, { "set", NULL, do_cond_set, NULL, NULL },
{ "clr", NULL, do_cond_clr, NULL }, { "clr", NULL, do_cond_clr, NULL, NULL },
{ "clear", NULL, do_cond_clr, NULL }, { "clear", NULL, do_cond_clr, NULL, NULL },
{ NULL, NULL, NULL, NULL } { NULL, NULL, NULL, NULL, NULL }
}; };
struct cmd command[] = { struct cmd command[] = {
{ "status", NULL, show_status, NULL }, /* default cmd */ { "status", NULL, show_status, NULL, NULL }, /* default cmd */
{ "ident", NULL, show_ident, NULL }, { "ident", NULL, show_ident, NULL, NULL },
{ "debug", NULL, toggle_debug, NULL }, { "debug", NULL, toggle_debug, NULL, NULL },
{ "devel", NULL, do_devel, NULL }, { "devel", NULL, do_devel, NULL, NULL },
{ "help", NULL, do_help, NULL }, { "help", NULL, do_help, NULL, NULL },
{ "version", NULL, show_version, NULL }, { "version", NULL, show_version, NULL, NULL },
{ "list", NULL, serv_list, NULL }, { "list", NULL, serv_list, NULL, NULL },
{ "ls", NULL, serv_list, NULL }, { "ls", NULL, serv_list, NULL, NULL },
{ "enable", NULL, serv_enable, NULL }, { "enable", NULL, serv_enable, NULL, NULL },
{ "disable", NULL, serv_disable, NULL }, { "disable", NULL, serv_disable, NULL, NULL },
{ "touch", NULL, serv_touch, NULL }, { "touch", NULL, serv_touch, NULL, NULL },
{ "show", NULL, serv_show, NULL }, { "show", NULL, serv_show, NULL, NULL },
{ "edit", NULL, serv_edit, NULL }, { "edit", NULL, serv_edit, NULL, NULL },
{ "create", NULL, serv_creat, NULL }, { "create", NULL, serv_creat, NULL, NULL },
{ "delete", NULL, serv_delete, NULL }, { "delete", NULL, serv_delete, NULL, NULL },
{ "reload", NULL, do_reload, NULL }, { "reload", NULL, do_reload, NULL, NULL },
{ "cond", cond, NULL, NULL }, { "cond", cond, NULL, NULL, NULL },
{ "log", NULL, do_log, NULL }, { "log", NULL, do_log, NULL, NULL },
{ "start", NULL, do_start, NULL }, { "start", NULL, do_start, NULL, NULL },
{ "stop", NULL, do_stop, NULL }, { "stop", NULL, do_stop, NULL, NULL },
{ "restart", NULL, do_restart, NULL }, { "restart", NULL, do_restart, NULL, NULL },
{ "signal", NULL, NULL, NULL, do_signal },
{ "cgroup", NULL, show_cgroup, &cgrp }, { "cgroup", NULL, show_cgroup, &cgrp, NULL },
{ "ps", NULL, show_cgps, &cgrp }, { "ps", NULL, show_cgps, &cgrp, NULL },
{ "top", NULL, show_cgtop, &cgrp }, { "top", NULL, show_cgtop, &cgrp, NULL },
{ "runlevel", NULL, do_runlevel, NULL }, { "runlevel", NULL, do_runlevel, NULL, NULL },
{ "reboot", NULL, do_reboot, NULL }, { "reboot", NULL, do_reboot, NULL, NULL },
{ "halt", NULL, do_halt, NULL }, { "halt", NULL, do_halt, NULL, NULL },
{ "poweroff", NULL, do_poweroff, NULL }, { "poweroff", NULL, do_poweroff, NULL, NULL },
{ "suspend", NULL, do_suspend, NULL }, { "suspend", NULL, do_suspend, NULL, NULL },
{ "utmp", NULL, do_utmp, &utmp }, { "utmp", NULL, do_utmp, &utmp, NULL },
{ NULL, NULL, NULL, NULL } { NULL, NULL, NULL, NULL, NULL }
}; };
int interactive = 1, c; int interactive = 1, c;
@@ -1090,7 +1167,7 @@ int main(int argc, char *argv[])
cgrp = cgroup_avail(); cgrp = cgroup_avail();
utmp = has_utmp(); utmp = has_utmp();
while ((c = getopt_long(argc, argv, "1bcdfh?pqtv", long_options, NULL)) != EOF) { while ((c = getopt_long(argc, argv, "1bcdfh?pqtvV", long_options, NULL)) != EOF) {
switch(c) { switch(c) {
case '1': case '1':
ionce = 1; ionce = 1;
@@ -1131,6 +1208,9 @@ int main(int argc, char *argv[])
case 'v': case 'v':
verbose = 1; verbose = 1;
break; break;
case 'V':
return show_version(NULL);
} }
} }
+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;
+17 -13
View File
@@ -59,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 */
@@ -95,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;
} }
@@ -111,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;
} }
@@ -125,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:
+4 -4
View File
@@ -40,10 +40,10 @@
* 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 (void); void log_init (void);
void log_exit (void); void log_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:
+78 -20
View File
@@ -571,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;
@@ -628,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);
@@ -701,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;
} }
/** /**
@@ -743,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;
@@ -755,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);
} else
service_cleanup(svc); service_cleanup(svc);
svc_set_state(svc, SVC_HALTED_STATE);
} else
svc_set_state(svc, SVC_STOPPING_STATE);
} else {
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;
@@ -778,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;
} }
} }
@@ -829,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 {
@@ -880,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);
} }
@@ -1318,7 +1325,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
} }
/* 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) {
@@ -1548,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);
@@ -1564,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);
} }
@@ -1593,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];
@@ -1621,7 +1656,7 @@ static void service_retry(svc_t *svc)
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;
@@ -1677,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);
}
}
} }
/* /*
@@ -1719,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);
@@ -1737,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;
@@ -1797,7 +1853,8 @@ restart:
* Restart directly after the first crash, * Restart directly after the first crash,
* then retry after 2 sec * then retry after 2 sec
*/ */
_d("delayed restart of %s", svc->cmd); if (!svc->respawn)
_d("delayed restart of %s", svc->cmd);
service_timeout_after(svc, 1, service_retry); service_timeout_after(svc, 1, service_retry);
break; break;
} }
@@ -1813,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) {
+76 -11
View File
@@ -71,6 +71,7 @@
#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>
#ifdef _LIBITE_LITE #ifdef _LIBITE_LITE
@@ -172,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;
@@ -191,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");
@@ -203,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);
} }
@@ -211,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);
@@ -223,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();
@@ -235,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);
@@ -255,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();
+23 -23
View File
@@ -256,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;
} }
@@ -473,23 +473,23 @@ 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) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { svc = svc_find_by_pid(pid);
int v = 0; if (svc)
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue;
return svc; return svc;
}
return NULL;
} }
/*
* 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)) {
if (svc->pid != pid) int v = 0;
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue; continue;
return svc; return svc;
@@ -650,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;
@@ -686,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 {
@@ -699,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 {
@@ -720,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);
} }
} }
+30 -2
View File
@@ -222,7 +222,7 @@ 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_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; }
@@ -278,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)
@@ -307,7 +332,10 @@ static inline char *svc_status(svc_t *svc)
return "unknown"; return "unknown";
case SVC_DONE_STATE: case SVC_DONE_STATE:
return "done"; if (svc->started)
return "done";
else
return "failed";
case SVC_STOPPING_STATE: case SVC_STOPPING_STATE:
switch (svc->type) { switch (svc->type) {
+39 -3
View File
@@ -264,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];
@@ -297,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)
{ {
+2
View File
@@ -61,9 +61,11 @@ 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);
+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,12 +7,13 @@ 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 "Running test teardown." say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_RCSD/service.conf" texec rm -f "$FINIT_RCSD/service.conf"
texec rm -f /test_assets/service.sh texec rm -f /test_assets/service.sh
} }
say "Test start $(date)" say "Test start $(date)"
@@ -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"
+13 -6
View File
@@ -3,24 +3,31 @@
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 "Running test teardown." say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF" texec rm -f "$FINIT_CONF"
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"
+7 -6
View File
@@ -7,12 +7,13 @@ 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 "Running test teardown." say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_RCSD/service.conf" texec rm -f "$FINIT_RCSD/service.conf"
texec rm -f /test_assets/service.sh texec rm -f /test_assets/service.sh
} }
say "Test start $(date)" say "Test start $(date)"
@@ -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"
+7 -6
View File
@@ -7,12 +7,13 @@ 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 "Running test teardown." say "Test done $(date)"
say "Running test teardown."
texec rm -f "$FINIT_CONF" texec rm -f "$FINIT_CONF"
texec rm -f /test_assets/service.sh texec rm -f /test_assets/service.sh
} }
say "Test start $(date)" say "Test start $(date)"
@@ -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
+5 -5
View File
@@ -33,12 +33,12 @@ mount -t proc none /proc
mount -t sysfs none /sys mount -t sysfs none /sys
if [ "$(ls -A /tmp)" ]; then if [ "$(ls -A /tmp)" ]; then
mkdir -p /tmp.shadow mkdir -p /tmp.shadow
mount --bind /tmp /tmp.shadow mount --bind /tmp /tmp.shadow
mount -t tmpfs none /tmp mount -t tmpfs none /tmp
cp -a /tmp.shadow/* /tmp/ cp -a /tmp.shadow/* /tmp/
else else
mount -t tmpfs none /tmp mount -t tmpfs none /tmp
fi fi
mkdir -p "$FINIT_RCSD" mkdir -p "$FINIT_RCSD"
+7 -7
View File
@@ -4,10 +4,10 @@ set -eu
cmd="" cmd=""
if [ "$#" -lt 1 ]; then if [ "$#" -lt 1 ]; then
cmd="/bin/sh" cmd="/bin/sh"
else else
cmd=$1 cmd=$1
shift shift
fi fi
TEST_DIR=$(dirname "$0")/.. TEST_DIR=$(dirname "$0")/..
@@ -15,11 +15,11 @@ TENV_ROOT="${TENV_ROOT:-$(pwd)/${TEST_DIR}/tenv-root}"
PID_FILE="$TENV_ROOT/running_test.pid" PID_FILE="$TENV_ROOT/running_test.pid"
if [ -f "$PID_FILE" ] ; then if [ -f "$PID_FILE" ] ; then
target=$(cat "$PID_FILE") target=$(cat "$PID_FILE")
target=$(pgrep -P "$target") target=$(pgrep -P "$target")
else else
echo "No test running!" echo "No test running!"
exit 1 exit 1
fi fi
source "$TEST_DIR/test.env" source "$TEST_DIR/test.env"
+13 -13
View File
@@ -24,9 +24,9 @@
set -eu set -eu
if [ "$#" -lt 2 ]; then if [ "$#" -lt 2 ]; then
echo "Usage:" echo "Usage:"
echo " $0 [target-pid] [command [arguments]]" echo " $0 [target-pid] [command [arguments]]"
exit 1 exit 1
fi fi
target="$1" target="$1"
shift shift
@@ -45,13 +45,13 @@ export PS4='+ '
PATH="$TESTENV_PATH" PATH="$TESTENV_PATH"
export PATH export PATH
"$nsenter" \ "$nsenter" \
--preserve-credentials \ --preserve-credentials \
--user \ --user \
--mount \ --mount \
--uts \ --uts \
--ipc \ --ipc \
--net \ --net \
--pid \ --pid \
-w -t "$target" \ -w -t "$target" \
"$chroot" "$TENV_ROOT" "$@" "$chroot" "$TENV_ROOT" "$@"
+104 -74
View File
@@ -21,100 +21,124 @@
# 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()
"$TEST_DIR/tenv/exec.sh" "$finit_pid" "$@" {
assert "Finit has registered new PID" "$(texec initctl |grep $1 |awk '{print $1}')" -eq "$(texec cat $2)"
} }
pause() { # shellcheck disable=SC2086
echo "Press any key to continue... " assert_pidfile()
read -r REPLY {
assert "Process has PID file" "$(texec find $1 2>/dev/null)"
} }
toggle_finit_debug() { # shellcheck disable=SC2154
say 'Toggle finit debug' texec()
texec initctl debug {
sleep 0.5 "$TEST_DIR/tenv/exec.sh" "$finit_pid" "$@"
}
pause()
{
echo "Press any key to continue... "
read -r REPLY
}
toggle_finit_debug()
{
say 'Toggle finit debug'
texec initctl debug
sleep 0.5
} }
color_reset='\e[0m' 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 {
shift __assert_msg=$1
if [ ! "$@" ]; then shift
log "$fg_red" ✘ "$__assert_msg ($*)"
return 1 if [ ! "$@" ]; then
fi log "$fg_red" ✘ "$__assert_msg ($*)"
log "$fg_green" ✔ "$__assert_msg" return 1
fi
log "$fg_green" ✔ "$__assert_msg"
} }
retry() { retry()
__retry_cmd=$1 {
shift __retry_cmd=$1
case "$#" in shift
2)
__retry_n=$1
__retry_sleep=$2
;;
1)
__retry_n=$1
__retry_sleep=0.1
;;
*)
__retry_n=50
__retry_sleep=0.1
;;
esac
for _ in $(seq 1 "$__retry_n"); do case "$#" in
sleep "$__retry_sleep" 2)
__retry_cmd_out=$(eval "$__retry_cmd") && \ __retry_n=$1
echo "$__retry_cmd_out" && \ __retry_sleep=$2
return 0 ;;
done 1)
__retry_cmd_exit="$?" __retry_n=$1
echo "$__retry_cmd_out" __retry_sleep=0.1
return "$__retry_cmd_exit" ;;
*)
__retry_n=50
__retry_sleep=0.1
;;
esac
for _ in $(seq 1 "$__retry_n"); do
sleep "$__retry_sleep"
__retry_cmd_out=$(eval "$__retry_cmd") && \
echo "$__retry_cmd_out" && \
return 0
done
__retry_cmd_exit="$?"
echo "$__retry_cmd_out"
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
test_teardown test_teardown
fi fi
log "$color_reset" '--' '' log "$color_reset" '--' ''
if [ "$test_status" -eq 0 ]; then if [ "$test_status" -eq 0 ]; then
log "$fg_green" 'TEST PASS' '' log "$fg_green" 'TEST PASS' ''
else else
log "$fg_red" 'TEST FAIL' '' log "$fg_red" 'TEST FAIL' ''
fi fi
if [ -n "${finit_pid+x}" ]; then if [ -n "${finit_pid+x}" ]; then
texec kill -SIGUSR2 1 texec kill -SIGUSR2 1
fi fi
wait wait
if [ -d "$TENV_ROOT/var/lock" ]; then rm -f "$TENV_ROOT"/running_test.pid
chmod +r "$TENV_ROOT/var/lock"
fi
rm -f "$TENV_ROOT"/running_test.pid
} }
trap teardown EXIT trap teardown EXIT
@@ -127,18 +151,24 @@ export TENV_ROOT
# Setup test environment # Setup test environment
if [ -n "${DEBUG:-}" ]; then 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
+67 -41
View File
@@ -20,51 +20,59 @@
# 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.
DEST ?= ../tenv-root DEST ?= ../tenv-root
srcdir ?= ../ srcdir ?= ../
ARCH ?= x86_64 CACHE ?= ~/.cache
ARCH ?= x86_64
FINITBIN ?= ./sbin/finit FINITBIN ?= ./sbin/finit
BBVER ?= 1.31.0 BBVER ?= 1.31.0
BBBIN = busybox-$(ARCH) BBBIN = busybox-$(ARCH)
BBURL ?= https://www.busybox.net/downloads/binaries/$(BBVER)-defconfig-multiarch-musl/$(BBBIN) BBURL ?= https://www.busybox.net/downloads/binaries/$(BBVER)-defconfig-multiarch-musl/$(BBBIN)
binaries = $(DEST)/bin/cat \ binaries = $(DEST)/bin/awk \
$(DEST)/bin/cp \ $(DEST)/bin/cat \
$(DEST)/bin/date \ $(DEST)/bin/cp \
$(DEST)/bin/echo \ $(DEST)/bin/date \
$(DEST)/bin/env \ $(DEST)/bin/echo \
$(DEST)/bin/find \ $(DEST)/bin/env \
$(DEST)/bin/kill \ $(DEST)/bin/find \
$(DEST)/bin/ls \ $(DEST)/bin/grep \
$(DEST)/bin/mkdir \ $(DEST)/bin/kill \
$(DEST)/bin/mkfifo \ $(DEST)/bin/ls \
$(DEST)/bin/mknod \ $(DEST)/bin/mkdir \
$(DEST)/bin/mount \ $(DEST)/bin/mkfifo \
$(DEST)/bin/printf \ $(DEST)/bin/mknod \
$(DEST)/bin/pgrep \ $(DEST)/bin/mount \
$(DEST)/bin/ps \ $(DEST)/bin/printf \
$(DEST)/bin/rm \ $(DEST)/bin/pgrep \
$(DEST)/bin/sh \ $(DEST)/bin/ps \
$(DEST)/bin/sleep \ $(DEST)/bin/rm \
$(DEST)/bin/top \ $(DEST)/bin/sh \
$(DEST)/bin/touch $(DEST)/bin/sleep \
$(DEST)/bin/tail \
$(DEST)/bin/top \
$(DEST)/bin/touch \
$(DEST)/bin/start-stop-daemon
dirs = $(DEST)/bin \ dirs = $(DEST)/bin \
$(DEST)/dev \ $(DEST)/dev \
$(DEST)/etc \ $(DEST)/etc \
$(DEST)/proc \ $(DEST)/proc \
$(DEST)/sbin \ $(DEST)/sbin \
$(DEST)/sys \ $(DEST)/var \
$(DEST)/test_assets \ $(DEST)/run \
$(DEST)/tmp $(DEST)/sys \
$(DEST)/test_assets \
$(DEST)/tmp
_libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') _libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+')
libs = $(foreach path,$(_libs_src),$(abspath $(DEST))$(path)) libs = $(foreach path,$(_libs_src),$(abspath $(DEST))$(path))
all: $(dirs) $(binaries) $(libs) $(DEST)/bin/chrootsetup.sh all: $(dirs) $(binaries) $(libs) $(DEST)/bin/chrootsetup.sh
touch $(DEST)/etc/fstab
$(dirs): $(dirs):
mkdir -p $@ mkdir -p $@
@@ -72,11 +80,29 @@ $(dirs):
$(DEST)/bin/$(BBBIN).md5: $(DEST)/bin/$(BBBIN).md5:
cp $(srcdir)/tenv/$(notdir $@) $@ cp $(srcdir)/tenv/$(notdir $@) $@
.PHONY: $(DEST)/bin/$(BBBIN)
$(DEST)/bin/$(BBBIN): $(DEST)/bin/$(BBBIN).md5 $(DEST)/bin/$(BBBIN): $(DEST)/bin/$(BBBIN).md5
wget -O $@ $(BBURL) @cd $(dir $@)
chmod +x $@ @if ! md5sum --status -c $(BBBIN).md5 2>/dev/null; then \
cd $(dir $@); \ if [ -d $(CACHE) ]; then \
md5sum -c $(BBBIN).md5 echo "Cannot find $(BBBIN), checking $(CACHE) ..."; \
cd $(CACHE); \
cp $(DEST)/bin/$(BBBIN).md5 .; \
if ! md5sum --status -c $(BBBIN).md5; then \
echo "No $(BBBIN) available downloading ..."; \
wget $(BBURL); \
else \
echo "Found valid $(BBBIN) in cache!"; \
fi; \
cp $(BBBIN) $@; \
else \
echo "Cannot find $(BBBIN), downloading ..."; \
wget -O $@ $(BBURL); \
fi; \
cd $(dir $@); \
md5sum -c $(BBBIN).md5; \
fi
@chmod +x $@
$(DEST)/bin/chrootsetup.sh: $(DEST)/bin/chrootsetup.sh:
cp $(srcdir)/tenv/$(notdir $@) $@ cp $(srcdir)/tenv/$(notdir $@) $@
+8 -7
View File
@@ -29,6 +29,7 @@ TENV_ROOT="${TENV_ROOT:-$(pwd)/${TEST_DIR}/tenv-root}"
unshare=$(command -v unshare) unshare=$(command -v unshare)
chroot=$(command -v chroot) chroot=$(command -v chroot)
export container="unshare"
export PS1='\w \$ ' export PS1='\w \$ '
export PS2='> ' export PS2='> '
export PS3='#? ' export PS3='#? '
@@ -39,10 +40,10 @@ export PATH
# Not supported by Busybox unshare: # Not supported by Busybox unshare:
# --cgroup --time # --cgroup --time
exec "$unshare" \ exec "$unshare" \
--user --map-root-user \ --user --map-root-user \
--fork --pid --mount-proc \ --fork --pid --mount-proc \
--mount \ --mount \
--mount-proc \ --mount-proc \
--uts --ipc --net \ --uts --ipc --net \
"$chroot" "$TENV_ROOT" /bin/chrootsetup.sh "$@" "$chroot" "$TENV_ROOT" /bin/chrootsetup.sh "$@"