diff --git a/client.c b/client.c index 7d8a39df..3d045f28 100644 --- a/client.c +++ b/client.c @@ -34,7 +34,7 @@ typedef struct { char *cmd; - int (*cb)(void); + int (*cb)(char *arg); } command_t; @@ -67,7 +67,7 @@ static int do_send(struct init_request *rq, ssize_t len) return result; } -static int toggle_debug(void) +static int toggle_debug(char *UNUSED(arg)) { struct init_request rq = { .magic = INIT_MAGIC, @@ -77,53 +77,69 @@ static int toggle_debug(void) return do_send(&rq, sizeof(rq)); } -static int reload_conf(void) -{ - struct init_request rq = { - .magic = INIT_MAGIC, - .cmd = INIT_CMD_RELOAD, - }; - - return do_send(&rq, sizeof(rq)); -} - -static int set_runlevel(int lvl) +static int set_runlevel(char *arg) { struct init_request rq = { .magic = INIT_MAGIC, .cmd = INIT_CMD_RUNLVL, - .runlevel = lvl, + .runlevel = (int)arg[0], }; return do_send(&rq, sizeof(rq)); } -int show_version(void) +static int do_svc(int cmd, char *jobid) +{ + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = cmd, + }; + + if (!jobid) { + if (cmd == INIT_CMD_RELOAD_SVC) { + rq.cmd = INIT_CMD_RELOAD; + goto exit; + } + + return 1; + } + strlcpy(rq.data, jobid, sizeof(rq.data)); + +exit: + return do_send(&rq, sizeof(rq)); +} + +static int do_start (char *arg) { return do_svc(INIT_CMD_START_SVC, arg); } +static int do_stop (char *arg) { return do_svc(INIT_CMD_STOP_SVC, arg); } +static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD_SVC, arg); } +static int do_restart(char *arg) { return do_svc(INIT_CMD_RESTART_SVC, arg); } + +static int show_version(char *UNUSED(arg)) { puts("v" VERSION); return 0; } -static char *svc_status(svc_t *svc) -{ - if (svc->pid) - return "running"; - - if (svc_is_inetd(svc)) - return "inetd"; - - return "waiting"; -} - -static int show_status(void) +/* In verbose mode we skip the header and each service description. + * This in favor of having all info on one line so a machine can more + * easily parse it. */ +static int show_status(char *UNUSED(arg)) { svc_t *svc; - printf("Status PID Runlevels Service Description\n"); - printf("==============================================================================\n"); + if (!verbose) { + printf("# Status PID Runlevels Service Description\n"); + printf("====================================================================================\n"); + } + for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { - printf("%7s %-6d %-9s %-20s %s\n", svc_status(svc), svc->pid, - runlevel_string(svc->runlevels), svc->cmd, svc->desc); + char jobid[10]; + + 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 i; char args[80] = ""; @@ -132,7 +148,11 @@ static int show_status(void) strlcat(args, svc->args[i], sizeof(args)); strlcat(args, " ", sizeof(args)); } - printf(" %s\n", args); + printf("%-6s %7s %-6d %-9s %s %s\n", jobid, svc_status(svc), svc->pid, + runlevel_string(svc->runlevels), svc->cmd, args); + } else { + printf("%-6s %7s %-6d %-9s %-20s %s\n", jobid, svc_status(svc), svc->pid, + runlevel_string(svc->runlevels), svc->cmd, svc->desc); } } @@ -143,14 +163,19 @@ static int usage(int rc) { fprintf(stderr, "Usage: %s [OPTIONS] [ | ]\n\n" "Options:\n" - " -d, --debug Toggle Finit debug\n" - " -v, --verbose Verbose output\n" - " -h, --help This help text\n\n" + " -d, --debug Toggle Finit debug\n" + " -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" - " status Show status of services\n" - " version Show Finit version\n\n", __progname); + " debug Toggle Finit debug\n" + " q | reload Reload *.conf in /etc/finit.d/\n" + " runlevel <1-6> Set new runlevel\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); return rc; } @@ -160,10 +185,13 @@ int client(int argc, char *argv[]) int c; command_t command[] = { { "debug", toggle_debug }, - { "q", reload_conf }, /* SysV init compat. */ - { "reload", reload_conf }, -// { "runlevel", set_runlevel }, + { "q", do_reload }, /* SysV init compat. */ + { "reload", do_reload }, + { "runlevel", set_runlevel }, { "status", show_status }, + { "start", do_start }, + { "stop", do_stop }, + { "restart", do_restart }, { "version", show_version }, { NULL, NULL } }; @@ -178,7 +206,7 @@ int client(int argc, char *argv[]) while ((c = getopt_long(argc, argv, "h?dv", long_options, NULL)) != EOF) { switch(c) { case 'd': - toggle_debug(); + toggle_debug(NULL); break; case 'h': @@ -192,15 +220,16 @@ int client(int argc, char *argv[]) } if (optind < argc) { - char *cmd = argv[optind]; + char *cmd = argv[optind++]; + char *job = optind < argc ? argv[optind] : NULL; /* Compat: 'init <1-9>' */ if (strlen(cmd) == 1 && isdigit((int)cmd[0])) - return set_runlevel((int)cmd[0]); + return set_runlevel(cmd); for (c = 0; command[c].cmd; c++) { if (!strcmp(command[c].cmd, cmd)) - return command[c].cb(); + return command[c].cb(job); } } diff --git a/finit.h b/finit.h index cf5f385d..e5697897 100644 --- a/finit.h +++ b/finit.h @@ -69,15 +69,20 @@ # define DEFHOST "noname" #endif -#define CMD_SIZE 256 -#define LINE_SIZE 1024 -#define BUF_SIZE 4096 +#define CMD_SIZE 256 +#define LINE_SIZE 1024 +#define BUF_SIZE 4096 -#define INIT_MAGIC 0x03091969 -#define INIT_CMD_RUNLVL 1 -#define INIT_CMD_DEBUG 2 -#define INIT_CMD_RELOAD 3 -#define INIT_CMD_ACK 255 +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_RUNLVL 1 +#define INIT_CMD_DEBUG 2 /* Toggle Finit debug */ +#define INIT_CMD_RELOAD 3 /* Reload *.conf in /etc/finit.d/ */ +#define INIT_CMD_START_SVC 4 +#define INIT_CMD_STOP_SVC 5 +#define INIT_CMD_RELOAD_SVC 6 /* SIGHUP service */ +#define INIT_CMD_RESTART_SVC 7 /* STOP + START service */ +#define INIT_CMD_NACK 254 +#define INIT_CMD_ACK 255 struct init_request { int magic; /* Magic number */ diff --git a/plugins/initctl.c b/plugins/initctl.c index 52573c20..f04a35f2 100644 --- a/plugins/initctl.c +++ b/plugins/initctl.c @@ -53,6 +53,48 @@ static void fifo_open(void) } } +static svc_t *convert_jobid(char *jobid) +{ + int job, id = 1; + char *token; + + if (!jobid) + return NULL; + + token = strtok(jobid, ":"); + if (!token) { + job = atoi(jobid); + } else { + job = atoi(token); + token = strtok(NULL, ""); + if (token) + id = atoi(token); + } + + _d("Got job %d id %d ...", job, id); + return svc_find_by_jobid(job, id); +} + +static int do_start(char *jobid) +{ + return service_start(convert_jobid(jobid)); +} + +static int do_stop(char *jobid) +{ + return service_stop(convert_jobid(jobid)); +} + +static int do_reload(char *jobid) +{ + return service_reload(convert_jobid(jobid)); +} + +static int do_restart(char *jobid) +{ + return service_restart(convert_jobid(jobid)); +} + /* Standard reboot/shutdown utilities talk to init using /dev/initctl. * We should check if the fifo was recreated and reopen it. */ @@ -62,6 +104,7 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events)) _d("Receiving request on descriptor %d, from %s ...", fd, FINIT_FIFO); while (1) { + int result = 0; ssize_t len = read(fd, &rq, sizeof(rq)); if (len <= 0) { @@ -125,6 +168,22 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events)) reload_finit_d(); break; + case INIT_CMD_START_SVC: + result = do_start(rq.data); + break; + + case INIT_CMD_STOP_SVC: + result = do_stop(rq.data); + break; + + case INIT_CMD_RELOAD_SVC: + result = do_reload(rq.data); + break; + + case INIT_CMD_RESTART_SVC: + result = do_restart(rq.data); + break; + case INIT_CMD_ACK: _d("Client failed reading ACK."); goto leave; @@ -134,10 +193,13 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events)) break; } - rq.cmd = INIT_CMD_ACK; + if (result) + rq.cmd = INIT_CMD_NACK; + else + rq.cmd = INIT_CMD_ACK; len = write(fd, &rq, sizeof(rq)); if (len != sizeof(rq)) - _d("Failed sending ACK back to client."); + _d("Failed sending ACK/NACK back to client."); else sleep(1); /* Give client time to read FIFO */ } diff --git a/service.c b/service.c index 392679a6..c01a7509 100644 --- a/service.c +++ b/service.c @@ -26,13 +26,13 @@ #include #include #include +#include "libite/lite.h" #include "finit.h" #include "helpers.h" #include "private.h" #include "sig.h" #include "tty.h" -#include "libite/lite.h" #include "service.h" #include "inetd.h" @@ -140,7 +140,7 @@ int service_start(svc_t *svc) sigset_t nmask, omask; if (!svc) - return 0; + return 1; respawn = svc->pid != 0; /* Don't try and start service if it doesn't exist. */ @@ -153,7 +153,7 @@ int service_start(svc_t *svc) print_result(1); } - return 0; + return 1; } /* Ignore if finit is SIGSTOP'ed */ @@ -171,7 +171,7 @@ int service_start(svc_t *svc) sd = accept(sd, NULL, NULL); if (sd < 0) { FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port); - return 0; + return 1; } _d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port); @@ -189,7 +189,7 @@ int service_start(svc_t *svc) if (svc->inetd.type == SOCK_STREAM) close(sd); - return 0; + return 1; } FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname); @@ -299,10 +299,8 @@ int service_stop(svc_t *svc) { int res = 1; - if (!svc) { - _e("Failed, no svc pointer."); + if (!svc) return 1; - } if (svc->pid <= 1) { _d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc); @@ -328,6 +326,21 @@ exit: return res; } +/* stop + start */ +int service_restart(svc_t *svc) +{ + int result = 0; + + if (!svc) + return 1; + + result += service_stop(svc); + sleep(1); + result += service_start(svc); + + return result; +} + /** * service_reload - Send SIGHUP to a service * @svc: Service to reload @@ -587,24 +600,28 @@ int service_register(int type, char *line, time_t mtime, char *username) #ifndef INETD_DISABLED /* Find plugin that provides a callback for this inetd service */ - if (type == SVC_TYPE_INETD && !strncasecmp(cmd, "internal", 8)) { - plugin = plugin_find(service); - if (!plugin || !plugin->inetd.cmd) { - _e("Inetd service %s has no internal plugin, skipping.", service); - return errno = ENOENT; + if (type == SVC_TYPE_INETD) { + if (!strncasecmp(cmd, "internal", 8)) { + plugin = plugin_find(service); + if (!plugin || !plugin->inetd.cmd) { + _e("Inetd service %s has no internal plugin, skipping.", service); + return errno = ENOENT; + } } - } - /* Check if known inetd, then add ifname for filtering only. */ - svc = find_inetd_svc(cmd, service, proto, port); - if (svc) - return inetd_allow(&svc->inetd, iface); + /* Check if known inetd, then add ifname for filtering only. */ + svc = find_inetd_svc(cmd, service, proto, port); + if (svc) + return inetd_allow(&svc->inetd, iface); + + id = svc_next_id(cmd); + } #endif svc = svc_find(cmd, id); if (!svc) { - _d("Creating new svc for %s id #%d", cmd, id); - svc = svc_new(id); + _d("Creating new svc for %s id #%d type %d", cmd, id, type); + svc = svc_new(cmd, id, type); if (!svc) { _e("Out of memory, cannot register service %s", cmd); return errno = ENOMEM; @@ -613,7 +630,6 @@ int service_register(int type, char *line, time_t mtime, char *username) if (svc->mtime != mtime) svc->dirty = 1; svc->mtime = mtime; - svc->type = type; if (desc) strlcpy(svc->desc, desc + 3, sizeof(svc->desc)); @@ -632,6 +648,8 @@ int service_register(int type, char *line, time_t mtime, char *username) if (svc_is_inetd(svc)) { int result; + _d("Creating new svc job %d inetd %s proto %s iface %s port %s", + svc->job, service, proto, iface, port); result = inetd_new(&svc->inetd, service, proto, forking); result += inetd_init(&svc->inetd, svc, iface, port); @@ -647,9 +665,6 @@ int service_register(int type, char *line, time_t mtime, char *username) /* Internal plugin provides this service */ svc->inetd.cmd = plugin->inetd.cmd; } else { - /* External program to call */ - strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); - strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); while ((cmd = strtok(NULL, " "))) strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); @@ -808,7 +823,7 @@ static svc_t *find_inetd_svc(char *path, char *service, char *proto, char *port) continue; if (inetd_match(&svc->inetd, service, proto, port)) { - _d("Found a matching svc for %s", path); + _d("Found a matching inetd svc for %s %s %s %s", path, service, proto, port); return svc; } } diff --git a/service.h b/service.h index 93a175d2..93db9396 100644 --- a/service.h +++ b/service.h @@ -34,6 +34,7 @@ svc_cmd_t service_enabled (svc_t *svc, int event, void *arg); int service_start (svc_t *svc); int service_stop (svc_t *svc); +int service_restart (svc_t *svc); int service_reload (svc_t *svc); void service_reload_dynamic (void); diff --git a/svc.c b/svc.c index 229a1257..84b333a3 100644 --- a/svc.c +++ b/svc.c @@ -23,11 +23,14 @@ */ #include +#include "libite/lite.h" #include "finit.h" #include "svc.h" #include "helpers.h" +/* Each svc_t needs a unique job# */ +static int jobcounter = 1; static svc_t *__connect_shm(void) { @@ -45,22 +48,37 @@ static svc_t *__connect_shm(void) /** * svc_new - Create a new service - * @id: Instance id + * @cmd: External program to call, or 'internal' for internal inetd services + * @id: Instance id + * @type: Service type, one of service, task, run or inetd * * Returns: * A pointer to a new &svc_t object, or %NULL if out of empty slots. */ -svc_t *svc_new(int id) +svc_t *svc_new(char *cmd, int id, int type) { - int i; - svc_t *list = __connect_shm(); + int i, job = -1; + svc_t *svc, *list = __connect_shm(); + + /* Find first job n:o if registering multiple instances */ + for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + if (!strcmp(svc->cmd, cmd)) { + job = svc->job; + break; + } + } + if (job == -1) + job = jobcounter++; for (i = 0; i < MAX_NUM_SVC; i++) { svc_t *svc = &list[i]; if (svc->type == SVC_TYPE_FREE) { memset(svc, 0, sizeof(*svc)); - svc->id = id; + svc->type = type; + svc->job = job; + svc->id = id; + strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); return svc; } @@ -186,18 +204,18 @@ void svc_foreach_dynamic(void (*cb)(svc_t *)) /** * svc_find - Find a service object by its full path name - * @path: Full path name, e.g., /sbin/syslogd + * @cmd: Full path name, e.g., /sbin/syslogd * * Returns: * A pointer to an &svc_t object, or %NULL if not found. */ -svc_t *svc_find(char *path, int id) +svc_t *svc_find(char *cmd, int id) { svc_t *svc; for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { - if (svc->id == id && !strncmp(path, svc->cmd, strlen(svc->cmd))) { - _d("Found a matching svc for %s", path); + if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) { + _d("Found a matching svc for %s", cmd); return svc; } } @@ -224,6 +242,26 @@ svc_t *svc_find_by_pid(pid_t pid) return NULL; } +/** + * svc_find_by_jobid - Find an service object by its JOB:ID + * @job: Job n:o + * @id: Instance id + * + * Returns: + * A pointer to an &svc_t object, or %NULL if not found. + */ +svc_t *svc_find_by_jobid(int job, int id) +{ + svc_t *svc; + + for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + if (svc->job == job && svc->id == id) + return svc; + } + + return NULL; +} + /** * svc_mark_dynamic - Mark dynamically loaded services for deletion. * @@ -264,6 +302,54 @@ void svc_clean_dynamic(void (*cb)(svc_t *)) } } +char *svc_status(svc_t *svc) +{ + if (svc->pid) + return "running"; + + if (svc_is_inetd(svc)) + return "waiting"; + + return "stopped"; +} + +/* Same base service, return unique ID */ +int svc_next_id(char *cmd) +{ + int id = 0; + svc_t *svc; + + for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + if (!strcmp(svc->cmd, cmd) && id < svc->id) + id = svc->id; + } + + return id + 1; +} + +int svc_is_unique(svc_t *svc) +{ + svc_t *list = __connect_shm(); + int i, unique = 1; + + for (i = 0; i < MAX_NUM_SVC; i++) { + svc_t *s = &list[i]; + + if (svc->type == SVC_TYPE_FREE) + continue; + + if (s == svc) + continue; + + if (s->job == svc->job) { + unique = 0; + break; + } + } + + return unique; +} + /** * Local Variables: diff --git a/svc.h b/svc.h index 827ba016..16535416 100644 --- a/svc.h +++ b/svc.h @@ -61,7 +61,7 @@ typedef enum { * initctl service */ typedef struct svc { /* Instance specifics */ - int id; + int job, id; /* JOB:ID */ /* Service details */ pid_t pid; @@ -114,11 +114,12 @@ static inline svc_t *finit_svc_connect(void) return (svc_t *)ptr; } -svc_t *svc_new (int id); +svc_t *svc_new (char *cmd, int id, int type); int svc_del (svc_t *svc); -svc_t *svc_find (char *path, int id); +svc_t *svc_find (char *cmd, int id); svc_t *svc_find_by_pid (pid_t pid); +svc_t *svc_find_by_jobid (int job, int id); svc_t *svc_iterator (int first); svc_t *svc_inetd_iterator (int first); @@ -130,6 +131,10 @@ void svc_foreach_dynamic (void (*cb)(svc_t *)); void svc_mark_dynamic (void); void svc_clean_dynamic (void (*cb)(svc_t *)); +char *svc_status (svc_t *svc); +int svc_next_id (char *cmd); +int svc_is_unique (svc_t *svc); + static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; } static inline int svc_is_daemon(svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }