From 577622c9452fc07198898339eba084616509c311 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sun, 21 Jun 2015 15:39:16 +0200 Subject: [PATCH] Add support for calling initctl with process name as well as JOBID This patch makes it possible to communicate process name or JOB[:ID] from initctl to Finit. When the process name translates into a service with multiple instances Finit applies the requested command to all instances. For example, two Dropbear SSH services running on two different ports, calling `initctl stop dropbear` stops both services. Signed-off-by: Joachim Nilsson --- api.c | 69 ++++++++++++++++++----------------- helpers.c | 37 +++++++++++++++++++ helpers.h | 2 ++ initctl.c | 105 +++++++++++++++++++++++++++++------------------------- service.c | 25 ++----------- svc.c | 34 ++++++++++++++++-- svc.h | 1 + 7 files changed, 166 insertions(+), 107 deletions(-) diff --git a/api.c b/api.c index 29108063..bfd540d8 100644 --- a/api.c +++ b/api.c @@ -41,45 +41,37 @@ uev_t api_watcher; -/* Make sure the job id is NUL terminated and on the form SVC:ID. */ -static char *sanitize_jobid(char *jobid, size_t len) +/* Sanitize user input, make sure to NUL terminate. */ +static char *sanitize(char *arg, size_t len) { size_t i = 0; - static char buf[42]; - while (isdigit(jobid[i]) && i < len) + while ((isalnum(arg[i]) || isspace(arg[i])) && i < len) i++; - if (jobid[i++] == ':') { - while (isdigit(jobid[i]) && i < len) - i++; - } - - if (i + 1 < sizeof(buf)) { - memcpy(buf, jobid, i); - buf[i + 1] = 0; - - return buf; + if (i + 1 < len) { + arg[i + 1] = 0; + return arg; } return NULL; } -static svc_t *convert_jobid(int noid, char *jobid, size_t len) +static svc_t *convert(int noid, char *arg, size_t len) { int job, id = 1; char *token, *ptr; - jobid = sanitize_jobid(jobid, len); - if (!jobid) + arg = sanitize(arg, len); + if (!arg) return NULL; - if (noid && (ptr = strchr(jobid, ':'))) + if (noid && (ptr = strchr(arg, ':'))) ptr = 0; - token = strtok(jobid, ":"); + token = strtok(arg, ":"); if (!token) { - job = atoi(jobid); + job = atoi(arg); } else { job = atoi(token); token = strtok(NULL, ""); @@ -91,31 +83,51 @@ static svc_t *convert_jobid(int noid, char *jobid, size_t len) return svc_find_by_jobid(job, id); } +static int call(int (*action)(svc_t *), char *buf, size_t len) +{ + int result = 0; + char *job = strtok(sanitize(buf, len), " "); + + while (job) { + svc_t *svc; + + if (isdigit(job[0])) + result += action(convert(0, job, len)); + else + for (svc = svc_named_iterator(1, job); svc; svc = svc_named_iterator(0, job)) + result += action(svc); + + job = strtok(NULL, " "); + } + + return result; +} + static int do_start(char *buf, size_t len) { - return service_start(convert_jobid(0, buf, len)); + return call(service_start, buf, len); } static int do_stop(char *buf, size_t len) { - return service_stop(convert_jobid(0, buf, len)); + return call(service_stop, buf, len); } static int do_reload(char *buf, size_t len) { - return service_reload(convert_jobid(0, buf, len)); + return call(service_reload, buf, len); } static int do_restart(char *buf, size_t len) { - return service_restart(convert_jobid(0, buf, len)); + return call(service_restart, buf, len); } static int do_query_inetd(char *buf, size_t len) { svc_t *svc; - svc = convert_jobid(1, buf, len); + svc = convert(1, buf, len); if (!svc || !svc_is_inetd(svc)) { _e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", buf); return 1; @@ -152,8 +164,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) _e("Failed reading initctl request, error %d: %s", errno, strerror(errno)); } - _d("Nothing to do, bailing out."); - break; } @@ -237,11 +247,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) len = write(sd, &rq, sizeof(rq)); if (len != sizeof(rq)) _d("Failed sending ACK/NACK back to client."); -#if 0 - else - sleep(1); /* Give client time to read FIFO */ - _e("Ready for next station."); -#endif } leave: diff --git a/helpers.c b/helpers.c index 2c1b8d61..bf4fba6b 100644 --- a/helpers.c +++ b/helpers.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "finit.h" #include "helpers.h" @@ -122,6 +123,42 @@ int atonum(char *str) return val; } +static int encode(int lvl) +{ + if (!lvl) return 0; + return lvl + '0'; +} + +void runlevel_set(int pre, int now) +{ + struct utmp utent; + + utent.ut_type = RUN_LVL; + utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF); + strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user)); + + setutent(); + pututline(&utent); + endutent(); +} + +int runlevel_get(void) +{ + int lvl = '?'; /* Non-existing runlevel */ + struct utmp *utent; + + setutent(); + while ((utent = getutent())) { + if (utent->ut_type == RUN_LVL) { + lvl = utent->ut_pid & 0xFF; + break; + } + } + endutent(); + + return lvl - '0'; +} + /** * runlevel_string - Convert a bit encoded runlevel to .conf syntax * @levels: Bit encoded runlevels diff --git a/helpers.h b/helpers.h index 6887e7ce..122b9c92 100644 --- a/helpers.h +++ b/helpers.h @@ -69,6 +69,8 @@ int makepath (char *path); void ifconfig (char *ifname, char *addr, char *mask, int up); int atonum (char *str); +void runlevel_set (int pre, int now); +int runlevel_get (void); char *runlevel_string (int levels); pid_t pidfile_read (char *pidfile); diff --git a/initctl.c b/initctl.c index 3af26293..e4763d16 100644 --- a/initctl.c +++ b/initctl.c @@ -38,9 +38,9 @@ typedef struct { int (*cb)(char *arg); } command_t; -int debug = 0; -int verbose = 0; - +int debug = 0; +int verbose = 0; +int runlevel = 0; static int do_send(struct init_request *rq, ssize_t len) { @@ -93,14 +93,14 @@ static int set_runlevel(char *arg) return do_send(&rq, sizeof(rq)); } -static int do_svc(int cmd, char *jobid) +static int do_svc(int cmd, char *arg) { struct init_request rq = { .magic = INIT_MAGIC, .cmd = cmd, }; - if (!jobid) { + if (!arg) { if (cmd == INIT_CMD_RELOAD_SVC) { rq.cmd = INIT_CMD_RELOAD; goto exit; @@ -108,7 +108,7 @@ static int do_svc(int cmd, char *jobid) return 1; } - strlcpy(rq.data, jobid, sizeof(rq.data)); + strlcpy(rq.data, arg, sizeof(rq.data)); exit: return do_send(&rq, sizeof(rq)); @@ -132,57 +132,59 @@ static int show_status(char *UNUSED(arg)) { svc_t *svc; + /* Fetch UTMP runlevel, needed for svc_status() call below */ + runlevel = runlevel_get(); + if (!verbose) { - printf("# Status PID Runlevels Service Description\n"); + printf("# Status PID Runlevels Service Description\n"); printf("====================================================================================\n"); } for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { int inetd = svc_is_inetd(svc); - char jobid[10]; + char jobid[10], args[80] = ""; + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_QUERY_INETD, + }; if (svc_is_unique(svc)) snprintf(jobid, sizeof(jobid), "%d", svc->job); else snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id); - if (verbose) { - int internal = !strcmp("internal", svc->cmd); - char *info, args[80] = ""; - struct init_request rq = { - .magic = INIT_MAGIC, - .cmd = INIT_CMD_QUERY_INETD, - }; + printf("%-5s %7s %-6d %-9s ", jobid, svc_status(svc), svc->pid, runlevel_string(svc->runlevels)); + if (!verbose) { + printf("%-20s %s\n", svc->cmd, svc->desc); + continue; + } - printf("%-6s %7s %-6d %-9s ", jobid, svc_status(svc), svc->pid, runlevel_string(svc->runlevels)); - if (inetd) { - snprintf(rq.data, sizeof(rq.data), "%s", jobid); - if (do_send(&rq, sizeof(rq))) { - snprintf(args, sizeof(args), "Unknown inetd"); - info = args; - } else { - info = rq.data; - if (!internal) { - char *ptr = strchr(info, ' '); - if (ptr) - info = ptr + 1; - } - } + if (inetd) { + char *info; - printf("%s %s\n", svc->cmd, info); + snprintf(rq.data, sizeof(rq.data), "%s", jobid); + if (do_send(&rq, sizeof(rq))) { + snprintf(args, sizeof(args), "Unknown inetd"); + info = args; } else { - int i; - - for (i = 1; i < MAX_NUM_SVC_ARGS; i++) { - strlcat(args, svc->args[i], sizeof(args)); - strlcat(args, " ", sizeof(args)); + info = rq.data; + if (strcmp("internal", svc->cmd)) { + char *ptr = strchr(info, ' '); + if (ptr) + info = ptr + 1; } - - printf("%s %s\n", svc->cmd, args); } + + printf("%s %s\n", svc->cmd, info); } else { - printf("%-6s %7s %-6d %-9s %-20s %s\n", jobid, svc_status(svc), svc->pid, - runlevel_string(svc->runlevels), svc->cmd, svc->desc); + int i; + + for (i = 1; i < MAX_NUM_SVC_ARGS; i++) { + strlcat(args, svc->args[i], sizeof(args)); + strlcat(args, " ", sizeof(args)); + } + + printf("%s %s\n", svc->cmd, args); } } @@ -196,15 +198,15 @@ static int usage(int rc) " -v, --verbose Verbose output\n" " -h, --help This help text\n\n" "Commands:\n" - " debug Toggle Finit debug\n" - " reload Reload *.conf in /etc/finit.d/\n" - " runlevel <0-9> Change runlevel: 0 halt, 6 reboot\n" - " status Show status of services\n" - " start Start stopped service\n" - " stop Stop running service\n" - " restart Restart (stop/start) running service\n" - " reload Reload (SIGHUP) running service\n" - " version Show Finit version\n\n", __progname); + " debug Toggle Finit debug\n" + " reload Reload *.conf in /etc/finit.d/\n" + " runlevel <0-9> Change runlevel: 0 halt, 6 reboot\n" + " status Show status of services\n" + " start Start stopped job or service\n" + " stop Stop running job or service\n" + " restart Restart (stop/start) job or service\n" + " reload Reload (SIGHUP) job or service\n" + " version Show Finit version\n\n", __progname); return rc; } @@ -244,7 +246,12 @@ int main(int argc, char *argv[]) if (optind < argc) { char *cmd = argv[optind++]; - char *arg = optind < argc ? argv[optind] : NULL; + char arg[120] = ""; + + while (optind < argc) { + strlcat(arg, argv[optind++], sizeof(arg)); + strlcat(arg, " ", sizeof(arg)); + } for (c = 0; command[c].cmd; c++) { if (!strcmp(command[c].cmd, cmd)) diff --git a/service.c b/service.c index 17e22dab..2f8382e4 100644 --- a/service.c +++ b/service.c @@ -24,7 +24,6 @@ #include #include -#include #include #include "libite/lite.h" @@ -38,10 +37,9 @@ #define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */ -static int is_norespawn (void); +static int is_norespawn (void); static void restart_lost_procs (void); static void svc_dance (svc_t *svc); -static void utmp_save (int pre, int now); static svc_t *find_inetd_svc (char *path, char *service, char *proto); @@ -433,7 +431,7 @@ void service_runlevel(int newlevel) runlevel = newlevel; _d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); - utmp_save(prevlevel, newlevel); + runlevel_set(prevlevel, newlevel); /* Make sure to reload all *.conf in /etc/finit.d/ */ svc_mark_dynamic(); @@ -817,25 +815,6 @@ static void svc_dance(svc_t *svc) } } -static int encode(int lvl) -{ - if (!lvl) return 0; - return lvl + '0'; -} - -static void utmp_save(int pre, int now) -{ - struct utmp utent; - - utent.ut_type = RUN_LVL; - utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF); - strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user)); - - setutent(); - pututline(&utent); - endutent(); -} - #ifndef INETD_DISABLED static svc_t *find_inetd_svc(char *path, char *service, char *proto) { diff --git a/svc.c b/svc.c index 0011d64b..f1757d3e 100644 --- a/svc.c +++ b/svc.c @@ -170,6 +170,31 @@ svc_t *svc_dynamic_iterator(int first) } +/** + * svc_named_iterator - Iterates over all instances of a service. + * @first: Get first &svc_t object, or next until end. + * @cmd: Service name to look for. + * + * Returns: + * The first matching &svc_t when @first is set, otherwise the next + * &svc_t instance with the same @cmd until the end when %NULL is + * returned. + */ +svc_t *svc_named_iterator(int first, char *cmd) +{ + svc_t *svc; + + for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + char *name = basename(svc->cmd); + + if (!strncmp(name, cmd, strlen(name))) + return svc; + } + + return NULL; +} + + /** * svc_foreach - Run a callback for each registered service * @cb: Callback to run for each service @@ -307,10 +332,13 @@ char *svc_status(svc_t *svc) if (svc->pid) return "running"; - if (svc_is_inetd(svc)) - return "inetd"; + if (!ISSET(svc->runlevels, runlevel)) + return "halted"; - return "waiting"; + if (svc_is_inetd(svc)) + return "waiting"; + + return "stopped"; } /* Same base service, return unique ID */ diff --git a/svc.h b/svc.h index 653a72c8..1472aafd 100644 --- a/svc.h +++ b/svc.h @@ -121,6 +121,7 @@ svc_t *svc_find_by_jobid (int job, int id); svc_t *svc_iterator (int first); svc_t *svc_inetd_iterator (int first); svc_t *svc_dynamic_iterator (int first); +svc_t *svc_named_iterator (int first, char *cmd); void svc_foreach (void (*cb)(svc_t *)); void svc_foreach_dynamic (void (*cb)(svc_t *));