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, diff --git a/src/initctl.c b/src/initctl.c index 59d86fb5..87192067 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; @@ -638,13 +639,33 @@ 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" "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" @@ -654,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" @@ -680,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, @@ -738,6 +774,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 +785,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 a07dd7e2..130ba0c0 100644 --- a/src/serv.c +++ b/src/serv.c @@ -32,20 +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) { @@ -69,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; @@ -105,97 +91,155 @@ 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); 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; + else + paste(path, len, FINIT_RCSD, name); + } else + strlcat(path, name, len); + + return path; +} + 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) { - 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; } @@ -209,28 +253,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) - return serv_list(NULL); + 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("available"); + } /* 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)) @@ -243,12 +278,45 @@ 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) { - 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); } /**