From 23e0928e5c5fcffea4e76c414667202d6fec63df Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 00:17:00 +0100 Subject: [PATCH 01/24] Minor, unused arguments no longer need magic tricks or hand waving Signed-off-by: Joachim Nilsson --- plugins/pidfile.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 5bbabb76..ee699e58 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -92,11 +92,10 @@ static void pidfile_callback(void *arg, int fd, int events) * * We must wait for the service to create/touch its pidfile. */ -static void pidfile_reconf(void *_null) +static void pidfile_reconf(void *arg) { static char cond[MAX_COND_LEN]; svc_t *svc; - (void)(_null); int restart = 0; do { From bf004d9ec4264c578749366d979dc52f90e3d920 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 00:18:13 +0100 Subject: [PATCH 02/24] svc: Re-entrant replacement to svc_iterator() --> svc_iterator1() Signed-off-by: Joachim Nilsson --- src/svc.c | 51 ++++++++++++++++++++++++++++++++++++--------------- src/svc.h | 1 + 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/svc.c b/src/svc.c index 254845bb..9f5b1c5a 100644 --- a/src/svc.c +++ b/src/svc.c @@ -133,6 +133,37 @@ int svc_del(svc_t *svc) return 0; } +/** + * svc_iterator1 - Naive iterator over all registered services. + * @pos: Iterator variable, preserves state between calls + * @first: Get first &svc_t object, or next until end. + * + * Returns: + * The first &svc_t when @first is set, otherwise the next &svc_t until + * the end when %NULL is returned. + */ +svc_t *svc_iterator1(int *pos, int first) +{ + int i; + svc_t *list = __connect_shm(); + + if (first) + i = 0; + else + i = *(int *)pos; + + while (i < MAX_NUM_SVC) { + svc_t *svc = &list[i++]; + + if (svc->type != SVC_TYPE_FREE) { + *(int *)pos = i; + return svc; + } + } + + return NULL; +} + /** * svc_iterator - Naive iterator over all registered services. * @first: Get first &svc_t object, or next until end. @@ -143,36 +174,26 @@ int svc_del(svc_t *svc) */ svc_t *svc_iterator(int first) { - static int i = 0; - svc_t *list = __connect_shm(); + static int i; - if (first) - i = 0; - - while (i < MAX_NUM_SVC) { - svc_t *svc = &list[i++]; - - if (svc->type != SVC_TYPE_FREE) - return svc; - } - - return NULL; + return svc_iterator1(&i, first); } /** * svc_inetd_iterator - Naive iterator over all registered inetd services. + * @pos: Iterator variable, preserves state between calls * @first: Get first &svc_t object, or next until end. * * Returns: * The first inetd &svc_t when @first is set, otherwise the next * inetd &svc_t until the end when %NULL is returned. */ -svc_t *svc_inetd_iterator(int first) +svc_t *svc_inetd_iterator(int *pos, int first) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { if (svc_is_inetd(svc)) return svc; } diff --git a/src/svc.h b/src/svc.h index e4c19b4c..4b08238d 100644 --- a/src/svc.h +++ b/src/svc.h @@ -159,6 +159,7 @@ svc_t *svc_inetd_iterator (int first); svc_t *svc_dynamic_iterator (int first); svc_t *svc_named_iterator (int first, char *cmd); svc_t *svc_job_iterator (int first, int job); +svc_t *svc_iterator1 (int *pos, int first); void svc_foreach (void (*cb)(svc_t *)); void svc_foreach_dynamic (void (*cb)(svc_t *)); From 7f04382eeef7043f5f04d8fb7cbf24e16853de64 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 00:21:07 +0100 Subject: [PATCH 03/24] Refactor, use new re-entrant svc_iterator1() API Signed-off-by: Joachim Nilsson --- plugins/pidfile.c | 3 ++- src/cond-w.c | 3 ++- src/service.c | 7 ++++-- src/svc.c | 57 ++++++++++++++++++++++++++++++----------------- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index ee699e58..971491a5 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -95,13 +95,14 @@ static void pidfile_callback(void *arg, int fd, int events) static void pidfile_reconf(void *arg) { static char cond[MAX_COND_LEN]; + int pos; svc_t *svc; int restart = 0; do { restart = 0; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { mkcond(cond, sizeof(cond), svc->cmd); if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc) && diff --git a/src/cond-w.c b/src/cond-w.c index 8f77643f..c6aed182 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -88,10 +88,11 @@ static int svc_has_cond(svc_t *svc) static void cond_update(const char *name) { + int pos; svc_t *svc; _d("%s", name); - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) continue; diff --git a/src/service.c b/src/service.c index 09ad7e92..dca11235 100644 --- a/src/service.c +++ b/src/service.c @@ -1023,11 +1023,13 @@ void service_step_all(int types) */ void service_runtask_clean(void) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!svc_is_runtask(svc)) continue; + svc->once = 0; if (svc->state == SVC_DONE_STATE) svc_set_state(svc, SVC_HALTED_STATE); @@ -1051,9 +1053,10 @@ void service_runtask_clean(void) */ int service_runtask_completed(int skip) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!svc_is_runtask(svc)) continue; diff --git a/src/svc.c b/src/svc.c index 9f5b1c5a..0a62ebac 100644 --- a/src/svc.c +++ b/src/svc.c @@ -79,11 +79,11 @@ static svc_t *__connect_shm(void) */ svc_t *svc_new(char *cmd, int id, int type) { - int i, job = -1; + int i, pos, job = -1; svc_t *svc, *list = __connect_shm(); /* Find first job n:o if registering multiple instances */ - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!strcmp(svc->cmd, cmd)) { job = svc->job; break; @@ -277,12 +277,13 @@ svc_t *svc_job_iterator(int first, int job) */ void svc_foreach(void (*cb)(svc_t *)) { + int pos; svc_t *svc; if (!cb) return; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) cb(svc); } @@ -293,12 +294,13 @@ void svc_foreach(void (*cb)(svc_t *)) */ void svc_foreach_dynamic(void (*cb)(svc_t *)) { + int pos; svc_t *svc; if (!cb) return; - for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0)) + for (svc = svc_dynamic_iterator(&pos, 1); svc; svc = svc_dynamic_iterator(&pos, 0)) cb(svc); } @@ -310,12 +312,13 @@ void svc_foreach_dynamic(void (*cb)(svc_t *)) */ void svc_foreach_type(int types, void (*cb)(svc_t *)) { + int pos; svc_t *svc; if (!cb) return; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!(svc->type & types)) continue; @@ -333,9 +336,10 @@ void svc_foreach_type(int types, void (*cb)(svc_t *)) */ svc_t *svc_stop_completed(void) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (svc->state == SVC_STOPPING_STATE) return svc; } @@ -352,9 +356,10 @@ svc_t *svc_stop_completed(void) */ svc_t *svc_find(char *cmd, int id) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) return svc; } @@ -371,9 +376,10 @@ svc_t *svc_find(char *cmd, int id) */ svc_t *svc_find_by_pid(pid_t pid) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (svc->pid == pid) return svc; } @@ -391,9 +397,10 @@ svc_t *svc_find_by_pid(pid_t pid) */ svc_t *svc_find_by_jobid(int job, int id) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (svc->job == job && svc->id == id) return svc; } @@ -411,10 +418,11 @@ svc_t *svc_find_by_jobid(int job, int id) */ svc_t *svc_find_by_nameid(char *name, int id) { + int pos; char *ptr; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { ptr = strrchr(svc->cmd, '/'); if (ptr) ptr++; @@ -440,11 +448,12 @@ svc_t *svc_find_by_nameid(char *name, int id) */ void svc_mark_dynamic(void) { - svc_t *svc = svc_dynamic_iterator(1); + int pos; + svc_t *svc = svc_dynamic_iterator(&pos, 1); while (svc) { *((int *)&svc->dirty) = -1; - svc = svc_dynamic_iterator(0); + svc = svc_dynamic_iterator(&pos, 0); } } @@ -478,14 +487,15 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime) */ void svc_clean_dynamic(void (*cb)(svc_t *)) { - svc_t *svc = svc_dynamic_iterator(1); + int pos; + svc_t *svc = svc_dynamic_iterator(&pos, 1); while (svc) { if (svc->dirty == -1 && cb) { cb(svc); svc_mark_clean(svc); } - svc = svc_dynamic_iterator(0); + svc = svc_dynamic_iterator(&pos, 0); } } @@ -516,9 +526,10 @@ int svc_clean_bootstrap(svc_t *svc) */ void svc_prune_bootstrap(void) { + int pos; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!svc->pid) svc_clean_bootstrap(svc); } @@ -614,10 +625,10 @@ const char *svc_dirtystr(svc_t *svc) /* Same base service, return unique ID */ int svc_next_id(char *cmd) { - int id = 0; + int pos, id = 0; svc_t *svc; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { if (!strcmp(svc->cmd, cmd) && id < svc->id) id = svc->id; } @@ -670,14 +681,16 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun int job = atonum(token); if (!ptr) { - svc = svc_job_iterator(1, job); + int pos; + + svc = svc_job_iterator(&pos, 1, job); if (!svc && not_found) result += not_found(NULL, job); while (svc) { if (found) result += found(svc); - svc = svc_job_iterator(0, job); + svc = svc_job_iterator(&pos, 0, job); } } else { *ptr++ = 0; @@ -692,14 +705,16 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun } } else { if (!ptr) { - svc = svc_named_iterator(1, token); + int pos; + + svc = svc_named_iterator(&pos, 1, token); if (!svc && not_found) result += not_found(token, id); while (svc) { if (found) result += found(svc); - svc = svc_named_iterator(0, token); + svc = svc_named_iterator(&pos, 0, token); } } else { *ptr++ = 0; From d7d8b8487d926697830ee9ba9477635f1ef09e5c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 07:49:40 +0100 Subject: [PATCH 04/24] Refactor other iterators to use new re-entrant svc_iterator1() API Signed-off-by: Joachim Nilsson --- src/inetd.c | 6 ++++-- src/svc.c | 15 +++++++++------ src/svc.h | 8 ++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/inetd.c b/src/inetd.c index c6dc36dd..e32fe19c 100644 --- a/src/inetd.c +++ b/src/inetd.c @@ -225,10 +225,11 @@ static void socket_cb(uev_t *w, void *arg, int events) */ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name) { + int pos; svc_t *svc; char pname[NI_MAXHOST]; - for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { + for (svc = svc_inetd_iterator(&pos, 1); svc; svc = svc_inetd_iterator(&pos, 0)) { inetd_t *i = &svc->inetd; if (!i->builtin || i->type != SOCK_DGRAM) @@ -665,9 +666,10 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len) svc_t *inetd_find_svc(char *path, char *service, char *proto) { + int pos; svc_t *svc; - for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { + for (svc = svc_inetd_iterator(&pos, 1); svc; svc = svc_inetd_iterator(&pos, 0)) { if (strncmp(path, svc->cmd, strlen(svc->cmd))) continue; diff --git a/src/svc.c b/src/svc.c index 0a62ebac..054b8b05 100644 --- a/src/svc.c +++ b/src/svc.c @@ -204,17 +204,18 @@ svc_t *svc_inetd_iterator(int *pos, int first) /** * svc_dynamic_iterator - Naive iterator over all registered dynamic services. + * @pos: Iterator variable, preserves state between calls * @first: Get first &svc_t object, or next until end. * * Returns: * The first dynamically loaded &svc_t when @first is set, otherwise the * next dynamically loaded &svc_t until the end when %NULL is returned. */ -svc_t *svc_dynamic_iterator(int first) +svc_t *svc_dynamic_iterator(int *pos, int first) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { if (svc->mtime.tv_sec) return svc; } @@ -225,6 +226,7 @@ svc_t *svc_dynamic_iterator(int first) /** * svc_named_iterator - Iterates over all instances of a service. + * @pos: Iterator variable, preserves state between calls * @first: Get first &svc_t object, or next until end. * @cmd: Service name to look for. * @@ -233,11 +235,11 @@ svc_t *svc_dynamic_iterator(int first) * &svc_t instance with the same @cmd until the end when %NULL is * returned. */ -svc_t *svc_named_iterator(int first, char *cmd) +svc_t *svc_named_iterator(int *pos, int first, char *cmd) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { char *name = basename(svc->cmd); if (!strncmp(name, cmd, strlen(name))) @@ -250,6 +252,7 @@ svc_t *svc_named_iterator(int first, char *cmd) /** * svc_job_iterator - Iterates over all instances of a service. + * @pos: Iterator variable, preserves state between calls * @first: Get first &svc_t object, or next until end. * @job: Job to look for. * @@ -258,11 +261,11 @@ svc_t *svc_named_iterator(int first, char *cmd) * &svc_t instance with the same @job until the end when %NULL is * returned. */ -svc_t *svc_job_iterator(int first, int job) +svc_t *svc_job_iterator(int *pos, int first, int job) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { if (svc->job == job) return svc; } diff --git a/src/svc.h b/src/svc.h index 4b08238d..c359e41d 100644 --- a/src/svc.h +++ b/src/svc.h @@ -155,11 +155,11 @@ svc_t *svc_find_by_jobid (int job, int id); svc_t *svc_find_by_nameid (char *name, int id); svc_t *svc_iterator (int first); -svc_t *svc_inetd_iterator (int first); -svc_t *svc_dynamic_iterator (int first); -svc_t *svc_named_iterator (int first, char *cmd); -svc_t *svc_job_iterator (int first, int job); svc_t *svc_iterator1 (int *pos, int first); +svc_t *svc_inetd_iterator (int *pos, int first); +svc_t *svc_dynamic_iterator (int *pos, int first); +svc_t *svc_named_iterator (int *pos, int first, char *cmd); +svc_t *svc_job_iterator (int *pos, int first, int job); void svc_foreach (void (*cb)(svc_t *)); void svc_foreach_dynamic (void (*cb)(svc_t *)); From 9a8b55737fba4bb387fb1501effff688f57b3c3f Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 11:03:15 +0100 Subject: [PATCH 05/24] Move svc_status() + svc_dirtystr() to header file as static inline Signed-off-by: Joachim Nilsson --- src/svc.c | 64 ----------------------------------------------------- src/svc.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/src/svc.c b/src/svc.c index 054b8b05..39ed62e0 100644 --- a/src/svc.c +++ b/src/svc.c @@ -561,70 +561,6 @@ int svc_enabled(svc_t *svc) return 1; } -char *svc_status(svc_t *svc) -{ - switch (svc->state) { - case SVC_HALTED_STATE: - switch (svc->block) { - case SVC_BLOCK_NONE: - return "halted"; - - case SVC_BLOCK_MISSING: - return "missing"; - - case SVC_BLOCK_CRASHING: - return "crashed"; - - case SVC_BLOCK_USER: - return "stopped"; - - case SVC_BLOCK_BUSY: - return "busy"; - - case SVC_BLOCK_RESTARTING: - return "restart"; - } - - case SVC_DONE_STATE: - return "done"; - - case SVC_STOPPING_STATE: - switch (svc->type) { - case SVC_TYPE_INETD_CONN: - case SVC_TYPE_RUN: - case SVC_TYPE_TASK: - return "active"; - - default: - return "stopping"; - } - - case SVC_WAITING_STATE: - return "waiting"; - - case SVC_READY_STATE: - return "ready"; - - case SVC_RUNNING_STATE: - return "running"; - - default: - return "UNKNOWN"; - } -} - -const char *svc_dirtystr(svc_t *svc) -{ - if (svc_is_removed(svc)) - return "removed"; - if (svc_is_updated(svc)) - return "updated"; - if (svc_is_changed(svc)) - return "UNKNOWN"; - - return "clean"; -} - /* Same base service, return unique ID */ int svc_next_id(char *cmd) { diff --git a/src/svc.h b/src/svc.h index c359e41d..4f6623ab 100644 --- a/src/svc.h +++ b/src/svc.h @@ -176,8 +176,6 @@ int svc_clean_bootstrap (svc_t *svc); void svc_prune_bootstrap (void); int svc_enabled (svc_t *svc); -char *svc_status (svc_t *svc); -const char *svc_dirtystr (svc_t *svc); int svc_next_id (char *cmd); int svc_is_unique (svc_t *svc); @@ -210,6 +208,70 @@ static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; } static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; } +static inline char *svc_status(svc_t *svc) +{ + switch (svc->state) { + case SVC_HALTED_STATE: + switch (svc->block) { + case SVC_BLOCK_NONE: + return "halted"; + + case SVC_BLOCK_MISSING: + return "missing"; + + case SVC_BLOCK_CRASHING: + return "crashed"; + + case SVC_BLOCK_USER: + return "stopped"; + + case SVC_BLOCK_BUSY: + return "busy"; + + case SVC_BLOCK_RESTARTING: + return "restart"; + } + + case SVC_DONE_STATE: + return "done"; + + case SVC_STOPPING_STATE: + switch (svc->type) { + case SVC_TYPE_INETD_CONN: + case SVC_TYPE_RUN: + case SVC_TYPE_TASK: + return "active"; + + default: + return "stopping"; + } + + case SVC_WAITING_STATE: + return "waiting"; + + case SVC_READY_STATE: + return "ready"; + + case SVC_RUNNING_STATE: + return "running"; + + default: + return "UNKNOWN"; + } +} + +static inline const char *svc_dirtystr(svc_t *svc) +{ + if (svc_is_removed(svc)) + return "removed"; + if (svc_is_updated(svc)) + return "updated"; + if (svc_is_changed(svc)) + return "UNKNOWN"; + + return "clean"; +} + #endif /* FINIT_SVC_H_ */ /** From c0515d112e1e3db32d74851a47265848d44df9a2 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 11:06:19 +0100 Subject: [PATCH 06/24] New client side API to access svc_t entries without shared memory This change is one of the required intermediate steps to remove the shared memory store for all svc_t, this in turn to avoid any external programs manipulating the internal memory of PID 1. Signed-off-by: Joachim Nilsson --- src/Makefile.am | 2 +- src/api.c | 86 +++++++++++++++++++++++++++++++++++++++++++------ src/client.c | 68 ++++++++++++++++++++++++++++++++++++++ src/client.h | 9 ++++-- src/finit.h | 3 ++ src/initctl.c | 53 ++++++++---------------------- 6 files changed, 169 insertions(+), 52 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index d3ae9d46..bebd6c14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,7 +44,7 @@ finit_LDADD += -ldl endif initctl_SOURCES = initctl.c client.c client.h \ - serv.c serv.h svc.c svc.h \ + serv.c serv.h svc.h \ cond.c cond.h util.c util.h initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 initctl_CFLAGS += $(lite_CFLAGS) diff --git a/src/api.c b/src/api.c index 024883d4..c35e1000 100644 --- a/src/api.c +++ b/src/api.c @@ -21,6 +21,7 @@ * THE SOFTWARE. */ +#include #include #include #include @@ -87,15 +88,42 @@ static int do_start (char *buf, size_t len) { return call(service_start, buf, static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); } static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); } -#ifdef INETD_ENABLED -static int do_query_inetd(char *buf, size_t len) +static char query_buf[368]; +static int missing(char *job, int id) +{ + char buf[20]; + char idstr[10] = ""; + + if (!job) + job = ""; + if (id > 1) + snprintf(idstr, sizeof(idstr), ":%d", id); + + snprintf(buf, sizeof(buf), "%s%s ", job, idstr); + strlcat(query_buf, buf, sizeof(query_buf)); + + return 1; +} + +static int do_query(struct init_request *rq, size_t len) +{ + memset(query_buf, 0, sizeof(query_buf)); + if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) { + memcpy(rq->data, query_buf, sizeof(rq->data)); + return 1; + } + + return 0; +} + +static svc_t *do_find(char *buf, size_t len) { int id = 1; - char *ptr, *input = sanitize(buf, len); - svc_t *svc; + char *ptr, *input; + input = sanitize(buf, len); if (!input) - return -1; + return NULL; ptr = strchr(input, ':'); if (ptr) { @@ -103,11 +131,20 @@ static int do_query_inetd(char *buf, size_t len) id = atonum(ptr); } - svc = svc_find_by_jobid(atonum(input), id); - if (!svc || !svc_is_inetd(svc)) { - _e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input); + if (isdigit(input[0])) + return svc_find_by_jobid(atonum(input), id); + + return svc_find_by_nameid(input, id); +} + +#ifdef INETD_ENABLED +static int do_query_inetd(char *buf, size_t len) +{ + svc_t *svc; + + svc = do_find(buf, len); + if (!svc || !svc_is_inetd(svc)) return 1; - } return inetd_filter_str(&svc->inetd, buf, len); } @@ -164,6 +201,22 @@ static int do_handle_emit(char *buf, size_t len) return result; } +static void send_svc(int sd, svc_t *svc) +{ + svc_t empty; + size_t len; + + if (!svc) { + empty.pid = -1; + svc = ∅ + } + + len = write(sd, svc, sizeof(*svc)); + if (len != sizeof(*svc)) + _d("Failed sending svc_t to client"); +} + + /* * In contrast to the SysV compat handling in plugins/initctl.c, when * `initctl runlevel 0` is issued we default to POWERDOWN the system @@ -290,6 +343,21 @@ static void api_cb(uev_t *w, void *arg, int events) wdogpid = rq.runlevel; break; + case INIT_CMD_SVC_ITER: + _d("svc iter, first: %d", rq.runlevel); + send_svc(sd, svc_iterator(rq.runlevel)); + goto leave; + + case INIT_CMD_SVC_QUERY: + _d("svc query: %s", rq.data); + result = do_query(&rq, len); + break; + + case INIT_CMD_SVC_FIND: + _d("svc find: %s", rq.data); + send_svc(sd, do_find(rq.data, sizeof(rq.data))); + goto leave; + default: _d("Unsupported cmd: %d", rq.cmd); break; diff --git a/src/client.c b/src/client.c index 50e8738e..d5fec8cf 100644 --- a/src/client.c +++ b/src/client.c @@ -89,6 +89,74 @@ exit: return result; } +svc_t *client_svc_iterator(int first) +{ + int sd = -1; + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_SVC_ITER, + }; + static svc_t svc; + + sd = client_connect(); + if (sd == -1) + return NULL; + + if (first) + rq.runlevel = 1; + else + rq.runlevel = 0; + + if (write(sd, &rq, sizeof(rq)) != sizeof(rq)) + goto error; + if (read(sd, &svc, sizeof(svc)) != sizeof(svc)) + goto error; + + client_disconnect(); + if (svc.pid < 0) + return NULL; + + return &svc; +error: + perror("Failed communicating with finit"); + client_disconnect(); + sd = -1; + + return NULL; +} + +svc_t *client_svc_find(char *arg) +{ + int sd = -1; + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_SVC_FIND, + }; + static svc_t svc; + + sd = client_connect(); + if (sd == -1) + return NULL; + + strlcpy(rq.data, arg, sizeof(rq.data)); + if (write(sd, &rq, sizeof(rq)) != sizeof(rq)) + goto error; + if (read(sd, &svc, sizeof(svc)) != sizeof(svc)) + goto error; + + client_disconnect(); + if (svc.pid < 0) + return NULL; + + return &svc; +error: + client_disconnect(); + perror("Failed communicating with finit"); + sd = -1; + + return NULL; +} + /** * Local Variables: * indent-tabs-mode: t diff --git a/src/client.h b/src/client.h index b123af8b..c2e00b6d 100644 --- a/src/client.h +++ b/src/client.h @@ -25,15 +25,18 @@ #define FINIT_CLIENT_H_ #include "finit.h" +#include "svc.h" typedef struct { char *cmd; int (*cb)(char *arg); } command_t; -int client_connect (void); -int client_disconnect (void); +int client_connect (void); +int client_disconnect (void); -int client_send (struct init_request *rq, ssize_t len); +int client_send (struct init_request *rq, ssize_t len); +svc_t *client_svc_iterator (int first); +svc_t *client_svc_find (char *arg); #endif /* FINIT_CLIENT_H_ */ diff --git a/src/finit.h b/src/finit.h index 14e848c9..f60f4ef8 100644 --- a/src/finit.h +++ b/src/finit.h @@ -78,6 +78,9 @@ #define INIT_CMD_EMIT 15 #define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */ +#define INIT_CMD_SVC_ITER 129 +#define INIT_CMD_SVC_QUERY 130 +#define INIT_CMD_SVC_FIND 131 #define INIT_CMD_NACK 254 #define INIT_CMD_ACK 255 diff --git a/src/initctl.c b/src/initctl.c index 85da7acb..6df4ecc0 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -138,21 +138,6 @@ static int do_svc(int cmd, char *arg) static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); } static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); } -static int missing(char *job, int id) -{ - char idstr[10] = ""; - - if (!job) - job = ""; - if (id > 1) - snprintf(idstr, sizeof(idstr), ":%d", id); - - if (verbose) - fprintf(stderr, "No such service %s%s\n", job, idstr); - - return 1; -} - /* * This is a wrapper for do_svc() that adds a simple sanity check of * the service(s) provided as argument. If a service does not exist @@ -160,8 +145,14 @@ static int missing(char *job, int id) */ static int do_startstop(int cmd, char *arg) { - /* Include \0 in len, needed by parser */ - if (svc_parse_jobstr(arg, strlen(arg) + 1, NULL, missing)) { + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_SVC_QUERY + }; + + strlcpy(rq.data, arg, sizeof(rq.data)); + if (client_send(&rq, sizeof(rq))) { + fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data); fprintf(stderr, "Usage: initctl %s [:ID]\n", cmd == INIT_CMD_START_SVC ? "start" : (cmd == INIT_CMD_STOP_SVC ? "stop" : "restart")); @@ -257,7 +248,7 @@ static int do_cond_show(char *arg) printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0); - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { if (!svc->cond[0]) continue; @@ -420,23 +411,7 @@ static int show_status(char *arg) runlevel = runlevel_get(NULL); if (arg && arg[0]) { - int id = 1; - char *ptr, *next; - - ptr = strchr(arg, ':'); - if (ptr) { - *ptr++ = 0; - next = strchr(ptr, ' '); - if (next) - *next = 0; - id = atonum(ptr); - } - - if (isdigit(arg[0])) - svc = svc_find_by_jobid(atonum(arg), id); - else - svc = svc_find_by_nameid(arg, id); - + svc = client_svc_find(arg); if (!svc) return 1; @@ -447,12 +422,12 @@ static int show_status(char *arg) if (!verbose) printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0); - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { char jobid[10], args[512] = "", *lvls; - if (svc_is_unique(svc)) - snprintf(jobid, sizeof(jobid), "%d", svc->job); - else +// if (svc_is_unique(svc)) +// snprintf(jobid, sizeof(jobid), "%d", svc->job); +// else snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id); printf("%-5s %7s ", jobid, svc_status(svc)); From d7401b2a961d9fd9aeca72011394d5a00e0b2b50 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 11:20:21 +0100 Subject: [PATCH 07/24] Follow-up to issue #81: remove SysV shared memory IPC altogether This change removes a long standing kernel requirement and architectural mishap in the design of Finit v1. It is no longer necessary to include CONFIG_SYSVIPC in the kernel to use Finit and its command line tool initctl. It is also no longer possible to connect to the internals of PID 1 using shmat(), the initctl tool has already been updated to use the domain socket API. Signed-off-by: Joachim Nilsson --- doc/TODO.md | 3 --- src/finit.c | 4 ---- src/svc.c | 31 +++++++------------------------ src/svc.h | 19 ------------------- 4 files changed, 7 insertions(+), 50 deletions(-) diff --git a/doc/TODO.md b/doc/TODO.md index cbf98bbe..d6bf8aa0 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -21,9 +21,6 @@ can also be used. General ------- -* Remove shm, let `initctl` call `api.c` for each `svc_t` instead of - opening up the inner parts of PID 1 to other processes. Also, this - would prevent issues like #81 from happening. * Add support for timed shutdown in Finit, including cancelled shutdown. * SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add API restart to SIGHUP callback for the same functionality in Finit. diff --git a/src/finit.c b/src/finit.c index b8d445bb..811a6e26 100644 --- a/src/finit.c +++ b/src/finit.c @@ -378,10 +378,6 @@ int main(int argc, char* argv[]) makedir("/dev/pts", 0755); mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620"); - makedir("/dev/shm", 0755); - if (!fismnt("/dev/shm")) - mount("shm", "/dev/shm", "tmpfs", 0, NULL); - /* * New tmpfs based /run for volatile runtime data * For details, see http://lwn.net/Articles/436012/ diff --git a/src/svc.c b/src/svc.c index 39ed62e0..7d706ce7 100644 --- a/src/svc.c +++ b/src/svc.c @@ -35,37 +35,20 @@ /* Each svc_t needs a unique job# */ static int jobcounter = 1; -static svc_t *workaround = NULL; +static svc_t *svc_list = NULL; static svc_t *__connect_shm(void) { - svc_t *list; + if (svc_list) + return svc_list; - list = finit_svc_connect(); - if (!list) { - if (workaround) - return workaround; - - /* Linux not built with CONFIG_SYSVIPC, or libc does not support shmat()/shmget() */ - if (ENOSYS == errno) - warn("Kernel does support SYSV shmat() IPC, error %d", errno); - - /* Try to prevent PID 1 from aborting, issue #81 */ - if (getpid() == 1) { - warnx("Implementing PID 1 workaround, initctl tool will not work ..."); - if (!workaround) - workaround = calloc(MAX_NUM_SVC, sizeof(svc_t)); - - list = workaround; - } - } - - if (!list) { - warn("Failed connecting to shared memory, error %d", errno); + svc_list = calloc(MAX_NUM_SVC, sizeof(svc_t)); + if (!svc_list) { + warn("Failed allocating static list of services, error %d", errno); abort(); } - return list; + return svc_list; } /** diff --git a/src/svc.h b/src/svc.h index 4f6623ab..540f4442 100644 --- a/src/svc.h +++ b/src/svc.h @@ -27,7 +27,6 @@ #include /* IPC_CREAT */ #include -#include /* shmat() */ #include /* pid_t */ #include @@ -66,7 +65,6 @@ typedef enum { SVC_BLOCK_RESTARTING, } svc_block_t; -#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */ #define MAX_ARG_LEN 64 #define MAX_STR_LEN 64 #define MAX_COND_LEN (MAX_ARG_LEN * 3) @@ -129,23 +127,6 @@ typedef struct svc { void (*timer_cb)(struct svc *svc); } svc_t; -typedef struct svc_map svc_map_t; - -/* Put services array in shm */ -static inline svc_t *finit_svc_connect(void) -{ - static void *ptr = (void *)-1; - - if ((void *)-1 == ptr) { - ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC, - 0600 | IPC_CREAT), NULL, 0); - if ((void *)-1 == ptr) - return NULL; - } - - return (svc_t *)ptr; -} - svc_t *svc_new (char *cmd, int id, int type); int svc_del (svc_t *svc); From 0f1f51b5ad79f9b05afd508be76a03dbcafcb1c0 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 17:50:02 +0100 Subject: [PATCH 08/24] Refactor, change from static array of svc_t to linked list Signed-off-by: Joachim Nilsson --- plugins/pidfile.c | 3 +- src/api.c | 9 +- src/cond-w.c | 3 +- src/inetd.c | 6 +- src/service.c | 6 +- src/svc.c | 225 +++++++++++++++++----------------------------- src/svc.h | 15 ++-- 7 files changed, 103 insertions(+), 164 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 971491a5..7c6827d3 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -95,14 +95,13 @@ static void pidfile_callback(void *arg, int fd, int events) static void pidfile_reconf(void *arg) { static char cond[MAX_COND_LEN]; - int pos; svc_t *svc; int restart = 0; do { restart = 0; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { mkcond(cond, sizeof(cond), svc->cmd); if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc) && diff --git a/src/api.c b/src/api.c index c35e1000..617bad7b 100644 --- a/src/api.c +++ b/src/api.c @@ -225,6 +225,7 @@ static void send_svc(int sd, svc_t *svc) static void api_cb(uev_t *w, void *arg, int events) { int sd, lvl; + static svc_t *svc; struct init_request rq; sd = accept(w->fd, NULL, NULL); @@ -345,7 +346,13 @@ static void api_cb(uev_t *w, void *arg, int events) case INIT_CMD_SVC_ITER: _d("svc iter, first: %d", rq.runlevel); - send_svc(sd, svc_iterator(rq.runlevel)); + /* + * XXX: This severly limits the number of + * simultaneous client connections, but will + * have to do for now. + */ + svc = svc_iterator(rq.runlevel ? NULL : svc); + send_svc(sd, svc); goto leave; case INIT_CMD_SVC_QUERY: diff --git a/src/cond-w.c b/src/cond-w.c index c6aed182..758de5a3 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -88,11 +88,10 @@ static int svc_has_cond(svc_t *svc) static void cond_update(const char *name) { - int pos; svc_t *svc; _d("%s", name); - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) continue; diff --git a/src/inetd.c b/src/inetd.c index e32fe19c..00a6face 100644 --- a/src/inetd.c +++ b/src/inetd.c @@ -225,11 +225,10 @@ static void socket_cb(uev_t *w, void *arg, int events) */ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name) { - int pos; svc_t *svc; char pname[NI_MAXHOST]; - for (svc = svc_inetd_iterator(&pos, 1); svc; svc = svc_inetd_iterator(&pos, 0)) { + for (svc = svc_inetd_iterator(NULL); svc; svc = svc_inetd_iterator(svc)) { inetd_t *i = &svc->inetd; if (!i->builtin || i->type != SOCK_DGRAM) @@ -666,10 +665,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len) svc_t *inetd_find_svc(char *path, char *service, char *proto) { - int pos; svc_t *svc; - for (svc = svc_inetd_iterator(&pos, 1); svc; svc = svc_inetd_iterator(&pos, 0)) { + for (svc = svc_inetd_iterator(NULL); svc; svc = svc_inetd_iterator(svc)) { if (strncmp(path, svc->cmd, strlen(svc->cmd))) continue; diff --git a/src/service.c b/src/service.c index dca11235..d8f03c8c 100644 --- a/src/service.c +++ b/src/service.c @@ -1023,10 +1023,9 @@ void service_step_all(int types) */ void service_runtask_clean(void) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!svc_is_runtask(svc)) continue; @@ -1053,10 +1052,9 @@ void service_runtask_clean(void) */ int service_runtask_completed(int skip) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!svc_is_runtask(svc)) continue; diff --git a/src/svc.c b/src/svc.c index 7d706ce7..e5bd5c64 100644 --- a/src/svc.c +++ b/src/svc.c @@ -27,6 +27,7 @@ #include #include #include +#include /* BSD sys/queue.h API */ #include "finit.h" #include "svc.h" @@ -35,21 +36,7 @@ /* Each svc_t needs a unique job# */ static int jobcounter = 1; -static svc_t *svc_list = NULL; - -static svc_t *__connect_shm(void) -{ - if (svc_list) - return svc_list; - - svc_list = calloc(MAX_NUM_SVC, sizeof(svc_t)); - if (!svc_list) { - warn("Failed allocating static list of services, error %d", errno); - abort(); - } - - return svc_list; -} +static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list); /** * svc_new - Create a new service @@ -62,11 +49,12 @@ static svc_t *__connect_shm(void) */ svc_t *svc_new(char *cmd, int id, int type) { - int i, pos, job = -1; - svc_t *svc, *list = __connect_shm(); + int job = -1; + char *desc; + svc_t *svc; /* Find first job n:o if registering multiple instances */ - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!strcmp(svc->cmd, cmd)) { job = svc->job; break; @@ -75,32 +63,26 @@ svc_t *svc_new(char *cmd, int id, int type) if (job == -1) job = jobcounter++; - for (i = 0; i < MAX_NUM_SVC; i++) { - svc_t *svc = &list[i]; + svc = calloc(1, sizeof(*svc)); + if (!svc) + return NULL; - if (svc->type == SVC_TYPE_FREE) { - char *desc; + svc->type = type; + svc->job = job; + svc->id = id; + strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); - memset(svc, 0, sizeof(*svc)); - svc->type = type; - svc->job = job; - svc->id = id; - strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); + /* Default description, if missing */ + desc = rindex(cmd, '/'); + if (desc) + desc++; + else + desc = cmd; + strlcpy(svc->desc, desc, sizeof(svc->desc)); - /* Default description, if missing */ - desc = rindex(cmd, '/'); - if (desc) - desc++; - else - desc = cmd; - strlcpy(svc->desc, desc, sizeof(svc->desc)); + TAILQ_INSERT_TAIL(&svc_list, svc, link); - return svc; - } - } - - errno = ENOMEM; - return NULL; + return svc; } /** @@ -112,71 +94,45 @@ svc_t *svc_new(char *cmd, int id, int type) */ int svc_del(svc_t *svc) { - svc->type = SVC_TYPE_FREE; + TAILQ_REMOVE(&svc_list, svc, link); + memset(svc, 0, sizeof(*svc)); + free(svc); + return 0; } /** - * svc_iterator1 - Naive iterator over all registered services. - * @pos: Iterator variable, preserves state between calls - * @first: Get first &svc_t object, or next until end. + * svc_iterator - Naive iterator over all registered services. + * @iter: %NULL for first entry, use returned value for subsequent calls * * Returns: - * The first &svc_t when @first is set, otherwise the next &svc_t until - * the end when %NULL is returned. + * The first &svc_t when %NULL is given as argument, otherwise the next + * &svc_t until the end when %NULL is returned. */ -svc_t *svc_iterator1(int *pos, int first) +svc_t *svc_iterator(svc_t *iter) { - int i; - svc_t *list = __connect_shm(); - - if (first) - i = 0; - else - i = *(int *)pos; - - while (i < MAX_NUM_SVC) { - svc_t *svc = &list[i++]; - - if (svc->type != SVC_TYPE_FREE) { - *(int *)pos = i; - return svc; - } - } + if (!iter) + return TAILQ_FIRST(&svc_list); + if (iter && iter != TAILQ_END(&svc_list)) + return TAILQ_NEXT(iter, link); + return NULL; } -/** - * svc_iterator - Naive iterator over all registered services. - * @first: Get first &svc_t object, or next until end. - * - * Returns: - * The first &svc_t when @first is set, otherwise the next &svc_t until - * the end when %NULL is returned. - */ -svc_t *svc_iterator(int first) -{ - static int i; - - return svc_iterator1(&i, first); -} - - /** * svc_inetd_iterator - Naive iterator over all registered inetd services. - * @pos: Iterator variable, preserves state between calls - * @first: Get first &svc_t object, or next until end. + * @iter: %NULL for first entry, use returned value for subsequent calls * * Returns: - * The first inetd &svc_t when @first is set, otherwise the next - * inetd &svc_t until the end when %NULL is returned. + * The first inetd &svc_t when %NULL is given as argument, otherwise the + * next inetd &svc_t until the end when %NULL is returned. */ -svc_t *svc_inetd_iterator(int *pos, int first) +svc_t *svc_inetd_iterator(svc_t *iter) { svc_t *svc; - for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { + for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { if (svc_is_inetd(svc)) return svc; } @@ -187,18 +143,18 @@ svc_t *svc_inetd_iterator(int *pos, int first) /** * svc_dynamic_iterator - Naive iterator over all registered dynamic services. - * @pos: Iterator variable, preserves state between calls - * @first: Get first &svc_t object, or next until end. + * @iter: %NULL for first entry, use returned value for subsequent calls * * Returns: - * The first dynamically loaded &svc_t when @first is set, otherwise the - * next dynamically loaded &svc_t until the end when %NULL is returned. + * The first dynamically loaded &svc_t when %NULL is given as argument, + * otherwise the next dynamically loaded &svc_t until the end when %NULL + * is returned. */ -svc_t *svc_dynamic_iterator(int *pos, int first) +svc_t *svc_dynamic_iterator(svc_t *iter) { svc_t *svc; - for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { + for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { if (svc->mtime.tv_sec) return svc; } @@ -209,20 +165,19 @@ svc_t *svc_dynamic_iterator(int *pos, int first) /** * svc_named_iterator - Iterates over all instances of a service. - * @pos: Iterator variable, preserves state between calls - * @first: Get first &svc_t object, or next until end. - * @cmd: Service name to look for. + * @iter: %NULL for first entry, use returned value for subsequent calls + * @cmd: Service name to look for. * * Returns: - * The first matching &svc_t when @first is set, otherwise the next - * &svc_t instance with the same @cmd until the end when %NULL is + * The first matching &svc_t when %NULL is given as argument, otherwise + * the next &svc_t with the same @cmd name until the end when %NULL is * returned. */ -svc_t *svc_named_iterator(int *pos, int first, char *cmd) +svc_t *svc_named_iterator(svc_t *iter, char *cmd) { svc_t *svc; - for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { + for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { char *name = basename(svc->cmd); if (!strncmp(name, cmd, strlen(name))) @@ -235,20 +190,19 @@ svc_t *svc_named_iterator(int *pos, int first, char *cmd) /** * svc_job_iterator - Iterates over all instances of a service. - * @pos: Iterator variable, preserves state between calls - * @first: Get first &svc_t object, or next until end. - * @job: Job to look for. + * @iter: %NULL for first entry, use returned value for subsequent calls + * @job: Job to look for. * * Returns: - * The first matching &svc_t when @first is set, otherwise the next - * &svc_t instance with the same @job until the end when %NULL is + * The first matching &svc_t when %NULL is given as argument, otherwise + * the next &svc_t with the same @job ID until the end when %NULL is * returned. */ -svc_t *svc_job_iterator(int *pos, int first, int job) +svc_t *svc_job_iterator(svc_t *iter, int job) { svc_t *svc; - for (svc = svc_iterator1(pos, first); svc; svc = svc_iterator1(pos, 0)) { + for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { if (svc->job == job) return svc; } @@ -263,13 +217,12 @@ svc_t *svc_job_iterator(int *pos, int first, int job) */ void svc_foreach(void (*cb)(svc_t *)) { - int pos; svc_t *svc; if (!cb) return; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) cb(svc); } @@ -280,13 +233,12 @@ void svc_foreach(void (*cb)(svc_t *)) */ void svc_foreach_dynamic(void (*cb)(svc_t *)) { - int pos; svc_t *svc; if (!cb) return; - for (svc = svc_dynamic_iterator(&pos, 1); svc; svc = svc_dynamic_iterator(&pos, 0)) + for (svc = svc_dynamic_iterator(NULL); svc; svc = svc_dynamic_iterator(svc)) cb(svc); } @@ -298,13 +250,12 @@ void svc_foreach_dynamic(void (*cb)(svc_t *)) */ void svc_foreach_type(int types, void (*cb)(svc_t *)) { - int pos; svc_t *svc; if (!cb) return; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!(svc->type & types)) continue; @@ -322,10 +273,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *)) */ svc_t *svc_stop_completed(void) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (svc->state == SVC_STOPPING_STATE) return svc; } @@ -342,10 +292,9 @@ svc_t *svc_stop_completed(void) */ svc_t *svc_find(char *cmd, int id) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) return svc; } @@ -362,10 +311,9 @@ svc_t *svc_find(char *cmd, int id) */ svc_t *svc_find_by_pid(pid_t pid) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (svc->pid == pid) return svc; } @@ -383,10 +331,9 @@ svc_t *svc_find_by_pid(pid_t pid) */ svc_t *svc_find_by_jobid(int job, int id) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (svc->job == job && svc->id == id) return svc; } @@ -404,11 +351,10 @@ svc_t *svc_find_by_jobid(int job, int id) */ svc_t *svc_find_by_nameid(char *name, int id) { - int pos; char *ptr; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { ptr = strrchr(svc->cmd, '/'); if (ptr) ptr++; @@ -434,12 +380,11 @@ svc_t *svc_find_by_nameid(char *name, int id) */ void svc_mark_dynamic(void) { - int pos; - svc_t *svc = svc_dynamic_iterator(&pos, 1); + svc_t *svc = svc_dynamic_iterator(NULL); while (svc) { *((int *)&svc->dirty) = -1; - svc = svc_dynamic_iterator(&pos, 0); + svc = svc_dynamic_iterator(svc); } } @@ -473,15 +418,14 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime) */ void svc_clean_dynamic(void (*cb)(svc_t *)) { - int pos; - svc_t *svc = svc_dynamic_iterator(&pos, 1); + svc_t *svc = svc_dynamic_iterator(NULL); while (svc) { if (svc->dirty == -1 && cb) { cb(svc); svc_mark_clean(svc); } - svc = svc_dynamic_iterator(&pos, 0); + svc = svc_dynamic_iterator(svc); } } @@ -512,10 +456,9 @@ int svc_clean_bootstrap(svc_t *svc) */ void svc_prune_bootstrap(void) { - int pos; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!svc->pid) svc_clean_bootstrap(svc); } @@ -547,10 +490,10 @@ int svc_enabled(svc_t *svc) /* Same base service, return unique ID */ int svc_next_id(char *cmd) { - int pos, id = 0; + int id = 0; svc_t *svc; - for (svc = svc_iterator1(&pos, 1); svc; svc = svc_iterator1(&pos, 0)) { + for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { if (!strcmp(svc->cmd, cmd) && id < svc->id) id = svc->id; } @@ -560,12 +503,10 @@ int svc_next_id(char *cmd) int svc_is_unique(svc_t *svc) { - svc_t *list = __connect_shm(); - int i, unique = 1; - - for (i = 0; i < MAX_NUM_SVC; i++) { - svc_t *s = &list[i]; + svc_t *s; + int unique = 1; + for (s = svc_iterator(NULL); s; s = svc_iterator(svc)) { if (svc->type == SVC_TYPE_FREE) continue; @@ -603,16 +544,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun int job = atonum(token); if (!ptr) { - int pos; - - svc = svc_job_iterator(&pos, 1, job); + svc = svc_job_iterator(NULL, job); if (!svc && not_found) result += not_found(NULL, job); while (svc) { if (found) result += found(svc); - svc = svc_job_iterator(&pos, 0, job); + svc = svc_job_iterator(svc, job); } } else { *ptr++ = 0; @@ -627,16 +566,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun } } else { if (!ptr) { - int pos; - - svc = svc_named_iterator(&pos, 1, token); + svc = svc_named_iterator(NULL, token); if (!svc && not_found) result += not_found(token, id); while (svc) { if (found) result += found(svc); - svc = svc_named_iterator(&pos, 0, token); + svc = svc_named_iterator(svc, token); } } else { *ptr++ = 0; diff --git a/src/svc.h b/src/svc.h index 540f4442..43056d69 100644 --- a/src/svc.h +++ b/src/svc.h @@ -29,6 +29,7 @@ #include #include /* pid_t */ #include +#include /* BSD sys/queue.h API */ #include "inetd.h" #include "helpers.h" @@ -70,7 +71,6 @@ typedef enum { #define MAX_COND_LEN (MAX_ARG_LEN * 3) #define MAX_USER_LEN 16 #define MAX_NUM_FDS 64 /* Max number of I/O plugins */ -#define MAX_NUM_SVC 64 /* Enough? */ #define MAX_NUM_SVC_ARGS 32 /* @@ -80,6 +80,8 @@ typedef enum { * initctl service */ typedef struct svc { + TAILQ_ENTRY(svc) link; + /* Instance specifics */ int job, id; /* JOB:ID */ @@ -135,12 +137,11 @@ svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_jobid (int job, int id); svc_t *svc_find_by_nameid (char *name, int id); -svc_t *svc_iterator (int first); -svc_t *svc_iterator1 (int *pos, int first); -svc_t *svc_inetd_iterator (int *pos, int first); -svc_t *svc_dynamic_iterator (int *pos, int first); -svc_t *svc_named_iterator (int *pos, int first, char *cmd); -svc_t *svc_job_iterator (int *pos, int first, int job); +svc_t *svc_iterator (svc_t *iter); +svc_t *svc_inetd_iterator (svc_t *iter); +svc_t *svc_dynamic_iterator (svc_t *iter); +svc_t *svc_named_iterator (svc_t *iter, char *cmd); +svc_t *svc_job_iterator (svc_t *iter, int job); void svc_foreach (void (*cb)(svc_t *)); void svc_foreach_dynamic (void (*cb)(svc_t *)); From db50c09ec57e823eb2fc25f9eaf3bf694d9cf84c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Dec 2017 21:04:17 +0100 Subject: [PATCH 09/24] initctl: Improve `show SVC` command output a bit, provide summary Signed-off-by: Joachim Nilsson --- src/initctl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/initctl.c b/src/initctl.c index 6df4ecc0..000b6eeb 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -415,8 +415,14 @@ static int show_status(char *arg) if (!svc) return 1; - printf("%s\n", svc_status(svc)); - return 0; + printf("Service : %s\n", svc->cmd); + printf("Description : %s\n", svc->desc); + printf("PID : %d\n", svc->pid); + printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); + printf("Status : %s\n", svc_status(svc)); + printf("\n"); + + return do_log(svc->cmd); } if (!verbose) From 7784480abe2593a8fd006220bcce5f7afcbbcba0 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 20 Dec 2017 00:10:29 +0100 Subject: [PATCH 10/24] Postpone networking stuff, + hooks, a bit to the finalize() step This patch moves the networking bit to the latter part of bootstrap, called finalize(), in favor of starting the event loop earlier. Signed-off-by: Joachim Nilsson --- src/finit.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/finit.c b/src/finit.c index 811a6e26..4eeb1172 100644 --- a/src/finit.c +++ b/src/finit.c @@ -253,6 +253,15 @@ static void emergency_shell(void) */ static void finalize(void) { + /* + * Network stuff + */ + networking(); + umask(022); + + /* Hooks that rely on loopback, or basic networking being up. */ + plugin_run_hooks(HOOK_NETWORK_UP); + /* * Start all tasks/services in the configured runlevel */ @@ -489,15 +498,6 @@ int main(int argc, char* argv[]) sm_init(&sm); sm_step(&sm); - /* - * Network stuff - */ - networking(); - umask(022); - - /* Hooks that rely on loopback, or basic networking being up. */ - plugin_run_hooks(HOOK_NETWORK_UP); - /* Start new initctl API responder */ api_init(&loop); From 61a9926442d1e7bbac83dd5ad77786ef2bd757bd Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 20 Dec 2017 00:13:21 +0100 Subject: [PATCH 11/24] Minor, staticify Signed-off-by: Joachim Nilsson --- src/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api.c b/src/api.c index 617bad7b..e89473ba 100644 --- a/src/api.c +++ b/src/api.c @@ -44,7 +44,7 @@ #include "service.h" #include "util.h" -uev_t api_watcher; +static uev_t api_watcher; static int call(int (*action)(svc_t *), char *buf, size_t len) { From a9456cec41a692c52ba1f8819301160b255a5be7 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 20 Dec 2017 09:02:38 +0100 Subject: [PATCH 12/24] svc: Remove unused function, svc_foreach_dynamic() Signed-off-by: Joachim Nilsson --- src/svc.c | 16 ---------------- src/svc.h | 1 - 2 files changed, 17 deletions(-) diff --git a/src/svc.c b/src/svc.c index e5bd5c64..223d65e8 100644 --- a/src/svc.c +++ b/src/svc.c @@ -227,22 +227,6 @@ void svc_foreach(void (*cb)(svc_t *)) } -/** - * svc_foreach_dynamic - Run a callback for each registered dynamic service - * @cb: Callback to run for each dynamic service - */ -void svc_foreach_dynamic(void (*cb)(svc_t *)) -{ - svc_t *svc; - - if (!cb) - return; - - for (svc = svc_dynamic_iterator(NULL); svc; svc = svc_dynamic_iterator(svc)) - cb(svc); -} - - /** * svc_foreach_type - Run a callback for each matching type * @types: Mask of service types diff --git a/src/svc.h b/src/svc.h index 43056d69..2c2f67ee 100644 --- a/src/svc.h +++ b/src/svc.h @@ -144,7 +144,6 @@ svc_t *svc_named_iterator (svc_t *iter, char *cmd); svc_t *svc_job_iterator (svc_t *iter, int job); void svc_foreach (void (*cb)(svc_t *)); -void svc_foreach_dynamic (void (*cb)(svc_t *)); void svc_foreach_type (int types, void (*cb)(svc_t *)); svc_t *svc_stop_completed (void); From f3669c75375b2c596aedac1675637ef7b09074a2 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 20 Dec 2017 09:04:14 +0100 Subject: [PATCH 13/24] svc: Remove svc_dynamic_iterator(), soon no difference between svc's This is a prelude to a major behavioral change. The difference between a static and dynamic service will be removed in Finit v3.1. This means all .conf files, including /etc/finit.conf, will be re-read on reload. However, not all configuration directives will be re-read, some will only be applicable at bootstrap, i.e. runlevel S. Signed-off-by: Joachim Nilsson --- src/svc.c | 30 ++++-------------------------- src/svc.h | 1 - 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/svc.c b/src/svc.c index 223d65e8..b6f72252 100644 --- a/src/svc.c +++ b/src/svc.c @@ -141,28 +141,6 @@ svc_t *svc_inetd_iterator(svc_t *iter) } -/** - * svc_dynamic_iterator - Naive iterator over all registered dynamic services. - * @iter: %NULL for first entry, use returned value for subsequent calls - * - * Returns: - * The first dynamically loaded &svc_t when %NULL is given as argument, - * otherwise the next dynamically loaded &svc_t until the end when %NULL - * is returned. - */ -svc_t *svc_dynamic_iterator(svc_t *iter) -{ - svc_t *svc; - - for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { - if (svc->mtime.tv_sec) - return svc; - } - - return NULL; -} - - /** * svc_named_iterator - Iterates over all instances of a service. * @iter: %NULL for first entry, use returned value for subsequent calls @@ -364,11 +342,11 @@ svc_t *svc_find_by_nameid(char *name, int id) */ void svc_mark_dynamic(void) { - svc_t *svc = svc_dynamic_iterator(NULL); + svc_t *svc = svc_iterator(NULL); while (svc) { *((int *)&svc->dirty) = -1; - svc = svc_dynamic_iterator(svc); + svc = svc_iterator(svc); } } @@ -402,14 +380,14 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime) */ void svc_clean_dynamic(void (*cb)(svc_t *)) { - svc_t *svc = svc_dynamic_iterator(NULL); + svc_t *svc = svc_iterator(NULL); while (svc) { if (svc->dirty == -1 && cb) { cb(svc); svc_mark_clean(svc); } - svc = svc_dynamic_iterator(svc); + svc = svc_iterator(svc); } } diff --git a/src/svc.h b/src/svc.h index 2c2f67ee..81ef5437 100644 --- a/src/svc.h +++ b/src/svc.h @@ -139,7 +139,6 @@ svc_t *svc_find_by_nameid (char *name, int id); svc_t *svc_iterator (svc_t *iter); svc_t *svc_inetd_iterator (svc_t *iter); -svc_t *svc_dynamic_iterator (svc_t *iter); svc_t *svc_named_iterator (svc_t *iter, char *cmd); svc_t *svc_job_iterator (svc_t *iter, int job); From e7cca05fe3dd2b90d7e6eebdca7a857e6bd9e568 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 20 Dec 2017 09:16:55 +0100 Subject: [PATCH 14/24] Reserve certain configuration directives for bootstrap only The following configuration directives, previously only allowed in /etc/finit.conf, are now also only allowed in runlevel S, i.e. at bootstrap: - host - mknod - network - runparts - runlevel This change is in preparation for allowing reload of finit.conf as well as /etc/finit.d/*.conf, which we already support. Signed-off-by: Joachim Nilsson --- src/conf.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/conf.c b/src/conf.c index 946c1440..4431a46e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -35,6 +35,7 @@ #include "tty.h" #include "helpers.h" +#define BOOTSTRAP (runlevel == 0) #define MATCH_CMD(l, c, x) \ (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) @@ -242,13 +243,13 @@ static void parse_static(char *line) char *x; char cmd[CMD_SIZE]; - if (MATCH_CMD(line, "host ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "host ", x)) { if (hostname) free(hostname); hostname = strdup(strip_line(x)); return; } - if (MATCH_CMD(line, "mknod ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) { char *dev = strip_line(x); strcpy(cmd, "mknod "); @@ -258,13 +259,13 @@ static void parse_static(char *line) return; } - if (MATCH_CMD(line, "network ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) { if (network) free(network); network = strdup(strip_line(x)); return; } - if (MATCH_CMD(line, "runparts ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) { if (runparts) free(runparts); runparts = strdup(strip_line(x)); return; @@ -289,10 +290,12 @@ static void parse_static(char *line) return; } - /* The desired runlevel to start when leaving bootstrap (S). + /* + * The desired runlevel to start when leaving bootstrap (S). * Finit supports 1-9, but most systems only use 1-6, where - * 6 is reserved for reboot */ - if (MATCH_CMD(line, "runlevel ", x)) { + * 6 is reserved for reboot and 0 for halt/poweroff. + */ + if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) { char *token = strip_line(x); const char *err = NULL; From 6f26f2f9d059d8e2b241e8bcf4176bfd51644c4c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 20 Dec 2017 09:30:49 +0100 Subject: [PATCH 15/24] TODO: Idea about addding support for an optional rescue mode Signed-off-by: Joachim Nilsson --- doc/TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/TODO.md b/doc/TODO.md index d6bf8aa0..c8fd0b28 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -21,6 +21,9 @@ can also be used. General ------- +* Add support for `rescue` on the kernel command line, start rescue mode + if enabled with `configure` script. Could mean skip `/etc/finit.conf` + et al and jump straight to a single getty login on console. * Add support for timed shutdown in Finit, including cancelled shutdown. * SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add API restart to SIGHUP callback for the same functionality in Finit. From c59f7d23f40987ad37293b95dfc06af390725f3d Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 12:40:43 +0100 Subject: [PATCH 16/24] Add and improve debug messages, clean out old dev. comments Signed-off-by: Joachim Nilsson --- src/api.c | 1 + src/conf.c | 7 +------ src/finit.c | 9 +++++++++ src/service.c | 1 - src/sm.c | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/api.c b/src/api.c index e89473ba..6d494d63 100644 --- a/src/api.c +++ b/src/api.c @@ -401,6 +401,7 @@ int api_init(uev_ctx_t *ctx) .sun_path = INIT_SOCKET, }; + _d("Setting up external API socket ..."); sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (-1 == sd) { _pe("Failed starting external API socket"); diff --git a/src/conf.c b/src/conf.c index 4431a46e..f215f710 100644 --- a/src/conf.c +++ b/src/conf.c @@ -398,7 +398,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime) /* Prepare default limits for each service */ memcpy(rlimit, global_rlimit, sizeof(rlimit)); - _d("Parsing %s", file); + _d("Parsing %s <<<<<<", file); while (!feof(fp)) { char line[LINE_SIZE] = ""; @@ -417,7 +417,6 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime) return 0; } -/* Reads /etc/finit.conf, once at boot */ static int parse_conf(char *file) { FILE *fp; @@ -508,10 +507,6 @@ int conf_reload_dynamic(void) rp = realpath(path, NULL); if (!rp) { - /* - * XXX: Prune from rcsd? - * XXX: Possibly temporary service from last boot - */ logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno)); continue; } diff --git a/src/finit.c b/src/finit.c index 4eeb1172..c89baf7d 100644 --- a/src/finit.c +++ b/src/finit.c @@ -256,18 +256,22 @@ static void finalize(void) /* * Network stuff */ + _d("Setting up networking ..."); networking(); umask(022); /* Hooks that rely on loopback, or basic networking being up. */ + _d("Calling all network up hooks ..."); plugin_run_hooks(HOOK_NETWORK_UP); /* * Start all tasks/services in the configured runlevel */ + _d("Change to default runlevel, start all services ..."); service_runlevel(cfglevel); /* Clean up bootstrap-only tasks/services that never started */ + _d("Clean up all bootstrap-only tasks/services ..."); svc_prune_bootstrap(); /* All services/tasks/inetd/etc. in configure runlevel have started */ @@ -291,13 +295,16 @@ static void finalize(void) } /* Hooks that should run at the very end */ + _d("Calling all system up hooks ..."); plugin_run_hooks(HOOK_SYSTEM_UP); service_step_all(SVC_TYPE_ANY); /* Enable silent mode before starting TTYs */ + _d("Going silent ..."); log_silent(); /* Delayed start of TTYs at bootstrap */ + _d("Launching all getty services ..."); tty_runlevel(); } @@ -505,11 +512,13 @@ int main(int argc, char* argv[]) * Wait for all SVC_TYPE_RUNTASK to have completed their work in * [S], or timeout, before calling finalize() */ + _d("Starting bootstrap finalize timer ..."); uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000); /* * Enter main loop to monior /dev/initctl and services */ + _d("Entering main loop ..."); return uev_run(&loop, 0); } diff --git a/src/service.c b/src/service.c index d8f03c8c..6d33e364 100644 --- a/src/service.c +++ b/src/service.c @@ -1095,7 +1095,6 @@ void service_bootstrap_cb(uev_t *w, void *arg, int events) finalize(); } - /** * Local Variables: * indent-tabs-mode: t diff --git a/src/sm.c b/src/sm.c index 4d90a941..47628d80 100644 --- a/src/sm.c +++ b/src/sm.c @@ -95,7 +95,8 @@ void sm_step(sm_t *sm) restart: old_state = sm->state; - _d("state: %s", sm_status(sm->state)); + _d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d", + sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload); switch (sm->state) { case SM_BOOTSTRAP_STATE: From 2f9b97b3a7e0c241c393f61997f13a066976af77 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 12:49:04 +0100 Subject: [PATCH 17/24] Update libite (-lite) requried version: 1.9.2 --> 2.0.1 Signed-off-by: Joachim Nilsson --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 728d03e3..0a8ae8a8 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ AC_TYPE_UINT32_T # Check for required libraries PKG_CHECK_MODULES([uev], [libuev >= 2.1.0]) -PKG_CHECK_MODULES([lite], [libite >= 1.9.2]) +PKG_CHECK_MODULES([lite], [libite >= 2.0.1]) # Check for configured Finit features AC_ARG_ENABLE(emergency_shell, From bb3d9c86702edf125f6ee1ac00d4ddcab37867dd Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 12:50:18 +0100 Subject: [PATCH 18/24] initctl: Add support for touch , mark .conf for reload Signed-off-by: Joachim Nilsson --- src/initctl.c | 2 ++ src/serv.c | 25 +++++++++++++++++++++++++ src/serv.h | 1 + 3 files changed, 28 insertions(+) diff --git a/src/initctl.c b/src/initctl.c index 000b6eeb..f003b635 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -524,6 +524,7 @@ static int usage(int rc) " list List all .conf in /etc/finit.d/\n" " enable Enable .conf in /etc/finit.d/available/\n" " disable Disable .conf in /etc/finit.d/[enabled/]\n" + " touch Mark .conf in /etc/finit.d/ for reload\n" " reload Reload *.conf in /etc/finit.d/ (activates changes)\n" // " reload [:ID] Reload (SIGHUP) service by job# or name\n" "\n" @@ -566,6 +567,7 @@ int main(int argc, char *argv[]) { "list", serv_list }, { "enable", serv_enable }, { "disable", serv_disable }, + { "touch", serv_touch }, { "reload", do_reload }, { "cond", do_cond }, diff --git a/src/serv.c b/src/serv.c index 64fe864a..b6579be8 100644 --- a/src/serv.c +++ b/src/serv.c @@ -24,6 +24,7 @@ #include "config.h" #include +#include /* Definition of AT_* constants */ #include #include #include @@ -168,6 +169,30 @@ int serv_disable(char *arg) return remove(link) != 0; } +int serv_touch(char *arg) +{ + char corr[40]; + + if (!strstr(arg, ".conf")) { + snprintf(corr, sizeof(corr), "%s.conf", arg); + arg = corr; + } + + pushd(FINIT_RCSD); + if (!fexist(arg)) { + popd(); + if (!strstr(arg, "finit.conf")) + errx(1, "Service %s is not enabled", arg); + arg = FINIT_CONF; + } + + /* libite:touch() follows symlinks */ + if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW)) + err(1, "Failed marking %s for reload", arg); + + return 0; +} + /** * Local Variables: * indent-tabs-mode: t diff --git a/src/serv.h b/src/serv.h index 9692d4cc..0fc46249 100644 --- a/src/serv.h +++ b/src/serv.h @@ -27,6 +27,7 @@ int serv_list (char *arg); int serv_enable (char *arg); int serv_disable (char *arg); +int serv_touch (char *arg); #endif /* FINIT_SERV_H_ */ From b439ac670daf24aec0f9fb943ebcbec90130327c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 12:52:25 +0100 Subject: [PATCH 19/24] .conf: don't rely on mtime to determine if service needs reloading Until now Finit has used mtime to determine if a file has been changed, or simply touched to request reload, when `initctl reload` is called. Using mtime for this purpose is a monumentally bad idea since time can be changed at any point: a user may adjust the time, NTP continously tunes the clock, and time stamps are stored as the walltime. So if we compare timestamps against the last time we (booted or) did reload, we would miss .conf file changes. This patch replaces the mtime mistake with an inotify watcher for the following files: /etc/finit.d/*.conf, /etc/finit.d/available/*.conf, and /etc/finit.conf. All file changes between (bootstraps and) calls to `initctl reload` are tracked, like the mtime backend it replaces. Signed-off-by: Joachim Nilsson --- src/conf.c | 247 +++++++++++++++++++++++++++++++++++++++++++++----- src/conf.h | 7 +- src/finit.c | 16 +--- src/service.c | 12 ++- src/service.h | 2 +- src/sm.c | 4 +- src/svc.c | 11 --- src/svc.h | 6 +- src/tty.c | 42 ++++----- src/tty.h | 4 +- 10 files changed, 261 insertions(+), 90 deletions(-) diff --git a/src/conf.c b/src/conf.c index f215f710..813ef68e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -25,8 +25,10 @@ #include #include +#include #include #include +#include /* BSD sys/queue.h API */ #include #include "finit.h" @@ -40,8 +42,17 @@ (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) struct rlimit global_rlimit[RLIMIT_NLIMITS]; -static int parse_conf(char *file); +struct conf_change { + TAILQ_ENTRY(conf_change) link; + char *name; +}; + +static uev_t w1, w2, w3; +static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list); + +static int parse_conf(char *file); +static void drop_changes(void); void conf_parse_cmdline(void) { @@ -308,7 +319,7 @@ static void parse_static(char *line) } } -static void parse_dynamic(char *line, struct rlimit rlimit[], struct timeval *mtime) +static void parse_dynamic(char *line, struct rlimit rlimit[], char *file) { char *x; char cmd[CMD_SIZE]; @@ -334,26 +345,26 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], struct timeval *mt /* Monitored daemon, will be respawned on exit */ if (MATCH_CMD(line, "service ", x)) { - service_register(SVC_TYPE_SERVICE, x, rlimit, mtime); + service_register(SVC_TYPE_SERVICE, x, rlimit, file); return; } /* One-shot task, will not be respawned */ if (MATCH_CMD(line, "task ", x)) { - service_register(SVC_TYPE_TASK, x, rlimit, mtime); + service_register(SVC_TYPE_TASK, x, rlimit, file); return; } /* Like task but waits for completion, useful w/ [S] */ if (MATCH_CMD(line, "run ", x)) { - service_register(SVC_TYPE_RUN, x, rlimit, mtime); + service_register(SVC_TYPE_RUN, x, rlimit, file); return; } /* Classic inetd service */ if (MATCH_CMD(line, "inetd ", x)) { #ifdef INETD_ENABLED - service_register(SVC_TYPE_INETD, x, rlimit, mtime); + service_register(SVC_TYPE_INETD, x, rlimit, file); #else _e("Finit built with inetd support disabled, cannot register service inetd %s!", x); #endif @@ -368,7 +379,7 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], struct timeval *mt /* Regular or serial TTYs to run getty */ if (MATCH_CMD(line, "tty ", x)) { - tty_register(strip_line(x), rlimit, mtime); + tty_register(strip_line(x), rlimit, file); return; } } @@ -384,7 +395,7 @@ static void tabstospaces(char *line) } } -static int parse_conf_dynamic(char *file, struct timeval *mtime) +static int parse_conf_dynamic(char *file) { FILE *fp; struct rlimit rlimit[RLIMIT_NLIMITS]; @@ -409,7 +420,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime) tabstospaces(line); _d("%s", line); - parse_dynamic(line, rlimit, mtime); + parse_dynamic(line, rlimit, file); } fclose(fp); @@ -458,21 +469,26 @@ static int parse_conf(char *file) return 0; } -/* Reload all *.conf in /etc/finit.d/ */ -int conf_reload_dynamic(void) +/* + * Reload /etc/finit.conf and all *.conf in /etc/finit.d/ + */ +int conf_reload(void) { int i, num; - char *dir = rcsd; struct dirent **e; /* Mark and sweep */ svc_mark_dynamic(); tty_mark(); - num = scandir(dir, &e, NULL, alphasort); + /* First, read /etc/finit.conf */ + parse_conf(FINIT_CONF); + + /* Next, read all *.conf in /etc/finit.d/ */ + num = scandir(rcsd, &e, NULL, alphasort); if (num < 0) { - _d("Skipping %s, no files found ...", dir); - return 1; + _d("Skipping %s, no files found ...", rcsd); + return 0; } /* Update global limits in case operator used prlimit(1) on us */ @@ -484,13 +500,11 @@ int conf_reload_dynamic(void) char path[LINE_SIZE]; size_t len; struct stat st; - struct timeval mtime; - snprintf(path, sizeof(path), "%s/%s", dir, name); + snprintf(path, sizeof(path), "%s/%s", rcsd, name); /* Check that it's an actual file ... beyond any symlinks */ if (lstat(path, &st)) { - lost: _d("Skipping %s, cannot access: %s", path, strerror(errno)); continue; } @@ -521,27 +535,210 @@ int conf_reload_dynamic(void) continue; } - if (stat(path, &st)) - goto lost; /* Weird, lost file between here and lstat() */ - - TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim); - parse_conf_dynamic(path, &mtime); + parse_conf_dynamic(path); } while (num--) free(e[num]); free(e); + /* Drop record of all .conf changes */ + drop_changes(); + + /* + * Set host name, from %DEFHOST, *.conf or /etc/hostname. The + * latter wins, if neither exists we default to "noname" + */ set_hostname(&hostname); return 0; } -int conf_parse_config(void) +static struct conf_change *conf_find(char *file) +{ + struct conf_change *node, *tmp; + + TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) { + if (string_compare(node->name, file)) + return node; + } + + return NULL; +} + +static void drop_change(struct conf_change *node) +{ + if (!node) + return; + + TAILQ_REMOVE(&conf_change_list, node, link); + free(node->name); + free(node); +} + + +static void drop_changes(void) +{ + struct conf_change *node, *tmp; + + TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) + drop_change(node); +} + +static int do_change(char *name, uint32_t mask) +{ + struct conf_change *node; + + node = conf_find(name); + if (mask & (IN_DELETE | IN_MOVED_FROM)) { + drop_change(node); + return 0; + } + + if (node) { + _d("Event already registered for %s ...", name); + return 0; + } + + node = malloc(sizeof(*node)); + if (!node) + return 1; + + node->name = strdup(name); + if (!node->name) { + free(node); + return 1; + } + + TAILQ_INSERT_HEAD(&conf_change_list, node,link); + + return 0; +} + +int conf_changed(char *file) +{ + char *ptr; + + if (file && (ptr = strrchr(file, '/'))) + file = ++ptr; + + if (conf_find(file)) + return 1; + + return 0; +} + +static void conf_cb(uev_t *w, void *arg, int events) +{ + static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; + struct inotify_event *ev; + ssize_t sz, len; + + sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); + if (sz <= 0) { + _pe("invalid inotify event"); + return; + } + ev_buf[sz] = 0; + ev = (struct inotify_event *)ev_buf; + + if (arg) { + do_change(arg, ev->mask); + return; + } + + for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev); + len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) { + if (do_change(ev->name, ev->mask)) { + _pe("conf_monitor: Out of memory"); + break; + } + } +} + +static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt) +{ + struct stat st; + uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE; + char *arg = NULL; + int fd, wd; + + if (!ctx) + return 0; + + if (stat(path, &st)) { + _d("No such file or directory, skipping %s", path); + w->fd = -1; + return 0; + } + if (!S_ISDIR(st.st_mode)) { + arg = strrchr(path, '/'); + if (!arg) + arg = path; + else + arg++; + } + + if (w->fd >= 0) + close(w->fd); + + fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) { + _pe("Failed creating inotify descriptor"); + w->fd = -1; + return 1; + } + + /* + * Only forward error, don't report error, + * user may not have @path and that's OK + */ + wd = inotify_add_watch(fd, path, mask | opt); + if (wd < 0) { + w->fd = -1; + close(fd); + return 1; + } + + if (uev_io_init(ctx, w, conf_cb, arg, fd, UEV_READ)) { + _pe("Failed setting up I/O callback for %s watcher", path); + w->fd = -1; + close(fd); + return 1; + } + + return 0; +} + +/* + * Set up inotify watcher and load all *.conf in /etc/finit.d/ + */ +int conf_monitor(uev_ctx_t *ctx) +{ + int rc = 0; + + /* + * If only one watcher fails, that's OK. A user may have only + * one of /etc/finit.conf or /etc/finit.d in use, and may also + * have or not have symlinks in place. We need to monitor for + * changes to either symlink or target. + */ + rc += add_watcher(ctx, &w1, FINIT_RCSD, 0); + rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW); + rc += add_watcher(ctx, &w3, FINIT_CONF, 0); + + return rc + conf_reload(); +} + +/* + * Prepare .conf parser and load all .conf files + */ +int conf_init(void) { hostname = strdup(DEFHOST); + w1.fd = w2.fd = w3.fd = -1; - return parse_conf(FINIT_CONF) || conf_reload_dynamic(); + return conf_monitor(NULL); } /** diff --git a/src/conf.h b/src/conf.h index fd830d89..70144606 100644 --- a/src/conf.h +++ b/src/conf.h @@ -31,11 +31,14 @@ extern struct rlimit global_rlimit[]; int str2rlim(char *str); char *rlim2str(int rlim); +int conf_init (void); +void conf_reload (void); +int conf_changed (char *file); +int conf_monitor (uev_ctx_t *ctx); + void conf_parse_cmdline (void); int conf_parse_runlevels (char *runlevels); void conf_parse_cond (svc_t *svc, char *cond); -int conf_parse_config (void); -void conf_reload_dynamic (void); #endif /* FINIT_CONF_H_ */ diff --git a/src/finit.c b/src/finit.c index c89baf7d..ed0e3c18 100644 --- a/src/finit.c +++ b/src/finit.c @@ -447,19 +447,15 @@ int main(int argc, char* argv[]) } /* - * Parse /etc/finit.conf and all *.conf in /etc/finit.d/ to - * figure out how to bootstrap the system. + * Initialize .conf system and load static /etc/finit.conf */ - conf_parse_config(); + conf_init(); /* * Start built-in watchdog as soon as possible, if enabled */ wdogpid = watchdog(argv[0]); - /* Set hostname as soon as possible, for syslog et al. */ - set_hostname(&hostname); - /* * Mount filesystems */ @@ -488,12 +484,10 @@ int main(int argc, char* argv[]) sig_setup(&loop); /* - * Reload all *.conf in /etc/finit.d, the mount command (above) - * may have brought in new files (overlayfs). - * - * XXX: Can we detect remounted /etc to trigger this instead? + * Set up inotify watcher for /etc/finit.d and read all .conf + * files to figure out how to bootstrap the system. */ - conf_reload_dynamic(); + conf_monitor(&loop); _d("Base FS up, calling hooks ..."); plugin_run_hooks(HOOK_BASEFS_UP); diff --git a/src/service.c b/src/service.c index 6d33e364..16fe6b2e 100644 --- a/src/service.c +++ b/src/service.c @@ -474,7 +474,7 @@ void service_runlevel(int newlevel) * @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2) * @cfg: Configuration, complete command, with -- for description text * @rlimit: Limits for this service/task/run/inetd, may be global limits - * @mtime: The modification time if service is loaded from /etc/finit.d + * @file: The file name service was loaded from * * This function is used to register commands to be run on different * system runlevels with optional username. The @type argument details @@ -521,7 +521,7 @@ void service_runlevel(int newlevel) * Returns: * POSIX OK(0) on success, or non-zero errno exit status on failure. */ -int service_register(int type, char *cfg, struct rlimit rlimit[], struct timeval *mtime) +int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) { int i = 0; int id = -1; @@ -727,9 +727,13 @@ recreate: memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit)); /* New, recently modified or unchanged ... used on reload. */ - svc_check_dirty(svc, mtime); - free(line); + if (file && conf_changed(file)) + svc_mark_dirty(svc); + else + svc_mark_clean(svc); + /* Free duped line, from above */ + free(line); return 0; } diff --git a/src/service.h b/src/service.h index 5e70751c..00be550d 100644 --- a/src/service.h +++ b/src/service.h @@ -28,7 +28,7 @@ #include "svc.h" void service_runlevel (int newlevel); -int service_register (int type, char *line, struct rlimit rlimit[], struct timeval *mtime); +int service_register (int type, char *line, struct rlimit rlimit[], char *file); void service_unregister (svc_t *svc); void service_runtask_clean (void); diff --git a/src/sm.c b/src/sm.c index 47628d80..2d3e6fc8 100644 --- a/src/sm.c +++ b/src/sm.c @@ -144,7 +144,7 @@ restart: /* Make sure to (re)load all *.conf in /etc/finit.d/ */ if (runlevel != 0 && runlevel != 6) - conf_reload_dynamic(); + conf_reload(); /* Reset once flag of runtasks */ service_runtask_clean(); @@ -199,7 +199,7 @@ restart: case SM_RELOAD_CHANGE_STATE: /* First reload all *.conf in /etc/finit.d/ */ - conf_reload_dynamic(); + conf_reload(); /* * Then, mark all affected service conditions as in-flux and diff --git a/src/svc.c b/src/svc.c index b6f72252..b72f9754 100644 --- a/src/svc.c +++ b/src/svc.c @@ -360,17 +360,6 @@ void svc_mark_clean(svc_t *svc) *((int *)&svc->dirty) = 0; } -void svc_check_dirty(svc_t *svc, struct timeval *mtime) -{ - if (mtime && timercmp(&svc->mtime, mtime, !=)) - svc_mark_dirty(svc); - else - svc_mark_clean(svc); - - svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0; - svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0; -} - /** * svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d * @cb: Callback to run for each stale service diff --git a/src/svc.h b/src/svc.h index 81ef5437..d804e09b 100644 --- a/src/svc.h +++ b/src/svc.h @@ -92,9 +92,7 @@ typedef struct svc { pid_t pid; const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */ svc_type_t type; /* Service, run, task, inetd, ... */ - struct timeval 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 */ + const int dirty; /* -1: removal, 0: unmodified, 1: modified */ int starting; /* ... waiting for pidfile to be re-asserted */ int runlevels; int sighup; /* This service supports SIGHUP :) */ @@ -148,7 +146,6 @@ void svc_foreach_type (int types, void (*cb)(svc_t *)); svc_t *svc_stop_completed (void); void svc_mark_dynamic (void); -void svc_check_dirty (svc_t *svc, struct timeval *mtime); void svc_mark_dirty (svc_t *svc); void svc_mark_clean (svc_t *svc); void svc_clean_dynamic (void (*cb)(svc_t *)); @@ -168,7 +165,6 @@ 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) { return 0 != svc->starting; } -static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; } 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; } static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; } diff --git a/src/tty.c b/src/tty.c index ff2545ea..47be0a46 100644 --- a/src/tty.c +++ b/src/tty.c @@ -90,22 +90,8 @@ void tty_mark(void) { tty_node_t *tty; - LIST_FOREACH(tty, &tty_list, link) { - if (tty->mtime.tv_sec) - tty->dirty = -1; - } -} - -void tty_check(tty_node_t *tty, struct timeval *mtime) -{ - if (mtime && timercmp(&tty->mtime, mtime, !=)) - tty->dirty = 1; /* Modified, restart */ - else - tty->dirty = 0; /* Not modified */ - - /* Update mtime, if given */ - tty->mtime.tv_sec = mtime ? mtime->tv_sec : 0; - tty->mtime.tv_usec = mtime ? mtime->tv_usec : 0; + LIST_FOREACH(tty, &tty_list, link) + tty->dirty = -1; } void tty_sweep(void) @@ -113,14 +99,15 @@ void tty_sweep(void) tty_node_t *tty, *tmp; LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) { - if (tty->mtime.tv_sec && tty->dirty) { - _d("TTY %s dirty, stopping ...", tty->data.name); - tty_stop(&tty->data); + if (!tty->dirty) + continue; - if (tty->dirty == -1) { - _d("TTY %s removed, cleaning up.", tty->data.name); - tty_unregister(tty); - } + _d("TTY %s dirty, stopping ...", tty->data.name); + tty_stop(&tty->data); + + if (tty->dirty == -1) { + _d("TTY %s removed, cleaning up.", tty->data.name); + tty_unregister(tty); } } } @@ -129,7 +116,7 @@ void tty_sweep(void) * tty_register - Register a getty on a device * @line: Configuration, text after initial "tty" * @rlimit: Limits for this service/task/run/inetd, may be global limits - * @mtime: Modification time, to propagate to lower layers + * @file: The file name TTY was loaded from * * A Finit tty line can use the internal getty implementation or an * external one, like the BusyBox getty for instance. This function @@ -147,7 +134,7 @@ void tty_sweep(void) * Different getty implementations prefer the TTY device argument in * different order, so take care to investigate this first. */ -int tty_register(char *line, struct rlimit rlimit[], struct timeval *mtime) +int tty_register(char *line, struct rlimit rlimit[], char *file) { tty_node_t *entry; int insert = 0, noclear = 0, nowait = 0; @@ -282,7 +269,10 @@ int tty_register(char *line, struct rlimit rlimit[], struct timeval *mtime) /* Register configured limits */ memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit)); - tty_check(entry, mtime); + if (file && conf_changed(file)) + entry->dirty = 1; /* Modified, restart */ + else + entry->dirty = 0; /* Not modified */ _d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT "); return 0; diff --git a/src/tty.h b/src/tty.h index 50c6cd96..b739eaaa 100644 --- a/src/tty.h +++ b/src/tty.h @@ -54,7 +54,6 @@ typedef struct tty_node { finit_tty_t data; /* XXX: Yes, TTYs should be refactored into a separate SVC type. */ - struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */ int dirty; /* Set if old mtime != new mtime => reloaded, * or -1 when marked for removal */ } tty_node_t; @@ -62,10 +61,9 @@ typedef struct tty_node { //extern LIST_HEAD(, tty_node) tty_list; void tty_mark (void); -void tty_check (tty_node_t *tty, struct timeval *mtime); void tty_sweep (void); -int tty_register (char *line, struct rlimit rlimit[], struct timeval *mtime); +int tty_register (char *line, struct rlimit rlimit[], char *file); int tty_unregister (tty_node_t *tty); tty_node_t *tty_find (char *dev); From baf9c4f61f3ebcae837f2984a3c12e3321107025 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 13:12:04 +0100 Subject: [PATCH 20/24] Travis-CI: Update build deps, libite v1.9.2 --> v2.0.1 Signed-off-by: Joachim Nilsson --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e105872..8298a228 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,11 +34,11 @@ addons: install: - wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz - - wget https://github.com/troglobit/libite/releases/download/v1.9.2/libite-1.9.2.tar.xz + - wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz - tar xf libuev-2.1.0.tar.xz - - tar xf libite-1.9.2.tar.xz + - tar xf libite-2.0.1.tar.xz - (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip) - - (cd libite-1.9.2 && ./configure --prefix=/tmp && make && make install-strip) + - (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip) # Custom build script for the time being, no "make test" yet script: From 85fe95835a375bf5ad46290c2fdeabf8dc7e640b Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 13:17:39 +0100 Subject: [PATCH 21/24] doc/build.md: Mention required library versions Signed-off-by: Joachim Nilsson --- doc/build.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/build.md b/doc/build.md index a490384b..ea292dbd 100644 --- a/doc/build.md +++ b/doc/build.md @@ -26,6 +26,12 @@ optional plugins to enable. It depends on two external libraries: PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure +The configure script checks for all dependencies, including the correct +version of the above mentioned libraries. Currently requried versions: + +- libite v2.0.1 +- libuEv v2.1.0 + Configure --------- From 95518df62d9a9a58ce5ffb40542afbc873a7af76 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 16:43:26 +0100 Subject: [PATCH 22/24] Some systems rely on /dev/shm being there for `mount -a` This was accidentally removed in d7401b2, when svc_t was refactored from using shared memory. Signed-off-by: Joachim Nilsson --- src/finit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/finit.c b/src/finit.c index ed0e3c18..bf555ac0 100644 --- a/src/finit.c +++ b/src/finit.c @@ -394,6 +394,9 @@ int main(int argc, char* argv[]) makedir("/dev/pts", 0755); mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620"); + /* Some systems rely on /dev/shm being there, for `mount -a` below */ + makedir("/dev/shm", 0755); + /* * New tmpfs based /run for volatile runtime data * For details, see http://lwn.net/Articles/436012/ From 8992e7bf2be04e829df494a424f1c86798cfdd4d Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Fri, 22 Dec 2017 23:56:14 +0100 Subject: [PATCH 23/24] Refactor svc_iterator() into a proper iterator, add `first` flag Signed-off-by: Joachim Nilsson --- plugins/pidfile.c | 4 +- src/api.c | 5 +- src/cond-w.c | 4 +- src/inetd.c | 8 ++-- src/service.c | 8 ++-- src/svc.c | 119 +++++++++++++++++++++++++--------------------- src/svc.h | 8 ++-- 7 files changed, 83 insertions(+), 73 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 7c6827d3..2491d1f1 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -95,13 +95,13 @@ static void pidfile_callback(void *arg, int fd, int events) static void pidfile_reconf(void *arg) { static char cond[MAX_COND_LEN]; - svc_t *svc; + svc_t *svc, *iter = NULL; int restart = 0; do { restart = 0; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { mkcond(cond, sizeof(cond), svc->cmd); if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc) && diff --git a/src/api.c b/src/api.c index 6d494d63..327dcde9 100644 --- a/src/api.c +++ b/src/api.c @@ -225,7 +225,8 @@ static void send_svc(int sd, svc_t *svc) static void api_cb(uev_t *w, void *arg, int events) { int sd, lvl; - static svc_t *svc; + svc_t *svc; + static svc_t *iter = NULL; struct init_request rq; sd = accept(w->fd, NULL, NULL); @@ -351,7 +352,7 @@ static void api_cb(uev_t *w, void *arg, int events) * simultaneous client connections, but will * have to do for now. */ - svc = svc_iterator(rq.runlevel ? NULL : svc); + svc = svc_iterator(&iter, rq.runlevel); send_svc(sd, svc); goto leave; diff --git a/src/cond-w.c b/src/cond-w.c index 758de5a3..bb6033ab 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -88,10 +88,10 @@ static int svc_has_cond(svc_t *svc) static void cond_update(const char *name) { - svc_t *svc; + svc_t *svc, *iter = NULL; _d("%s", name); - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) continue; diff --git a/src/inetd.c b/src/inetd.c index 00a6face..2b1d4364 100644 --- a/src/inetd.c +++ b/src/inetd.c @@ -225,10 +225,10 @@ static void socket_cb(uev_t *w, void *arg, int events) */ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name) { - svc_t *svc; + svc_t *svc, *iter = NULL; char pname[NI_MAXHOST]; - for (svc = svc_inetd_iterator(NULL); svc; svc = svc_inetd_iterator(svc)) { + for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) { inetd_t *i = &svc->inetd; if (!i->builtin || i->type != SOCK_DGRAM) @@ -665,9 +665,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len) svc_t *inetd_find_svc(char *path, char *service, char *proto) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_inetd_iterator(NULL); svc; svc = svc_inetd_iterator(svc)) { + for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) { if (strncmp(path, svc->cmd, strlen(svc->cmd))) continue; diff --git a/src/service.c b/src/service.c index 16fe6b2e..74fb9410 100644 --- a/src/service.c +++ b/src/service.c @@ -1027,9 +1027,9 @@ void service_step_all(int types) */ void service_runtask_clean(void) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc_is_runtask(svc)) continue; @@ -1056,9 +1056,9 @@ void service_runtask_clean(void) */ int service_runtask_completed(int skip) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc_is_runtask(svc)) continue; diff --git a/src/svc.c b/src/svc.c index b72f9754..d5c1a01d 100644 --- a/src/svc.c +++ b/src/svc.c @@ -51,10 +51,10 @@ svc_t *svc_new(char *cmd, int id, int type) { int job = -1; char *desc; - svc_t *svc; + svc_t *svc, *iter = NULL; /* Find first job n:o if registering multiple instances */ - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!strcmp(svc->cmd, cmd)) { job = svc->job; break; @@ -103,36 +103,46 @@ int svc_del(svc_t *svc) /** * svc_iterator - Naive iterator over all registered services. - * @iter: %NULL for first entry, use returned value for subsequent calls + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next * * Returns: - * The first &svc_t when %NULL is given as argument, otherwise the next - * &svc_t until the end when %NULL is returned. + * An &svc_t pointer, or %NULL when no more entries can be found. */ -svc_t *svc_iterator(svc_t *iter) +svc_t *svc_iterator(svc_t **iter, int first) { - if (!iter) - return TAILQ_FIRST(&svc_list); + svc_t *svc; - if (iter && iter != TAILQ_END(&svc_list)) - return TAILQ_NEXT(iter, link); - - return NULL; + if (!iter) { + errno = EINVAL; + return NULL; + } + + if (first) + svc = TAILQ_FIRST(&svc_list); + else + svc = *iter; + + if (svc) + *iter = TAILQ_NEXT(svc, link); + + return svc; } /** * svc_inetd_iterator - Naive iterator over all registered inetd services. - * @iter: %NULL for first entry, use returned value for subsequent calls + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next * * Returns: * The first inetd &svc_t when %NULL is given as argument, otherwise the * next inetd &svc_t until the end when %NULL is returned. */ -svc_t *svc_inetd_iterator(svc_t *iter) +svc_t *svc_inetd_iterator(svc_t **iter, int first) { svc_t *svc; - for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { if (svc_is_inetd(svc)) return svc; } @@ -143,19 +153,20 @@ svc_t *svc_inetd_iterator(svc_t *iter) /** * svc_named_iterator - Iterates over all instances of a service. - * @iter: %NULL for first entry, use returned value for subsequent calls - * @cmd: Service name to look for. + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next + * @cmd: Service name to look for. * * Returns: * The first matching &svc_t when %NULL is given as argument, otherwise * the next &svc_t with the same @cmd name until the end when %NULL is * returned. */ -svc_t *svc_named_iterator(svc_t *iter, char *cmd) +svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd) { svc_t *svc; - for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { char *name = basename(svc->cmd); if (!strncmp(name, cmd, strlen(name))) @@ -168,19 +179,20 @@ svc_t *svc_named_iterator(svc_t *iter, char *cmd) /** * svc_job_iterator - Iterates over all instances of a service. - * @iter: %NULL for first entry, use returned value for subsequent calls - * @job: Job to look for. + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next + * @job: Job to look for. * * Returns: * The first matching &svc_t when %NULL is given as argument, otherwise * the next &svc_t with the same @job ID until the end when %NULL is * returned. */ -svc_t *svc_job_iterator(svc_t *iter, int job) +svc_t *svc_job_iterator(svc_t **iter, int first, int job) { svc_t *svc; - for (svc = svc_iterator(iter); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { if (svc->job == job) return svc; } @@ -195,12 +207,12 @@ svc_t *svc_job_iterator(svc_t *iter, int job) */ void svc_foreach(void (*cb)(svc_t *)) { - svc_t *svc; + svc_t *svc, *iter = NULL; if (!cb) return; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) cb(svc); } @@ -212,12 +224,12 @@ void svc_foreach(void (*cb)(svc_t *)) */ void svc_foreach_type(int types, void (*cb)(svc_t *)) { - svc_t *svc; + svc_t *svc, *iter = NULL; if (!cb) return; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!(svc->type & types)) continue; @@ -235,9 +247,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *)) */ svc_t *svc_stop_completed(void) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->state == SVC_STOPPING_STATE) return svc; } @@ -254,9 +266,9 @@ svc_t *svc_stop_completed(void) */ svc_t *svc_find(char *cmd, int id) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) return svc; } @@ -273,9 +285,9 @@ svc_t *svc_find(char *cmd, int id) */ svc_t *svc_find_by_pid(pid_t pid) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->pid == pid) return svc; } @@ -293,9 +305,9 @@ svc_t *svc_find_by_pid(pid_t pid) */ svc_t *svc_find_by_jobid(int job, int id) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->job == job && svc->id == id) return svc; } @@ -314,9 +326,9 @@ svc_t *svc_find_by_jobid(int job, int id) svc_t *svc_find_by_nameid(char *name, int id) { char *ptr; - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { ptr = strrchr(svc->cmd, '/'); if (ptr) ptr++; @@ -342,12 +354,10 @@ svc_t *svc_find_by_nameid(char *name, int id) */ void svc_mark_dynamic(void) { - svc_t *svc = svc_iterator(NULL); + svc_t *svc, *iter = NULL; - while (svc) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) *((int *)&svc->dirty) = -1; - svc = svc_iterator(svc); - } } void svc_mark_dirty(svc_t *svc) @@ -369,14 +379,13 @@ void svc_mark_clean(svc_t *svc) */ void svc_clean_dynamic(void (*cb)(svc_t *)) { - svc_t *svc = svc_iterator(NULL); + svc_t *svc, *iter = NULL; - while (svc) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->dirty == -1 && cb) { cb(svc); svc_mark_clean(svc); } - svc = svc_iterator(svc); } } @@ -407,9 +416,9 @@ int svc_clean_bootstrap(svc_t *svc) */ void svc_prune_bootstrap(void) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc->pid) svc_clean_bootstrap(svc); } @@ -442,9 +451,9 @@ int svc_enabled(svc_t *svc) int svc_next_id(char *cmd) { int id = 0; - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(NULL); svc; svc = svc_iterator(svc)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!strcmp(svc->cmd, cmd) && id < svc->id) id = svc->id; } @@ -454,10 +463,10 @@ int svc_next_id(char *cmd) int svc_is_unique(svc_t *svc) { - svc_t *s; + svc_t *s, *iter = NULL; int unique = 1; - for (s = svc_iterator(NULL); s; s = svc_iterator(svc)) { + for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) { if (svc->type == SVC_TYPE_FREE) continue; @@ -488,21 +497,21 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun token = strtok_r(input, " ", &pos); while (token) { int id = 1; - svc_t *svc; + svc_t *svc, *iter = NULL; char *ptr = strchr(token, ':'); if (isdigit(token[0])) { int job = atonum(token); if (!ptr) { - svc = svc_job_iterator(NULL, job); + svc = svc_job_iterator(&iter, 1, job); if (!svc && not_found) result += not_found(NULL, job); while (svc) { if (found) result += found(svc); - svc = svc_job_iterator(svc, job); + svc = svc_job_iterator(&iter, 0, job); } } else { *ptr++ = 0; @@ -517,14 +526,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun } } else { if (!ptr) { - svc = svc_named_iterator(NULL, token); + svc = svc_named_iterator(&iter, 1, token); if (!svc && not_found) result += not_found(token, id); while (svc) { if (found) result += found(svc); - svc = svc_named_iterator(svc, token); + svc = svc_named_iterator(&iter, 0, token); } } else { *ptr++ = 0; diff --git a/src/svc.h b/src/svc.h index d804e09b..6242b770 100644 --- a/src/svc.h +++ b/src/svc.h @@ -135,10 +135,10 @@ svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_jobid (int job, int id); svc_t *svc_find_by_nameid (char *name, int id); -svc_t *svc_iterator (svc_t *iter); -svc_t *svc_inetd_iterator (svc_t *iter); -svc_t *svc_named_iterator (svc_t *iter, char *cmd); -svc_t *svc_job_iterator (svc_t *iter, int job); +svc_t *svc_iterator (svc_t **iter, int first); +svc_t *svc_inetd_iterator (svc_t **iter, int first); +svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd); +svc_t *svc_job_iterator (svc_t **iter, int first, int job); void svc_foreach (void (*cb)(svc_t *)); void svc_foreach_type (int types, void (*cb)(svc_t *)); From f7c0366fa2b7123cb03799e4dab9c6d6f3427ae5 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sat, 23 Dec 2017 00:38:01 +0100 Subject: [PATCH 24/24] Fix segfault on X86_64, call va_end()+va_start() for each vfprintf() ... varargs is a special kind of hell. Signed-off-by: Joachim Nilsson --- src/log.c | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/log.c b/src/log.c index 3a7a5499..e38b84d9 100644 --- a/src/log.c +++ b/src/log.c @@ -105,45 +105,43 @@ int log_is_debug(void) return debug; } -static void early_logit(int prio, const char *fmt, va_list ap) -{ - FILE *fp; - - if (!debug && prio > loglevel) - return; - - fp = fopen("/dev/kmsg", "w"); - if (fp) { - fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio); - vfprintf(fp, fmt, ap); - fclose(fp); - - if (debug) - vfprintf(stderr, fmt, ap); - } else { - vfprintf(stderr, fmt, ap); - } -} - /* * Log to /dev/kmsg until syslogd has started, then openlog() * and continue logging as a regular daemon. */ void logit(int prio, const char *fmt, ...) { + FILE *fp; va_list ap; va_start(ap, fmt); - if (!up) { - if (!fexist("/dev/log")) { - early_logit(prio, fmt, ap); - goto done; - } - log_open(); + if (up || fexist("/dev/log")) { + if (!up) + log_open(); + vsyslog(prio, fmt, ap); + goto done; + } + + if (!debug && prio > loglevel) + goto done; + + fp = fopen("/dev/kmsg", "w"); + if (!fp) { + vfprintf(stderr, fmt, ap); + goto done; + } + + fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio); + vfprintf(fp, fmt, ap); + fclose(fp); + + if (debug) { + va_end(ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); } - vsyslog(prio, fmt, ap); done: va_end(ap); }