From 2026486fdaa46c271cdaf7277175a36dbaacf4fa Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sun, 7 Jun 2015 12:10:38 +0200 Subject: [PATCH] Refactor: Cleanup and expand client code to support Further refactoring of the client code. Supported commands: - debug: Toggle PID 1 debug flag - version: Show Finit version or custom header string - reload: Reload config files in /etc/finit.d/, same as SIGHUP Also supported is the legacy 'init q' command, to reload config. The 'status' command now supports an optiona '-v', either as an argument to init itself, or as argument to the command. When it is given 'status' lists verbose information about all services. Currently only the service arguments are listed. Signed-off-by: Joachim Nilsson --- TODO.md | 5 - client.c | 239 +++++++++++++++++++++++++++------------------- conf.c | 35 +++++++ finit.c | 28 ------ finit.h | 9 +- helpers.c | 44 +++++++++ helpers.h | 2 + plugins/initctl.c | 4 + sig.c | 12 +-- 9 files changed, 233 insertions(+), 145 deletions(-) diff --git a/TODO.md b/TODO.md index 80684f2d..83ec7402 100644 --- a/TODO.md +++ b/TODO.md @@ -28,14 +28,9 @@ General service it monitors + `.sh`. * Add support for `/etc/sysctl.d` * Add compile-time supoprt for running `/bin/sh` instead of `getty` -* Add support for `init ` and `SIGHUP` to - have finit reload its `finit.conf`, but also -* Add support for inotify to automatically reload `finit.conf` * Implement `initctl stop|start|restart|reload|status ` and `service stop|start|restart|reload|status` on top * Add PRE and POST hooks for when switching between runlevels -* Add support for `init -v,--version | version` -* Cleanup move sources from top-level directory to `src/` and `include/` * Make sure to install `queue.h` to `$(PREFIX)/include/finit/queue.h` * Move `finit.conf` "check" command to plugin which checks `/etc/fstab` instead. This is the de-facto practice. But keep the check command diff --git a/client.c b/client.c index edb4d5dc..ef91bef9 100644 --- a/client.c +++ b/client.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include #include #include @@ -31,107 +32,20 @@ #include "libite/lite.h" +typedef struct { + char *cmd; + int (*cb)(void); +} command_t; -static char *runlevel_string(svc_t *svc) -{ - int i, pos = 1;; - static char lvl[10] = "[]"; - for (i = 0; i < 10; i++) { - if (ISSET(svc->runlevels, i)) { - if (i == 0) - lvl[pos++] = 'S'; - else - lvl[pos++] = '0' + i; - } - } - lvl[pos++] = ']'; - lvl[pos] = 0; - - return lvl; -} - -static int status(void) -{ - svc_t *svc; - - 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->pid ? "running" : "waiting", svc->pid, - runlevel_string(svc), svc->cmd, svc->desc); - } - - return 0; -} - -static int usage(int rc) -{ - fprintf(stderr, "Usage: %s [OPTIONS] [status | <1-6>]\n\n" - "Options:\n" - " -d, --debug Enable/Disable debug\n" - " -h, --help This help text\n\n" - "Commands:\n" - " status Show status of services\n\n", __progname); - - return rc; -} - -int client(int argc, char *argv[]) +static int do_send(struct init_request *rq, ssize_t len) { int fd; - int c; int result = -1; - ssize_t len; - struct { - char *cmd; - int (*cb)(void); - } command[] = { - { "status", status }, - { NULL, NULL } - }; - struct option long_options[] = { - {"debug", 0, NULL, 'd'}, - {"help", 0, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - struct init_request rq = { - .magic = INIT_MAGIC, - .cmd = 0 - }; - - while ((c = getopt_long(argc, argv, "h?d", long_options, NULL)) != EOF) { - switch(c) { - case 'd': - rq.cmd = INIT_CMD_DEBUG; - break; - - case 'h': - case '?': - return usage(0); - default: - return usage(1); - } - } - - if (!rq.cmd) { - if (argc < 2) { - fprintf(stderr, "Missing argument.\n"); - return usage(1); - } - - for (c = 0; command[c].cmd; c++) { - if (!strcmp(command[c].cmd, argv[1])) - return command[c].cb(); - } - - rq.cmd = INIT_CMD_RUNLVL; - rq.runlevel = (int)argv[1][0]; - } + ssize_t num; if (!fexist(FINIT_FIFO)) { - fprintf(stderr, "/sbin/init does not support %s!\n", FINIT_FIFO); + fprintf(stderr, "%s does not support %s!\n", __progname, FINIT_FIFO); return 1; } @@ -141,10 +55,10 @@ int client(int argc, char *argv[]) return 1; } - len = write(fd, &rq, sizeof(rq)); - if (len == sizeof(rq)) { - len = read(fd, &rq, sizeof(rq)); - if (len == sizeof(rq) && rq.cmd == INIT_CMD_ACK) + num = write(fd, rq, len); + if (num == len) { + num = read(fd, rq, len); + if (num == len && rq->cmd == INIT_CMD_ACK) result = 0; } @@ -153,6 +67,135 @@ int client(int argc, char *argv[]) return result; } +static int toggle_debug(void) +{ + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_DEBUG, + }; + + 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) +{ + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_RUNLVL, + .runlevel = lvl, + }; + + return do_send(&rq, sizeof(rq)); +} + +int show_version(void) +{ + return banner(); +} + +static int show_status(void) +{ + svc_t *svc; + + 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->pid ? "running" : "waiting", svc->pid, + runlevel_string(svc->runlevels), svc->cmd, svc->desc); + if (verbose) { + int i; + char args[80] = ""; + + for (i = 1; i < MAX_NUM_SVC_ARGS; i++) { + strlcat(args, svc->args[i], sizeof(args)); + strlcat(args, " ", sizeof(args)); + } + printf(" %s\n", args); + } + } + + return 0; +} + +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" + "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); + + return rc; +} + +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 }, + { "status", show_status }, + { "version", show_version }, + { NULL, NULL } + }; + struct option long_options[] = { + {"debug", 0, NULL, 'd'}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + verbose = 0; + while ((c = getopt_long(argc, argv, "h?dv", long_options, NULL)) != EOF) { + switch(c) { + case 'd': + toggle_debug(); + break; + + case 'h': + case '?': + return usage(0); + + case 'v': + verbose = 1; + break; + } + } + + if (optind < argc) { + char *cmd = argv[optind]; + + /* Compat: 'init <1-9>' */ + if (strlen(cmd) == 1 && isdigit((int)cmd[0])) + return set_runlevel((int)cmd[0]); + + for (c = 0; command[c].cmd; c++) { + if (!strcmp(command[c].cmd, cmd)) + return command[c].cb(); + } + } + + return usage(1); +} + /** * Local Variables: * version-control: t diff --git a/conf.c b/conf.c index 3affdba2..dc799a64 100644 --- a/conf.c +++ b/conf.c @@ -54,6 +54,25 @@ static char *strip_line(char *line) return line; } +void parse_kernel_cmdline(void) +{ + FILE *fp; + char line[LINE_SIZE]; + + if ((fp = fopen("/proc/cmdline", "r")) != NULL) { + fgets(line, sizeof(line), fp); + _d("Kernel command line: %s", line); + + if (strstr(line, "finit_debug") || strstr(line, "--debug")) + debug = 1; + + if (!debug && strstr(line, "quiet")) + verbose = 0; + + fclose(fp); + } +} + /* Convert optional "[!123456789S]" string into a bitmask */ int parse_runlevels(char *runlevels) { @@ -378,6 +397,22 @@ int parse_finit_conf(char *file) return result; } +/* Called on SIGHUP or 'init q' */ +void reload_finit_d(void) +{ + /* Mark and sweep */ + svc_mark_dynamic(); + + /* Reload all *.conf in /etc/finit.d/ */ + parse_finit_d(rcsd); + + /* Reload dirty services */ + service_reload_dynamic(); + + /* Cleanup stale services */ + svc_clean_dynamic(service_unregister); +} + /** * Local Variables: diff --git a/finit.c b/finit.c index d270752c..37f5745d 100644 --- a/finit.c +++ b/finit.c @@ -52,34 +52,6 @@ char *console = NULL; uev_ctx_t *ctx = NULL; /* Main loop context */ -static void parse_kernel_cmdline(void) -{ - FILE *fp; - char line[LINE_SIZE]; - - if ((fp = fopen("/proc/cmdline", "r")) != NULL) { - fgets(line, sizeof(line), fp); - _d("Kernel command line: %s", line); - - if (strstr(line, "finit_debug") || strstr(line, "--debug")) - debug = 1; - - if (!debug && strstr(line, "quiet")) - verbose = 0; - - fclose(fp); - } -} - -static void banner(void) -{ - delline(); -#ifdef INIT_HEADING - echo("%s", INIT_HEADING); -#else - echo("Finit version " VERSION " (" WHOAMI ") " __DATE__ " " __TIME__); -#endif -} int main(int argc, char* argv[]) { diff --git a/finit.h b/finit.h index 51e66405..073340ad 100644 --- a/finit.h +++ b/finit.h @@ -76,6 +76,7 @@ #define INIT_MAGIC 0x03091969 #define INIT_CMD_RUNLVL 1 #define INIT_CMD_DEBUG 2 +#define INIT_CMD_RELOAD 3 #define INIT_CMD_ACK 255 struct init_request { @@ -99,9 +100,11 @@ extern char *console; extern char *__progname; /* conf.c */ -int parse_runlevels (char *runlevels); -int parse_finit_conf (char *file); -int parse_finit_d (char *dir); +void parse_kernel_cmdline (void); +int parse_runlevels (char *runlevels); +int parse_finit_conf (char *file); +int parse_finit_d (char *dir); +void reload_finit_d (void); #endif /* FINIT_H_ */ diff --git a/helpers.c b/helpers.c index 50884c0a..00190ddb 100644 --- a/helpers.c +++ b/helpers.c @@ -136,6 +136,50 @@ int atonum(char *str) return val; } +/** + * runlevel_string - Convert a bit encoded runlevel to .conf syntax + * @levels: Bit encoded runlevels + * + * Returns: + * Pointer to string on the form [2345] + */ +char *runlevel_string(int levels) +{ + int i, pos = 1;; + static char lvl[10] = "[]"; + + for (i = 0; i < 10; i++) { + if (ISSET(levels, i)) { + if (i == 0) + lvl[pos++] = 'S'; + else + lvl[pos++] = '0' + i; + } + } + lvl[pos++] = ']'; + lvl[pos] = 0; + + return lvl; +} + +/** + * banner - Show Finit version at boot or from cmdline + * + * This function prints the default Finit version, or a custom heading. + * + * Returns: + * This function is always successful, returning POSIX OK(0). + */ +int banner(void) +{ + delline(); +#ifdef INIT_HEADING + echo("%s", INIT_HEADING); +#else + echo("Finit version " VERSION " (" WHOAMI ") " __DATE__ " " __TIME__); +#endif + return 0; +} /** * pidfile_read - Reads a PID value from a pidfile. diff --git a/helpers.h b/helpers.h index 4d2d8257..aabc389d 100644 --- a/helpers.h +++ b/helpers.h @@ -95,6 +95,8 @@ int makepath (char *path); void ifconfig (char *ifname, char *addr, char *mask, int up); int atonum (char *str); +char *runlevel_string (int levels); +int banner (void); pid_t pidfile_read (char *pidfile); pid_t pidfile_poll (char *cmd, char *path); diff --git a/plugins/initctl.c b/plugins/initctl.c index e7dd08e9..52573c20 100644 --- a/plugins/initctl.c +++ b/plugins/initctl.c @@ -121,6 +121,10 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events)) debug = !debug; break; + case INIT_CMD_RELOAD: + reload_finit_d(); + break; + case INIT_CMD_ACK: _d("Client failed reading ACK."); goto leave; diff --git a/sig.c b/sig.c index b5fcbc39..5634136f 100644 --- a/sig.c +++ b/sig.c @@ -93,17 +93,7 @@ void do_shutdown(int sig) */ static void sighup_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) { - /* Mark and sweep */ - svc_mark_dynamic(); - - /* Reload all *.conf in /etc/finit.d/ */ - parse_finit_d(rcsd); - - /* Reload dirty services */ - service_reload_dynamic(); - - /* Cleanup stale services */ - svc_clean_dynamic(service_unregister); + reload_finit_d(); } /*