From 2fd4418371a42be654cc4e9a3962dafa7111ad29 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 17:02:37 +0100 Subject: [PATCH 1/9] initctl: add support for reading config to 'create' from stdin Signed-off-by: Joachim Wiberg --- src/serv.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/serv.c b/src/serv.c index a07dd7e2..139c90ab 100644 --- a/src/serv.c +++ b/src/serv.c @@ -120,6 +120,43 @@ int serv_list(char *arg) return 0; } +/* + * Return path to configuration file for 'name', relative to FINIT_RCSD. + * This may be any of the following, provided sysconfdir is /etc: + * + * - /etc/finit.d/$name.conf + * - /etc/finit.d/available/$name.conf + * + * The system *may* have a /etc/finit.d/available/ directory, or it may + * just use a plain /etc/finit.d/ -- we do not set policy. + * + * If the resulting file doesn't exist, and creat is not set, *or* + * the base directory doesn't exist, we return NULL. +.*/ +static char *conf(char *path, size_t len, char *name, int creat) +{ + char corr[40]; + + if (!strstr(name, ".conf")) { + snprintf(corr, sizeof(corr), "%s.conf", name); + name = corr; + } + + if (!fisdir(FINIT_RCSD)) + return NULL; + + paste(path, len, FINIT_RCSD, "available/"); + if (!fisdir(path)) { + if (creat && mkdir(path, 0755) && errno != EEXIST) + return NULL; + + paste(path, len, FINIT_RCSD, name); + } else + strlcat(path, name, len); + + return path; +} + int serv_enable(char *arg) { char corr[40]; @@ -248,7 +285,35 @@ int serv_edit(char *arg) int serv_creat(char *arg) { - return do_edit(arg, 1); + char buf[256]; + char *fn; + FILE *fp; + + if (!arg || !arg[0]) + errx(1, "missing argument to create"); + + /* Input from a pipe or a proper TTY? */ + if (isatty(STDIN_FILENO)) + return do_edit(arg, 1); + + /* Open fn for writing from pipe */ + fn = conf(buf, sizeof(buf), arg, 1); + if (!fn) + err(1, "failed creating conf %s", arg); + + if (!icreate && fexist(fn)) { + warnx("%s already exists, skipping (use -c to override)", fn); + fn = "/dev/null"; + } + + fp = fopen(fn, "w"); + if (!fp) + err(1, "failed opening %s for writing", fn); + + while (fgets(buf, sizeof(buf), stdin)) + fputs(buf, fp); + + return fclose(fp); } /** From 689e6cd52aaa1a4aaaa8d25dafaf64215092cb40 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 17:04:46 +0100 Subject: [PATCH 2/9] initctl: add -c create flag, to force creation at edit Signed-off-by: Joachim Wiberg --- src/initctl.c | 9 ++++++++- src/serv.c | 33 +++++++++++++++------------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/initctl.c b/src/initctl.c index 59d86fb5..4b15a959 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -48,6 +48,7 @@ struct command { int (*cb)(char *arg); }; +int icreate = 0; int heading = 1; int numeric = 0; int verbose = 0; @@ -645,6 +646,7 @@ static int usage(int rc) "\n" "Options:\n" " -b, --batch Batch mode, no screen size probing\n" + " -c, --create Create missing paths (and files) as needed\n" " -n, --numeric Show JOB:ID instead of NAME:ID\n" " -t, --no-heading Skip table headings\n" " -v, --verbose Verbose output\n" @@ -738,6 +740,7 @@ int main(int argc, char *argv[]) }; struct option long_options[] = { { "batch", 0, NULL, 'b' }, + { "create", 0, NULL, 'c' }, { "help", 0, NULL, 'h' }, { "numeric", 0, NULL, 'n' }, { "no-heading", 0, NULL, 't' }, @@ -748,12 +751,16 @@ int main(int argc, char *argv[]) if (transform(progname(argv[0]))) return reboot_main(argc, argv); - while ((c = getopt_long(argc, argv, "bh?ntv", long_options, NULL)) != EOF) { + while ((c = getopt_long(argc, argv, "bch?ntv", long_options, NULL)) != EOF) { switch(c) { case 'b': interactive = 0; break; + case 'c': + icreate = 1; + break; + case 'h': case '?': return usage(0); diff --git a/src/serv.c b/src/serv.c index 139c90ab..a85d5304 100644 --- a/src/serv.c +++ b/src/serv.c @@ -35,6 +35,7 @@ static const char *cwd; static const char *available = FINIT_RCSD "/available"; static const char *enabled = FINIT_RCSD "/enabled"; +extern int icreate; /* initctl -c */ static void pushd(const char *dir) { @@ -246,28 +247,19 @@ static int do_edit(char *arg, int creat) "mg", "vi" }; - char corr[40]; char path[256]; + char *fn; - if (!arg || !arg[0]) - return serv_list(NULL); - - if (!strstr(arg, ".conf")) { - snprintf(corr, sizeof(corr), "%s.conf", arg); - arg = corr; - } - - pushd(FINIT_RCSD); - if (mkdir("available", 0755) && EEXIST != errno) - err(1, "Failed creating %s/available directory", FINIT_RCSD); - - snprintf(path, sizeof(path), "%s/%s", available, arg); - if (!fexist(path)) { - if (!creat) + fn = conf(path, sizeof(path), arg, creat); + if (!fexist(fn)) { + if (!creat) { + warnx("Cannot find %s, use create command, or select one of:", arg); return serv_list(NULL); + } /* XXX: fill with template/commented-out examples */ - } + } else if (creat) + warnx("the file %s already exists, falling back to edit.", fn); for (size_t i = 0; i < NELEMS(editor); i++) { if (systemf("%s %s 2>/dev/null", editor[i], path)) @@ -280,7 +272,12 @@ static int do_edit(char *arg, int creat) int serv_edit(char *arg) { - return do_edit(arg, 0); + if (!arg || !arg[0]) { + warnx("missing argument to edit, may be one of:"); + return serv_list("available"); + } + + return do_edit(arg, icreate); } int serv_creat(char *arg) From e9ed90cb34e45bca60a95f25a9f97d7619a358f7 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 19:21:41 +0100 Subject: [PATCH 3/9] initctl: let touch command handle both rcsd and available dirs Signed-off-by: Joachim Wiberg --- src/serv.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/serv.c b/src/serv.c index a85d5304..c90dafad 100644 --- a/src/serv.c +++ b/src/serv.c @@ -213,27 +213,26 @@ int serv_disable(char *arg) int serv_touch(char *arg) { - char corr[40]; + char path[256]; + char *fn; - if (!arg || !arg[0]) - return serv_list(NULL); - - if (!strstr(arg, ".conf")) { - snprintf(corr, sizeof(corr), "%s.conf", arg); - arg = corr; + if (!arg || !arg[0]) { + warnx("missing argument to touch, may be one of:"); + return serv_list("enabled"); } - pushd(FINIT_RCSD); - if (!fexist(arg)) { - popd(); + fn = conf(path, sizeof(path), arg, 0); + if (!fexist(fn)) { if (!strstr(arg, "finit.conf")) - errx(1, "Service %s is not enabled", arg); - arg = FINIT_CONF; + errx(1, "%s not available.", arg); + + strlcpy(path, FINIT_CONF, sizeof(path)); + fn = path; } /* libite:touch() follows symlinks */ - if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW)) - err(1, "Failed marking %s for reload", arg); + if (utimensat(AT_FDCWD, fn, NULL, AT_SYMLINK_NOFOLLOW)) + err(1, "failed marking %s for reload", fn); return 0; } From 83cdb884d93d92dcc4e8f12b5fa34524fb3dfae3 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 19:22:50 +0100 Subject: [PATCH 4/9] initctl: extend semantics for enable/disable cmd A system may, or may not, have the available/ and enabled/ subdirectories in /etc/finit.d. This is up to distros but we support the following, example uses /etc/finit.d as the FINIT_RCSD base directory: 1. No subdirectories - enable: nop - disable: nop 2. Only available/ - enable: checks if there's a service in available/ then symlinks to /etc/finit.d/ - disable: checks if service in /etc/finit.d is a symlink, then removes it 3. Both available/ and enabled/ - enable: relative symlink in enabled/ is created if service is available in available/ - disable: symlink in enabled/ is removed if exists Signed-off-by: Joachim Wiberg --- src/serv.c | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/serv.c b/src/serv.c index c90dafad..0eb36391 100644 --- a/src/serv.c +++ b/src/serv.c @@ -161,54 +161,62 @@ static char *conf(char *path, size_t len, char *name, int creat) int serv_enable(char *arg) { char corr[40]; - char link[256]; char path[256]; + int ena; - if (!arg || !arg[0]) - return serv_list(NULL); + if (!arg || !arg[0]) { + warnx("missing argument to enable, may be one of:"); + return serv_list("available"); + } if (!strstr(arg, ".conf")) { snprintf(corr, sizeof(corr), "%s.conf", arg); arg = corr; } - pushd(FINIT_RCSD); - if (mkdir("enabled", 0755) && EEXIST != errno) - err(1, "Failed creating %s/enabled directory", FINIT_RCSD); + if (chdir(FINIT_RCSD)) + err(1, "failed cd %s", FINIT_RCSD); - snprintf(path, sizeof(path), "%s/%s", available, arg); + if (icreate && mkdir("enabled", 0755) && EEXIST != errno) + err(1, "failed creating %s/enabled directory", FINIT_RCSD); + ena = !chdir("enabled"); /* System *may* have enabled/ dir. */ + + snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg); if (!fexist(path)) - errx(1, "Cannot find %s", path); + errx(1, "cannot find %s", conf(path, sizeof(path), arg, 0)); - snprintf(link, sizeof(link), "%s/%s", enabled, arg); - if (fexist(link)) - errx(1, "%s already exists", link); + if (fexist(arg)) + errx(1, "%s already enabled", arg); - return symlink(path, link) != 0; + return symlink(path, arg) != 0; } int serv_disable(char *arg) { - char corr[40]; - char link[256]; struct stat st; + char corr[40]; - if (!arg || !arg[0]) - return serv_list(NULL); + if (!arg || !arg[0]) { + warnx("missing argument to disable, may be one of:"); + return serv_list("enabled"); + } if (!strstr(arg, ".conf")) { snprintf(corr, sizeof(corr), "%s.conf", arg); arg = corr; } - snprintf(link, sizeof(link), "%s/%s", enabled, arg); - if (stat(link, &st)) - err(1, "Cannot find %s", link); + if (chdir(FINIT_RCSD)) + err(1, "failed cd %s", FINIT_RCSD); + chdir("enabled"); /* System *may* have enabled/ dir. */ + + if (stat(arg, &st)) + errx(1, "%s not (an) enabled (service).", arg); if ((st.st_mode & S_IFMT) == S_IFLNK) - errx(1, "%s is not a symlink, move manually to %s first", link, available); + errx(1, "cannot disable %s, not a symlink.", arg); - return remove(link) != 0; + return remove(arg) != 0; } int serv_touch(char *arg) From c7d189f199367113d186c8857aa1843971ea0f58 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 19:27:22 +0100 Subject: [PATCH 5/9] initctl: refactor list command slightly Signed-off-by: Joachim Wiberg --- src/serv.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/serv.c b/src/serv.c index 0eb36391..1e87af72 100644 --- a/src/serv.c +++ b/src/serv.c @@ -32,21 +32,8 @@ #include "util.h" -static const char *cwd; -static const char *available = FINIT_RCSD "/available"; -static const char *enabled = FINIT_RCSD "/enabled"; extern int icreate; /* initctl -c */ -static void pushd(const char *dir) -{ - cwd = get_current_dir_name(); - chdir(dir); -} - -static void popd(void) -{ - chdir(cwd); -} static int calc_width(char *arr[], size_t len) { @@ -70,11 +57,9 @@ static void do_list(const char *path) glob_t gl; size_t i; - pushd(path); - if (glob("*.conf", 0, NULL, &gl)) { - chdir(cwd); + chdir(path); + if (glob("*.conf", 0, NULL, &gl)) return; - } if (gl.gl_pathc <= 0) goto done; @@ -106,15 +91,29 @@ static void do_list(const char *path) done: globfree(&gl); - popd(); } int serv_list(char *arg) { - if (fisdir(available)) - do_list(available); - if (fisdir(enabled)) - do_list(enabled); + char path[256]; + + if (arg && arg[0]) { + paste(path, sizeof(path), FINIT_RCSD, arg); + if (fisdir(path)) { + do_list(path); + return 0; + } + /* fall back to list all */ + } + + paste(path, sizeof(path), FINIT_RCSD, "available"); + if (fisdir(path)) + do_list(path); + + paste(path, sizeof(path), FINIT_RCSD, "enabled"); + if (fisdir(path)) + do_list(path); + if (fisdir(FINIT_RCSD)) do_list(FINIT_RCSD); From f9532d11ba68bf7484b58ba79e1a2d2249c96db3 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 19:27:58 +0100 Subject: [PATCH 6/9] initctl: only show enable/disable commands on compat systems The enable/disable commands require either both the available/ and enabled/ subdirectories in /etc/finit.d, or at least available/ Signed-off-by: Joachim Wiberg --- src/initctl.c | 52 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/initctl.c b/src/initctl.c index 4b15a959..87192067 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -639,8 +639,27 @@ static int transform(char *nm) return 0; } +static int has_conf(char *path, size_t len, char *name) +{ + paste(path, len, FINIT_RCSD, name); + if (!fisdir(path)) { + strlcpy(path, FINIT_RCSD, len); + return 0; + } + + return 1; +} + static int usage(int rc) { + int has_rcsd = fisdir(FINIT_RCSD); + int has_ena; + char avail[256]; + char ena[256]; + + has_conf(avail, sizeof(avail), "available"); + has_ena = has_conf(ena, sizeof(ena), "enabled"); + fprintf(stderr, "Usage: %s [OPTIONS] [COMMAND]\n" "\n" @@ -656,15 +675,30 @@ static int usage(int rc) " debug Toggle Finit (daemon) debug\n" " help This help text\n" " version Show Finit version\n" - "\n" - " ls | list List all .conf in /etc/finit.d/\n" - " create Create .conf in /etc/finit.d/available/\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" + "\n", prognm); + + if (has_rcsd) + fprintf(stderr, + " ls | list List all .conf in " FINIT_RCSD "\n" + " create Create .conf in %s\n" + " edit Edit .conf in %s\n" + " touch Change .conf in %s\n", + avail, avail, avail); + if (has_ena) + fprintf(stderr, + " enable Enable .conf in %s\n", avail); + if (has_ena) + fprintf(stderr, + " disable Disable .conf in %s\n", ena); + if (has_rcsd) + fprintf(stderr, + " reload Reload *.conf in " FINIT_RCSD " (activate changes)\n"); + else + fprintf(stderr, + " reload Reload " FINIT_CONF " (activate changes)\n"); + + fprintf(stderr, // " reload [:ID] Reload (SIGHUP) service by job# or name\n" - " edit Edit .conf in /etc/finit.d/available/\n" "\n" " cond show Show condition status\n" " cond dump Dump all conditions and their status\n" @@ -682,7 +716,7 @@ static int usage(int rc) " reboot Reboot system\n" " halt Halt system\n" " poweroff Halt and power off system\n" - " suspend Suspend system\n", prognm); + " suspend Suspend system\n"); if (has_utmp()) fprintf(stderr, From aa998583a781936b3ce96d301e83e9114a3d711d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 19:29:38 +0100 Subject: [PATCH 7/9] Update example ASCII graphic a bit, add log and env commands Signed-off-by: Joachim Wiberg --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5b5a9053..c8eb6f7c 100644 --- a/README.md +++ b/README.md @@ -101,14 +101,16 @@ in full in [doc/config.md](doc/config.md). Here's a quick overview of some of the most common components needed to start a UNIX daemon: ``` -service [LVLS] /path/to/daemon ARGS -- Some text -^ ^ ^ ^ ^ ^ -| | | | | `-- Optional description -| | | | `---------- Daemon arguments -| | | `-------------------------- Path to daemon -| | `--------------------------------- Optional conditions -| `---------------------------------------- Optional Runlevels - `------------------------------------------------ Monitored application +service [LVLS] log env:[-]/etc/default/daemon daemon ARGS -- Daemon daemon +^ ^ ^ ^ ^ ^ ^ ^ +| | | | | | | `-- Optional description +| | | | | | `----------- Daemon arguments +| | | | | `-------------------------- Path to daemon +| | | | `---------------------------------------------------- Optional env. file +| | | `-------------------------------------------------------- Redirect output to log +| | `--------------------------------------------------------------- Optional conditions +| `---------------------------------------------------------------------- Optional Runlevels + `------------------------------------------------------------------------------ Monitored application ``` Some components are optional: runlevel(s), condition(s) and description, From 0d52583624407a93fc704198a04d3c46406495b1 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 20:13:32 +0100 Subject: [PATCH 8/9] initctl: return correct path if -c is given Signed-off-by: Joachim Wiberg --- src/serv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serv.c b/src/serv.c index 1e87af72..0b6eeb41 100644 --- a/src/serv.c +++ b/src/serv.c @@ -149,8 +149,8 @@ static char *conf(char *path, size_t len, char *name, int creat) if (!fisdir(path)) { if (creat && mkdir(path, 0755) && errno != EEXIST) return NULL; - - paste(path, len, FINIT_RCSD, name); + else + paste(path, len, FINIT_RCSD, name); } else strlcat(path, name, len); From 510bd1ac0b611b2093d956bc46b99266411ba8b2 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 27 Feb 2021 20:13:57 +0100 Subject: [PATCH 9/9] initctl: if edit cannot find the file, list available ones Signed-off-by: Joachim Wiberg --- src/serv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serv.c b/src/serv.c index 0b6eeb41..130ba0c0 100644 --- a/src/serv.c +++ b/src/serv.c @@ -260,7 +260,7 @@ static int do_edit(char *arg, int creat) if (!fexist(fn)) { if (!creat) { warnx("Cannot find %s, use create command, or select one of:", arg); - return serv_list(NULL); + return serv_list("available"); } /* XXX: fill with template/commented-out examples */