From f4a0b99df89233d786df9cc2b4bd28de552461be Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 22 Feb 2021 16:18:22 +0100 Subject: [PATCH 01/26] Fix #155: add support for env:/a/bc to service/run/task This patch makes it possible to create a service with optional arguments, similar to the systemd EnvironmentFile= setting. The major difference to systemd, is that Finit does not attempt to start a service if it cannot find the env. file, unless it is prefixed with '-'. Example: /etc/default/syslogd: SYSLOGD_OPTS="-m0 -r 128k:10 -s" /etc/finit.d/enabled/syslogd.conf service env:-/etc/default/syslogd syslogd -F $SYSLOGD_OPTS Only downside, right now, is that changing the contents of the file /etc/default/syslogd does not mark the syslogd.conf as modified and a subsequent `initctl reload` will *not* restart syslogd. Signed-off-by: Joachim Wiberg --- src/initctl.c | 1 + src/service.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++---- src/svc.h | 1 + 3 files changed, 187 insertions(+), 13 deletions(-) diff --git a/src/initctl.c b/src/initctl.c index fd12d5dd..3efbb3b2 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -494,6 +494,7 @@ static int show_status(char *arg) printf("Service : %s\n", svc->cmd); printf("Description : %s\n", svc->desc); printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident))); + printf("Environment : %s\n", svc->env); printf("PID : %d\n", svc->pid); printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf); printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); diff --git a/src/service.c b/src/service.c index 10d34055..25452d60 100644 --- a/src/service.c +++ b/src/service.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "cgroup.h" #include "conf.h" @@ -261,6 +262,121 @@ static int redirect(svc_t *svc) return 0; } +/* + * non-zero env, no checking if file exists or not + */ +static char *svc_getenv(svc_t *svc) +{ + int v = 0; + + if (!svc->env[0]) + return NULL; + + if (svc->env[0] == '-') + v = 1; + + return &svc->env[v]; +} + +/* + * Check if svc has env, if env file exists returns true + * if it doesn't exist, but has '-', also true. if svc + * doesn't have env, also true. + */ +static int check_env(svc_t *svc) +{ + char *env = svc_getenv(svc); + + if (!env) + return 1; + + return fexist(env); +} + +/* + * Source environment file, if it exists + * Note: must be called from privsepped child + */ +static void source_env(svc_t *svc) +{ + char buf[LINE_SIZE]; + char *line; + size_t len; + FILE *fp; + char *fn; + + fn = svc_getenv(svc); + if (!fn) + return; + + /* Warning in service_start() after check_env() */ + fp = fopen(fn, "r"); + if (!fp) + return; + + line = buf; + len = sizeof(buf); + while (fgets(line, len, fp)) { + char *key = chomp(line); + char *value, *end; + + /* skip any leading whitespace */ + while (isspace(*key)) + key++; + + /* skip comments */ + if (*key == '#' || *key == ';') + continue; + + /* find end of line */ + end = key; + while (*end) + end++; + + /* strip trailing whitespace */ + if (end > key) { + end--; + while (isspace(*end)) + *end-- = 0; + } + + value = strchr(key, '='); + if (!value) + continue; + *value++ = 0; + + /* strip leading whitespace from value */ + while (isspace(*value)) + value++; + + /* unquote value, if quoted */ + if (value[0] == '"' || value[0] == '\'') { + char q = value[0]; + + if (*end == q) { + value = &value[1]; + *end = 0; + } + } + + /* find end of key */ + end = key; + while (*end) + end++; + + /* strip trailing whitespace */ + if (end > key) { + end--; + while (isspace(*end)) + *end-- = 0; + } + + setenv(key, value, 1); + } + + fclose(fp); +} + static int is_norespawn(void) { return sig_stopped() || @@ -277,9 +393,10 @@ static int is_norespawn(void) */ static int service_start(svc_t *svc) { - int i, result = 0, do_progress = 1; - pid_t pid; + int result = 0, do_progress = 1; sigset_t nmask, omask; + pid_t pid; + size_t i; if (!svc) return 1; @@ -290,11 +407,17 @@ static int service_start(svc_t *svc) /* Don't try and start service if it doesn't exist. */ if (!whichp(svc->cmd)) { - print(1, "Service %s does not exist", svc->cmd); + print(1, "%s: does not exist", svc->cmd); svc_missing(svc); return 1; } + /* Unlike systemd we do not allow starting service if env is missing, unless - */ + if (!check_env(svc)) { + print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env); + return 1; + } + if (svc_is_sysv(svc)) logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd); @@ -355,12 +478,45 @@ static int service_start(svc_t *svc) } } + /* Source any environment from env:/path/to/file */ + source_env(svc); + if (!svc_is_sysv(svc)) { + wordexp_t we = { 0 }; + int rc; + + if ((rc = wordexp(svc->cmd, &we, 0))) { + nomem: + _e("%s: failed wordexp(): %d", svc->cmd, rc); + wordfree(&we); + _exit(1); + } + for (i = 0; i < MAX_NUM_SVC_ARGS; i++) { if (strlen(svc->args[i]) == 0) break; + + if ((rc = wordexp(svc->args[i], &we, WRDE_APPEND))) + goto nomem; + } + + if (we.we_wordc > MAX_NUM_SVC_ARGS) { + logit(LOG_ERR, "%s: too man args after expansion.", svc->cmd); + goto nomem; + } + + for (i = 0; i < we.we_wordc; i++) { + if (strlen(we.we_wordv[i]) >= sizeof(svc->args[i])) { + logit(LOG_ERR, "%s: expanded arg. '%s' too long", we.we_wordv[i]); + rc = WRDE_NOSPACE; + goto nomem; + } + + /* overwrite the child's svc with expanded args */ + strlcpy(svc->args[i], we.we_wordv[i], sizeof(svc->args[i])); args[i] = svc->args[i]; } + wordfree(&we); } else { i = 0; args[i++] = svc->cmd; @@ -372,12 +528,11 @@ static int service_start(svc_t *svc) sig_unblock(); /* - * The setsid() call is the most humble of all in this - * function. It takes care to detach the process from - * its controlling terminal, preventing daemons from - * leaking to the console, and allowing us to run such - * programs like `lxc-start -F` in the foreground to - * properly monitor them. + * The setsid() call takes care to detach the process + * from its controlling terminal, preventing daemons + * from leaking to the console, and allowing us to run + * such programs like `lxc-start -F` in the foreground + * to properly monitor them. * * If you find yourself here wanting to fix the output * to the console at boot, for debugging or similar, @@ -386,9 +541,9 @@ static int service_start(svc_t *svc) setsid(); if (svc_is_runtask(svc)) - status = exec_runtask(svc->cmd, args); + status = exec_runtask(args[0], &args[1]); else - status = execvp(svc->cmd, args); + status = execvp(args[0], &args[1]); _exit(status); } else if (debug) { @@ -396,7 +551,7 @@ static int service_start(svc_t *svc) for (i = 0; i < MAX_NUM_SVC_ARGS; i++) { if (strlen(svc->args[i]) == 0) - continue; + break; if (buf[0]) strlcat(buf, " ", sizeof(buf)); strlcat(buf, svc->args[i], sizeof(buf)); @@ -661,6 +816,19 @@ static void parse_log(svc_t *svc, char *arg) } } +static void parse_env(svc_t *svc, char *env) +{ + if (!env) + return; + + if (strlen(env) >= sizeof(svc->env)) { + _e("%s: env file is too long (>%d chars)", svc->cmd, sizeof(svc->env)); + return; + } + + strlcpy(svc->env, env, sizeof(svc->env)); +} + static void parse_sighalt(svc_t *svc, char *arg) { int signo; @@ -798,7 +966,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) char *cmd, *desc, *runlevels = NULL, *cond = NULL; char *username = NULL, *log = NULL, *pid = NULL; char *name = NULL, *halt = NULL, *delay = NULL; - char *id = NULL; + char *id = NULL, *env = NULL; int levels = 0; int manual = 0; char *line; @@ -860,6 +1028,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) halt = &cmd[5]; else if (!strncasecmp(cmd, "kill:", 5)) delay = &cmd[5]; + else if (!strncasecmp(cmd, "env:", 4)) + env = &cmd[4]; else break; @@ -925,6 +1095,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) parse_log(svc, log); if (desc) strlcpy(svc->desc, desc, sizeof(svc->desc)); + if (env) + parse_env(svc, env); /* Set configured limits */ memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit)); diff --git a/src/svc.h b/src/svc.h index 83f3cd68..23cd5a81 100644 --- a/src/svc.h +++ b/src/svc.h @@ -134,6 +134,7 @@ typedef struct svc { char cmd[MAX_ARG_LEN]; char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN]; char desc[MAX_STR_LEN]; + char env[MAX_ARG_LEN]; /* * Used to forcefully kill services that won't shutdown on From 4480010d4a46498097c8936bcbe46e99b21d3ed2 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Mon, 22 Feb 2021 23:43:15 +0100 Subject: [PATCH 02/26] test: Add support for teardown in tests If a test defines a function 'test_teardown(), that function will be called before the test env teardown is run. Signed-off-by: Magnus Malm --- test/lib.sh | 5 +++++ test/test_start_stop_service/test.sh | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/lib.sh b/test/lib.sh index afa2d6f1..953525a5 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -69,6 +69,11 @@ say() { teardown() { test_status="$?" + + if type test_teardown > /dev/null 2>&1 ; then + test_teardown + fi + log "$color_reset" '--' '' if [ "$test_status" -eq 0 ]; then log "$fg_green" 'TEST PASS' '' diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh index d4146e4a..ae5fe8ff 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/test_start_stop_service/test.sh @@ -10,6 +10,14 @@ assert_num_children() { assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" } +test_teardown() { + say "Test done $(date)" + say "Running test teardown..." + + # Teardown + rm -f test_root/bin/service.sh +} + # Test say 'Set up a service' cp "$TEST_DIR"/test_start_stop_service/service.sh "$TEST_DIR"/test_root/test_assets/ @@ -27,6 +35,3 @@ say 'Reload Finit' texec kill -SIGHUP 1 retry 'assert_num_children 0 service.sh' - -# Teardown -rm -f test_root/bin/service.sh From c0bbb0fc5173ae6e6a22d4b545494a124e9bd460 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 23 Feb 2021 08:18:59 +0100 Subject: [PATCH 03/26] plugins: pidfile: rename variables and local fns - new namespace before breaking out to shared object - use inotify naming fd -> wd Signed-off-by: Joachim Wiberg --- plugins/pidfile.c | 104 +++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 277351af..ad5b6241 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -35,25 +35,25 @@ #include "plugin.h" #include "service.h" -struct wd_entry { - TAILQ_ENTRY(wd_entry) link; +struct iwatch_path { + TAILQ_ENTRY(iwatch_path) link; char *path; - int fd; + int wd; }; -struct context { +struct iwatch { int fd; - TAILQ_HEAD(, wd_entry) wd_list; + TAILQ_HEAD(, iwatch_path) iwp_list; }; -static struct context pidfile_ctx; +static struct iwatch iw_pidfile; -static int watcher_add(struct context *ctx, char *path) +static int iwatch_add(struct iwatch *iw, char *path) { - struct wd_entry *wd; + struct iwatch_path *iwp; uint32_t mask = IN_ONLYDIR | IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO; char *ptr; - int fd; + int wd; _d("pidfile: Adding new watcher for path %s", path); ptr = strstr(path, "run/"); @@ -72,33 +72,33 @@ static int watcher_add(struct context *ctx, char *path) } } - fd = inotify_add_watch(ctx->fd, path, mask); - if (fd < 0) { + wd = inotify_add_watch(iw->fd, path, mask); + if (wd < 0) { _pe("inotify_add_watch()"); return -1; } - wd = malloc(sizeof(struct wd_entry)); - if (!wd) { - _pe("Failed allocating new `struct wd_entry`"); - inotify_rm_watch(ctx->fd, fd); + iwp = malloc(sizeof(struct iwatch_path)); + if (!iwp) { + _pe("Failed allocating new `struct iwatch_path`"); + inotify_rm_watch(iw->fd, wd); return -1; } - wd->path = path; - wd->fd = fd; - TAILQ_INSERT_HEAD(&ctx->wd_list, wd, link); + iwp->path = path; + iwp->wd = wd; + TAILQ_INSERT_HEAD(&iw->iwp_list, iwp, link); return 0; } -static int watcher_del(struct context *ctx, struct wd_entry *wde) +static int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp) { - _d("pidfile: Removing watcher for removed path %s", wde->path); - TAILQ_REMOVE(&ctx->wd_list, wde, link); - inotify_rm_watch(ctx->fd, wde->fd); - free(wde->path); - free(wde); + _d("pidfile: Removing watcher for removed path %s", iwp->path); + TAILQ_REMOVE(&iw->iwp_list, iwp, link); + inotify_rm_watch(iw->fd, iwp->wd); + free(iwp->path); + free(iwp); return 0; } @@ -138,7 +138,7 @@ static void update_conds(char *dir, char *name, uint32_t mask) } /* synthesize events in case of new run dirs */ -static void scan_for_pidfiles(struct context *ctx, char *dir, int len) +static void scan_for_pidfiles(struct iwatch *iw, char *dir, int len) { glob_t gl; size_t i; @@ -162,23 +162,23 @@ static void scan_for_pidfiles(struct context *ctx, char *dir, int len) globfree(&gl); } -static void handle_dir(struct context *ctx, struct wd_entry *wde, char *name, int mask) +static void handle_dir(struct iwatch *iw, struct iwatch_path *iwp, char *name, int mask) { char *path; int plen; int exist = 0; - plen = snprintf(NULL, 0, "%s/%s", wde->path, name) + 1; + plen = snprintf(NULL, 0, "%s/%s", iwp->path, name) + 1; path = malloc(plen); if (!path) { _pe("Failed allocating path buffer for watcher"); return; } - snprintf(path, plen, "%s/%s", wde->path, name); + snprintf(path, plen, "%s/%s", iwp->path, name); _d("pidfile: path is %s", path); - TAILQ_FOREACH(wde, &ctx->wd_list, link) { - if (!strcmp(wde->path, path)) { + TAILQ_FOREACH(iwp, &iw->iwp_list, link) { + if (!strcmp(iwp->path, path)) { exist = 1; break; } @@ -186,15 +186,15 @@ static void handle_dir(struct context *ctx, struct wd_entry *wde, char *name, in if (mask & IN_CREATE) { if (!exist) { - int rc = watcher_add(ctx, path); + int rc = iwatch_add(iw, path); - scan_for_pidfiles(ctx, path, plen); + scan_for_pidfiles(iw, path, plen); if (!rc) return; } } else if (mask & IN_DELETE) { if (exist) - watcher_del(&pidfile_ctx, wde); + iwatch_del(&iw_pidfile, iwp); } free(path); @@ -224,7 +224,7 @@ static void pidfile_callback(void *arg, int fd, int events) off = 0; for (off = 0; off < sz; off += sizeof(*ev) + ev->len) { - struct wd_entry *wde; + struct iwatch_path *iwp; ev = (struct inotify_event *)&buf[off]; @@ -233,23 +233,23 @@ static void pidfile_callback(void *arg, int fd, int events) continue; /* Find base path for this event */ - TAILQ_FOREACH(wde, &pidfile_ctx.wd_list, link) { - if (wde->fd == ev->wd) + TAILQ_FOREACH(iwp, &iw_pidfile.iwp_list, link) { + if (iwp->wd == ev->wd) break; } if (ev->mask & IN_ISDIR) { - handle_dir(&pidfile_ctx, wde, ev->name, ev->mask); + handle_dir(&iw_pidfile, iwp, ev->name, ev->mask); continue; } if (ev->mask & IN_DELETE) { - _d("pidfile %s/%s removed ...", wde->path, ev->name); + _d("pidfile %s/%s removed ...", iwp->path, ev->name); continue; } if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) - update_conds(wde->path, ev->name, ev->mask); + update_conds(iwp->path, ev->name, ev->mask); } done: free(buf); @@ -305,13 +305,13 @@ static void pidfile_init(void *arg) return; } - TAILQ_INIT(&pidfile_ctx.wd_list); - if (watcher_add(&pidfile_ctx, path)) - close(pidfile_ctx.fd); + TAILQ_INIT(&iw_pidfile.iwp_list); + if (iwatch_add(&iw_pidfile, path)) + close(iw_pidfile.fd); _d("pidfile monitor active"); } -static struct context pidfile_ctx; +static struct iwatch iw_pidfile; /* * When performing an `initctl reload` with one (unchanged) service @@ -340,26 +340,26 @@ static plugin_t plugin = { PLUGIN_INIT(plugin_init) { - pidfile_ctx.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (pidfile_ctx.fd < 0) { + iw_pidfile.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (iw_pidfile.fd < 0) { _pe("inotify_init()"); return; } - plugin.io.fd = pidfile_ctx.fd; + plugin.io.fd = iw_pidfile.fd; plugin_register(&plugin); } PLUGIN_EXIT(plugin_exit) { - struct wd_entry *wde, *tmp; + struct iwatch_path *iwp, *tmp; - TAILQ_FOREACH_SAFE(wde, &pidfile_ctx.wd_list, link, tmp) { - inotify_rm_watch(pidfile_ctx.fd, wde->fd); - free(wde->path); - free(wde); + TAILQ_FOREACH_SAFE(iwp, &iw_pidfile.iwp_list, link, tmp) { + inotify_rm_watch(iw_pidfile.fd, iwp->wd); + free(iwp->path); + free(iwp); } - close(pidfile_ctx.fd); + close(iw_pidfile.fd); plugin_unregister(&plugin); } From 3d01728d2e1c9b13dcd16953d4a1518cf48da8ad Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Mon, 22 Feb 2021 23:46:40 +0100 Subject: [PATCH 04/26] test: Add testenv_enter.sh to ease debugging testenv The script testenv_enter.sh, by default, runs /bin/sh in the environment currently setup. A bit easier than copy pasting the old hint text printed before test run. Signed-off-by: Magnus Malm --- test/lib.sh | 2 ++ test/testenv_enter.sh | 23 +++++++++++++++++++++++ test/testenv_start.sh | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100755 test/testenv_enter.sh diff --git a/test/lib.sh b/test/lib.sh index 953525a5..c87ccd5a 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -83,6 +83,7 @@ teardown() { texec kill -SIGUSR2 1 wait + rm "$TEST_DIR"/running_test.pid } trap teardown EXIT @@ -98,6 +99,7 @@ log "$color_reset" '--' '' # Setup "$TEST_DIR/testenv_start.sh" finit & finit_ppid=$! +echo "$finit_ppid" > "$TEST_DIR"/running_test.pid finit_pid=$(retry "pgrep -P $finit_ppid") tty=/dev/$(texec cat /sys/class/tty/console/active) diff --git a/test/testenv_enter.sh b/test/testenv_enter.sh new file mode 100755 index 00000000..a2507928 --- /dev/null +++ b/test/testenv_enter.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -eu + +cmd="" +if [ "$#" -lt 1 ]; then + cmd="/bin/sh" +else + cmd=$1 + shift +fi + +PID_FILE=$(dirname "$0")/running_test.pid + +if [ -f "$PID_FILE" ] ; then + target=$(cat "$PID_FILE") + target=$(pgrep -P "$target") +else + echo "No test running!" + exit 1 +fi + +"$(dirname "$0")"/testenv_exec.sh "$target" "$cmd" "$@" diff --git a/test/testenv_start.sh b/test/testenv_start.sh index 5c8cee8d..db529429 100755 --- a/test/testenv_start.sh +++ b/test/testenv_start.sh @@ -5,7 +5,7 @@ set -eu TEST_DIR=$(dirname "$0") TESTS_ROOT="$(pwd)/${TEST_DIR}/test_root" ->&2 echo "Hint: Execute '${TEST_DIR}/testenv_exec.sh \$(pgrep -P $$) /bin/sh; reset' to enter the test namespace" +>&2 echo "Hint: Execute 'test/testenv_enter.sh' to enter the test namespace" unshare=$(command -v unshare) chroot=$(command -v chroot) From 5e20abef8ddd15daefdb4e6323d5be8feb1711c0 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 00:24:54 +0100 Subject: [PATCH 05/26] test: Print running test name with log() Signed-off-by: Magnus Malm --- test/lib.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/lib.sh b/test/lib.sh index c87ccd5a..fc85507c 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -21,7 +21,7 @@ fg_red='\e[1;31m' fg_green='\e[1;32m' fg_yellow='\e[1;33m' log() { - printf "< TEST > %b%b%b %s\n" "$1" "$2" "$color_reset" "$3" + printf "< $TEST_NAME > %b%b%b %s\n" "$1" "$2" "$color_reset" "$3" } assert() { @@ -92,6 +92,9 @@ FINIT_CONF=$(grep FINIT_CONF "$TEST_DIR/../config.h" | cut -d' ' -f3 | cut -d'"' export FINIT_CONF FINIT_CONF_DIR="$(dirname "$FINIT_CONF")"/finit.d export FINIT_CONF_DIR +TEST_NAME="$(dirname "$0")" +TEST_NAME=${TEST_NAME#*/} +export TEST_NAME log "$color_reset" 'Test start' '' log "$color_reset" '--' '' From 3ec788a6cf7dfd0af76552fd988bfba4e6beb6bc Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 00:29:08 +0100 Subject: [PATCH 06/26] test: Minor tweaks of how/when to print testenv stuff Signed-off-by: Magnus Malm --- test/lib.sh | 10 +++++++--- test/testenv_start.sh | 2 -- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/test/lib.sh b/test/lib.sh index fc85507c..922b067f 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -96,13 +96,17 @@ TEST_NAME="$(dirname "$0")" TEST_NAME=${TEST_NAME#*/} export TEST_NAME -log "$color_reset" 'Test start' '' -log "$color_reset" '--' '' +# Setup test environment -# Setup "$TEST_DIR/testenv_start.sh" finit & finit_ppid=$! echo "$finit_ppid" > "$TEST_DIR"/running_test.pid + +>&2 echo "Hint: Execute 'test/testenv_enter.sh' to enter the test namespace" +>&2 echo "finit conf '$FINIT_CONF'" +>&2 echo "finit_conf dir '$FINIT_CONF_DIR'" +log "$color_reset" 'Setup of test environment done' '' + finit_pid=$(retry "pgrep -P $finit_ppid") tty=/dev/$(texec cat /sys/class/tty/console/active) diff --git a/test/testenv_start.sh b/test/testenv_start.sh index db529429..7dcf2869 100755 --- a/test/testenv_start.sh +++ b/test/testenv_start.sh @@ -5,8 +5,6 @@ set -eu TEST_DIR=$(dirname "$0") TESTS_ROOT="$(pwd)/${TEST_DIR}/test_root" ->&2 echo "Hint: Execute 'test/testenv_enter.sh' to enter the test namespace" - unshare=$(command -v unshare) chroot=$(command -v chroot) From 6d9374590faf6401751c72aa6417256081eb624f Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 00:31:56 +0100 Subject: [PATCH 07/26] test: Add test start timestamp Signed-off-by: Magnus Malm --- test/test_start_stop_service/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh index ae5fe8ff..5816854b 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/test_start_stop_service/test.sh @@ -19,6 +19,7 @@ test_teardown() { } # Test +say "Test start $(date)" say 'Set up a service' cp "$TEST_DIR"/test_start_stop_service/service.sh "$TEST_DIR"/test_root/test_assets/ texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" From 8a2cc2aeb8f68be77e9e4d9148f8f758bc988dd1 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 00:44:33 +0100 Subject: [PATCH 08/26] test: Move service.sh to test/common/ Signed-off-by: Magnus Malm --- test/{test_start_stop_service => common}/service.sh | 0 test/test_start_stop_service/test.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{test_start_stop_service => common}/service.sh (100%) diff --git a/test/test_start_stop_service/service.sh b/test/common/service.sh similarity index 100% rename from test/test_start_stop_service/service.sh rename to test/common/service.sh diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh index 5816854b..c0e66564 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/test_start_stop_service/test.sh @@ -21,7 +21,7 @@ test_teardown() { # Test say "Test start $(date)" say 'Set up a service' -cp "$TEST_DIR"/test_start_stop_service/service.sh "$TEST_DIR"/test_root/test_assets/ +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" say 'Reload Finit' From bb095bfd5d9f4d102c8dde9b8c55e504938298f5 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 00:49:10 +0100 Subject: [PATCH 09/26] test: Minor tweak on test's output Signed-off-by: Magnus Malm --- test/test_start_stop_service/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh index c0e66564..8637bc14 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/test_start_stop_service/test.sh @@ -20,7 +20,7 @@ test_teardown() { # Test say "Test start $(date)" -say 'Set up a service' +say 'Add a dynamic service in /etc/finit.conf' cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" @@ -29,7 +29,7 @@ texec kill -SIGHUP 1 retry 'assert_num_children 1 service.sh' -say 'Remove the service' +say 'Remove the dynamic service from /etc/finit.conf' texec sh -c "echo > $FINIT_CONF" say 'Reload Finit' From eda9ae4c3e3e35698ddfa328909b6ad0c0efa678 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 00:50:58 +0100 Subject: [PATCH 10/26] test: Rename start/stop service test to add/remove dynamic service Signed-off-by: Magnus Malm --- test/Makefile.am | 2 +- .../test.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{test_start_stop_service => test_add_remove_dynamic_service}/test.sh (100%) diff --git a/test/Makefile.am b/test/Makefile.am index 29f9741c..c8584af6 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ AM_TESTS_ENVIRONMENT = make -C test_root/; TESTS = checkself.sh -TESTS += test_start_stop_service/test.sh +TESTS += test_add_remove_dynamic_service/test.sh clean-local: make -C test_root/ clean diff --git a/test/test_start_stop_service/test.sh b/test/test_add_remove_dynamic_service/test.sh similarity index 100% rename from test/test_start_stop_service/test.sh rename to test/test_add_remove_dynamic_service/test.sh From 27ad3b5a7f3c55a8879451fa748ae6782882f94e Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 01:05:07 +0100 Subject: [PATCH 11/26] test: Minor cleanup in lib.sh Signed-off-by: Magnus Malm --- test/lib.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/lib.sh b/test/lib.sh index 922b067f..82e6b1e2 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -90,14 +90,17 @@ trap teardown EXIT FINIT_CONF=$(grep FINIT_CONF "$TEST_DIR/../config.h" | cut -d' ' -f3 | cut -d'"' -f2) export FINIT_CONF + FINIT_CONF_DIR="$(dirname "$FINIT_CONF")"/finit.d export FINIT_CONF_DIR + TEST_NAME="$(dirname "$0")" TEST_NAME=${TEST_NAME#*/} export TEST_NAME # Setup test environment +# Setup test environment "$TEST_DIR/testenv_start.sh" finit & finit_ppid=$! echo "$finit_ppid" > "$TEST_DIR"/running_test.pid From b364fd7d6a2245b6af20bf919a99c96287f96113 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 01:37:32 +0100 Subject: [PATCH 12/26] test: Add test for dynamically add/remove service in finit.d/ files Signed-off-by: Magnus Malm --- test/Makefile.am | 1 + test/common/service.conf | 1 + .../test.sh | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/common/service.conf create mode 100755 test/test_add_remove_dynamic_service_sub_config/test.sh diff --git a/test/Makefile.am b/test/Makefile.am index c8584af6..20a60dca 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,7 @@ AM_TESTS_ENVIRONMENT = make -C test_root/; TESTS = checkself.sh TESTS += test_add_remove_dynamic_service/test.sh +TESTS += test_add_remove_dynamic_service_sub_config/test.sh clean-local: make -C test_root/ clean diff --git a/test/common/service.conf b/test/common/service.conf new file mode 100644 index 00000000..3fd97586 --- /dev/null +++ b/test/common/service.conf @@ -0,0 +1 @@ +service [2345] kill:20 log /test_assets/service.sh diff --git a/test/test_add_remove_dynamic_service_sub_config/test.sh b/test/test_add_remove_dynamic_service_sub_config/test.sh new file mode 100755 index 00000000..a2cf9d85 --- /dev/null +++ b/test/test_add_remove_dynamic_service_sub_config/test.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0")/.. +# shellcheck source=/dev/null +. "$TEST_DIR/lib.sh" + +assert_num_children() { + assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" +} + +test_teardown() { + say "Test done $(date)" + say "Running test teardown..." + + # Teardown + rm -f test_root/bin/service.sh +} + +# Test +say "Test start $(date)" +# toggle_finit_debug +say "Add a dynamic service in $FINIT_CONF_DIR/service.conf" +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh pid:/run/service.pid' > $FINIT_CONF_DIR/service.conf" + +say 'Reload Finit' +texec kill -SIGHUP 1 + +pause + +retry 'assert_num_children 1 service.sh' 5 + +say 'Remove the dynamic service from /etc/finit.d/service.conf' +texec sh -c "echo > $FINIT_CONF_DIR/service.conf" + +say 'Reload Finit' +texec kill -SIGHUP 1 + +retry 'assert_num_children 0 service.sh' 5 From 6bcb9e9030b7c61f05a9caed28f8b53212d7dc96 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 11:20:38 +0100 Subject: [PATCH 13/26] test: Populate/cleanup /etc "manually" via Makefile/tests For now, do not mount /etc as tmpfs, but instead create the dir when building the rootfs via test/test_root/Makefile. Signed-off-by: Magnus Malm --- test/test_root/Makefile | 3 ++- test/test_root/bin/chrootsetup.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_root/Makefile b/test/test_root/Makefile index 29dd7d68..0fa7304c 100644 --- a/test/test_root/Makefile +++ b/test/test_root/Makefile @@ -35,7 +35,8 @@ dirs = ./dev \ ./sbin \ ./sys \ ./test_assets \ - ./tmp + ./tmp \ + ./etc _libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') libs = $(foreach path,$(_libs_src),.$(path)) diff --git a/test/test_root/bin/chrootsetup.sh b/test/test_root/bin/chrootsetup.sh index fe4e0e4c..5f22b209 100755 --- a/test/test_root/bin/chrootsetup.sh +++ b/test/test_root/bin/chrootsetup.sh @@ -17,7 +17,7 @@ mount -n -t tmpfs none /dev mount -t proc none /proc mount -t sysfs none /sys # mount -t tmpfs none /tmp -mount -t tmpfs none /etc +# mount -t tmpfs none /etc # mkdir -p /dev/pts # mount -t devpts none /dev/pts From 8381976f05eaf9011bea04327e6b601a90db8f1f Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 23 Feb 2021 15:03:03 +0100 Subject: [PATCH 14/26] plugins: boilerplate for made-easy integration with FRR Signed-off-by: Joachim Wiberg --- plugins/bootmisc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/bootmisc.c b/plugins/bootmisc.c index b25fffa6..fd56eb09 100644 --- a/plugins/bootmisc.c +++ b/plugins/bootmisc.c @@ -167,6 +167,8 @@ static void setup(void *arg) makedir("/var/run/pluto", 0755); /* Needed by Openswan */ makedir("/var/run/quagga", 0755); /* Needed by Quagga */ makedir("/var/log/quagga", 0755); /* Needed by Quagga */ + makedir("/var/run/frr", 0755); /* Needed by FRR */ + makedir("/var/log/frr", 0755); /* Needed by FRR */ makedir("/var/run/sshd", 01755); /* OpenSSH */ gid = getgroup("tty"); From 51fe6a15e9730bfd6d730a066dbd5d363288a74a Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 15:27:33 +0100 Subject: [PATCH 15/26] test: Improve add/remove sub config test Signed-off-by: Magnus Malm --- test/test_add_remove_dynamic_service_sub_config/test.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/test_add_remove_dynamic_service_sub_config/test.sh b/test/test_add_remove_dynamic_service_sub_config/test.sh index a2cf9d85..97b82fc4 100755 --- a/test/test_add_remove_dynamic_service_sub_config/test.sh +++ b/test/test_add_remove_dynamic_service_sub_config/test.sh @@ -20,16 +20,14 @@ test_teardown() { # Test say "Test start $(date)" -# toggle_finit_debug + say "Add a dynamic service in $FINIT_CONF_DIR/service.conf" cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ -texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh pid:/run/service.pid' > $FINIT_CONF_DIR/service.conf" +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" say 'Reload Finit' texec kill -SIGHUP 1 -pause - retry 'assert_num_children 1 service.sh' 5 say 'Remove the dynamic service from /etc/finit.d/service.conf' From 25ffc3192f78551b7cc07b5eff0edb01586e6d21 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Tue, 23 Feb 2021 15:30:42 +0100 Subject: [PATCH 16/26] test: Add two new tests * test_start_stop_service Test that we can start/stop (via initctl) a service configured in finit.conf * test_start_stop_service_sub_config Test that we can start/stop (via initctl) a service configured in finit.d/ files Signed-off-by: Magnus Malm --- test/Makefile.am | 2 + test/test_start_stop_service/test.sh | 40 ++++++++++++++++++ .../test.sh | 41 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100755 test/test_start_stop_service/test.sh create mode 100755 test/test_start_stop_service_sub_config/test.sh diff --git a/test/Makefile.am b/test/Makefile.am index 20a60dca..bb72e0b7 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,6 +2,8 @@ AM_TESTS_ENVIRONMENT = make -C test_root/; TESTS = checkself.sh TESTS += test_add_remove_dynamic_service/test.sh TESTS += test_add_remove_dynamic_service_sub_config/test.sh +TESTS += test_start_stop_service/test.sh +TESTS += test_start_stop_service_sub_config/test.sh clean-local: make -C test_root/ clean diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh new file mode 100755 index 00000000..7d0c2c5d --- /dev/null +++ b/test/test_start_stop_service/test.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0")/.. +# shellcheck source=/dev/null +. "$TEST_DIR/lib.sh" + +assert_num_children() { + assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" +} + +test_teardown() { + say "Test done $(date)" + say "Running test teardown..." + + # Teardown + rm -f test_root/bin/service.sh +} + +# Test +say "Test start $(date)" +say 'Add service stanza in /etc/finit.conf' +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 1 service.sh' + +say 'Stop the service' +texec sh -c "initctl stop service.sh" + +retry 'assert_num_children 0 service.sh' + +say 'Start the service again' +texec sh -c "initctl start service.sh" + +retry 'assert_num_children 1 service.sh' diff --git a/test/test_start_stop_service_sub_config/test.sh b/test/test_start_stop_service_sub_config/test.sh new file mode 100755 index 00000000..87026274 --- /dev/null +++ b/test/test_start_stop_service_sub_config/test.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +set -eu + +TEST_DIR=$(dirname "$0")/.. +# shellcheck source=/dev/null +. "$TEST_DIR/lib.sh" + +assert_num_children() { + assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" +} + +test_teardown() { + say "Test done $(date)" + say "Running test teardown..." + + # Teardown + rm -f test_root/bin/service.sh +} + +# Test +say "Test start $(date)" +say 'Add service stanza in /etc/finit.d/service.conf' +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +# texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" +texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" + +say 'Reload Finit' +texec sh -c "initctl reload" + +retry 'assert_num_children 1 service.sh' + +say 'Stop the service' +texec sh -c "initctl stop service.sh" + +retry 'assert_num_children 0 service.sh' + +say 'Start the service again' +texec sh -c "initctl start service.sh" + +retry 'assert_num_children 1 service.sh' From 47dcd1cb8a98288f438128a5c3e5159d9f754d9b Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 23 Feb 2021 15:45:21 +0100 Subject: [PATCH 17/26] test: silence make in-out directory Signed-off-by: Joachim Wiberg --- test/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Makefile.am b/test/Makefile.am index bb72e0b7..b7542fd0 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,5 @@ -AM_TESTS_ENVIRONMENT = make -C test_root/; +AM_TESTS_ENVIRONMENT = make --silent --no-print-directory -C test_root/; + TESTS = checkself.sh TESTS += test_add_remove_dynamic_service/test.sh TESTS += test_add_remove_dynamic_service_sub_config/test.sh From f72e0a646e6fb11e584e03f193f39f81d331a603 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 23 Feb 2021 15:45:50 +0100 Subject: [PATCH 18/26] test: fix make warning 'etc' given more than once Signed-off-by: Joachim Wiberg --- test/test_root/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_root/Makefile b/test/test_root/Makefile index 0fa7304c..29dd7d68 100644 --- a/test/test_root/Makefile +++ b/test/test_root/Makefile @@ -35,8 +35,7 @@ dirs = ./dev \ ./sbin \ ./sys \ ./test_assets \ - ./tmp \ - ./etc + ./tmp _libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') libs = $(foreach path,$(_libs_src),.$(path)) From 8dc1ad00d5f39ee243c8e2774452da15a47f8aa0 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 23 Feb 2021 16:34:56 +0100 Subject: [PATCH 19/26] plugins: refactor, break out inotify watcher to src/iwatch.[ch] Signed-off-by: Joachim Wiberg --- plugins/pidfile.c | 154 +++++++++++++--------------------------------- src/Makefile.am | 1 + src/iwatch.c | 118 +++++++++++++++++++++++++++++++++++ src/iwatch.h | 64 +++++++++++++++++++ 4 files changed, 225 insertions(+), 112 deletions(-) create mode 100644 src/iwatch.c create mode 100644 src/iwatch.h diff --git a/plugins/pidfile.c b/plugins/pidfile.c index ad5b6241..753ba07a 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -25,8 +25,6 @@ #include #include #include -#include -#include #include "finit.h" #include "cond.h" @@ -34,28 +32,15 @@ #include "pid.h" #include "plugin.h" #include "service.h" - -struct iwatch_path { - TAILQ_ENTRY(iwatch_path) link; - char *path; - int wd; -}; - -struct iwatch { - int fd; - TAILQ_HEAD(, iwatch_path) iwp_list; -}; +#include "iwatch.h" static struct iwatch iw_pidfile; -static int iwatch_add(struct iwatch *iw, char *path) -{ - struct iwatch_path *iwp; - uint32_t mask = IN_ONLYDIR | IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY | IN_MOVED_TO; - char *ptr; - int wd; - _d("pidfile: Adding new watcher for path %s", path); +static int pidfile_add_path(struct iwatch *iw, char *path) +{ + char *ptr; + ptr = strstr(path, "run/"); if (ptr) { char *slash; @@ -66,44 +51,16 @@ static int iwatch_add(struct iwatch *iw, char *path) ptr = slash++; slash = strchr(ptr, '/'); if (slash) { - _d("pidfile: Path too deep, skipping."); + _d("Path too deep, skipping."); return -1; } } } - wd = inotify_add_watch(iw->fd, path, mask); - if (wd < 0) { - _pe("inotify_add_watch()"); - return -1; - } - - iwp = malloc(sizeof(struct iwatch_path)); - if (!iwp) { - _pe("Failed allocating new `struct iwatch_path`"); - inotify_rm_watch(iw->fd, wd); - return -1; - } - - iwp->path = path; - iwp->wd = wd; - TAILQ_INSERT_HEAD(&iw->iwp_list, iwp, link); - - return 0; + return iwatch_add(iw, path, IN_ONLYDIR); } -static int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp) -{ - _d("pidfile: Removing watcher for removed path %s", iwp->path); - TAILQ_REMOVE(&iw->iwp_list, iwp, link); - inotify_rm_watch(iw->fd, iwp->wd); - free(iwp->path); - free(iwp); - - return 0; -} - -static void update_conds(char *dir, char *name, uint32_t mask) +static void pidfile_update_conds(char *dir, char *name, uint32_t mask) { char cond[MAX_COND_LEN]; char fn[PATH_MAX]; @@ -114,11 +71,11 @@ static void update_conds(char *dir, char *name, uint32_t mask) svc = svc_find_by_pidfile(fn); if (!svc) { - _d("pidfile: No matching svc for %s", fn); + _d("No matching svc for %s", fn); return; } - _d("pidfile: Found svc %s for %s with pid %d", svc->name, fn, svc->pid); + _d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid); mkcond(svc, cond, sizeof(cond)); if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { @@ -138,7 +95,7 @@ static void update_conds(char *dir, char *name, uint32_t mask) } /* synthesize events in case of new run dirs */ -static void scan_for_pidfiles(struct iwatch *iw, char *dir, int len) +static void pidfile_scandir(struct iwatch *iw, char *dir, int len) { glob_t gl; size_t i; @@ -157,47 +114,33 @@ static void scan_for_pidfiles(struct iwatch *iw, char *dir, int len) for (i = 0; i < gl.gl_pathc; i++) { _d("scan found %s", gl.gl_pathv[i]); - update_conds(dir, gl.gl_pathv[i], IN_CREATE); + pidfile_update_conds(dir, gl.gl_pathv[i], IN_CREATE); } globfree(&gl); } -static void handle_dir(struct iwatch *iw, struct iwatch_path *iwp, char *name, int mask) +/* + * create/remove sub-directory in monitored directory + */ +static void pidfile_handle_dir(struct iwatch *iw, char *dir, char *name, int mask) { - char *path; - int plen; - int exist = 0; + char path[strlen(dir) + strlen(name) + 2]; + struct iwatch_path *iwp; - plen = snprintf(NULL, 0, "%s/%s", iwp->path, name) + 1; - path = malloc(plen); - if (!path) { - _pe("Failed allocating path buffer for watcher"); - return; - } - snprintf(path, plen, "%s/%s", iwp->path, name); - _d("pidfile: path is %s", path); + snprintf(path, sizeof(path), "%s/%s", dir, name); + _d("path: %s", path); - TAILQ_FOREACH(iwp, &iw->iwp_list, link) { - if (!strcmp(iwp->path, path)) { - exist = 1; - break; - } - } + iwp = iwatch_find_by_path(iw, path); if (mask & IN_CREATE) { - if (!exist) { - int rc = iwatch_add(iw, path); - - scan_for_pidfiles(iw, path, plen); - if (!rc) - return; + if (!iwp) { + pidfile_add_path(iw, path); + pidfile_scandir(iw, path, sizeof(path)); } } else if (mask & IN_DELETE) { - if (exist) + if (iwp) iwatch_del(&iw_pidfile, iwp); } - - free(path); } static void pidfile_callback(void *arg, int fd, int events) @@ -213,14 +156,14 @@ static void pidfile_callback(void *arg, int fd, int events) return; } - _d("pidfile: Entering ... reading %zu bytes into ev_buf[]", buflen - 1); + _d("Entering ... reading %zu bytes into ev_buf[]", buflen - 1); sz = read(fd, buf, buflen - 1); if (sz <= 0) { _pe("invalid inotify event"); goto done; } buf[sz] = 0; - _d("pidfile: Read %zd bytes, processing ...", sz); + _d("Read %zd bytes, processing ...", sz); off = 0; for (off = 0; off < sz; off += sizeof(*ev) + ev->len) { @@ -228,18 +171,17 @@ static void pidfile_callback(void *arg, int fd, int events) ev = (struct inotify_event *)&buf[off]; - _d("pidfile: path %s, event: 0x%08x", ev->name, ev->mask); + _d("path %s, event: 0x%08x", ev->name, ev->mask); if (!ev->mask) continue; /* Find base path for this event */ - TAILQ_FOREACH(iwp, &iw_pidfile.iwp_list, link) { - if (iwp->wd == ev->wd) - break; - } + iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd); + if (!iwp) + continue; if (ev->mask & IN_ISDIR) { - handle_dir(&iw_pidfile, iwp, ev->name, ev->mask); + pidfile_handle_dir(&iw_pidfile, iwp->path, ev->name, ev->mask); continue; } @@ -249,7 +191,7 @@ static void pidfile_callback(void *arg, int fd, int events) } if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) - update_conds(iwp->path, ev->name, ev->mask); + pidfile_update_conds(iwp->path, ev->name, ev->mask); } done: free(buf); @@ -305,14 +247,10 @@ static void pidfile_init(void *arg) return; } - TAILQ_INIT(&iw_pidfile.iwp_list); - if (iwatch_add(&iw_pidfile, path)) - close(iw_pidfile.fd); - _d("pidfile monitor active"); + if (pidfile_add_path(&iw_pidfile, path)) + iwatch_exit(&iw_pidfile); } -static struct iwatch iw_pidfile; - /* * When performing an `initctl reload` with one (unchanged) service * depending on, e.g. `net/iface/lo`, its condition will not be set @@ -340,27 +278,19 @@ static plugin_t plugin = { PLUGIN_INIT(plugin_init) { - iw_pidfile.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (iw_pidfile.fd < 0) { - _pe("inotify_init()"); - return; - } + int fd; - plugin.io.fd = iw_pidfile.fd; + fd = iwatch_init(&iw_pidfile); + if (fd < 0) + return; + + plugin.io.fd = fd; plugin_register(&plugin); } PLUGIN_EXIT(plugin_exit) { - struct iwatch_path *iwp, *tmp; - - TAILQ_FOREACH_SAFE(iwp, &iw_pidfile.iwp_list, link, tmp) { - inotify_rm_watch(iw_pidfile.fd, iwp->wd); - free(iwp->path); - free(iwp); - } - close(iw_pidfile.fd); - + iwatch_exit(&iw_pidfile); plugin_unregister(&plugin); } diff --git a/src/Makefile.am b/src/Makefile.am index 491efa6c..c3198a80 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,6 +26,7 @@ finit_SOURCES = api.c cgroup.c cgroup.h \ exec.c finit.c finit.h \ getty.c stty.c \ helpers.c helpers.h \ + iwatch.c iwatch.h \ log.c log.h \ mdadm.c mount.c \ pid.c pid.h \ diff --git a/src/iwatch.c b/src/iwatch.c new file mode 100644 index 00000000..41cc18fd --- /dev/null +++ b/src/iwatch.c @@ -0,0 +1,118 @@ +#include + +#include "finit.h" +#include "iwatch.h" +#include "log.h" + + +int iwatch_init(struct iwatch *iw) +{ + if (!iw) { + errno = EINVAL; + return -1; + } + + TAILQ_INIT(&iw->iwp_list); + + iw->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (iw->fd < 0) { + _pe("inotify_init()"); + return -1; + } + + return iw->fd; +} + +void iwatch_exit(struct iwatch *iw) +{ + struct iwatch_path *iwp, *tmp; + + TAILQ_FOREACH_SAFE(iwp, &iw->iwp_list, link, tmp) { + TAILQ_REMOVE(&iw->iwp_list, iwp, link); + inotify_rm_watch(iw->fd, iwp->wd); + free(iwp->path); + free(iwp); + } + + close(iw->fd); +} + +int iwatch_add(struct iwatch *iw, char *file, uint32_t mask) +{ + struct iwatch_path *iwp; + char *path; + int wd; + + _d("Adding new watcher for path %s", file); + + path = strdup(file); + if (!path) { + _pe("Out of memory"); + return -1; + } + + wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask); + if (wd < 0) { + _pe("inotify_add_watch(%s)", path); + free(path); + return -1; + } + + iwp = malloc(sizeof(struct iwatch_path)); + if (!iwp) { + _pe("Failed allocating new `struct iwatch_path`"); + inotify_rm_watch(iw->fd, wd); + free(path); + return -1; + } + + iwp->path = path; + iwp->wd = wd; + TAILQ_INSERT_HEAD(&iw->iwp_list, iwp, link); + + return 0; +} + +int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp) +{ + _d("Removing watcher for removed path %s", iwp->path); + + TAILQ_REMOVE(&iw->iwp_list, iwp, link); + inotify_rm_watch(iw->fd, iwp->wd); + free(iwp->path); + free(iwp); + + return 0; +} + +struct iwatch_path *iwatch_find_by_wd(struct iwatch *iw, int wd) +{ + struct iwatch_path *iwp; + + TAILQ_FOREACH(iwp, &iw->iwp_list, link) { + if (iwp->wd == wd) + return iwp; + } + + return NULL; +} + +struct iwatch_path *iwatch_find_by_path(struct iwatch *iw, const char *path) +{ + struct iwatch_path *iwp; + + TAILQ_FOREACH(iwp, &iw->iwp_list, link) { + if (!strcmp(iwp->path, path)) + return iwp; + } + + return NULL; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ + diff --git a/src/iwatch.h b/src/iwatch.h new file mode 100644 index 00000000..b59c3fb9 --- /dev/null +++ b/src/iwatch.h @@ -0,0 +1,64 @@ +/* inotify watcher for files or directories + * + * Copyright (c) 2015-2016 Tobias Waldekranz + * Copyright (c) 2016-2021 Joachim Wiberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef FINIT_IWATCH_H_ +#define FINIT_IWATCH_H_ + +#include +#include +#include + + +/* Monitors changes to both directories and files by default */ +#define IWATCH_MASK (IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE) + +struct iwatch_path { + TAILQ_ENTRY(iwatch_path) link; + char *path; + int wd; +}; + +struct iwatch { + int fd; + TAILQ_HEAD(, iwatch_path) iwp_list; +}; + + +int iwatch_init (struct iwatch *iw); +void iwatch_exit (struct iwatch *iw); + +int iwatch_add (struct iwatch *iw, char *file, uint32_t mask); +int iwatch_del (struct iwatch *iw, struct iwatch_path *iwp); + +struct iwatch_path *iwatch_find_by_wd (struct iwatch *iw, int wd); +struct iwatch_path *iwatch_find_by_path (struct iwatch *iw, const char *path); + +#endif /* FINIT_IWATCH_H_ */ + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ From dd6ebc3b65e5839d796a8f24a9b3ce79e8affc00 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 23 Feb 2021 20:26:57 +0100 Subject: [PATCH 20/26] Drop svc pidfile matching, only match against PID in pidfile With the redesign from to in d1fac6f we moved to matching svc_t only against their PID, which could pop up in any *.pid or */pid in /var/run. This patch drops the (hopefully) last remnants of the old legacy. To ensure we don't try reading the PID value from socket files, like /var/run/initctl, we add simple fnmatch() of the inotified file. Two calls to fnmatch(), for portability reasons, not every system has GNU libc extensions like FNM_EXTMATCH. Signed-off-by: Joachim Wiberg --- plugins/pidfile.c | 4 ++++ src/svc.c | 27 ++++----------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 753ba07a..88bfce66 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include #include #include #include @@ -69,6 +70,9 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask) _d("Got dir: %s, name: %s, mask: %08x", dir, name, mask); snprintf(fn, sizeof(fn), "%s/%s", dir, name); + if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0)) + return; + svc = svc_find_by_pidfile(fn); if (!svc) { _d("No matching svc for %s", fn); diff --git a/src/svc.c b/src/svc.c index 61b0efa7..b3eb29a0 100644 --- a/src/svc.c +++ b/src/svc.c @@ -395,7 +395,7 @@ svc_t *svc_find_by_nameid(char *name, char *id) } /** - * svc_find_by_plidfile - Find an service object by its PID file + * svc_find_by_plidfile - Find a service by its PID file * @fn: PID file, can be absolute path or relative to /run * * This function is primarily used by the pidfile plugin to track the @@ -412,32 +412,13 @@ svc_t *svc_find_by_nameid(char *name, char *id) svc_t *svc_find_by_pidfile(char *fn) { svc_t *svc, *iter = NULL; - char adjpath[strlen(fn) + 10]; /* + sizeof("/var/run/") */ - char fnpath[PATH_MAX]; + pid_t pid; - pid_runpath(fn, adjpath, sizeof(adjpath)); - if (!realpath(adjpath, fnpath)) { - if (errno != ENOENT) - _pe("adjpath: %s, errno %d", adjpath, errno); + pid = pid_file_read(fn); + if (pid == -1) return NULL; - } for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { - char path[PATH_MAX]; - char *pidfn; - pid_t pid; - - pidfn = pid_file(svc); - if (!pidfn || !realpath(pidfn, path)) { - if (errno != ENOENT) - _pe("pidfn: %s, errno %d", pidfn ?: "", errno); - continue; - } - - if (!string_compare(fnpath, path)) - continue; - - pid = pid_file_read(path); if (svc->pid != pid) continue; From e067f23ef2c351f3e25b5e42322ea6822a0d5b9e Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 24 Feb 2021 09:49:39 +0100 Subject: [PATCH 21/26] iwatch: add license+copyright header and initialized guard Signed-off-by: Joachim Wiberg --- src/iwatch.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/iwatch.c b/src/iwatch.c index 41cc18fd..48bedea1 100644 --- a/src/iwatch.c +++ b/src/iwatch.c @@ -1,9 +1,41 @@ +/* inotify watcher for files or directories + * + * Copyright (c) 2015-2016 Tobias Waldekranz + * Copyright (c) 2016-2021 Joachim Wiberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include #include "finit.h" #include "iwatch.h" #include "log.h" +/* + * iwatch is initialized and used mainly by the pidfile plugin, which is + * one of the cornerstones in the condition subsystem. Other parts of + * Finit may use iwatch too, like env: watchers, but are disabled if the + * pidfile plugin hasn't called iwatch_init() -- this is by design. + */ +static int initialized = 0; + int iwatch_init(struct iwatch *iw) { @@ -19,6 +51,7 @@ int iwatch_init(struct iwatch *iw) _pe("inotify_init()"); return -1; } + initialized = 1; return iw->fd; } @@ -35,6 +68,7 @@ void iwatch_exit(struct iwatch *iw) } close(iw->fd); + initialized = 0; } int iwatch_add(struct iwatch *iw, char *file, uint32_t mask) @@ -43,6 +77,9 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask) char *path; int wd; + if (!initialized) + return -1; + _d("Adding new watcher for path %s", file); path = strdup(file); @@ -75,6 +112,9 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask) int iwatch_del(struct iwatch *iw, struct iwatch_path *iwp) { + if (!initialized) + return -1; + _d("Removing watcher for removed path %s", iwp->path); TAILQ_REMOVE(&iw->iwp_list, iwp, link); @@ -89,6 +129,9 @@ struct iwatch_path *iwatch_find_by_wd(struct iwatch *iw, int wd) { struct iwatch_path *iwp; + if (!initialized) + return NULL; + TAILQ_FOREACH(iwp, &iw->iwp_list, link) { if (iwp->wd == wd) return iwp; @@ -101,6 +144,9 @@ struct iwatch_path *iwatch_find_by_path(struct iwatch *iw, const char *path) { struct iwatch_path *iwp; + if (!initialized) + return NULL; + TAILQ_FOREACH(iwp, &iw->iwp_list, link) { if (!strcmp(iwp->path, path)) return iwp; From bc4d4b7f35424ed11319481f90fb16d1288275f8 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 24 Feb 2021 09:52:34 +0100 Subject: [PATCH 22/26] plugins: pidfile: set up I/O callback on successful iwatch_init() In case of trouble, make sure we don't have a callback set up w/o a valid file descriptor. Signed-off-by: Joachim Wiberg --- plugins/pidfile.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 88bfce66..e2a55023 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -273,10 +273,6 @@ static plugin_t plugin = { .name = __FILE__, .hook[HOOK_BASEFS_UP] = { .cb = pidfile_init }, .hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf }, - .io = { - .cb = pidfile_callback, - .flags = PLUGIN_IO_READ, - }, .depends = { "netlink" }, }; @@ -289,6 +285,9 @@ PLUGIN_INIT(plugin_init) return; plugin.io.fd = fd; + plugin.io.cb = pidfile_callback; + plugin.io.flags = PLUGIN_IO_READ; + plugin_register(&plugin); } From f21409856036ec307f57dbb4752b44bbb72ae846 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Wed, 24 Feb 2021 13:13:38 +0100 Subject: [PATCH 23/26] test: Minor refactor to reduce test code duplication Signed-off-by: Magnus Malm --- test/lib.sh | 4 ++++ test/test_add_remove_dynamic_service/test.sh | 4 ---- test/test_add_remove_dynamic_service_sub_config/test.sh | 4 ---- test/test_start_stop_service/test.sh | 4 ---- test/test_start_stop_service_sub_config/test.sh | 4 ---- 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/test/lib.sh b/test/lib.sh index 82e6b1e2..fa38a54e 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -1,5 +1,9 @@ #!/bin/sh +assert_num_children() { + assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" +} + texec() { # shellcheck disable=SC2154 "$TEST_DIR/testenv_exec.sh" "$finit_pid" "$@" diff --git a/test/test_add_remove_dynamic_service/test.sh b/test/test_add_remove_dynamic_service/test.sh index 8637bc14..ed093131 100755 --- a/test/test_add_remove_dynamic_service/test.sh +++ b/test/test_add_remove_dynamic_service/test.sh @@ -6,10 +6,6 @@ TEST_DIR=$(dirname "$0")/.. # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" -assert_num_children() { - assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" -} - test_teardown() { say "Test done $(date)" say "Running test teardown..." diff --git a/test/test_add_remove_dynamic_service_sub_config/test.sh b/test/test_add_remove_dynamic_service_sub_config/test.sh index 97b82fc4..2e7196f4 100755 --- a/test/test_add_remove_dynamic_service_sub_config/test.sh +++ b/test/test_add_remove_dynamic_service_sub_config/test.sh @@ -6,10 +6,6 @@ TEST_DIR=$(dirname "$0")/.. # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" -assert_num_children() { - assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" -} - test_teardown() { say "Test done $(date)" say "Running test teardown..." diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh index 7d0c2c5d..e1e21c4c 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/test_start_stop_service/test.sh @@ -6,10 +6,6 @@ TEST_DIR=$(dirname "$0")/.. # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" -assert_num_children() { - assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" -} - test_teardown() { say "Test done $(date)" say "Running test teardown..." diff --git a/test/test_start_stop_service_sub_config/test.sh b/test/test_start_stop_service_sub_config/test.sh index 87026274..897592c4 100755 --- a/test/test_start_stop_service_sub_config/test.sh +++ b/test/test_start_stop_service_sub_config/test.sh @@ -6,10 +6,6 @@ TEST_DIR=$(dirname "$0")/.. # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" -assert_num_children() { - assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" -} - test_teardown() { say "Test done $(date)" say "Running test teardown..." From 2c055cb954b07b068499d60d6fe60afecf0235e7 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Wed, 24 Feb 2021 13:20:13 +0100 Subject: [PATCH 24/26] test: Minor adjustments to improve consistency between test code structure Signed-off-by: Magnus Malm --- test/lib.sh | 6 ++++++ test/test_add_remove_dynamic_service/test.sh | 13 ++++++++----- .../test.sh | 16 +++++++++------- test/test_start_stop_service/test.sh | 13 ++++++++----- test/test_start_stop_service_sub_config/test.sh | 14 ++++++++------ 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/test/lib.sh b/test/lib.sh index fa38a54e..52b0c227 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -4,6 +4,12 @@ assert_num_children() { assert "$1 services are running" "$(texec pgrep -P 1 "$2" | wc -l)" -eq "$1" } +remove_thing() { + THING="$(realpath "$1")" + say "Remove ${THING#*/test/*}" + rm -f "$THING" +} + texec() { # shellcheck disable=SC2154 "$TEST_DIR/testenv_exec.sh" "$finit_pid" "$@" diff --git a/test/test_add_remove_dynamic_service/test.sh b/test/test_add_remove_dynamic_service/test.sh index ed093131..50eb2235 100755 --- a/test/test_add_remove_dynamic_service/test.sh +++ b/test/test_add_remove_dynamic_service/test.sh @@ -3,21 +3,24 @@ set -eu TEST_DIR=$(dirname "$0")/.. +TESTENV_ROOT="$TEST_DIR"/test_root + # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" test_teardown() { say "Test done $(date)" - say "Running test teardown..." + say "Running test teardown." - # Teardown - rm -f test_root/bin/service.sh + remove_thing "$TESTENV_ROOT/$FINIT_CONF" + remove_thing "$TESTENV_ROOT"/test_assets/service.sh } -# Test say "Test start $(date)" -say 'Add a dynamic service in /etc/finit.conf' + cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ + +say "Add a dynamic service in $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" say 'Reload Finit' diff --git a/test/test_add_remove_dynamic_service_sub_config/test.sh b/test/test_add_remove_dynamic_service_sub_config/test.sh index 2e7196f4..0f756701 100755 --- a/test/test_add_remove_dynamic_service_sub_config/test.sh +++ b/test/test_add_remove_dynamic_service_sub_config/test.sh @@ -3,28 +3,30 @@ set -eu TEST_DIR=$(dirname "$0")/.. +TESTENV_ROOT="$TEST_DIR"/test_root + # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" test_teardown() { say "Test done $(date)" - say "Running test teardown..." + say "Running test teardown." - # Teardown - rm -f test_root/bin/service.sh + remove_thing "$TESTENV_ROOT/$FINIT_CONF_DIR/service.conf" + remove_thing "$TESTENV_ROOT"/test_assets/service.sh } -# Test say "Test start $(date)" -say "Add a dynamic service in $FINIT_CONF_DIR/service.conf" cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ + +say "Add a dynamic service in $FINIT_CONF_DIR/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" say 'Reload Finit' texec kill -SIGHUP 1 -retry 'assert_num_children 1 service.sh' 5 +retry 'assert_num_children 1 service.sh' say 'Remove the dynamic service from /etc/finit.d/service.conf' texec sh -c "echo > $FINIT_CONF_DIR/service.conf" @@ -32,4 +34,4 @@ texec sh -c "echo > $FINIT_CONF_DIR/service.conf" say 'Reload Finit' texec kill -SIGHUP 1 -retry 'assert_num_children 0 service.sh' 5 +retry 'assert_num_children 0 service.sh' diff --git a/test/test_start_stop_service/test.sh b/test/test_start_stop_service/test.sh index e1e21c4c..e55553d8 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/test_start_stop_service/test.sh @@ -3,21 +3,24 @@ set -eu TEST_DIR=$(dirname "$0")/.. +TESTENV_ROOT="$TEST_DIR"/test_root + # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" test_teardown() { say "Test done $(date)" - say "Running test teardown..." + say "Running test teardown." - # Teardown - rm -f test_root/bin/service.sh + remove_thing "$TESTENV_ROOT/$FINIT_CONF" + remove_thing "$TESTENV_ROOT"/test_assets/service.sh } -# Test say "Test start $(date)" -say 'Add service stanza in /etc/finit.conf' + cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ + +say "Add service stanza in $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" say 'Reload Finit' diff --git a/test/test_start_stop_service_sub_config/test.sh b/test/test_start_stop_service_sub_config/test.sh index 897592c4..cfc684fb 100755 --- a/test/test_start_stop_service_sub_config/test.sh +++ b/test/test_start_stop_service_sub_config/test.sh @@ -3,22 +3,24 @@ set -eu TEST_DIR=$(dirname "$0")/.. +TESTENV_ROOT="$TEST_DIR"/test_root + # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" test_teardown() { say "Test done $(date)" - say "Running test teardown..." + say "Running test teardown." - # Teardown - rm -f test_root/bin/service.sh + remove_thing "$TESTENV_ROOT/$FINIT_CONF_DIR/service.conf" + remove_thing "$TESTENV_ROOT"/test_assets/service.sh } -# Test say "Test start $(date)" -say 'Add service stanza in /etc/finit.d/service.conf' + cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ -# texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" + +say "Add service stanza in $FINIT_CONF_DIR/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" say 'Reload Finit' From e77f3f4d74eb668ffa888024e455740bda3c254b Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Wed, 24 Feb 2021 14:31:18 +0100 Subject: [PATCH 25/26] test: Use initctl in favor of kill -SIGHUP when reloading finit Signed-off-by: Magnus Malm --- test/test_add_remove_dynamic_service/test.sh | 4 ++-- test/test_add_remove_dynamic_service_sub_config/test.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_add_remove_dynamic_service/test.sh b/test/test_add_remove_dynamic_service/test.sh index 50eb2235..f109196d 100755 --- a/test/test_add_remove_dynamic_service/test.sh +++ b/test/test_add_remove_dynamic_service/test.sh @@ -24,7 +24,7 @@ say "Add a dynamic service in $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" say 'Reload Finit' -texec kill -SIGHUP 1 +texec sh -c "initctl reload" retry 'assert_num_children 1 service.sh' @@ -32,6 +32,6 @@ say 'Remove the dynamic service from /etc/finit.conf' texec sh -c "echo > $FINIT_CONF" say 'Reload Finit' -texec kill -SIGHUP 1 +texec sh -c "initctl reload" retry 'assert_num_children 0 service.sh' diff --git a/test/test_add_remove_dynamic_service_sub_config/test.sh b/test/test_add_remove_dynamic_service_sub_config/test.sh index 0f756701..a6c1f554 100755 --- a/test/test_add_remove_dynamic_service_sub_config/test.sh +++ b/test/test_add_remove_dynamic_service_sub_config/test.sh @@ -24,7 +24,7 @@ say "Add a dynamic service in $FINIT_CONF_DIR/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" say 'Reload Finit' -texec kill -SIGHUP 1 +texec sh -c "initctl reload" retry 'assert_num_children 1 service.sh' @@ -32,6 +32,6 @@ say 'Remove the dynamic service from /etc/finit.d/service.conf' texec sh -c "echo > $FINIT_CONF_DIR/service.conf" say 'Reload Finit' -texec kill -SIGHUP 1 +texec sh -c "initctl reload" retry 'assert_num_children 0 service.sh' From 76b2a846a25e3e0d299fd1d7b65fd0794e97eed0 Mon Sep 17 00:00:00 2001 From: Magnus Malm Date: Wed, 24 Feb 2021 14:54:45 +0100 Subject: [PATCH 26/26] test: Slightly improve names of test directories Signed-off-by: Magnus Malm --- test/Makefile.am | 14 +++++++------- .../test.sh | 4 ++-- .../test.sh | 4 ++-- .../test.sh | 4 ++-- .../test.sh | 4 ++-- test/{test_root => test-root}/.gitignore | 0 test/{test_root => test-root}/Makefile | 0 test/{test_root => test-root}/bin/chrootsetup.sh | 0 test/{test_root => test-root}/busybox-x86_64.md5 | 0 test/testenv_exec.sh | 2 +- test/testenv_start.sh | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) rename test/{test_add_remove_dynamic_service_sub_config => add-remove-dynamic-service-sub-config}/test.sh (88%) rename test/{test_add_remove_dynamic_service => add-remove-dynamic-service}/test.sh (87%) rename test/{test_start_stop_service_sub_config => start-stop-service-sub-config}/test.sh (88%) rename test/{test_start_stop_service => start-stop-service}/test.sh (88%) rename test/{test_root => test-root}/.gitignore (100%) rename test/{test_root => test-root}/Makefile (100%) rename test/{test_root => test-root}/bin/chrootsetup.sh (100%) rename test/{test_root => test-root}/busybox-x86_64.md5 (100%) diff --git a/test/Makefile.am b/test/Makefile.am index b7542fd0..dc090d99 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,10 +1,10 @@ -AM_TESTS_ENVIRONMENT = make --silent --no-print-directory -C test_root/; +AM_TESTS_ENVIRONMENT = make --silent --no-print-directory -C test-root/; -TESTS = checkself.sh -TESTS += test_add_remove_dynamic_service/test.sh -TESTS += test_add_remove_dynamic_service_sub_config/test.sh -TESTS += test_start_stop_service/test.sh -TESTS += test_start_stop_service_sub_config/test.sh +TESTS = checkself.sh +TESTS += add-remove-dynamic-service/test.sh +TESTS += add-remove-dynamic-service-sub-config/test.sh +TESTS += start-stop-service/test.sh +TESTS += start-stop-service-sub-config/test.sh clean-local: - make -C test_root/ clean + make -C test-root/ clean diff --git a/test/test_add_remove_dynamic_service_sub_config/test.sh b/test/add-remove-dynamic-service-sub-config/test.sh similarity index 88% rename from test/test_add_remove_dynamic_service_sub_config/test.sh rename to test/add-remove-dynamic-service-sub-config/test.sh index a6c1f554..b541ac9f 100755 --- a/test/test_add_remove_dynamic_service_sub_config/test.sh +++ b/test/add-remove-dynamic-service-sub-config/test.sh @@ -3,7 +3,7 @@ set -eu TEST_DIR=$(dirname "$0")/.. -TESTENV_ROOT="$TEST_DIR"/test_root +TESTENV_ROOT="$TEST_DIR"/test-root # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" @@ -18,7 +18,7 @@ test_teardown() { say "Test start $(date)" -cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test-root/test_assets/ say "Add a dynamic service in $FINIT_CONF_DIR/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" diff --git a/test/test_add_remove_dynamic_service/test.sh b/test/add-remove-dynamic-service/test.sh similarity index 87% rename from test/test_add_remove_dynamic_service/test.sh rename to test/add-remove-dynamic-service/test.sh index f109196d..3abf3614 100755 --- a/test/test_add_remove_dynamic_service/test.sh +++ b/test/add-remove-dynamic-service/test.sh @@ -3,7 +3,7 @@ set -eu TEST_DIR=$(dirname "$0")/.. -TESTENV_ROOT="$TEST_DIR"/test_root +TESTENV_ROOT="$TEST_DIR"/test-root # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" @@ -18,7 +18,7 @@ test_teardown() { say "Test start $(date)" -cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test-root/test_assets/ say "Add a dynamic service in $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" diff --git a/test/test_start_stop_service_sub_config/test.sh b/test/start-stop-service-sub-config/test.sh similarity index 88% rename from test/test_start_stop_service_sub_config/test.sh rename to test/start-stop-service-sub-config/test.sh index cfc684fb..f2819679 100755 --- a/test/test_start_stop_service_sub_config/test.sh +++ b/test/start-stop-service-sub-config/test.sh @@ -3,7 +3,7 @@ set -eu TEST_DIR=$(dirname "$0")/.. -TESTENV_ROOT="$TEST_DIR"/test_root +TESTENV_ROOT="$TEST_DIR"/test-root # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" @@ -18,7 +18,7 @@ test_teardown() { say "Test start $(date)" -cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test-root/test_assets/ say "Add service stanza in $FINIT_CONF_DIR/service.conf" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF_DIR/service.conf" diff --git a/test/test_start_stop_service/test.sh b/test/start-stop-service/test.sh similarity index 88% rename from test/test_start_stop_service/test.sh rename to test/start-stop-service/test.sh index e55553d8..0e64ce46 100755 --- a/test/test_start_stop_service/test.sh +++ b/test/start-stop-service/test.sh @@ -3,7 +3,7 @@ set -eu TEST_DIR=$(dirname "$0")/.. -TESTENV_ROOT="$TEST_DIR"/test_root +TESTENV_ROOT="$TEST_DIR"/test-root # shellcheck source=/dev/null . "$TEST_DIR/lib.sh" @@ -18,7 +18,7 @@ test_teardown() { say "Test start $(date)" -cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test_root/test_assets/ +cp "$TEST_DIR"/common/service.sh "$TEST_DIR"/test-root/test_assets/ say "Add service stanza in $FINIT_CONF" texec sh -c "echo 'service [2345] kill:20 log /test_assets/service.sh' > $FINIT_CONF" diff --git a/test/test_root/.gitignore b/test/test-root/.gitignore similarity index 100% rename from test/test_root/.gitignore rename to test/test-root/.gitignore diff --git a/test/test_root/Makefile b/test/test-root/Makefile similarity index 100% rename from test/test_root/Makefile rename to test/test-root/Makefile diff --git a/test/test_root/bin/chrootsetup.sh b/test/test-root/bin/chrootsetup.sh similarity index 100% rename from test/test_root/bin/chrootsetup.sh rename to test/test-root/bin/chrootsetup.sh diff --git a/test/test_root/busybox-x86_64.md5 b/test/test-root/busybox-x86_64.md5 similarity index 100% rename from test/test_root/busybox-x86_64.md5 rename to test/test-root/busybox-x86_64.md5 diff --git a/test/testenv_exec.sh b/test/testenv_exec.sh index 5243c142..b6306838 100755 --- a/test/testenv_exec.sh +++ b/test/testenv_exec.sh @@ -11,7 +11,7 @@ target="$1" shift TEST_DIR=$(dirname "$0") -TESTS_ROOT="$(pwd)/${TEST_DIR}/test_root" +TESTS_ROOT="$(pwd)/${TEST_DIR}/test-root" nsenter=$(command -v nsenter) chroot=$(command -v chroot) diff --git a/test/testenv_start.sh b/test/testenv_start.sh index 7dcf2869..7f0d97f0 100755 --- a/test/testenv_start.sh +++ b/test/testenv_start.sh @@ -3,7 +3,7 @@ set -eu TEST_DIR=$(dirname "$0") -TESTS_ROOT="$(pwd)/${TEST_DIR}/test_root" +TESTS_ROOT="$(pwd)/${TEST_DIR}/test-root" unshare=$(command -v unshare) chroot=$(command -v chroot)