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