diff --git a/README.md b/README.md index 6f04343a..0e68cc58 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Features include: startup, support for systemd [sd_notify()][], or [s6 style][] too * Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes) * Pre/Post script actions + * Rudimentary [templating support](doc/config.md#templating) * Tooling to enable/disable services * Built-in getty * Built-in watchdog, with support for hand-over to [watchdogd](https://troglobit.com/watchdogd.html) diff --git a/doc/config.md b/doc/config.md index 648d0459..311b92a3 100644 --- a/doc/config.md +++ b/doc/config.md @@ -9,6 +9,7 @@ Finit Configuration * [Service Environment](#service-environment) * [Service Synchronization](#service-synchronization) * [Service Wrapper Scripts](#service-wrapper-scripts) +* [Templating](#templating) * [Cgroups](#cgroups) * [Configuration File Syntax](#configuration-file-syntax) * [Hostname](#hostname) @@ -353,6 +354,26 @@ example employs a wrapper script in `/etc/start.d`. > instead of sending SIGHUP at restart/reload events. +Templating +---------- + +Finit comes with rudimentary support for templating, similar to that of +systemd. Best illustrated with an example: + + $ initctl show avahi-autoipd@ + service :%i avahi-autoipd --syslog %i -- ZeroConf for %i + +To enable ZeroConf for, e.g., eth0, use + + $ initctl enable avahi-autoipd@eth0.conf + +The enabled symlink will be set up to avahi-autoipd@.conf and every in‐ +stance of %i will in the instantiated directive be replaced with eth0. +Inspect the result with: + + $ initctl status avahi-autoipd:eth0 + + Cgroups ------- diff --git a/man/finit.conf.5 b/man/finit.conf.5 index c41c0f74..10666497 100644 --- a/man/finit.conf.5 +++ b/man/finit.conf.5 @@ -2,7 +2,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.Dd Apr 22, 2023 +.Dd Apr 24, 2023 .Dt FINIT.CONF 5 SMM .Os Linux .Sh NAME @@ -325,9 +325,10 @@ service notify:s6 mdevd -C -O 4 -D %n When a service is ready, either by Finit detecting its PID file, or their respective readiness mechanism has been triggered, Finit creates then service's ready condition which other services can depend on: - - $ initctl -v cond get service/mdevd/ready - on +.Bd -unfilled -offset indent +$ initctl -v cond get service/mdevd/ready +on +.Ed .El .Pp If a service should not be automatically started, it can be configured @@ -818,6 +819,31 @@ the example sets to denote that it doesn't support SIGHUP. That way Finit will stop/start the service instead of sending SIGHUP at restart/reload events. +.Sh TEMPLATING +Finit comes with rudimentary support for templating, similar to that of +systemd. Best illustrated with an example: +.Bd -unfilled -offset indent +$ initctl show avahi-autoipd@ +service :%i avahi-autoipd --syslog %i -- ZeroConf for %i +.Ed +.Pp +To enable ZeroConf for, e.g., +.Cm eth0 , +use +.Bd -unfilled -offset indent +$ initctl enable avahi-autoipd@eth0.conf +.Ed +.Pp +The enabled symlink will be set up to +.Cm avahi-autoipd@.conf +and every instance of +.Cm %i +will in the instantiated directive be replaced with +.Cm eth0 . +Inspect the result with: +.Bd -unfilled -offset indent +$ initctl status avahi-autoipd:eth0 +.Ed .Sh CGROUPS There are three major cgroup configuration directives: .Pp diff --git a/src/conf.c b/src/conf.c index cecad9ce..769744d2 100644 --- a/src/conf.c +++ b/src/conf.c @@ -914,11 +914,79 @@ static int parse_dynamic(char *line, struct rlimit rlimit[], char *file) return 1; } +/* + * Very simple and crude implementation, only supports '%i' + */ +static char *instantiate(char *line, char *name) +{ + char *ptr, *end = strchr(line, 0); + char *pos = line; + size_t num = 0; + + if (!name[0] || !end) + return line; + + while ((ptr = strchr(pos, '%'))) { + num++; + pos++; + } + + ptr = realloc(line, strlen(line) + num * strlen(name) + 1); + if (!ptr) + return line; + + pos = line = ptr; + while ((ptr = strchr(pos, '%'))) { + if (!strncmp(ptr, "%i", 2)) { + char *rest = &ptr[2]; + char *next = ptr + strlen(name); + + memmove(next, rest, strlen(rest) + 1); + memcpy(ptr, name, strlen(name)); + + pos += strlen(name); + } else + pos++; + } + + return line; +} + +static int is_template(const char *file, char *name, size_t len) +{ + char *ptr, *nm; + size_t i = 0; + + ptr = strchr(file, '@'); + if (!ptr) + return 0; /* not a template */ + + nm = ptr + 1; + ptr = strstr(nm, ".conf"); + if (!strcmp(nm, ".conf") || !ptr) + return 1; /* template itself or invalid */ + + while (nm < ptr && i < len - 1) + name[i++] = *nm++; + name[i] = 0; + + return 1; /* instantiated template */ +} + static int parse_conf(char *file, int is_rcsd) { struct rlimit rlimit[RLIMIT_NLIMITS]; + char name[65] = { 0 }; FILE *fp; + if (is_template(file, name, sizeof(name))) { + if (!name[0]) { + dbg("*** Skipping template file %s", file); + return 0; + } + dbg("*** instantiating %s from %s ...", name, file); + } + fp = fopen(file, "r"); if (!fp) return 1; @@ -938,7 +1006,9 @@ static int parse_conf(char *file, int is_rcsd) continue; tabstospaces(line); -// dbg("%s", line); +// dbg("raw: %s", line); + line = instantiate(line, name); +// dbg("ins: %s", line); if (!parse_static(line, is_rcsd)) ; diff --git a/src/serv.c b/src/serv.c index 9180664a..f190191e 100644 --- a/src/serv.c +++ b/src/serv.c @@ -253,18 +253,18 @@ static char *conf(char *path, size_t len, char *name, int creat) int serv_enable(char *arg) { + char src[strlen(arg) + 6]; size_t arglen, len; - char corr[40]; + char *ptr; if (!arg || !arg[0]) { WARNX("missing argument to enable, may be one of:"); return serv_list("available"); } - if (!strstr(arg, ".conf")) { - snprintf(corr, sizeof(corr), "%s.conf", arg); - arg = corr; - } + strlcpy(src, arg, sizeof(src)); + if (!strstr(src, ".conf")) + strlcat(src, ".conf", sizeof(src)); if (chdir(finit_rcsd)) ERR(72, "failed cd %s", finit_rcsd); @@ -272,13 +272,21 @@ int serv_enable(char *arg) if (icreate && mkdir("enabled", 0755) && EEXIST != errno) ERR(73, "failed creating %s/enabled directory", finit_rcsd); - len = snprintf(NULL, 0, "%s/available/%s", finit_rcsd, arg); + len = snprintf(NULL, 0, "%s/available/%s", finit_rcsd, src); arglen = snprintf(NULL, 0, "%s/enabled/%s", finit_rcsd, arg); char argpath[arglen + 1], path[len + 1]; - snprintf(path, sizeof(path), "%s/available/%s", finit_rcsd, arg); + snprintf(path, sizeof(path), "%s/available/%s", finit_rcsd, src); + + /* check for template instatiation */ + ptr = strchr(path, '@'); + if (ptr++) { + *ptr = 0; + strlcat(path, ".conf", sizeof(path)); + } + if (!fexist(path)) - ERRX(72, "cannot find %s", path); + ERRX(72, "cannot find %s%s", ptr ? "template ": "", path); snprintf(argpath, sizeof(argpath), "%s/enabled/%s", finit_rcsd, arg); if (fexist(argpath))