From 4ae5c54f452087b5393b2700b92e76b190f618dd Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sun, 10 Apr 2016 19:14:48 +0200 Subject: [PATCH] Assert svc condition only when PID file has been created/updated This patch prevents too early start of services depending on other services. E.g, if service B depends on A then we must wait for A to signal that it is ready before we allow B to start -- in a Finit based system A does this by creating, or touching (updating mtime), its PID file. Services (like A) that support SIGHUP should call utime(), or utimensat(), on the PID file at the end of their SIGHUP handler. Signed-off-by: Joachim Nilsson --- plugins/pidfile.c | 15 +++++++++++---- service.c | 6 ++++++ svc.h | 5 +++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index d25a76f0..fee45573 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -44,13 +44,20 @@ static void pidfile_callback(void *UNUSED(arg), int fd, int UNUSED(events)) _d("%s: match %s", basename, svc->cmd); snprintf(cond, sizeof(cond), "svc%s", svc->cmd); - if (ev->mask & (IN_CREATE | IN_ATTRIB)) + if (ev->mask & (IN_CREATE | IN_ATTRIB)) { + svc_started(svc); cond_set(cond); - else if (ev->mask & IN_DELETE) + } else if (ev->mask & IN_DELETE) cond_clear(cond); } } +/* + * Assert condition only if the service is running, but not if it's + * recently been changed or while it's starting up. + * + * We must wait for the service to create/touch its pidfile. + */ static void pidfile_reconf(void *_null) { static char name[MAX_ARG_LEN]; @@ -59,14 +66,14 @@ static void pidfile_reconf(void *_null) (void)(_null); for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { - if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc)) { + if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc) && !svc_is_starting(svc)) { snprintf(name, MAX_ARG_LEN, "svc%s", svc->cmd); cond_set_path(cond_path(name), COND_ON); } } } -static void pidfile_init (void *arg) +static void pidfile_init(void *arg) { struct context *ctx = arg; diff --git a/service.c b/service.c index 36482a77..5794b0b2 100644 --- a/service.c +++ b/service.c @@ -151,6 +151,9 @@ static int service_start(svc_t *svc) return result; } + /* Declare we're waiting for svc to create its pidfile */ + svc_starting(svc); + /* Block sigchild while forking. */ sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); @@ -329,6 +332,9 @@ static int service_restart(svc_t *svc) if (verbose) print_desc("Restarting ", svc->desc); + /* Declare we're waiting for svc to re-assert/touch its pidfile */ + svc_starting(svc); + _d("Sending SIGHUP to PID %d", svc->pid); err = kill(svc->pid, SIGHUP); diff --git a/svc.h b/svc.h index 678f60fd..44172de7 100644 --- a/svc.h +++ b/svc.h @@ -88,6 +88,7 @@ typedef struct svc { time_t mtime; /* Modification time for .conf from /etc/finit.d/ */ const int dirty; /* Set if old mtime != new mtime => reloaded, * or -1 when marked for removal */ + int starting; /* ... waiting for pidfile to be re-asserted */ int runlevels; int sighup; /* This service supports SIGHUP :) */ svc_block_t block; /* Reason that this service is currently blocked */ @@ -168,6 +169,10 @@ int svc_is_unique (svc_t *svc); static inline int svc_in_runlevel(svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } static inline int svc_has_sighup(svc_t *svc) { return svc && 0 != svc->sighup; } +static inline void svc_starting (svc_t *svc) { svc->starting = 1; } +static inline void svc_started (svc_t *svc) { svc->starting = 0; } +static inline int svc_is_starting(svc_t *svc) { 0 != svc->starting; } + static inline int svc_is_dynamic(svc_t *svc) { return svc && 0 != svc->mtime; } static inline int svc_is_removed(svc_t *svc) { return svc && -1 == svc->dirty; } static inline int svc_is_changed(svc_t *svc) { return svc && 0 != svc->dirty; }