/* Legacy one-liner parser for /etc/finit.conf and the finit.d hierarchy * * NOTE: this parser is frozen at the Finit 4.x feature set. It is * kept, indefinitely, for backwards compatibility -- existing * configurations must keep working. All new directives and * options land in the libconfuse-based format, see conf.c * * Copyright (c) 2012-2025 Joachim Wiberg * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "config.h" /* Generated by configure script */ #include #include #include #include #ifdef _LIBITE_LITE # include # include /* BSD sys/queue.h API */ #else # include # include /* BSD sys/queue.h API */ #endif #include "finit.h" #include "conf.h" #include "legacy.h" #include "private.h" #include "service.h" #include "tty.h" #include "helpers.h" #include "util.h" /* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */ static void conf_parse_cgroup(char *line) { char config[strlen(line) + 1]; char *ptr, *name; name = strtok(line, " \t"); if (!name) return; if (strstr(name, "..") || strchr(name, '/')) return; /* illegal */ config[0] = 0; while ((ptr = strtok(NULL, " \t"))) { if (config[0]) strlcat(config, ",", sizeof(config)); strlcat(config, ptr, sizeof(config)); } cgroup_add(name, config, 0); } static int parse_static(char *line, int is_rcsd) { char cmd[CMD_SIZE]; char *x; if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) { if (hostname) free(hostname); hostname = strdup(strip_line(x)); return 0; } if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) { char *dev = strip_line(x); strcpy(cmd, "mknod "); strlcat(cmd, dev, sizeof(cmd)); run_interactive(cmd, "Creating device node %s", dev); return 0; } /* Kernel module to load */ if (BOOTSTRAP && MATCH_CMD(line, "module ", x)) { kmod_load(strip_line(x)); return 0; } if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) { if (network) free(network); network = strdup(strip_line(x)); return 0; } if (BOOTSTRAP && MATCH_CMD(line, "rcsd ", x)) { if (finit_rcsd) free(finit_rcsd); finit_rcsd = strdup(strip_line(x)); return 0; } if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) { if (runparts) free(runparts); runparts_progress = runparts_sysv = 0; while (x) { if (MATCH_CMD(x, "progress", x)) runparts_progress = 1; else if (MATCH_CMD(x, "sysv", x)) runparts_sysv = 1; else break; } runparts = strdup(strip_line(x)); return 0; } if (BOOTSTRAP && MATCH_CMD(line, "set ", x)) { conf_set_env(x); return 0; } if (MATCH_CMD(line, "include ", x)) { char *file = strip_line(x); strlcpy(cmd, file, sizeof(cmd)); if (!fexist(cmd)) { errx(1, "Cannot find include file %s, absolute path required!", x); return 1; } return conf_parse_file(cmd, is_rcsd); } if (MATCH_CMD(line, "log ", x)) { char *tok; static int size = 200000, count = 5; tok = strtok(x, ":= "); while (tok) { if (!strncmp(tok, "size", 4)) size = strtobytes(strtok(NULL, ":= ")); else if (!strncmp(tok, "count", 5)) count = strtobytes(strtok(NULL, ":= ")); tok = strtok(NULL, ":= "); } if (size >= 0) logfile_size_max = size; if (count >= 0) logfile_count_max = count; return 0; } if (MATCH_CMD(line, "shutdown ", x)) { if (sdown) free(sdown); sdown = strdup(strip_line(x)); return 0; } /* * 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 and 0 for halt/poweroff. */ if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) { char *token = strip_line(x); const char *err = NULL; cfglevel = strtonum(token, 1, 9, &err); if (err) cfglevel = RUNLEVEL; if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6) cfglevel = 2; /* Fallback */ return 0; } /* * Before Finit 5 the default readiness notification is PID. * This default can be changed by setting 'readiness none' in * the file /etc/finit.conf, only read once at bootstrap. */ if (BOOTSTRAP && MATCH_CMD(line, "readiness ", x)) { char *token = strip_line(x); if (!strcmp(token, "none")) readiness = SVC_NOTIFY_NONE; } if (MATCH_CMD(line, "reboot-delay ", x)) { syncsec = strtonum(strip_line(x), 0, 60, NULL); return 0; } if (MATCH_CMD(line, "reboot-watchdog ", x)) { wdtreboot = istrue(strip_line(x), 0); return 0; } /* * Periodic check and instability index leveler, seconds */ if (MATCH_CMD(line, "service-interval ", x)) { char *token = strip_line(x); const char *err = NULL; int val; /* 0 min to 1 day, should check at least daily */ val = strtonum(token, 0, 1440, &err); if (!err) { int disabled = !service_interval; service_interval = val * 1000; /* to milliseconds */ if (disabled) service_init(NULL); } return 0; } return 1; } static int parse_dynamic(char *line, struct rlimit rlimit[], char *file) { char *x; /* Monitored daemon, will be respawned on exit */ if (MATCH_CMD(line, "service ", x)) { service_register(SVC_TYPE_SERVICE, x, rlimit, file); return 0; } /* One-shot task, will not be respawned */ if (MATCH_CMD(line, "task ", x)) { service_register(SVC_TYPE_TASK, x, rlimit, file); return 0; } /* Like task but waits for completion, useful w/ [S] */ if (MATCH_CMD(line, "run ", x)) { service_register(SVC_TYPE_RUN, x, rlimit, file); return 0; } /* Similar to task but is treated like a SysV init script */ if (MATCH_CMD(line, "sysv ", x)) { service_register(SVC_TYPE_SYSV, x, rlimit, file); return 0; } /* Read resource limits */ if (MATCH_CMD(line, "rlimit ", x)) { conf_parse_rlimit(x, rlimit); return 0; } /* Read control group limits */ if (MATCH_CMD(line, "cgroup ", x)) { conf_parse_cgroup(x); return 0; } /* Set current cgroup for the following services/run/tasks */ if (MATCH_CMD(line, "cgroup.", x)) { char *group, *token, *saveptr; char settings[128] = {0}; /* Reset cgroup state */ cgroup_settings_current[0] = '\0'; cgroup_delegate_current = 0; /* First token is the group name (system/user/init) */ group = strtok_r(x, ",", &saveptr); if (group) strlcpy(cgroup_current, group, sizeof(cgroup_current)); else strlcpy(cgroup_current, x, sizeof(cgroup_current)); /* Parse any remaining comma-separated options */ while ((token = strtok_r(NULL, ",", &saveptr)) != NULL) { if (strncmp(token, "name:", 5) == 0) { /* Cgroup leaf name override - only valid in per-service directives */ warnx("cgroup %s: name: option not valid in global directive!", group); } else if (strcmp(token, "delegate") == 0) { cgroup_delegate_current = 1; } else { /* Other settings (cpu.weight:500, memory.max:1G, etc.) */ if (settings[0]) strlcat(settings, ",", sizeof(settings)); strlcat(settings, token, sizeof(settings)); } } if (settings[0]) strlcpy(cgroup_settings_current, settings, sizeof(cgroup_settings_current)); return 0; } /* Regular or serial TTYs to run getty */ if (MATCH_CMD(line, "tty ", x)) { service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file); return 0; } return 1; } /* * Parse FILE, or BUF when the frontend has already read and * instantiated it, i.e. for a template. Either way the text arriving * here is final, no %i is left to substitute. */ int legacy_parse_conf(char *file, char *buf, int is_rcsd) { struct rlimit rlimit[RLIMIT_NLIMITS]; FILE *fp; if (buf) fp = fmemopen(buf, strlen(buf), "r"); else fp = fopen(file, "r"); if (!fp) return 1; /* Prepare default limits and group for each service in /etc/finit.d/ */ if (is_rcsd) { memcpy(rlimit, global_rlimit, sizeof(rlimit)); cgroup_current[0] = 0; } dbg("*** Parsing %s", file); while (!feof(fp)) { char *line; line = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCCOMM); if (!line) continue; tabstospaces(line); if (!parse_static(line, is_rcsd)) ; else if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file)) ; else conf_set_env(line); free(line); } fclose(fp); return 0; } /** * Local Variables: * indent-tabs-mode: t * c-file-style: "linux" * End: */