diff --git a/.travis.yml b/.travis.yml index 6e105872..8298a228 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,11 +34,11 @@ addons: install: - wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz - - wget https://github.com/troglobit/libite/releases/download/v1.9.2/libite-1.9.2.tar.xz + - wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz - tar xf libuev-2.1.0.tar.xz - - tar xf libite-1.9.2.tar.xz + - tar xf libite-2.0.1.tar.xz - (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip) - - (cd libite-1.9.2 && ./configure --prefix=/tmp && make && make install-strip) + - (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip) # Custom build script for the time being, no "make test" yet script: diff --git a/configure.ac b/configure.ac index 728d03e3..0a8ae8a8 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ AC_TYPE_UINT32_T # Check for required libraries PKG_CHECK_MODULES([uev], [libuev >= 2.1.0]) -PKG_CHECK_MODULES([lite], [libite >= 1.9.2]) +PKG_CHECK_MODULES([lite], [libite >= 2.0.1]) # Check for configured Finit features AC_ARG_ENABLE(emergency_shell, diff --git a/doc/TODO.md b/doc/TODO.md index cbf98bbe..c8fd0b28 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -21,9 +21,9 @@ can also be used. General ------- -* Remove shm, let `initctl` call `api.c` for each `svc_t` instead of - opening up the inner parts of PID 1 to other processes. Also, this - would prevent issues like #81 from happening. +* Add support for `rescue` on the kernel command line, start rescue mode + if enabled with `configure` script. Could mean skip `/etc/finit.conf` + et al and jump straight to a single getty login on console. * Add support for timed shutdown in Finit, including cancelled shutdown. * SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add API restart to SIGHUP callback for the same functionality in Finit. diff --git a/doc/build.md b/doc/build.md index a490384b..ea292dbd 100644 --- a/doc/build.md +++ b/doc/build.md @@ -26,6 +26,12 @@ optional plugins to enable. It depends on two external libraries: PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure +The configure script checks for all dependencies, including the correct +version of the above mentioned libraries. Currently requried versions: + +- libite v2.0.1 +- libuEv v2.1.0 + Configure --------- diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 5bbabb76..2491d1f1 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -92,17 +92,16 @@ static void pidfile_callback(void *arg, int fd, int events) * * We must wait for the service to create/touch its pidfile. */ -static void pidfile_reconf(void *_null) +static void pidfile_reconf(void *arg) { static char cond[MAX_COND_LEN]; - svc_t *svc; - (void)(_null); + svc_t *svc, *iter = NULL; int restart = 0; do { restart = 0; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { mkcond(cond, sizeof(cond), svc->cmd); if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc) && diff --git a/src/Makefile.am b/src/Makefile.am index d3ae9d46..bebd6c14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,7 +44,7 @@ finit_LDADD += -ldl endif initctl_SOURCES = initctl.c client.c client.h \ - serv.c serv.h svc.c svc.h \ + serv.c serv.h svc.h \ cond.c cond.h util.c util.h initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 initctl_CFLAGS += $(lite_CFLAGS) diff --git a/src/api.c b/src/api.c index 024883d4..327dcde9 100644 --- a/src/api.c +++ b/src/api.c @@ -21,6 +21,7 @@ * THE SOFTWARE. */ +#include #include #include #include @@ -43,7 +44,7 @@ #include "service.h" #include "util.h" -uev_t api_watcher; +static uev_t api_watcher; static int call(int (*action)(svc_t *), char *buf, size_t len) { @@ -87,15 +88,42 @@ static int do_start (char *buf, size_t len) { return call(service_start, buf, static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); } static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); } -#ifdef INETD_ENABLED -static int do_query_inetd(char *buf, size_t len) +static char query_buf[368]; +static int missing(char *job, int id) +{ + char buf[20]; + char idstr[10] = ""; + + if (!job) + job = ""; + if (id > 1) + snprintf(idstr, sizeof(idstr), ":%d", id); + + snprintf(buf, sizeof(buf), "%s%s ", job, idstr); + strlcat(query_buf, buf, sizeof(query_buf)); + + return 1; +} + +static int do_query(struct init_request *rq, size_t len) +{ + memset(query_buf, 0, sizeof(query_buf)); + if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) { + memcpy(rq->data, query_buf, sizeof(rq->data)); + return 1; + } + + return 0; +} + +static svc_t *do_find(char *buf, size_t len) { int id = 1; - char *ptr, *input = sanitize(buf, len); - svc_t *svc; + char *ptr, *input; + input = sanitize(buf, len); if (!input) - return -1; + return NULL; ptr = strchr(input, ':'); if (ptr) { @@ -103,11 +131,20 @@ static int do_query_inetd(char *buf, size_t len) id = atonum(ptr); } - svc = svc_find_by_jobid(atonum(input), id); - if (!svc || !svc_is_inetd(svc)) { - _e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input); + if (isdigit(input[0])) + return svc_find_by_jobid(atonum(input), id); + + return svc_find_by_nameid(input, id); +} + +#ifdef INETD_ENABLED +static int do_query_inetd(char *buf, size_t len) +{ + svc_t *svc; + + svc = do_find(buf, len); + if (!svc || !svc_is_inetd(svc)) return 1; - } return inetd_filter_str(&svc->inetd, buf, len); } @@ -164,6 +201,22 @@ static int do_handle_emit(char *buf, size_t len) return result; } +static void send_svc(int sd, svc_t *svc) +{ + svc_t empty; + size_t len; + + if (!svc) { + empty.pid = -1; + svc = ∅ + } + + len = write(sd, svc, sizeof(*svc)); + if (len != sizeof(*svc)) + _d("Failed sending svc_t to client"); +} + + /* * In contrast to the SysV compat handling in plugins/initctl.c, when * `initctl runlevel 0` is issued we default to POWERDOWN the system @@ -172,6 +225,8 @@ static int do_handle_emit(char *buf, size_t len) static void api_cb(uev_t *w, void *arg, int events) { int sd, lvl; + svc_t *svc; + static svc_t *iter = NULL; struct init_request rq; sd = accept(w->fd, NULL, NULL); @@ -290,6 +345,27 @@ static void api_cb(uev_t *w, void *arg, int events) wdogpid = rq.runlevel; break; + case INIT_CMD_SVC_ITER: + _d("svc iter, first: %d", rq.runlevel); + /* + * XXX: This severly limits the number of + * simultaneous client connections, but will + * have to do for now. + */ + svc = svc_iterator(&iter, rq.runlevel); + send_svc(sd, svc); + goto leave; + + case INIT_CMD_SVC_QUERY: + _d("svc query: %s", rq.data); + result = do_query(&rq, len); + break; + + case INIT_CMD_SVC_FIND: + _d("svc find: %s", rq.data); + send_svc(sd, do_find(rq.data, sizeof(rq.data))); + goto leave; + default: _d("Unsupported cmd: %d", rq.cmd); break; @@ -326,6 +402,7 @@ int api_init(uev_ctx_t *ctx) .sun_path = INIT_SOCKET, }; + _d("Setting up external API socket ..."); sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (-1 == sd) { _pe("Failed starting external API socket"); diff --git a/src/client.c b/src/client.c index 50e8738e..d5fec8cf 100644 --- a/src/client.c +++ b/src/client.c @@ -89,6 +89,74 @@ exit: return result; } +svc_t *client_svc_iterator(int first) +{ + int sd = -1; + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_SVC_ITER, + }; + static svc_t svc; + + sd = client_connect(); + if (sd == -1) + return NULL; + + if (first) + rq.runlevel = 1; + else + rq.runlevel = 0; + + if (write(sd, &rq, sizeof(rq)) != sizeof(rq)) + goto error; + if (read(sd, &svc, sizeof(svc)) != sizeof(svc)) + goto error; + + client_disconnect(); + if (svc.pid < 0) + return NULL; + + return &svc; +error: + perror("Failed communicating with finit"); + client_disconnect(); + sd = -1; + + return NULL; +} + +svc_t *client_svc_find(char *arg) +{ + int sd = -1; + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_SVC_FIND, + }; + static svc_t svc; + + sd = client_connect(); + if (sd == -1) + return NULL; + + strlcpy(rq.data, arg, sizeof(rq.data)); + if (write(sd, &rq, sizeof(rq)) != sizeof(rq)) + goto error; + if (read(sd, &svc, sizeof(svc)) != sizeof(svc)) + goto error; + + client_disconnect(); + if (svc.pid < 0) + return NULL; + + return &svc; +error: + client_disconnect(); + perror("Failed communicating with finit"); + sd = -1; + + return NULL; +} + /** * Local Variables: * indent-tabs-mode: t diff --git a/src/client.h b/src/client.h index b123af8b..c2e00b6d 100644 --- a/src/client.h +++ b/src/client.h @@ -25,15 +25,18 @@ #define FINIT_CLIENT_H_ #include "finit.h" +#include "svc.h" typedef struct { char *cmd; int (*cb)(char *arg); } command_t; -int client_connect (void); -int client_disconnect (void); +int client_connect (void); +int client_disconnect (void); -int client_send (struct init_request *rq, ssize_t len); +int client_send (struct init_request *rq, ssize_t len); +svc_t *client_svc_iterator (int first); +svc_t *client_svc_find (char *arg); #endif /* FINIT_CLIENT_H_ */ diff --git a/src/cond-w.c b/src/cond-w.c index 8246f54d..e14e524c 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -122,10 +122,10 @@ static int svc_has_cond(svc_t *svc) static void cond_update(const char *name) { - svc_t *svc; + svc_t *svc, *iter = NULL; _d("%s", name); - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) continue; diff --git a/src/conf.c b/src/conf.c index 946c1440..813ef68e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -25,8 +25,10 @@ #include #include +#include #include #include +#include /* BSD sys/queue.h API */ #include #include "finit.h" @@ -35,12 +37,22 @@ #include "tty.h" #include "helpers.h" +#define BOOTSTRAP (runlevel == 0) #define MATCH_CMD(l, c, x) \ (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) struct rlimit global_rlimit[RLIMIT_NLIMITS]; -static int parse_conf(char *file); +struct conf_change { + TAILQ_ENTRY(conf_change) link; + char *name; +}; + +static uev_t w1, w2, w3; +static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list); + +static int parse_conf(char *file); +static void drop_changes(void); void conf_parse_cmdline(void) { @@ -242,13 +254,13 @@ static void parse_static(char *line) char *x; char cmd[CMD_SIZE]; - if (MATCH_CMD(line, "host ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "host ", x)) { if (hostname) free(hostname); hostname = strdup(strip_line(x)); return; } - if (MATCH_CMD(line, "mknod ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) { char *dev = strip_line(x); strcpy(cmd, "mknod "); @@ -258,13 +270,13 @@ static void parse_static(char *line) return; } - if (MATCH_CMD(line, "network ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) { if (network) free(network); network = strdup(strip_line(x)); return; } - if (MATCH_CMD(line, "runparts ", x)) { + if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) { if (runparts) free(runparts); runparts = strdup(strip_line(x)); return; @@ -289,10 +301,12 @@ static void parse_static(char *line) return; } - /* The desired runlevel to start when leaving bootstrap (S). + /* + * The desired runlevel to start when leaving bootstrap (S). * Finit supports 1-9, but most systems only use 1-6, where - * 6 is reserved for reboot */ - if (MATCH_CMD(line, "runlevel ", x)) { + * 6 is reserved for reboot and 0 for halt/poweroff. + */ + if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) { char *token = strip_line(x); const char *err = NULL; @@ -305,7 +319,7 @@ static void parse_static(char *line) } } -static void parse_dynamic(char *line, struct rlimit rlimit[], struct timeval *mtime) +static void parse_dynamic(char *line, struct rlimit rlimit[], char *file) { char *x; char cmd[CMD_SIZE]; @@ -331,26 +345,26 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], struct timeval *mt /* Monitored daemon, will be respawned on exit */ if (MATCH_CMD(line, "service ", x)) { - service_register(SVC_TYPE_SERVICE, x, rlimit, mtime); + service_register(SVC_TYPE_SERVICE, x, rlimit, file); return; } /* One-shot task, will not be respawned */ if (MATCH_CMD(line, "task ", x)) { - service_register(SVC_TYPE_TASK, x, rlimit, mtime); + service_register(SVC_TYPE_TASK, x, rlimit, file); return; } /* Like task but waits for completion, useful w/ [S] */ if (MATCH_CMD(line, "run ", x)) { - service_register(SVC_TYPE_RUN, x, rlimit, mtime); + service_register(SVC_TYPE_RUN, x, rlimit, file); return; } /* Classic inetd service */ if (MATCH_CMD(line, "inetd ", x)) { #ifdef INETD_ENABLED - service_register(SVC_TYPE_INETD, x, rlimit, mtime); + service_register(SVC_TYPE_INETD, x, rlimit, file); #else _e("Finit built with inetd support disabled, cannot register service inetd %s!", x); #endif @@ -365,7 +379,7 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], struct timeval *mt /* Regular or serial TTYs to run getty */ if (MATCH_CMD(line, "tty ", x)) { - tty_register(strip_line(x), rlimit, mtime); + tty_register(strip_line(x), rlimit, file); return; } } @@ -381,7 +395,7 @@ static void tabstospaces(char *line) } } -static int parse_conf_dynamic(char *file, struct timeval *mtime) +static int parse_conf_dynamic(char *file) { FILE *fp; struct rlimit rlimit[RLIMIT_NLIMITS]; @@ -395,7 +409,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime) /* Prepare default limits for each service */ memcpy(rlimit, global_rlimit, sizeof(rlimit)); - _d("Parsing %s", file); + _d("Parsing %s <<<<<<", file); while (!feof(fp)) { char line[LINE_SIZE] = ""; @@ -406,7 +420,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime) tabstospaces(line); _d("%s", line); - parse_dynamic(line, rlimit, mtime); + parse_dynamic(line, rlimit, file); } fclose(fp); @@ -414,7 +428,6 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime) return 0; } -/* Reads /etc/finit.conf, once at boot */ static int parse_conf(char *file) { FILE *fp; @@ -456,21 +469,26 @@ static int parse_conf(char *file) return 0; } -/* Reload all *.conf in /etc/finit.d/ */ -int conf_reload_dynamic(void) +/* + * Reload /etc/finit.conf and all *.conf in /etc/finit.d/ + */ +int conf_reload(void) { int i, num; - char *dir = rcsd; struct dirent **e; /* Mark and sweep */ svc_mark_dynamic(); tty_mark(); - num = scandir(dir, &e, NULL, alphasort); + /* First, read /etc/finit.conf */ + parse_conf(FINIT_CONF); + + /* Next, read all *.conf in /etc/finit.d/ */ + num = scandir(rcsd, &e, NULL, alphasort); if (num < 0) { - _d("Skipping %s, no files found ...", dir); - return 1; + _d("Skipping %s, no files found ...", rcsd); + return 0; } /* Update global limits in case operator used prlimit(1) on us */ @@ -482,13 +500,11 @@ int conf_reload_dynamic(void) char path[LINE_SIZE]; size_t len; struct stat st; - struct timeval mtime; - snprintf(path, sizeof(path), "%s/%s", dir, name); + snprintf(path, sizeof(path), "%s/%s", rcsd, name); /* Check that it's an actual file ... beyond any symlinks */ if (lstat(path, &st)) { - lost: _d("Skipping %s, cannot access: %s", path, strerror(errno)); continue; } @@ -505,10 +521,6 @@ int conf_reload_dynamic(void) rp = realpath(path, NULL); if (!rp) { - /* - * XXX: Prune from rcsd? - * XXX: Possibly temporary service from last boot - */ logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno)); continue; } @@ -523,27 +535,210 @@ int conf_reload_dynamic(void) continue; } - if (stat(path, &st)) - goto lost; /* Weird, lost file between here and lstat() */ - - TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim); - parse_conf_dynamic(path, &mtime); + parse_conf_dynamic(path); } while (num--) free(e[num]); free(e); + /* Drop record of all .conf changes */ + drop_changes(); + + /* + * Set host name, from %DEFHOST, *.conf or /etc/hostname. The + * latter wins, if neither exists we default to "noname" + */ set_hostname(&hostname); return 0; } -int conf_parse_config(void) +static struct conf_change *conf_find(char *file) +{ + struct conf_change *node, *tmp; + + TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) { + if (string_compare(node->name, file)) + return node; + } + + return NULL; +} + +static void drop_change(struct conf_change *node) +{ + if (!node) + return; + + TAILQ_REMOVE(&conf_change_list, node, link); + free(node->name); + free(node); +} + + +static void drop_changes(void) +{ + struct conf_change *node, *tmp; + + TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) + drop_change(node); +} + +static int do_change(char *name, uint32_t mask) +{ + struct conf_change *node; + + node = conf_find(name); + if (mask & (IN_DELETE | IN_MOVED_FROM)) { + drop_change(node); + return 0; + } + + if (node) { + _d("Event already registered for %s ...", name); + return 0; + } + + node = malloc(sizeof(*node)); + if (!node) + return 1; + + node->name = strdup(name); + if (!node->name) { + free(node); + return 1; + } + + TAILQ_INSERT_HEAD(&conf_change_list, node,link); + + return 0; +} + +int conf_changed(char *file) +{ + char *ptr; + + if (file && (ptr = strrchr(file, '/'))) + file = ++ptr; + + if (conf_find(file)) + return 1; + + return 0; +} + +static void conf_cb(uev_t *w, void *arg, int events) +{ + static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; + struct inotify_event *ev; + ssize_t sz, len; + + sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); + if (sz <= 0) { + _pe("invalid inotify event"); + return; + } + ev_buf[sz] = 0; + ev = (struct inotify_event *)ev_buf; + + if (arg) { + do_change(arg, ev->mask); + return; + } + + for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev); + len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) { + if (do_change(ev->name, ev->mask)) { + _pe("conf_monitor: Out of memory"); + break; + } + } +} + +static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt) +{ + struct stat st; + uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE; + char *arg = NULL; + int fd, wd; + + if (!ctx) + return 0; + + if (stat(path, &st)) { + _d("No such file or directory, skipping %s", path); + w->fd = -1; + return 0; + } + if (!S_ISDIR(st.st_mode)) { + arg = strrchr(path, '/'); + if (!arg) + arg = path; + else + arg++; + } + + if (w->fd >= 0) + close(w->fd); + + fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) { + _pe("Failed creating inotify descriptor"); + w->fd = -1; + return 1; + } + + /* + * Only forward error, don't report error, + * user may not have @path and that's OK + */ + wd = inotify_add_watch(fd, path, mask | opt); + if (wd < 0) { + w->fd = -1; + close(fd); + return 1; + } + + if (uev_io_init(ctx, w, conf_cb, arg, fd, UEV_READ)) { + _pe("Failed setting up I/O callback for %s watcher", path); + w->fd = -1; + close(fd); + return 1; + } + + return 0; +} + +/* + * Set up inotify watcher and load all *.conf in /etc/finit.d/ + */ +int conf_monitor(uev_ctx_t *ctx) +{ + int rc = 0; + + /* + * If only one watcher fails, that's OK. A user may have only + * one of /etc/finit.conf or /etc/finit.d in use, and may also + * have or not have symlinks in place. We need to monitor for + * changes to either symlink or target. + */ + rc += add_watcher(ctx, &w1, FINIT_RCSD, 0); + rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW); + rc += add_watcher(ctx, &w3, FINIT_CONF, 0); + + return rc + conf_reload(); +} + +/* + * Prepare .conf parser and load all .conf files + */ +int conf_init(void) { hostname = strdup(DEFHOST); + w1.fd = w2.fd = w3.fd = -1; - return parse_conf(FINIT_CONF) || conf_reload_dynamic(); + return conf_monitor(NULL); } /** diff --git a/src/conf.h b/src/conf.h index fd830d89..70144606 100644 --- a/src/conf.h +++ b/src/conf.h @@ -31,11 +31,14 @@ extern struct rlimit global_rlimit[]; int str2rlim(char *str); char *rlim2str(int rlim); +int conf_init (void); +void conf_reload (void); +int conf_changed (char *file); +int conf_monitor (uev_ctx_t *ctx); + void conf_parse_cmdline (void); int conf_parse_runlevels (char *runlevels); void conf_parse_cond (svc_t *svc, char *cond); -int conf_parse_config (void); -void conf_reload_dynamic (void); #endif /* FINIT_CONF_H_ */ diff --git a/src/finit.c b/src/finit.c index 27bd0c15..0c62729f 100644 --- a/src/finit.c +++ b/src/finit.c @@ -254,12 +254,25 @@ static void emergency_shell(void) */ static void finalize(void) { + /* + * Network stuff + */ + _d("Setting up networking ..."); + networking(); + umask(022); + + /* Hooks that rely on loopback, or basic networking being up. */ + _d("Calling all network up hooks ..."); + plugin_run_hooks(HOOK_NETWORK_UP); + /* * Start all tasks/services in the configured runlevel */ + _d("Change to default runlevel, start all services ..."); service_runlevel(cfglevel); /* Clean up bootstrap-only tasks/services that never started */ + _d("Clean up all bootstrap-only tasks/services ..."); svc_prune_bootstrap(); /* All services/tasks/inetd/etc. in configure runlevel have started */ @@ -283,13 +296,16 @@ static void finalize(void) } /* Hooks that should run at the very end */ + _d("Calling all system up hooks ..."); plugin_run_hooks(HOOK_SYSTEM_UP); service_step_all(SVC_TYPE_ANY); /* Enable silent mode before starting TTYs */ + _d("Going silent ..."); log_silent(); /* Delayed start of TTYs at bootstrap */ + _d("Launching all getty services ..."); tty_runlevel(); } @@ -379,9 +395,8 @@ int main(int argc, char* argv[]) makedir("/dev/pts", 0755); mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620"); + /* Some systems rely on /dev/shm being there, for `mount -a` below */ makedir("/dev/shm", 0755); - if (!fismnt("/dev/shm")) - mount("shm", "/dev/shm", "tmpfs", 0, NULL); /* * New tmpfs based /run for volatile runtime data @@ -436,19 +451,15 @@ int main(int argc, char* argv[]) } /* - * Parse /etc/finit.conf and all *.conf in /etc/finit.d/ to - * figure out how to bootstrap the system. + * Initialize .conf system and load static /etc/finit.conf */ - conf_parse_config(); + conf_init(); /* * Start built-in watchdog as soon as possible, if enabled */ wdogpid = watchdog(argv[0]); - /* Set hostname as soon as possible, for syslog et al. */ - set_hostname(&hostname); - /* * Mount filesystems */ @@ -480,12 +491,10 @@ int main(int argc, char* argv[]) cond_init(); /* - * Reload all *.conf in /etc/finit.d, the mount command (above) - * may have brought in new files (overlayfs). - * - * XXX: Can we detect remounted /etc to trigger this instead? + * Set up inotify watcher for /etc/finit.d and read all .conf + * files to figure out how to bootstrap the system. */ - conf_reload_dynamic(); + conf_monitor(&loop); _d("Base FS up, calling hooks ..."); plugin_run_hooks(HOOK_BASEFS_UP); @@ -497,15 +506,6 @@ int main(int argc, char* argv[]) sm_init(&sm); sm_step(&sm); - /* - * Network stuff - */ - networking(); - umask(022); - - /* Hooks that rely on loopback, or basic networking being up. */ - plugin_run_hooks(HOOK_NETWORK_UP); - /* Start new initctl API responder */ api_init(&loop); @@ -513,11 +513,13 @@ int main(int argc, char* argv[]) * Wait for all SVC_TYPE_RUNTASK to have completed their work in * [S], or timeout, before calling finalize() */ + _d("Starting bootstrap finalize timer ..."); uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000); /* * Enter main loop to monior /dev/initctl and services */ + _d("Entering main loop ..."); return uev_run(&loop, 0); } diff --git a/src/finit.h b/src/finit.h index 14e848c9..f60f4ef8 100644 --- a/src/finit.h +++ b/src/finit.h @@ -78,6 +78,9 @@ #define INIT_CMD_EMIT 15 #define INIT_CMD_GET_RUNLEVEL 16 #define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */ +#define INIT_CMD_SVC_ITER 129 +#define INIT_CMD_SVC_QUERY 130 +#define INIT_CMD_SVC_FIND 131 #define INIT_CMD_NACK 254 #define INIT_CMD_ACK 255 diff --git a/src/inetd.c b/src/inetd.c index c6dc36dd..2b1d4364 100644 --- a/src/inetd.c +++ b/src/inetd.c @@ -225,10 +225,10 @@ static void socket_cb(uev_t *w, void *arg, int events) */ int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name) { - svc_t *svc; + svc_t *svc, *iter = NULL; char pname[NI_MAXHOST]; - for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { + for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) { inetd_t *i = &svc->inetd; if (!i->builtin || i->type != SOCK_DGRAM) @@ -665,9 +665,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len) svc_t *inetd_find_svc(char *path, char *service, char *proto) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { + for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) { if (strncmp(path, svc->cmd, strlen(svc->cmd))) continue; diff --git a/src/initctl.c b/src/initctl.c index 85da7acb..f003b635 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -138,21 +138,6 @@ static int do_svc(int cmd, char *arg) static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); } static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); } -static int missing(char *job, int id) -{ - char idstr[10] = ""; - - if (!job) - job = ""; - if (id > 1) - snprintf(idstr, sizeof(idstr), ":%d", id); - - if (verbose) - fprintf(stderr, "No such service %s%s\n", job, idstr); - - return 1; -} - /* * This is a wrapper for do_svc() that adds a simple sanity check of * the service(s) provided as argument. If a service does not exist @@ -160,8 +145,14 @@ static int missing(char *job, int id) */ static int do_startstop(int cmd, char *arg) { - /* Include \0 in len, needed by parser */ - if (svc_parse_jobstr(arg, strlen(arg) + 1, NULL, missing)) { + struct init_request rq = { + .magic = INIT_MAGIC, + .cmd = INIT_CMD_SVC_QUERY + }; + + strlcpy(rq.data, arg, sizeof(rq.data)); + if (client_send(&rq, sizeof(rq))) { + fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data); fprintf(stderr, "Usage: initctl %s [:ID]\n", cmd == INIT_CMD_START_SVC ? "start" : (cmd == INIT_CMD_STOP_SVC ? "stop" : "restart")); @@ -257,7 +248,7 @@ static int do_cond_show(char *arg) printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0); - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { if (!svc->cond[0]) continue; @@ -420,39 +411,29 @@ static int show_status(char *arg) runlevel = runlevel_get(NULL); if (arg && arg[0]) { - int id = 1; - char *ptr, *next; - - ptr = strchr(arg, ':'); - if (ptr) { - *ptr++ = 0; - next = strchr(ptr, ' '); - if (next) - *next = 0; - id = atonum(ptr); - } - - if (isdigit(arg[0])) - svc = svc_find_by_jobid(atonum(arg), id); - else - svc = svc_find_by_nameid(arg, id); - + svc = client_svc_find(arg); if (!svc) return 1; - printf("%s\n", svc_status(svc)); - return 0; + printf("Service : %s\n", svc->cmd); + printf("Description : %s\n", svc->desc); + printf("PID : %d\n", svc->pid); + printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); + printf("Status : %s\n", svc_status(svc)); + printf("\n"); + + return do_log(svc->cmd); } if (!verbose) printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0); - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { char jobid[10], args[512] = "", *lvls; - if (svc_is_unique(svc)) - snprintf(jobid, sizeof(jobid), "%d", svc->job); - else +// if (svc_is_unique(svc)) +// snprintf(jobid, sizeof(jobid), "%d", svc->job); +// else snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id); printf("%-5s %7s ", jobid, svc_status(svc)); @@ -543,6 +524,7 @@ static int usage(int rc) " list List all .conf in /etc/finit.d/\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" // " reload [:ID] Reload (SIGHUP) service by job# or name\n" "\n" @@ -585,6 +567,7 @@ int main(int argc, char *argv[]) { "list", serv_list }, { "enable", serv_enable }, { "disable", serv_disable }, + { "touch", serv_touch }, { "reload", do_reload }, { "cond", do_cond }, diff --git a/src/log.c b/src/log.c index 3a7a5499..e38b84d9 100644 --- a/src/log.c +++ b/src/log.c @@ -105,45 +105,43 @@ int log_is_debug(void) return debug; } -static void early_logit(int prio, const char *fmt, va_list ap) -{ - FILE *fp; - - if (!debug && prio > loglevel) - return; - - fp = fopen("/dev/kmsg", "w"); - if (fp) { - fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio); - vfprintf(fp, fmt, ap); - fclose(fp); - - if (debug) - vfprintf(stderr, fmt, ap); - } else { - vfprintf(stderr, fmt, ap); - } -} - /* * Log to /dev/kmsg until syslogd has started, then openlog() * and continue logging as a regular daemon. */ void logit(int prio, const char *fmt, ...) { + FILE *fp; va_list ap; va_start(ap, fmt); - if (!up) { - if (!fexist("/dev/log")) { - early_logit(prio, fmt, ap); - goto done; - } - log_open(); + if (up || fexist("/dev/log")) { + if (!up) + log_open(); + vsyslog(prio, fmt, ap); + goto done; + } + + if (!debug && prio > loglevel) + goto done; + + fp = fopen("/dev/kmsg", "w"); + if (!fp) { + vfprintf(stderr, fmt, ap); + goto done; + } + + fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio); + vfprintf(fp, fmt, ap); + fclose(fp); + + if (debug) { + va_end(ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); } - vsyslog(prio, fmt, ap); done: va_end(ap); } diff --git a/src/serv.c b/src/serv.c index 64fe864a..b6579be8 100644 --- a/src/serv.c +++ b/src/serv.c @@ -24,6 +24,7 @@ #include "config.h" #include +#include /* Definition of AT_* constants */ #include #include #include @@ -168,6 +169,30 @@ int serv_disable(char *arg) return remove(link) != 0; } +int serv_touch(char *arg) +{ + char corr[40]; + + if (!strstr(arg, ".conf")) { + snprintf(corr, sizeof(corr), "%s.conf", arg); + arg = corr; + } + + pushd(FINIT_RCSD); + if (!fexist(arg)) { + popd(); + if (!strstr(arg, "finit.conf")) + errx(1, "Service %s is not enabled", arg); + arg = FINIT_CONF; + } + + /* libite:touch() follows symlinks */ + if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW)) + err(1, "Failed marking %s for reload", arg); + + return 0; +} + /** * Local Variables: * indent-tabs-mode: t diff --git a/src/serv.h b/src/serv.h index 9692d4cc..0fc46249 100644 --- a/src/serv.h +++ b/src/serv.h @@ -27,6 +27,7 @@ int serv_list (char *arg); int serv_enable (char *arg); int serv_disable (char *arg); +int serv_touch (char *arg); #endif /* FINIT_SERV_H_ */ diff --git a/src/service.c b/src/service.c index 09ad7e92..74fb9410 100644 --- a/src/service.c +++ b/src/service.c @@ -474,7 +474,7 @@ void service_runlevel(int newlevel) * @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2) * @cfg: Configuration, complete command, with -- for description text * @rlimit: Limits for this service/task/run/inetd, may be global limits - * @mtime: The modification time if service is loaded from /etc/finit.d + * @file: The file name service was loaded from * * This function is used to register commands to be run on different * system runlevels with optional username. The @type argument details @@ -521,7 +521,7 @@ void service_runlevel(int newlevel) * Returns: * POSIX OK(0) on success, or non-zero errno exit status on failure. */ -int service_register(int type, char *cfg, struct rlimit rlimit[], struct timeval *mtime) +int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) { int i = 0; int id = -1; @@ -727,9 +727,13 @@ recreate: memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit)); /* New, recently modified or unchanged ... used on reload. */ - svc_check_dirty(svc, mtime); - free(line); + if (file && conf_changed(file)) + svc_mark_dirty(svc); + else + svc_mark_clean(svc); + /* Free duped line, from above */ + free(line); return 0; } @@ -1023,11 +1027,12 @@ void service_step_all(int types) */ void service_runtask_clean(void) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc_is_runtask(svc)) continue; + svc->once = 0; if (svc->state == SVC_DONE_STATE) svc_set_state(svc, SVC_HALTED_STATE); @@ -1051,9 +1056,9 @@ void service_runtask_clean(void) */ int service_runtask_completed(int skip) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc_is_runtask(svc)) continue; @@ -1094,7 +1099,6 @@ void service_bootstrap_cb(uev_t *w, void *arg, int events) finalize(); } - /** * Local Variables: * indent-tabs-mode: t diff --git a/src/service.h b/src/service.h index 5e70751c..00be550d 100644 --- a/src/service.h +++ b/src/service.h @@ -28,7 +28,7 @@ #include "svc.h" void service_runlevel (int newlevel); -int service_register (int type, char *line, struct rlimit rlimit[], struct timeval *mtime); +int service_register (int type, char *line, struct rlimit rlimit[], char *file); void service_unregister (svc_t *svc); void service_runtask_clean (void); diff --git a/src/sm.c b/src/sm.c index 4d90a941..2d3e6fc8 100644 --- a/src/sm.c +++ b/src/sm.c @@ -95,7 +95,8 @@ void sm_step(sm_t *sm) restart: old_state = sm->state; - _d("state: %s", sm_status(sm->state)); + _d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d", + sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload); switch (sm->state) { case SM_BOOTSTRAP_STATE: @@ -143,7 +144,7 @@ restart: /* Make sure to (re)load all *.conf in /etc/finit.d/ */ if (runlevel != 0 && runlevel != 6) - conf_reload_dynamic(); + conf_reload(); /* Reset once flag of runtasks */ service_runtask_clean(); @@ -198,7 +199,7 @@ restart: case SM_RELOAD_CHANGE_STATE: /* First reload all *.conf in /etc/finit.d/ */ - conf_reload_dynamic(); + conf_reload(); /* * Then, mark all affected service conditions as in-flux and diff --git a/src/svc.c b/src/svc.c index 254845bb..d5c1a01d 100644 --- a/src/svc.c +++ b/src/svc.c @@ -27,6 +27,7 @@ #include #include #include +#include /* BSD sys/queue.h API */ #include "finit.h" #include "svc.h" @@ -35,38 +36,7 @@ /* Each svc_t needs a unique job# */ static int jobcounter = 1; -static svc_t *workaround = NULL; - -static svc_t *__connect_shm(void) -{ - svc_t *list; - - list = finit_svc_connect(); - if (!list) { - if (workaround) - return workaround; - - /* Linux not built with CONFIG_SYSVIPC, or libc does not support shmat()/shmget() */ - if (ENOSYS == errno) - warn("Kernel does support SYSV shmat() IPC, error %d", errno); - - /* Try to prevent PID 1 from aborting, issue #81 */ - if (getpid() == 1) { - warnx("Implementing PID 1 workaround, initctl tool will not work ..."); - if (!workaround) - workaround = calloc(MAX_NUM_SVC, sizeof(svc_t)); - - list = workaround; - } - } - - if (!list) { - warn("Failed connecting to shared memory, error %d", errno); - abort(); - } - - return list; -} +static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list); /** * svc_new - Create a new service @@ -79,11 +49,12 @@ static svc_t *__connect_shm(void) */ svc_t *svc_new(char *cmd, int id, int type) { - int i, job = -1; - svc_t *svc, *list = __connect_shm(); + int job = -1; + char *desc; + svc_t *svc, *iter = NULL; /* Find first job n:o if registering multiple instances */ - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!strcmp(svc->cmd, cmd)) { job = svc->job; break; @@ -92,32 +63,26 @@ svc_t *svc_new(char *cmd, int id, int type) if (job == -1) job = jobcounter++; - for (i = 0; i < MAX_NUM_SVC; i++) { - svc_t *svc = &list[i]; + svc = calloc(1, sizeof(*svc)); + if (!svc) + return NULL; - if (svc->type == SVC_TYPE_FREE) { - char *desc; + svc->type = type; + svc->job = job; + svc->id = id; + strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); - memset(svc, 0, sizeof(*svc)); - svc->type = type; - svc->job = job; - svc->id = id; - strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); + /* Default description, if missing */ + desc = rindex(cmd, '/'); + if (desc) + desc++; + else + desc = cmd; + strlcpy(svc->desc, desc, sizeof(svc->desc)); - /* Default description, if missing */ - desc = rindex(cmd, '/'); - if (desc) - desc++; - else - desc = cmd; - strlcpy(svc->desc, desc, sizeof(svc->desc)); + TAILQ_INSERT_TAIL(&svc_list, svc, link); - return svc; - } - } - - errno = ENOMEM; - return NULL; + return svc; } /** @@ -129,50 +94,55 @@ svc_t *svc_new(char *cmd, int id, int type) */ int svc_del(svc_t *svc) { - svc->type = SVC_TYPE_FREE; + TAILQ_REMOVE(&svc_list, svc, link); + memset(svc, 0, sizeof(*svc)); + free(svc); + return 0; } /** * svc_iterator - Naive iterator over all registered services. - * @first: Get first &svc_t object, or next until end. + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next * * Returns: - * The first &svc_t when @first is set, otherwise the next &svc_t until - * the end when %NULL is returned. + * An &svc_t pointer, or %NULL when no more entries can be found. */ -svc_t *svc_iterator(int first) -{ - static int i = 0; - svc_t *list = __connect_shm(); - - if (first) - i = 0; - - while (i < MAX_NUM_SVC) { - svc_t *svc = &list[i++]; - - if (svc->type != SVC_TYPE_FREE) - return svc; - } - - return NULL; -} - - -/** - * svc_inetd_iterator - Naive iterator over all registered inetd services. - * @first: Get first &svc_t object, or next until end. - * - * Returns: - * The first inetd &svc_t when @first is set, otherwise the next - * inetd &svc_t until the end when %NULL is returned. - */ -svc_t *svc_inetd_iterator(int first) +svc_t *svc_iterator(svc_t **iter, int first) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + if (!iter) { + errno = EINVAL; + return NULL; + } + + if (first) + svc = TAILQ_FIRST(&svc_list); + else + svc = *iter; + + if (svc) + *iter = TAILQ_NEXT(svc, link); + + return svc; +} + +/** + * svc_inetd_iterator - Naive iterator over all registered inetd services. + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next + * + * Returns: + * The first inetd &svc_t when %NULL is given as argument, otherwise the + * next inetd &svc_t until the end when %NULL is returned. + */ +svc_t *svc_inetd_iterator(svc_t **iter, int first) +{ + svc_t *svc; + + for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { if (svc_is_inetd(svc)) return svc; } @@ -181,42 +151,22 @@ svc_t *svc_inetd_iterator(int first) } -/** - * svc_dynamic_iterator - Naive iterator over all registered dynamic services. - * @first: Get first &svc_t object, or next until end. - * - * Returns: - * The first dynamically loaded &svc_t when @first is set, otherwise the - * next dynamically loaded &svc_t until the end when %NULL is returned. - */ -svc_t *svc_dynamic_iterator(int first) -{ - svc_t *svc; - - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { - if (svc->mtime.tv_sec) - return svc; - } - - return NULL; -} - - /** * svc_named_iterator - Iterates over all instances of a service. - * @first: Get first &svc_t object, or next until end. + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next * @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 + * The first matching &svc_t when %NULL is given as argument, otherwise + * the next &svc_t with the same @cmd name until the end when %NULL is * returned. */ -svc_t *svc_named_iterator(int first, char *cmd) +svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { char *name = basename(svc->cmd); if (!strncmp(name, cmd, strlen(name))) @@ -229,19 +179,20 @@ svc_t *svc_named_iterator(int first, char *cmd) /** * svc_job_iterator - Iterates over all instances of a service. - * @first: Get first &svc_t object, or next until end. + * @iter: Iterator, must be a valid pointer + * @first: If set, get first &svc_t, otherwise get next * @job: Job to look for. * * Returns: - * The first matching &svc_t when @first is set, otherwise the next - * &svc_t instance with the same @job until the end when %NULL is + * The first matching &svc_t when %NULL is given as argument, otherwise + * the next &svc_t with the same @job ID until the end when %NULL is * returned. */ -svc_t *svc_job_iterator(int first, int job) +svc_t *svc_job_iterator(svc_t **iter, int first, int job) { svc_t *svc; - for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) { if (svc->job == job) return svc; } @@ -256,28 +207,12 @@ svc_t *svc_job_iterator(int first, int job) */ void svc_foreach(void (*cb)(svc_t *)) { - svc_t *svc; + svc_t *svc, *iter = NULL; if (!cb) return; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) - cb(svc); -} - - -/** - * svc_foreach_dynamic - Run a callback for each registered dynamic service - * @cb: Callback to run for each dynamic service - */ -void svc_foreach_dynamic(void (*cb)(svc_t *)) -{ - svc_t *svc; - - if (!cb) - return; - - for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0)) + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) cb(svc); } @@ -289,12 +224,12 @@ void svc_foreach_dynamic(void (*cb)(svc_t *)) */ void svc_foreach_type(int types, void (*cb)(svc_t *)) { - svc_t *svc; + svc_t *svc, *iter = NULL; if (!cb) return; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!(svc->type & types)) continue; @@ -312,9 +247,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *)) */ svc_t *svc_stop_completed(void) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->state == SVC_STOPPING_STATE) return svc; } @@ -331,9 +266,9 @@ svc_t *svc_stop_completed(void) */ svc_t *svc_find(char *cmd, int id) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) return svc; } @@ -350,9 +285,9 @@ svc_t *svc_find(char *cmd, int id) */ svc_t *svc_find_by_pid(pid_t pid) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->pid == pid) return svc; } @@ -370,9 +305,9 @@ svc_t *svc_find_by_pid(pid_t pid) */ svc_t *svc_find_by_jobid(int job, int id) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->job == job && svc->id == id) return svc; } @@ -391,9 +326,9 @@ svc_t *svc_find_by_jobid(int job, int id) svc_t *svc_find_by_nameid(char *name, int id) { char *ptr; - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { ptr = strrchr(svc->cmd, '/'); if (ptr) ptr++; @@ -419,12 +354,10 @@ svc_t *svc_find_by_nameid(char *name, int id) */ void svc_mark_dynamic(void) { - svc_t *svc = svc_dynamic_iterator(1); + svc_t *svc, *iter = NULL; - while (svc) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) *((int *)&svc->dirty) = -1; - svc = svc_dynamic_iterator(0); - } } void svc_mark_dirty(svc_t *svc) @@ -437,17 +370,6 @@ void svc_mark_clean(svc_t *svc) *((int *)&svc->dirty) = 0; } -void svc_check_dirty(svc_t *svc, struct timeval *mtime) -{ - if (mtime && timercmp(&svc->mtime, mtime, !=)) - svc_mark_dirty(svc); - else - svc_mark_clean(svc); - - svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0; - svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0; -} - /** * svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d * @cb: Callback to run for each stale service @@ -457,14 +379,13 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime) */ void svc_clean_dynamic(void (*cb)(svc_t *)) { - svc_t *svc = svc_dynamic_iterator(1); + svc_t *svc, *iter = NULL; - while (svc) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (svc->dirty == -1 && cb) { cb(svc); svc_mark_clean(svc); } - svc = svc_dynamic_iterator(0); } } @@ -495,9 +416,9 @@ int svc_clean_bootstrap(svc_t *svc) */ void svc_prune_bootstrap(void) { - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!svc->pid) svc_clean_bootstrap(svc); } @@ -526,77 +447,13 @@ int svc_enabled(svc_t *svc) return 1; } -char *svc_status(svc_t *svc) -{ - switch (svc->state) { - case SVC_HALTED_STATE: - switch (svc->block) { - case SVC_BLOCK_NONE: - return "halted"; - - case SVC_BLOCK_MISSING: - return "missing"; - - case SVC_BLOCK_CRASHING: - return "crashed"; - - case SVC_BLOCK_USER: - return "stopped"; - - case SVC_BLOCK_BUSY: - return "busy"; - - case SVC_BLOCK_RESTARTING: - return "restart"; - } - - case SVC_DONE_STATE: - return "done"; - - case SVC_STOPPING_STATE: - switch (svc->type) { - case SVC_TYPE_INETD_CONN: - case SVC_TYPE_RUN: - case SVC_TYPE_TASK: - return "active"; - - default: - return "stopping"; - } - - case SVC_WAITING_STATE: - return "waiting"; - - case SVC_READY_STATE: - return "ready"; - - case SVC_RUNNING_STATE: - return "running"; - - default: - return "UNKNOWN"; - } -} - -const char *svc_dirtystr(svc_t *svc) -{ - if (svc_is_removed(svc)) - return "removed"; - if (svc_is_updated(svc)) - return "updated"; - if (svc_is_changed(svc)) - return "UNKNOWN"; - - return "clean"; -} - /* Same base service, return unique ID */ int svc_next_id(char *cmd) { int id = 0; - svc_t *svc; + svc_t *svc, *iter = NULL; - for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { if (!strcmp(svc->cmd, cmd) && id < svc->id) id = svc->id; } @@ -606,12 +463,10 @@ int svc_next_id(char *cmd) 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]; + svc_t *s, *iter = NULL; + int unique = 1; + for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) { if (svc->type == SVC_TYPE_FREE) continue; @@ -642,21 +497,21 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun token = strtok_r(input, " ", &pos); while (token) { int id = 1; - svc_t *svc; + svc_t *svc, *iter = NULL; char *ptr = strchr(token, ':'); if (isdigit(token[0])) { int job = atonum(token); if (!ptr) { - svc = svc_job_iterator(1, job); + svc = svc_job_iterator(&iter, 1, job); if (!svc && not_found) result += not_found(NULL, job); while (svc) { if (found) result += found(svc); - svc = svc_job_iterator(0, job); + svc = svc_job_iterator(&iter, 0, job); } } else { *ptr++ = 0; @@ -671,14 +526,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun } } else { if (!ptr) { - svc = svc_named_iterator(1, token); + svc = svc_named_iterator(&iter, 1, token); if (!svc && not_found) result += not_found(token, id); while (svc) { if (found) result += found(svc); - svc = svc_named_iterator(0, token); + svc = svc_named_iterator(&iter, 0, token); } } else { *ptr++ = 0; diff --git a/src/svc.h b/src/svc.h index e4c19b4c..6242b770 100644 --- a/src/svc.h +++ b/src/svc.h @@ -27,9 +27,9 @@ #include /* IPC_CREAT */ #include -#include /* shmat() */ #include /* pid_t */ #include +#include /* BSD sys/queue.h API */ #include "inetd.h" #include "helpers.h" @@ -66,13 +66,11 @@ typedef enum { SVC_BLOCK_RESTARTING, } svc_block_t; -#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */ #define MAX_ARG_LEN 64 #define MAX_STR_LEN 64 #define MAX_COND_LEN (MAX_ARG_LEN * 3) #define MAX_USER_LEN 16 #define MAX_NUM_FDS 64 /* Max number of I/O plugins */ -#define MAX_NUM_SVC 64 /* Enough? */ #define MAX_NUM_SVC_ARGS 32 /* @@ -82,6 +80,8 @@ typedef enum { * initctl service */ typedef struct svc { + TAILQ_ENTRY(svc) link; + /* Instance specifics */ int job, id; /* JOB:ID */ @@ -92,9 +92,7 @@ typedef struct svc { pid_t pid; const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */ svc_type_t type; /* Service, run, task, inetd, ... */ - struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */ - const int dirty; /* Set if old mtime != new mtime => reloaded, - * or -1 when marked for removal */ + const int dirty; /* -1: removal, 0: unmodified, 1: modified */ int starting; /* ... waiting for pidfile to be re-asserted */ int runlevels; int sighup; /* This service supports SIGHUP :) */ @@ -129,23 +127,6 @@ typedef struct svc { void (*timer_cb)(struct svc *svc); } svc_t; -typedef struct svc_map svc_map_t; - -/* Put services array in shm */ -static inline svc_t *finit_svc_connect(void) -{ - static void *ptr = (void *)-1; - - if ((void *)-1 == ptr) { - ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC, - 0600 | IPC_CREAT), NULL, 0); - if ((void *)-1 == ptr) - return NULL; - } - - return (svc_t *)ptr; -} - svc_t *svc_new (char *cmd, int id, int type); int svc_del (svc_t *svc); @@ -154,20 +135,17 @@ svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_jobid (int job, int id); svc_t *svc_find_by_nameid (char *name, 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); -svc_t *svc_job_iterator (int first, int job); +svc_t *svc_iterator (svc_t **iter, int first); +svc_t *svc_inetd_iterator (svc_t **iter, int first); +svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd); +svc_t *svc_job_iterator (svc_t **iter, int first, int job); void svc_foreach (void (*cb)(svc_t *)); -void svc_foreach_dynamic (void (*cb)(svc_t *)); void svc_foreach_type (int types, void (*cb)(svc_t *)); svc_t *svc_stop_completed (void); void svc_mark_dynamic (void); -void svc_check_dirty (svc_t *svc, struct timeval *mtime); void svc_mark_dirty (svc_t *svc); void svc_mark_clean (svc_t *svc); void svc_clean_dynamic (void (*cb)(svc_t *)); @@ -175,8 +153,6 @@ int svc_clean_bootstrap (svc_t *svc); void svc_prune_bootstrap (void); int svc_enabled (svc_t *svc); -char *svc_status (svc_t *svc); -const char *svc_dirtystr (svc_t *svc); int svc_next_id (char *cmd); int svc_is_unique (svc_t *svc); @@ -189,7 +165,6 @@ static inline void svc_starting (svc_t *svc) { svc->starting = 1; } static inline void svc_started (svc_t *svc) { svc->starting = 0; } static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; } -static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; } static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; } static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; } static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; } @@ -209,6 +184,70 @@ static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; } static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; } +static inline char *svc_status(svc_t *svc) +{ + switch (svc->state) { + case SVC_HALTED_STATE: + switch (svc->block) { + case SVC_BLOCK_NONE: + return "halted"; + + case SVC_BLOCK_MISSING: + return "missing"; + + case SVC_BLOCK_CRASHING: + return "crashed"; + + case SVC_BLOCK_USER: + return "stopped"; + + case SVC_BLOCK_BUSY: + return "busy"; + + case SVC_BLOCK_RESTARTING: + return "restart"; + } + + case SVC_DONE_STATE: + return "done"; + + case SVC_STOPPING_STATE: + switch (svc->type) { + case SVC_TYPE_INETD_CONN: + case SVC_TYPE_RUN: + case SVC_TYPE_TASK: + return "active"; + + default: + return "stopping"; + } + + case SVC_WAITING_STATE: + return "waiting"; + + case SVC_READY_STATE: + return "ready"; + + case SVC_RUNNING_STATE: + return "running"; + + default: + return "UNKNOWN"; + } +} + +static inline const char *svc_dirtystr(svc_t *svc) +{ + if (svc_is_removed(svc)) + return "removed"; + if (svc_is_updated(svc)) + return "updated"; + if (svc_is_changed(svc)) + return "UNKNOWN"; + + return "clean"; +} + #endif /* FINIT_SVC_H_ */ /** diff --git a/src/tty.c b/src/tty.c index ff2545ea..47be0a46 100644 --- a/src/tty.c +++ b/src/tty.c @@ -90,22 +90,8 @@ void tty_mark(void) { tty_node_t *tty; - LIST_FOREACH(tty, &tty_list, link) { - if (tty->mtime.tv_sec) - tty->dirty = -1; - } -} - -void tty_check(tty_node_t *tty, struct timeval *mtime) -{ - if (mtime && timercmp(&tty->mtime, mtime, !=)) - tty->dirty = 1; /* Modified, restart */ - else - tty->dirty = 0; /* Not modified */ - - /* Update mtime, if given */ - tty->mtime.tv_sec = mtime ? mtime->tv_sec : 0; - tty->mtime.tv_usec = mtime ? mtime->tv_usec : 0; + LIST_FOREACH(tty, &tty_list, link) + tty->dirty = -1; } void tty_sweep(void) @@ -113,14 +99,15 @@ void tty_sweep(void) tty_node_t *tty, *tmp; LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) { - if (tty->mtime.tv_sec && tty->dirty) { - _d("TTY %s dirty, stopping ...", tty->data.name); - tty_stop(&tty->data); + if (!tty->dirty) + continue; - if (tty->dirty == -1) { - _d("TTY %s removed, cleaning up.", tty->data.name); - tty_unregister(tty); - } + _d("TTY %s dirty, stopping ...", tty->data.name); + tty_stop(&tty->data); + + if (tty->dirty == -1) { + _d("TTY %s removed, cleaning up.", tty->data.name); + tty_unregister(tty); } } } @@ -129,7 +116,7 @@ void tty_sweep(void) * tty_register - Register a getty on a device * @line: Configuration, text after initial "tty" * @rlimit: Limits for this service/task/run/inetd, may be global limits - * @mtime: Modification time, to propagate to lower layers + * @file: The file name TTY was loaded from * * A Finit tty line can use the internal getty implementation or an * external one, like the BusyBox getty for instance. This function @@ -147,7 +134,7 @@ void tty_sweep(void) * Different getty implementations prefer the TTY device argument in * different order, so take care to investigate this first. */ -int tty_register(char *line, struct rlimit rlimit[], struct timeval *mtime) +int tty_register(char *line, struct rlimit rlimit[], char *file) { tty_node_t *entry; int insert = 0, noclear = 0, nowait = 0; @@ -282,7 +269,10 @@ int tty_register(char *line, struct rlimit rlimit[], struct timeval *mtime) /* Register configured limits */ memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit)); - tty_check(entry, mtime); + if (file && conf_changed(file)) + entry->dirty = 1; /* Modified, restart */ + else + entry->dirty = 0; /* Not modified */ _d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT "); return 0; diff --git a/src/tty.h b/src/tty.h index 50c6cd96..b739eaaa 100644 --- a/src/tty.h +++ b/src/tty.h @@ -54,7 +54,6 @@ typedef struct tty_node { finit_tty_t data; /* XXX: Yes, TTYs should be refactored into a separate SVC type. */ - struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */ int dirty; /* Set if old mtime != new mtime => reloaded, * or -1 when marked for removal */ } tty_node_t; @@ -62,10 +61,9 @@ typedef struct tty_node { //extern LIST_HEAD(, tty_node) tty_list; void tty_mark (void); -void tty_check (tty_node_t *tty, struct timeval *mtime); void tty_sweep (void); -int tty_register (char *line, struct rlimit rlimit[], struct timeval *mtime); +int tty_register (char *line, struct rlimit rlimit[], char *file); int tty_unregister (tty_node_t *tty); tty_node_t *tty_find (char *dev);