From c926160853f09b4dc7adf1af9e1a38436e84121c Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 4 Apr 2022 19:09:20 +0200 Subject: [PATCH] Issue #223: add type:forking service option, with updated docs - Add `type:forking` service option to trigger guessing pidfile to watch for, instead of `pid:!foo` option, which is not intuitive. This option may likely also survive into the new file format :) - Update docs and add examples - Update start-stop-serv.sh test case with this new variant Signed-off-by: Joachim Wiberg --- doc/config.md | 20 +++++++++++++++++--- doc/service.md | 12 +++++++++++- man/finit.conf.5 | 16 +++++++++++++++- src/pid.c | 5 +++++ src/service.c | 27 ++++++++++++++++++++++----- src/svc.h | 3 ++- test/start-stop-serv.sh | 3 ++- 7 files changed, 74 insertions(+), 12 deletions(-) diff --git a/doc/config.md b/doc/config.md index 14cd53f7..e78e0be5 100644 --- a/doc/config.md +++ b/doc/config.md @@ -323,6 +323,10 @@ calls `init-script restart` on `initctl reload`. Similar to how Forking services started with `sysv` scripts can be monitored by Finit by declaring the PID file to look for: `pid:!/path/to/pidfile.pid`. +Notice the leading `!`, it signifies Finit should not try to create the +file, but rather watch that file for the resulting forked-off PID. This +syntax also works for forking daemons that do not have a command line +option to run it in the foreground, more on this below in `service`. > `` is described in the [Services](#services) section. @@ -344,9 +348,19 @@ prevent them from forking off a sub-process in the background. This is the most reliable way to monitor a service. However, not all daemons support running in the foreground, or they may -start logging to the foreground as well, these are called forking -services and are supported using the same syntax as forking `sysv` -services, using the `pid:!/path/to/pidfile.pid` syntax. +start logging to the foreground as well, these are forking daemons and +are supported using the same syntax as forking `sysv` services, using +the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax +that may be more intuitive, where Finit can also guess the PID file +based on the daemon's command name: + + service type:forking ntpd -- NTP daemon + +This example lets BusyBox `ntpd` daemonize itself. Finit uses the +basename of the binary to guess the PID file to watch for the PID: +`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the +full `pid:!/path/to/file.pid`. + **Example:** diff --git a/doc/service.md b/doc/service.md index 1b9bd35f..1678c82c 100644 --- a/doc/service.md +++ b/doc/service.md @@ -27,7 +27,17 @@ Finit can *not* start and monitor a daemon that: ### Forks to Background w/ PID File - service pid:!/run/serv.pid serv -- Forking service +There are two syntax variants, type 1 and type 2. The former is the +traditional one used also for `sysv` start/stop scripts, and the latter +is inspired by systemd, with a twist -- it lets Finit guess the pifdile +to look for based on the standard path and the basename of the command. + + service pid:!/run/serv.pid serv -- Forking service, type 1 + service type:forking serv -- Forking service, type 2 + +In this example the resulting files to watch for are `/run/serv.pid` and +`/var/run/serv.pid`, respectively. On most modern Linux systems this is +the same directory (`/var/run` is a symlink to `../run`). ### Runs in Foreground w/ PID File diff --git a/man/finit.conf.5 b/man/finit.conf.5 index f4f18b16..ca1d044c 100644 --- a/man/finit.conf.5 +++ b/man/finit.conf.5 @@ -194,7 +194,21 @@ services and are supported using the same syntax as forking .Cm sysv services, using the .Cm pid:!/path/to/pidfile.pid -command modifier syntax. +command modifier syntax. There is an alternative syntax that may be +more intuitive, where Finit can also guess the PID file based on the +daemon's command name: +.Bd -unfilled -offset indent +service type:forking ntpd -- NTP daemon +.Ed +.Pp +Here we let BusyBox +.Nm ntpd +daemonize itself. Finit uses the basename of the binary to guess the +PID file to watch for the PID: +.Pa /var/run/ntpd.pid . +If Finit guesses wrong, you have to submit the full +.Cm pid:!/path/to/file.pid +option to your service stanza. .Pp .Sy Example: in the case of .Cm ospfd diff --git a/src/pid.c b/src/pid.c index 4508b5e7..e362cf3b 100644 --- a/src/pid.c +++ b/src/pid.c @@ -158,6 +158,11 @@ int pid_file_set(svc_t *svc, char *file, int not) file++; } + if (svc_is_forking(svc) && !not) { + logit(LOG_WARNING, "Service %s is forking, adjusting to pid:!%s", svc->name, file); + not = 1; + } + pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not); if (not) svc->pidfile[0] = '!'; diff --git a/src/service.c b/src/service.c index 4e07e813..1bf168c4 100644 --- a/src/service.c +++ b/src/service.c @@ -1154,7 +1154,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) char *dev = NULL; int respawn = 0; int levels = 0; - int manual = 0; + int forking = 0, manual = 0; int restart_max = SVC_RESPAWN_MAX; int restart_tmo = 0; unsigned oncrash_action = SVC_ONCRASH_IGNORE; @@ -1213,6 +1213,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) pid = cmd; else if (!strncasecmp(cmd, "name:", 5)) name = cmd; + else if (!strncasecmp(cmd, "type:forking", 10)) + forking = 1; else if (!strncasecmp(cmd, "manual:yes", 10)) manual = 1; else if (!strncasecmp(cmd, "restart:", 8)) @@ -1324,10 +1326,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) svc_unblock(svc); } - /* Decode any optional pid:/optional/path/to/file.pid */ - if (pid && (svc_is_daemon(svc) || svc_is_sysv(svc)) && pid_file_parse(svc, pid)) - _e("Invalid 'pid' argument to service: %s", pid); - if (username) { char *ptr = strchr(username, ':'); @@ -1406,6 +1404,25 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) if (file) strlcpy(svc->file, file, sizeof(svc->file)); svc->respawn = respawn; + svc->forking = forking; + + /* Decode any (optional) pid:/optional/path/to/file.pid */ + if (svc_is_daemon(svc) || svc_is_sysv(svc)) { + char tmp[sizeof(svc->name) + 6]; /* pid:! + svc->name */ + + /* no pid: set, figure out a default to track this svc */ + if (!pid && forking) { + snprintf(tmp, sizeof(tmp), "pid:!%s", svc->name); + pid = tmp; + logit(LOG_INFO, "%s: forking but no pid:!file set, guessing -> %s", svc->name, tmp); + } + + if (pid && pid_file_parse(svc, pid)) + logit(LOG_WARNING, "%s: service has invalid 'pid:' config: %s", svc->name, pid); + + if (svc->pidfile[0] == '!') + svc->forking = 1; + } /* Set configured limits */ memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit)); diff --git a/src/svc.h b/src/svc.h index d6baeb29..b3f22550 100644 --- a/src/svc.h +++ b/src/svc.h @@ -127,6 +127,7 @@ typedef struct svc { int starting; /* ... waiting for pidfile to be re-asserted */ int runlevels; int sighup; /* This service supports SIGHUP :) */ + int forking; /* This is a service/sysv daemon that forks, wait for it ... */ svc_block_t block; /* Reason that this service is currently stopped */ char cond[MAX_COND_LEN]; @@ -228,7 +229,7 @@ static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE 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_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; } +static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; } 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); } diff --git a/test/start-stop-serv.sh b/test/start-stop-serv.sh index 4176087a..302b5053 100755 --- a/test/start-stop-serv.sh +++ b/test/start-stop-serv.sh @@ -57,7 +57,8 @@ test_one() # shellcheck source=/dev/null . "$TEST_DIR/tenv/lib.sh" -test_one "/run/serv.pid" "service pid:!/run/serv.pid /test_assets/serv -- Forking service" +test_one "/run/serv.pid" "service pid:!/run/serv.pid /test_assets/serv -- Forking service, type 1" +test_one "/run/serv.pid" "service type:forking /test_assets/serv -- Forking service, type 2" # This one could never be started by and monitored by Finit: it forks to # background and does not create a PID file. Essentially it's lost to # Finit, and any other sane process monitor.