mirror of
https://github.com/troglobit/finit.git
synced 2026-10-18 10:59:34 +00:00
conf: cleanup and relocation of common parsing functions and helpers
- legacy.[ch]: strictly legacy .conf parser boilerplate only - conf.[ch]: .conf parser and generic configuration functionality Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
9 files changed
+831
-826
No files matched your search
@@ -26,6 +26,9 @@
|
||||
|
||||
#include <uev/uev.h>
|
||||
|
||||
#define CGROUP_NAME_SIZE 16
|
||||
#define CGROUP_SETTINGS_SIZE 128
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct svc svc_t;
|
||||
|
||||
|
||||
+653
-8
@@ -58,7 +58,9 @@
|
||||
#include <glob.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "devmon.h"
|
||||
#include "iwatch.h"
|
||||
#include "legacy.h"
|
||||
#include "private.h"
|
||||
@@ -66,8 +68,6 @@
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
|
||||
#define BOOTSTRAP (runlevel == INIT_LEVEL)
|
||||
|
||||
int runlevel = INIT_LEVEL; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
int cmdlevel = 0; /* runlevel override from cmdline */
|
||||
@@ -104,14 +104,19 @@ struct conf_change {
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct env_entry {
|
||||
TAILQ_ENTRY(env_entry) link;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static char cfg_errmsg[256];
|
||||
static struct iwatch iw_conf;
|
||||
static int iwatch_fd;
|
||||
static uev_t etcw;
|
||||
|
||||
static TAILQ_HEAD(, env_entry) env_list = TAILQ_HEAD_INITIALIZER(env_list);
|
||||
static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||
|
||||
static void drop_changes(void);
|
||||
|
||||
/*
|
||||
* libconfuse schema -- the new block format
|
||||
*
|
||||
@@ -288,14 +293,650 @@ static cfg_opt_t conf_opts[] = {
|
||||
CFG_END()
|
||||
};
|
||||
|
||||
static void drop_changes(void);
|
||||
|
||||
|
||||
static int validate_arg(const char *arg, const char *opt)
|
||||
{
|
||||
if (!arg) {
|
||||
errx(1, "option %s missing argument, skipping.", opt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* finit.cond = foo (=> <boot/foo>)
|
||||
* finit.config = /path/to/etc/alt-finit.conf
|
||||
* finit.debug = [on,off]
|
||||
* finit.fstab = /path/to/etc/fstab.aternative
|
||||
* finit.status = [on,off] (compat finit.show_status)
|
||||
* finit.status_style = [old,classic,modern]
|
||||
*/
|
||||
static void parse_finit_opts(char *opt)
|
||||
{
|
||||
char *arg;
|
||||
|
||||
arg = strchr(opt, '=');
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
if (string_compare(opt, "cond")) {
|
||||
if (validate_arg(arg, "finit.cond"))
|
||||
return;
|
||||
cond_boot_parse(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "config")) {
|
||||
if (validate_arg(arg, "finit.config"))
|
||||
return;
|
||||
if (finit_conf)
|
||||
free(finit_conf);
|
||||
finit_conf = strdup(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "debug")) {
|
||||
debug = istrue(arg, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "fstab")) {
|
||||
if (validate_arg(arg, "finit.fstab"))
|
||||
return;
|
||||
if (fstab)
|
||||
free(fstab);
|
||||
fstab = strdup(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status_style")) {
|
||||
if (validate_arg(arg, "finit.status_style"))
|
||||
return;
|
||||
|
||||
if (string_compare(arg, "old") || string_compare(arg, "classic"))
|
||||
show_progress(PROGRESS_CLASSIC);
|
||||
else
|
||||
show_progress(PROGRESS_MODERN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status") || string_compare(opt, "show_status")) {
|
||||
show_progress(istrue(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_fsck_opts(char *opt)
|
||||
{
|
||||
char *arg;
|
||||
|
||||
arg = strchr(opt, '=');
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
if (string_compare(opt, "mode")) {
|
||||
if (validate_arg(arg, "fsck.mode"))
|
||||
return;
|
||||
|
||||
if (!strcmp(arg, "skip"))
|
||||
fsck_mode = NULL;
|
||||
if (!strcmp(arg, "auto"))
|
||||
fsck_mode = "";
|
||||
if (!strcmp(arg, "force"))
|
||||
fsck_mode = "-f";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "repair")) {
|
||||
if (validate_arg(arg, "fsck.repair"))
|
||||
return;
|
||||
|
||||
if (!strcmp(arg, "no"))
|
||||
fsck_repair = "-n";
|
||||
if (!strcmp(arg, "preen"))
|
||||
fsck_repair = "-p";
|
||||
if (!strcmp(arg, "yes"))
|
||||
fsck_repair = "-y";
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When runlevel (single integer) is given on the command line,
|
||||
* it overrides the runlevel in finit.conf and the built-in
|
||||
* default (from configure). It do however have to pass the
|
||||
* same sanity checks.
|
||||
*/
|
||||
static int parse_runlevel(char *arg)
|
||||
{
|
||||
const char *err = NULL;
|
||||
char *ptr = arg;
|
||||
long long num;
|
||||
|
||||
/* Sanity check the token is just digit(s) */
|
||||
while (*ptr) {
|
||||
if (!isdigit(*ptr++))
|
||||
return 0;
|
||||
}
|
||||
|
||||
num = strtonum(arg, 1, 9, &err);
|
||||
if (err || num == 6) {
|
||||
dbg("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)num;
|
||||
}
|
||||
|
||||
static void parse_arg(char *arg)
|
||||
{
|
||||
if (!strncmp(arg, "finit.", 6)) {
|
||||
parse_finit_opts(&arg[6]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(arg, "fsck.", 5)) {
|
||||
parse_fsck_opts(&arg[5]);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef RESCUE_MODE
|
||||
if (string_compare(arg, "rescue") || string_compare(arg, "recover")) {
|
||||
rescue = 1;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (string_compare(arg, "single") || string_compare(arg, "S")) {
|
||||
single = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Put any new command line options before this line. */
|
||||
|
||||
cmdlevel = parse_runlevel(arg);
|
||||
}
|
||||
|
||||
#ifdef KERNEL_CMDLINE
|
||||
/*
|
||||
* Parse /proc/cmdline to find args for init. Don't use this!
|
||||
*
|
||||
* Instead, rely on the kernel to give Finit its arguments as
|
||||
* regular argc + argv[]. Only use this if the system you run
|
||||
* on has a broken initramfs system that cannot forward args
|
||||
* to Finit properly.
|
||||
*/
|
||||
static void parse_kernel_cmdline(void)
|
||||
{
|
||||
char line[LINE_SIZE], *cmdline, *tok;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
cmdline = chomp(line);
|
||||
dbg("%s", cmdline);
|
||||
|
||||
while ((tok = strtok(cmdline, " \t"))) {
|
||||
cmdline = NULL;
|
||||
parse_arg(tok);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
#else
|
||||
#define parse_kernel_cmdline()
|
||||
#endif
|
||||
|
||||
static void parse_kernel_loglevel(void)
|
||||
{
|
||||
char line[LINE_SIZE], *ptr;
|
||||
FILE *fp;
|
||||
int val;
|
||||
|
||||
fp = fopen("/proc/sys/kernel/printk", "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
ptr = chomp(line);
|
||||
dbg("%s", ptr);
|
||||
val = atoi(ptr);
|
||||
if (val >= 7)
|
||||
kerndebug = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kernel gives us all non-kernel options on our cmdline
|
||||
*/
|
||||
void conf_parse_cmdline(int argc, char *argv[])
|
||||
{
|
||||
const char *ptr;
|
||||
|
||||
/* Set up defaults */
|
||||
if ((ptr = FINIT_FSTAB))
|
||||
fstab = strdup(ptr);
|
||||
finit_conf = strdup(FINIT_CONF);
|
||||
finit_rcsd = strdup(FINIT_RCSD);
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
parse_arg(argv[i]);
|
||||
|
||||
parse_kernel_cmdline();
|
||||
parse_kernel_loglevel();
|
||||
}
|
||||
|
||||
/*
|
||||
* Sourced mainly by initctl and other Finit helper tools
|
||||
*/
|
||||
void conf_saverc(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
mkpath(_PATH_VARRUN "finit", 0755);
|
||||
fp = fopen(_PATH_VARRUN "finit/.initrc", "w");
|
||||
if (!fp) {
|
||||
err(1, "failed creating .finitrc");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "FINIT_CONF=%s\n", finit_conf);
|
||||
fprintf(fp, "FINIT_RCSD=%s\n", finit_rcsd);
|
||||
fprintf(fp, "FINIT_CGPATH=%s\n", FINIT_CGPATH);
|
||||
fprintf(fp, "INIT_SOCKET=%s\n", INIT_SOCKET);
|
||||
fprintf(fp, "INIT_MAGIC=0x%08x\n", INIT_MAGIC);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at bootstrap to log execution order (for debug)
|
||||
*/
|
||||
void conf_save_exec_order(svc_t *svc, char *cmdline, int result)
|
||||
{
|
||||
const char *fn = _PATH_VARRUN "finit/exec.order";
|
||||
const char *ststr = !result ? "[ OK ]" : "[FAIL]";
|
||||
static char *prepared = NULL;
|
||||
int first = !fexist(fn);
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(fn, "a+");
|
||||
if (!fp) {
|
||||
err(1, "failed writing to %s", fn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
fprintf(fp, "# Execution order of run/task/services at bootstrap\n");
|
||||
fprintf(fp, "# ST TYPE COMMAND LINE (DESC)\n");
|
||||
}
|
||||
|
||||
if (result == -1) {
|
||||
int len;
|
||||
|
||||
len = snprintf(NULL, 0, " %-7s %-64s (%s)", svc_typestr(svc), cmdline, svc->desc);
|
||||
if (len > 0) {
|
||||
if (prepared)
|
||||
free(prepared);
|
||||
prepared = malloc(++len);
|
||||
if (!prepared)
|
||||
goto done;
|
||||
snprintf(prepared, len, " %-7s %-64s (%s)", svc_typestr(svc), cmdline, svc->desc);
|
||||
}
|
||||
} else {
|
||||
if (prepared) {
|
||||
memcpy(prepared, ststr, strlen(ststr));
|
||||
fprintf(fp, "%s\n", prepared);
|
||||
free(prepared);
|
||||
prepared = NULL;
|
||||
} else {
|
||||
fprintf(fp, "%s %-7s %-64s (%s)\n", ststr, svc_typestr(svc), cmdline, svc->desc);
|
||||
}
|
||||
}
|
||||
done:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by plugins and similar that dynamically generate system services
|
||||
*/
|
||||
void conf_save_service(int type, char *cfg, char *file)
|
||||
{
|
||||
char fn[256];
|
||||
svc_t foo;
|
||||
FILE *fp;
|
||||
|
||||
mkpath(FINIT_RUNPATH_, 0755);
|
||||
snprintf(fn, sizeof(fn), "%s/%s", FINIT_RUNPATH_, file);
|
||||
if (fexist(fn))
|
||||
warnx("File %s already exists, overwriting.", fn);
|
||||
fp = fopen(fn, "w");
|
||||
if (!fp) {
|
||||
err(1, "Failed creating %s", fn);
|
||||
return;
|
||||
}
|
||||
|
||||
foo.type = type;
|
||||
fprintf(fp, "# Generated by finit:%s()\n", __func__);
|
||||
fprintf(fp, "%s %s\n", svc_typestr(&foo), cfg);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all environment variables read in parse_env(), they may be
|
||||
* removed now so let the next call to parse_env() restore them.
|
||||
*/
|
||||
void conf_reset_env(void)
|
||||
{
|
||||
struct env_entry *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &env_list, link, tmp) {
|
||||
TAILQ_REMOVE(&env_list, node, link);
|
||||
if (node->name) {
|
||||
unsetenv(node->name);
|
||||
free(node->name);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
if (!getenv("PATH"))
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
if (!getenv("SHELL"))
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
setenv("LOGNAME", "root", 1);
|
||||
setenv("USER", "root", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line
|
||||
* from finit.conf, or other .conf file. Note, PATH is always reset in
|
||||
* the conf_reset_env() function.
|
||||
*/
|
||||
void conf_set_env(char *line)
|
||||
{
|
||||
struct env_entry *node;
|
||||
char *key, *val;
|
||||
|
||||
key = conf_parse_env(line, &val);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
dbg("Global env '%s'='%s'", key, val);
|
||||
setenv(key, val, 1);
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
nomem:
|
||||
err(1, "Out of memory cannot track env vars");
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = strdup(key);
|
||||
if (!node->name) {
|
||||
free(node);
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
TAILQ_INSERT_HEAD(&env_list, node, link);
|
||||
}
|
||||
|
||||
/**
|
||||
* conf_parse_env - Parse a key=value line
|
||||
* @line: Line buffer without newline
|
||||
* @value: Whitespace trimmed value
|
||||
*
|
||||
* Used by service.c:source_env() and
|
||||
*
|
||||
* Returns:
|
||||
* %NULL on error, otherwise a whitespace trimmed key.
|
||||
*/
|
||||
char *conf_parse_env(char *line, char **value)
|
||||
{
|
||||
char *key, *val, *end;
|
||||
|
||||
/* skip any leading whitespace */
|
||||
key = line;
|
||||
while (isspace(*key))
|
||||
key++;
|
||||
|
||||
/* find end of line */
|
||||
end = key;
|
||||
while (*end)
|
||||
end++;
|
||||
|
||||
/* strip trailing whitespace */
|
||||
if (end > key) {
|
||||
end--;
|
||||
while (isspace(*end))
|
||||
*end-- = 0;
|
||||
}
|
||||
|
||||
val = strchr(key, '=');
|
||||
if (!val)
|
||||
return NULL;
|
||||
*val++ = 0;
|
||||
|
||||
/* strip leading whitespace from value */
|
||||
while (isspace(*val))
|
||||
val++;
|
||||
|
||||
/* unquote value, if quoted */
|
||||
unquote(&val, end);
|
||||
*value = val;
|
||||
|
||||
/* find end of key */
|
||||
end = key;
|
||||
while (*end)
|
||||
end++;
|
||||
|
||||
/* strip trailing whitespace */
|
||||
if (end > key) {
|
||||
end--;
|
||||
while (isspace(*end))
|
||||
*end-- = 0;
|
||||
}
|
||||
|
||||
/* strip any leading 'set ' */
|
||||
end = key;
|
||||
if (!strncmp(key, "set", 3))
|
||||
end += 3;
|
||||
|
||||
/* check key, no spaces allowed */
|
||||
while (*end && isspace(*end))
|
||||
end++;
|
||||
key = end;
|
||||
while (*end && !isspace(*end))
|
||||
end++;
|
||||
if (*end != 0) {
|
||||
warnx("'%s=%s': not a valid identifier", key, val);
|
||||
return NULL; /* invalid key */
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/* First form: `rlimit <hard|soft> RESOURCE LIMIT` */
|
||||
void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||
{
|
||||
char *level, *limit, *val;
|
||||
int resource = -1;
|
||||
rlim_t cfg;
|
||||
|
||||
level = strtok(line, " \t");
|
||||
if (!level)
|
||||
goto error;
|
||||
|
||||
limit = strtok(NULL, " \t");
|
||||
if (!limit)
|
||||
goto error;
|
||||
|
||||
val = strtok(NULL, " \t");
|
||||
if (!val) {
|
||||
/* Second form: `rlimit RESOURCE LIMIT` */
|
||||
val = limit;
|
||||
limit = level;
|
||||
level = "both";
|
||||
}
|
||||
|
||||
resource = str2rlim(limit);
|
||||
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
||||
goto error;
|
||||
|
||||
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
||||
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
||||
cfg = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *err = NULL;
|
||||
|
||||
cfg = strtonum(val, 0, (long long)2 << 31, &err);
|
||||
if (err) {
|
||||
logit(LOG_WARNING, "rlimit: invalid %s value: %s",
|
||||
rlim2str(resource), val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(level, "soft"))
|
||||
arr[resource].rlim_cur = cfg;
|
||||
else if (!strcmp(level, "hard"))
|
||||
arr[resource].rlim_max = cfg;
|
||||
else if (!strcmp(level, "both"))
|
||||
arr[resource].rlim_max = arr[resource].rlim_cur = cfg;
|
||||
else
|
||||
goto error;
|
||||
|
||||
return;
|
||||
error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||
int conf_parse_runlevels(const char *runlevels)
|
||||
{
|
||||
int i, not = 0, bitmask = 0;
|
||||
|
||||
if (!runlevels)
|
||||
runlevels = "[234]";
|
||||
i = 1;
|
||||
while (i) {
|
||||
int level;
|
||||
char lvl = runlevels[i++];
|
||||
|
||||
if (']' == lvl || 0 == lvl)
|
||||
break;
|
||||
if ('!' == lvl) {
|
||||
not = 1;
|
||||
bitmask = 0x7FE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('s' == lvl || 'S' == lvl)
|
||||
level = INIT_LEVEL;
|
||||
else
|
||||
level = lvl - '0';
|
||||
|
||||
if (level > INIT_LEVEL || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(bitmask, level);
|
||||
else
|
||||
SETBIT(bitmask, level);
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
void conf_parse_cond(svc_t *svc, char *cond)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
char *c;
|
||||
|
||||
if (!svc) {
|
||||
errx(1, "Invalid service pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default we assume UNIX daemons support SIGHUP */
|
||||
if (svc_is_daemon(svc))
|
||||
svc->sighup = 1;
|
||||
|
||||
if (!cond) {
|
||||
memset(svc->cond, 0, sizeof(svc->cond));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* First character must be '!' if:
|
||||
* - service: SIGHUP is not supported
|
||||
* - run/task: Do not block bootstrap
|
||||
*/
|
||||
ptr = cond;
|
||||
if (ptr[i] == '!') {
|
||||
ptr++;
|
||||
|
||||
if (svc_is_runtask(svc)) {
|
||||
/* see service_runtask_clean() */
|
||||
svc->sighup = 1;
|
||||
svc->once = 1;
|
||||
} else {
|
||||
svc->sighup = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (ptr[i] != '>' && ptr[i] != 0)
|
||||
i++;
|
||||
ptr[i] = 0;
|
||||
|
||||
if (i >= sizeof(svc->cond)) {
|
||||
logit(LOG_WARNING, "%s: too long list of conditions: %s", svc_ident(svc, NULL, 0), ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The '~' prefix means a reload of the upstream service is
|
||||
* propagated to this service -- it will be reloaded (SIGHUP)
|
||||
* or restarted (noreload), not just paused and resumed.
|
||||
* Syntax: <!~pid/foo,~pid/bar> or <~pid/foo>
|
||||
*
|
||||
* Strip '~' from each condition and set the service-level
|
||||
* flag. This allows '~' on any condition in the list.
|
||||
*/
|
||||
svc->cond[0] = 0;
|
||||
for (i = 0, c = strtok(ptr, ","); c; c = strtok(NULL, ","), i++) {
|
||||
if (c[0] == '~') {
|
||||
c++;
|
||||
svc->flux_reload = 1;
|
||||
}
|
||||
devmon_add_cond(c);
|
||||
if (i)
|
||||
strlcat(svc->cond, ",", sizeof(svc->cond));
|
||||
strlcat(svc->cond, c, sizeof(svc->cond));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try-parse error capture. Diagnostics are buffered, not printed:
|
||||
* on fallback to the legacy parser they are only debug logged. The
|
||||
* last message wins -- the parser stops at the first fatal error, so
|
||||
* the last callback before failure is the fatal one.
|
||||
*/
|
||||
static char cfg_errmsg[256];
|
||||
|
||||
static void cfg_error_cb(cfg_t *cfg, const char *fmt, va_list ap)
|
||||
{
|
||||
char msg[128];
|
||||
@@ -669,7 +1310,7 @@ static void env_translate(cfg_t *cfg, const char *section)
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s=%s", opt->name,
|
||||
cfg_opt_getnstr(opt, 0));
|
||||
legacy_parse_env(buf);
|
||||
conf_set_env(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,9 +1421,11 @@ static void conf_parse_statics(cfg_t *cfg)
|
||||
if (cfg_size(cfg, "service-interval")) {
|
||||
long val = cfg_getint(cfg, "service-interval");
|
||||
|
||||
/* 0 min to 1 day, should check at least daily */
|
||||
if (val >= 0 && val <= 1440) {
|
||||
int disabled = !service_interval;
|
||||
|
||||
/* milliseconds for libuEv timer */
|
||||
service_interval = (int)val * 1000;
|
||||
if (disabled)
|
||||
service_init(NULL);
|
||||
@@ -913,7 +1556,9 @@ static int is_new_format(char *file)
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse one Finit .conf file, in either format, see top of file.
|
||||
* Parse one Finit .conf file, in either format. The legacy .conf
|
||||
* include directive routes back through this so included files are
|
||||
* format-detected too.
|
||||
*/
|
||||
int conf_parse_file(char *file, int is_rcsd)
|
||||
{
|
||||
|
||||
+30
-22
@@ -27,6 +27,10 @@
|
||||
#include "cgroup.h"
|
||||
#include "svc.h"
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#define BOOTSTRAP (runlevel == INIT_LEVEL)
|
||||
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int cmdlevel;
|
||||
@@ -46,34 +50,38 @@ extern char *hostname;
|
||||
extern char *runparts;
|
||||
extern char *osheading;
|
||||
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
extern int runparts_progress;
|
||||
extern int runparts_sysv;
|
||||
|
||||
#define CGROUP_NAME_SIZE 16
|
||||
#define CGROUP_SETTINGS_SIZE 128
|
||||
extern int logfile_size_max;
|
||||
extern int logfile_count_max;
|
||||
|
||||
extern char cgroup_current[CGROUP_NAME_SIZE];
|
||||
extern char cgroup_settings_current[CGROUP_SETTINGS_SIZE];
|
||||
extern int cgroup_delegate_current;
|
||||
|
||||
extern struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||
extern struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||
extern char cgroup_current[CGROUP_NAME_SIZE];
|
||||
extern char cgroup_settings_current[CGROUP_SETTINGS_SIZE];
|
||||
extern int cgroup_delegate_current;
|
||||
|
||||
int str2rlim(char *str);
|
||||
char *rlim2str(int rlim);
|
||||
int conf_init (uev_ctx_t *ctx);
|
||||
int conf_reload (void);
|
||||
int conf_any_change (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (void);
|
||||
|
||||
int conf_init (uev_ctx_t *ctx);
|
||||
int conf_reload (void);
|
||||
int conf_any_change (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (void);
|
||||
void conf_saverc (void);
|
||||
void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
|
||||
void conf_save_service (int type, char *cfg, char *file);
|
||||
void conf_parse_cmdline (int argc, char *argv[]);
|
||||
|
||||
void conf_reset_env (void);
|
||||
void conf_saverc (void);
|
||||
void conf_save_exec_order (svc_t *svc, char *cmdline, int result);
|
||||
void conf_save_service (int type, char *cfg, char *file);
|
||||
void conf_parse_cmdline (int argc, char *argv[]);
|
||||
char *conf_parse_env (char *line, char **val);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
void conf_reset_env (void);
|
||||
void conf_set_env (char *line);
|
||||
char *conf_parse_env (char *line, char **val);
|
||||
|
||||
void conf_parse_rlimit (char *line, struct rlimit arr[]);
|
||||
int conf_parse_runlevels (const char *runlevels);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
int conf_parse_file (char *file, int is_rcsd);
|
||||
|
||||
#endif /* FINIT_CONF_H_ */
|
||||
|
||||
|
||||
@@ -470,6 +470,52 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
static int kmod_exists(const char *mod)
|
||||
{
|
||||
char buf[256];
|
||||
int found = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/modules", "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (!found && fgets(buf, sizeof(buf), fp)) {
|
||||
const char *kmod = strtok(buf, " \t");
|
||||
|
||||
if (kmod && !strcmp(kmod, mod))
|
||||
found = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a kernel module during bootstrap
|
||||
*/
|
||||
void kmod_load(char *mod)
|
||||
{
|
||||
char module[64] = { 0 };
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
if (runlevel != INIT_LEVEL)
|
||||
return;
|
||||
|
||||
/* Strip args for progress below and kmod_exists() */
|
||||
strlcpy(module, mod, sizeof(module));
|
||||
if (!strtok(module, " \t"))
|
||||
return;
|
||||
|
||||
if (kmod_exists(module))
|
||||
return;
|
||||
|
||||
strcpy(cmd, "modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
|
||||
run_interactive(cmd, "Loading kernel module %s", module);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bring up networking, but only if not single-user or rescue mode
|
||||
*/
|
||||
|
||||
@@ -71,6 +71,7 @@ int print_result (int fail);
|
||||
void print_exit (void);
|
||||
|
||||
void set_hostname (char **hostname);
|
||||
void kmod_load (char *mod);
|
||||
void networking (int updown);
|
||||
int in_container (void);
|
||||
void setprocnm (const char *name);
|
||||
@@ -113,6 +114,19 @@ static inline int dprint(int fd, const char *s, size_t len)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int istrue(char *arg, int defval)
|
||||
{
|
||||
if (!arg)
|
||||
goto fallback;
|
||||
|
||||
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
|
||||
return 1;
|
||||
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
|
||||
return 0;
|
||||
fallback:
|
||||
return defval;
|
||||
}
|
||||
|
||||
static inline char *fgetval(const char *line, const char *key, char *sep)
|
||||
{
|
||||
char *ptr, *str, *copy;
|
||||
|
||||
+3
-774
@@ -40,9 +40,7 @@
|
||||
# include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#endif
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "devmon.h"
|
||||
#include "legacy.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
@@ -50,775 +48,6 @@
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
|
||||
#define BOOTSTRAP (runlevel == INIT_LEVEL)
|
||||
|
||||
struct env_entry {
|
||||
TAILQ_ENTRY(env_entry) link;
|
||||
char *name;
|
||||
};
|
||||
static TAILQ_HEAD(, env_entry) env_list = TAILQ_HEAD_INITIALIZER(env_list);
|
||||
|
||||
static int get_bool(char *arg, int default_value)
|
||||
{
|
||||
if (!arg)
|
||||
goto fallback;
|
||||
|
||||
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
|
||||
return 1;
|
||||
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
|
||||
return 0;
|
||||
fallback:
|
||||
return default_value;
|
||||
}
|
||||
|
||||
static int validate_arg(char *arg, const char *opt)
|
||||
{
|
||||
if (!arg) {
|
||||
errx(1, "option %s missing argument, skipping.", opt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* finit.cond = foo (=> <boot/foo>)
|
||||
* finit.config = /path/to/etc/alt-finit.conf
|
||||
* finit.debug = [on,off]
|
||||
* finit.fstab = /path/to/etc/fstab.aternative
|
||||
* finit.status = [on,off] (compat finit.show_status)
|
||||
* finit.status_style = [old,classic,modern]
|
||||
*/
|
||||
static void parse_finit_opts(char *opt)
|
||||
{
|
||||
char *arg;
|
||||
|
||||
arg = strchr(opt, '=');
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
if (string_compare(opt, "cond")) {
|
||||
if (validate_arg(arg, "finit.cond"))
|
||||
return;
|
||||
cond_boot_parse(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "config")) {
|
||||
if (validate_arg(arg, "finit.config"))
|
||||
return;
|
||||
if (finit_conf)
|
||||
free(finit_conf);
|
||||
finit_conf = strdup(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "debug")) {
|
||||
debug = get_bool(arg, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "fstab")) {
|
||||
if (validate_arg(arg, "finit.fstab"))
|
||||
return;
|
||||
if (fstab)
|
||||
free(fstab);
|
||||
fstab = strdup(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status_style")) {
|
||||
if (validate_arg(arg, "finit.status_style"))
|
||||
return;
|
||||
|
||||
if (string_compare(arg, "old") || string_compare(arg, "classic"))
|
||||
show_progress(PROGRESS_CLASSIC);
|
||||
else
|
||||
show_progress(PROGRESS_MODERN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "status") || string_compare(opt, "show_status")) {
|
||||
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_fsck_opts(char *opt)
|
||||
{
|
||||
char *arg;
|
||||
|
||||
arg = strchr(opt, '=');
|
||||
if (arg)
|
||||
*arg++ = 0;
|
||||
|
||||
if (string_compare(opt, "mode")) {
|
||||
if (validate_arg(arg, "fsck.mode"))
|
||||
return;
|
||||
|
||||
if (!strcmp(arg, "skip"))
|
||||
fsck_mode = NULL;
|
||||
if (!strcmp(arg, "auto"))
|
||||
fsck_mode = "";
|
||||
if (!strcmp(arg, "force"))
|
||||
fsck_mode = "-f";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_compare(opt, "repair")) {
|
||||
if (validate_arg(arg, "fsck.repair"))
|
||||
return;
|
||||
|
||||
if (!strcmp(arg, "no"))
|
||||
fsck_repair = "-n";
|
||||
if (!strcmp(arg, "preen"))
|
||||
fsck_repair = "-p";
|
||||
if (!strcmp(arg, "yes"))
|
||||
fsck_repair = "-y";
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When runlevel (single integer) is given on the command line,
|
||||
* it overrides the runlevel in finit.conf and the built-in
|
||||
* default (from configure). It do however have to pass the
|
||||
* same sanity checks.
|
||||
*/
|
||||
static int parse_runlevel(char *arg)
|
||||
{
|
||||
const char *err = NULL;
|
||||
char *ptr = arg;
|
||||
long long num;
|
||||
|
||||
/* Sanity check the token is just digit(s) */
|
||||
while (*ptr) {
|
||||
if (!isdigit(*ptr++))
|
||||
return 0;
|
||||
}
|
||||
|
||||
num = strtonum(arg, 1, 9, &err);
|
||||
if (err || num == 6) {
|
||||
dbg("Not a valid runlevel (%s), valid levels are [1-9], excluding 6, skipping.", arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)num;
|
||||
}
|
||||
|
||||
static void parse_arg(char *arg)
|
||||
{
|
||||
if (!strncmp(arg, "finit.", 6)) {
|
||||
parse_finit_opts(&arg[6]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(arg, "fsck.", 5)) {
|
||||
parse_fsck_opts(&arg[5]);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef RESCUE_MODE
|
||||
if (string_compare(arg, "rescue") || string_compare(arg, "recover")) {
|
||||
rescue = 1;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (string_compare(arg, "single") || string_compare(arg, "S")) {
|
||||
single = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Put any new command line options before this line. */
|
||||
|
||||
cmdlevel = parse_runlevel(arg);
|
||||
}
|
||||
|
||||
#ifdef KERNEL_CMDLINE
|
||||
/*
|
||||
* Parse /proc/cmdline to find args for init. Don't use this!
|
||||
*
|
||||
* Instead, rely on the kernel to give Finit its arguments as
|
||||
* regular argc + argv[]. Only use this if the system you run
|
||||
* on has a broken initramfs system that cannot forward args
|
||||
* to Finit properly.
|
||||
*/
|
||||
static void parse_kernel_cmdline(void)
|
||||
{
|
||||
char line[LINE_SIZE], *cmdline, *tok;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
cmdline = chomp(line);
|
||||
dbg("%s", cmdline);
|
||||
|
||||
while ((tok = strtok(cmdline, " \t"))) {
|
||||
cmdline = NULL;
|
||||
parse_arg(tok);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
#else
|
||||
#define parse_kernel_cmdline()
|
||||
#endif
|
||||
|
||||
static void parse_kernel_loglevel(void)
|
||||
{
|
||||
char line[LINE_SIZE], *ptr;
|
||||
FILE *fp;
|
||||
int val;
|
||||
|
||||
fp = fopen("/proc/sys/kernel/printk", "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
ptr = chomp(line);
|
||||
dbg("%s", ptr);
|
||||
val = atoi(ptr);
|
||||
if (val >= 7)
|
||||
kerndebug = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kernel gives us all non-kernel options on our cmdline
|
||||
*/
|
||||
void conf_parse_cmdline(int argc, char *argv[])
|
||||
{
|
||||
const char *ptr;
|
||||
|
||||
/* Set up defaults */
|
||||
if ((ptr = FINIT_FSTAB))
|
||||
fstab = strdup(ptr);
|
||||
finit_conf = strdup(FINIT_CONF);
|
||||
finit_rcsd = strdup(FINIT_RCSD);
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
parse_arg(argv[i]);
|
||||
|
||||
parse_kernel_cmdline();
|
||||
parse_kernel_loglevel();
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all environment variables read in parse_env(), they may be
|
||||
* removed now so let the next call to parse_env() restore them.
|
||||
*/
|
||||
void conf_reset_env(void)
|
||||
{
|
||||
struct env_entry *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &env_list, link, tmp) {
|
||||
TAILQ_REMOVE(&env_list, node, link);
|
||||
if (node->name) {
|
||||
unsetenv(node->name);
|
||||
free(node->name);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
if (!getenv("PATH"))
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
if (!getenv("SHELL"))
|
||||
setenv("SHELL", _PATH_BSHELL, 1);
|
||||
setenv("LOGNAME", "root", 1);
|
||||
setenv("USER", "root", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sourced mainly by initctl and other Finit helper tools
|
||||
*/
|
||||
void conf_saverc(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
mkpath(_PATH_VARRUN "finit", 0755);
|
||||
fp = fopen(_PATH_VARRUN "finit/.initrc", "w");
|
||||
if (!fp) {
|
||||
err(1, "failed creating .finitrc");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "FINIT_CONF=%s\n", finit_conf);
|
||||
fprintf(fp, "FINIT_RCSD=%s\n", finit_rcsd);
|
||||
fprintf(fp, "FINIT_CGPATH=%s\n", FINIT_CGPATH);
|
||||
fprintf(fp, "INIT_SOCKET=%s\n", INIT_SOCKET);
|
||||
fprintf(fp, "INIT_MAGIC=0x%08x\n", INIT_MAGIC);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at bootstrap to log execution order (for debug)
|
||||
*/
|
||||
void conf_save_exec_order(svc_t *svc, char *cmdline, int result)
|
||||
{
|
||||
const char *fn = _PATH_VARRUN "finit/exec.order";
|
||||
const char *ststr = !result ? "[ OK ]" : "[FAIL]";
|
||||
static char *prepared = NULL;
|
||||
int first = !fexist(fn);
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(fn, "a+");
|
||||
if (!fp) {
|
||||
err(1, "failed writing to %s", fn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
fprintf(fp, "# Execution order of run/task/services at bootstrap\n");
|
||||
fprintf(fp, "# ST TYPE COMMAND LINE (DESC)\n");
|
||||
}
|
||||
|
||||
if (result == -1) {
|
||||
int len;
|
||||
|
||||
len = snprintf(NULL, 0, " %-7s %-64s (%s)", svc_typestr(svc), cmdline, svc->desc);
|
||||
if (len > 0) {
|
||||
if (prepared)
|
||||
free(prepared);
|
||||
prepared = malloc(++len);
|
||||
if (!prepared)
|
||||
goto done;
|
||||
snprintf(prepared, len, " %-7s %-64s (%s)", svc_typestr(svc), cmdline, svc->desc);
|
||||
}
|
||||
} else {
|
||||
if (prepared) {
|
||||
memcpy(prepared, ststr, strlen(ststr));
|
||||
fprintf(fp, "%s\n", prepared);
|
||||
free(prepared);
|
||||
prepared = NULL;
|
||||
} else {
|
||||
fprintf(fp, "%s %-7s %-64s (%s)\n", ststr, svc_typestr(svc), cmdline, svc->desc);
|
||||
}
|
||||
}
|
||||
done:
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by plugins and similar that dynamically generate system services
|
||||
*/
|
||||
void conf_save_service(int type, char *cfg, char *file)
|
||||
{
|
||||
char fn[256];
|
||||
svc_t foo;
|
||||
FILE *fp;
|
||||
|
||||
mkpath(FINIT_RUNPATH_, 0755);
|
||||
snprintf(fn, sizeof(fn), "%s/%s", FINIT_RUNPATH_, file);
|
||||
if (fexist(fn))
|
||||
warnx("File %s already exists, overwriting.", fn);
|
||||
fp = fopen(fn, "w");
|
||||
if (!fp) {
|
||||
err(1, "Failed creating %s", fn);
|
||||
return;
|
||||
}
|
||||
|
||||
foo.type = type;
|
||||
fprintf(fp, "# Generated by finit:%s()\n", __func__);
|
||||
fprintf(fp, "%s %s\n", svc_typestr(&foo), cfg);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* conf_parse_env - Parse a key=value line
|
||||
* @line: Line buffer without newline
|
||||
* @value: Whitespace trimmed value
|
||||
*
|
||||
* Returns:
|
||||
* %NULL on error, otherwise a whitespace trimmed key.
|
||||
*/
|
||||
char *conf_parse_env(char *line, char **value)
|
||||
{
|
||||
char *key, *val, *end;
|
||||
|
||||
/* skip any leading whitespace */
|
||||
key = line;
|
||||
while (isspace(*key))
|
||||
key++;
|
||||
|
||||
/* find end of line */
|
||||
end = key;
|
||||
while (*end)
|
||||
end++;
|
||||
|
||||
/* strip trailing whitespace */
|
||||
if (end > key) {
|
||||
end--;
|
||||
while (isspace(*end))
|
||||
*end-- = 0;
|
||||
}
|
||||
|
||||
val = strchr(key, '=');
|
||||
if (!val)
|
||||
return NULL;
|
||||
*val++ = 0;
|
||||
|
||||
/* strip leading whitespace from value */
|
||||
while (isspace(*val))
|
||||
val++;
|
||||
|
||||
/* unquote value, if quoted */
|
||||
unquote(&val, end);
|
||||
*value = val;
|
||||
|
||||
/* find end of key */
|
||||
end = key;
|
||||
while (*end)
|
||||
end++;
|
||||
|
||||
/* strip trailing whitespace */
|
||||
if (end > key) {
|
||||
end--;
|
||||
while (isspace(*end))
|
||||
*end-- = 0;
|
||||
}
|
||||
|
||||
/* strip any leading 'set ' */
|
||||
end = key;
|
||||
if (!strncmp(key, "set", 3))
|
||||
end += 3;
|
||||
|
||||
/* check key, no spaces allowed */
|
||||
while (*end && isspace(*end))
|
||||
end++;
|
||||
key = end;
|
||||
while (*end && !isspace(*end))
|
||||
end++;
|
||||
if (*end != 0) {
|
||||
warnx("'%s=%s': not a valid identifier", key, val);
|
||||
return NULL; /* invalid key */
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets, and makes a note of, all KEY=VALUE lines in a given .conf line
|
||||
* from finit.conf, or other .conf file. Note, PATH is always reset in
|
||||
* the conf_reset_env() function.
|
||||
*/
|
||||
void legacy_parse_env(char *line)
|
||||
{
|
||||
struct env_entry *node;
|
||||
char *key, *val;
|
||||
|
||||
key = conf_parse_env(line, &val);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
dbg("Global env '%s'='%s'", key, val);
|
||||
setenv(key, val, 1);
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
nomem:
|
||||
err(1, "Out of memory cannot track env vars");
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = strdup(key);
|
||||
if (!node->name) {
|
||||
free(node);
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
TAILQ_INSERT_HEAD(&env_list, node, link);
|
||||
}
|
||||
|
||||
static int kmod_exists(char *mod)
|
||||
{
|
||||
char buf[256];
|
||||
int found = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/proc/modules", "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (!found && fgets(buf, sizeof(buf), fp)) {
|
||||
char *kmod = strtok(buf, " \t");
|
||||
|
||||
if (kmod && !strcmp(kmod, mod))
|
||||
found = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void kmod_load(char *mod)
|
||||
{
|
||||
char module[64] = { 0 };
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
if (runlevel != INIT_LEVEL)
|
||||
return;
|
||||
|
||||
/* Strip args for progress below and kmod_exists() */
|
||||
strlcpy(module, mod, sizeof(module));
|
||||
if (!strtok(module, " \t"))
|
||||
return;
|
||||
|
||||
if (kmod_exists(module))
|
||||
return;
|
||||
|
||||
strcpy(cmd, "modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
|
||||
run_interactive(cmd, "Loading kernel module %s", module);
|
||||
}
|
||||
|
||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||
int conf_parse_runlevels(char *runlevels)
|
||||
{
|
||||
int i, not = 0, bitmask = 0;
|
||||
|
||||
if (!runlevels)
|
||||
runlevels = "[234]";
|
||||
i = 1;
|
||||
while (i) {
|
||||
int level;
|
||||
char lvl = runlevels[i++];
|
||||
|
||||
if (']' == lvl || 0 == lvl)
|
||||
break;
|
||||
if ('!' == lvl) {
|
||||
not = 1;
|
||||
bitmask = 0x7FE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('s' == lvl || 'S' == lvl)
|
||||
level = INIT_LEVEL;
|
||||
else
|
||||
level = lvl - '0';
|
||||
|
||||
if (level > INIT_LEVEL || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(bitmask, level);
|
||||
else
|
||||
SETBIT(bitmask, level);
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
void conf_parse_cond(svc_t *svc, char *cond)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
char *c;
|
||||
|
||||
if (!svc) {
|
||||
errx(1, "Invalid service pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default we assume UNIX daemons support SIGHUP */
|
||||
if (svc_is_daemon(svc))
|
||||
svc->sighup = 1;
|
||||
|
||||
if (!cond) {
|
||||
memset(svc->cond, 0, sizeof(svc->cond));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* First character must be '!' if:
|
||||
* - service: SIGHUP is not supported
|
||||
* - run/task: Do not block bootstrap
|
||||
*/
|
||||
ptr = cond;
|
||||
if (ptr[i] == '!') {
|
||||
ptr++;
|
||||
|
||||
if (svc_is_runtask(svc)) {
|
||||
/* see service_runtask_clean() */
|
||||
svc->sighup = 1;
|
||||
svc->once = 1;
|
||||
} else {
|
||||
svc->sighup = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (ptr[i] != '>' && ptr[i] != 0)
|
||||
i++;
|
||||
ptr[i] = 0;
|
||||
|
||||
if (i >= sizeof(svc->cond)) {
|
||||
logit(LOG_WARNING, "%s: too long list of conditions: %s", svc_ident(svc, NULL, 0), ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The '~' prefix means a reload of the upstream service is
|
||||
* propagated to this service -- it will be reloaded (SIGHUP)
|
||||
* or restarted (noreload), not just paused and resumed.
|
||||
* Syntax: <!~pid/foo,~pid/bar> or <~pid/foo>
|
||||
*
|
||||
* Strip '~' from each condition and set the service-level
|
||||
* flag. This allows '~' on any condition in the list.
|
||||
*/
|
||||
svc->cond[0] = 0;
|
||||
for (i = 0, c = strtok(ptr, ","); c; c = strtok(NULL, ","), i++) {
|
||||
if (c[0] == '~') {
|
||||
c++;
|
||||
svc->flux_reload = 1;
|
||||
}
|
||||
devmon_add_cond(c);
|
||||
if (i)
|
||||
strlcat(svc->cond, ",", sizeof(svc->cond));
|
||||
strlcat(svc->cond, c, sizeof(svc->cond));
|
||||
}
|
||||
}
|
||||
|
||||
struct rlimit_name {
|
||||
char *name;
|
||||
int val;
|
||||
};
|
||||
|
||||
static const struct rlimit_name rlimit_names[] = {
|
||||
{ "as", RLIMIT_AS },
|
||||
{ "core", RLIMIT_CORE },
|
||||
{ "cpu", RLIMIT_CPU },
|
||||
{ "data", RLIMIT_DATA },
|
||||
{ "fsize", RLIMIT_FSIZE },
|
||||
{ "locks", RLIMIT_LOCKS },
|
||||
{ "memlock", RLIMIT_MEMLOCK },
|
||||
{ "msgqueue", RLIMIT_MSGQUEUE },
|
||||
{ "nice", RLIMIT_NICE },
|
||||
{ "nofile", RLIMIT_NOFILE },
|
||||
{ "nproc", RLIMIT_NPROC },
|
||||
{ "rss", RLIMIT_RSS },
|
||||
{ "rtprio", RLIMIT_RTPRIO },
|
||||
#ifdef RLIMIT_RTTIME
|
||||
{ "rttime", RLIMIT_RTTIME },
|
||||
#endif
|
||||
{ "sigpending", RLIMIT_SIGPENDING },
|
||||
{ "stack", RLIMIT_STACK },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
int str2rlim(char *str)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (!strcmp(str, rn->name))
|
||||
return rn->val;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *rlim2str(int rlim)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (rn->val == rlim)
|
||||
return rn->name;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
char *lim2str(struct rlimit *rlim)
|
||||
{
|
||||
char tmp[25];
|
||||
static char buf[42];
|
||||
|
||||
buf[0] = 0;
|
||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
if (RLIM_INFINITY == rlim->rlim_max)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* First form: `rlimit <hard|soft> RESOURCE LIMIT` */
|
||||
void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||
{
|
||||
char *level, *limit, *val;
|
||||
int resource = -1;
|
||||
rlim_t cfg;
|
||||
|
||||
level = strtok(line, " \t");
|
||||
if (!level)
|
||||
goto error;
|
||||
|
||||
limit = strtok(NULL, " \t");
|
||||
if (!limit)
|
||||
goto error;
|
||||
|
||||
val = strtok(NULL, " \t");
|
||||
if (!val) {
|
||||
/* Second form: `rlimit RESOURCE LIMIT` */
|
||||
val = limit;
|
||||
limit = level;
|
||||
level = "both";
|
||||
}
|
||||
|
||||
resource = str2rlim(limit);
|
||||
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
||||
goto error;
|
||||
|
||||
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
||||
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
||||
cfg = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *err = NULL;
|
||||
|
||||
cfg = strtonum(val, 0, (long long)2 << 31, &err);
|
||||
if (err) {
|
||||
logit(LOG_WARNING, "rlimit: invalid %s value: %s",
|
||||
rlim2str(resource), val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(level, "soft"))
|
||||
arr[resource].rlim_cur = cfg;
|
||||
else if (!strcmp(level, "hard"))
|
||||
arr[resource].rlim_max = cfg;
|
||||
else if (!strcmp(level, "both"))
|
||||
arr[resource].rlim_max = arr[resource].rlim_cur = cfg;
|
||||
else
|
||||
goto error;
|
||||
|
||||
return;
|
||||
error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
/* cgroup NAME ctrl.prop:value,ctrl.prop:value ... */
|
||||
static void conf_parse_cgroup(char *line)
|
||||
@@ -897,7 +126,7 @@ static int parse_static(char *line, int is_rcsd)
|
||||
}
|
||||
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "set ", x)) {
|
||||
legacy_parse_env(x);
|
||||
conf_set_env(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -976,7 +205,7 @@ static int parse_static(char *line, int is_rcsd)
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "reboot-watchdog ", x)) {
|
||||
wdtreboot = get_bool(strip_line(x), 0);
|
||||
wdtreboot = istrue(strip_line(x), 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1190,7 +419,7 @@ int legacy_parse_conf(char *file, int is_rcsd)
|
||||
else if (!parse_dynamic(line, is_rcsd ? rlimit : global_rlimit, file))
|
||||
;
|
||||
else
|
||||
legacy_parse_env(line);
|
||||
conf_set_env(line);
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
+2
-21
@@ -24,27 +24,8 @@
|
||||
#ifndef FINIT_LEGACY_H_
|
||||
#define FINIT_LEGACY_H_
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
extern struct rlimit initial_rlimit[RLIMIT_NLIMITS];
|
||||
extern int runparts_progress;
|
||||
extern int runparts_sysv;
|
||||
|
||||
/*
|
||||
* legacy.c -- the frozen legacy one-liner parser. No new features
|
||||
* land here, only in the libconfuse format frontend in conf.c
|
||||
*/
|
||||
int legacy_parse_conf (char *file, int is_rcsd);
|
||||
void legacy_parse_env (char *line);
|
||||
void kmod_load (char *mod);
|
||||
void conf_parse_rlimit (char *line, struct rlimit arr[]);
|
||||
char *lim2str (struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* conf.c -- format-detecting frontend. The legacy include directive
|
||||
* routes back through this so included files are format-detected too.
|
||||
*/
|
||||
int conf_parse_file (char *file, int is_rcsd);
|
||||
int legacy_parse_conf (char *file, int is_rcsd);
|
||||
void legacy_parse_env (char *line);
|
||||
|
||||
#endif /* FINIT_LEGACY_H_ */
|
||||
|
||||
|
||||
+74
-1
@@ -43,8 +43,9 @@
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysinfo.h> /* sysinfo() */
|
||||
#include <sys/vfs.h> /* statfs */
|
||||
#include <sys/vfs.h> /* statfs */
|
||||
#include <linux/magic.h>
|
||||
#ifdef _LIBITE_LITE
|
||||
# include <libite/lite.h>
|
||||
@@ -108,6 +109,34 @@ static char *signames[] = {
|
||||
"SYS",
|
||||
};
|
||||
|
||||
struct rlimit_name {
|
||||
char *name;
|
||||
int val;
|
||||
};
|
||||
|
||||
static const struct rlimit_name rlimit_names[] = {
|
||||
{ "as", RLIMIT_AS },
|
||||
{ "core", RLIMIT_CORE },
|
||||
{ "cpu", RLIMIT_CPU },
|
||||
{ "data", RLIMIT_DATA },
|
||||
{ "fsize", RLIMIT_FSIZE },
|
||||
{ "locks", RLIMIT_LOCKS },
|
||||
{ "memlock", RLIMIT_MEMLOCK },
|
||||
{ "msgqueue", RLIMIT_MSGQUEUE },
|
||||
{ "nice", RLIMIT_NICE },
|
||||
{ "nofile", RLIMIT_NOFILE },
|
||||
{ "nproc", RLIMIT_NPROC },
|
||||
{ "rss", RLIMIT_RSS },
|
||||
{ "rtprio", RLIMIT_RTPRIO },
|
||||
#ifdef RLIMIT_RTTIME
|
||||
{ "rttime", RLIMIT_RTTIME },
|
||||
#endif
|
||||
{ "sigpending", RLIMIT_SIGPENDING },
|
||||
{ "stack", RLIMIT_STACK },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/* https://freedesktop.org/software/systemd/man/systemd.exec.html#id-1.20.8 */
|
||||
static char *exitcodes[] = {
|
||||
"SUCCESS", /* 0: Std C exit OK */
|
||||
@@ -483,6 +512,50 @@ char *code2str(int code)
|
||||
return exitcodes[code];
|
||||
}
|
||||
|
||||
int str2rlim(char *str)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (!strcmp(str, rn->name))
|
||||
return rn->val;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *rlim2str(int rlim)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (rn->val == rlim)
|
||||
return rn->name;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
char *lim2str(struct rlimit *rlim)
|
||||
{
|
||||
char tmp[25];
|
||||
static char buf[42];
|
||||
|
||||
buf[0] = 0;
|
||||
if (RLIM_INFINITY == rlim->rlim_cur)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
if (RLIM_INFINITY == rlim->rlim_max)
|
||||
snprintf(tmp, sizeof(tmp), "unlimited, ");
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
|
||||
strlcat(buf, tmp, sizeof(buf));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/resource.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
@@ -55,6 +56,7 @@ extern char *prognm;
|
||||
# include <lite/conio.h>
|
||||
# include <lite/lite.h>
|
||||
#endif
|
||||
|
||||
#include "log.h"
|
||||
|
||||
char *progname (char *arg0);
|
||||
@@ -81,6 +83,10 @@ char *sig2str (int sig);
|
||||
int str2sig (char *sig);
|
||||
char *code2str (int code);
|
||||
|
||||
int str2rlim (char *str);
|
||||
char *rlim2str (int rlim);
|
||||
char *lim2str (struct rlimit *rlim);
|
||||
|
||||
void do_sleep (unsigned int sec);
|
||||
void do_usleep (unsigned int usec);
|
||||
long jiffies (void);
|
||||
|
||||
Reference in new issue
Block a user