From c09eeca49145b034df6527c500099ba22f28e824 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Fri, 30 Oct 2015 12:32:32 -0500 Subject: Add rc.conf.d support This makes it possible to override settings in rc.conf by adding a directory @SYSCONFDIR@/rc.conf.d and putting files in this directory. The files will be processed in lexical order, and the last setting in these files will be used. --- src/librc/librc-misc.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++ src/librc/rc.h.in | 1 + 2 files changed, 104 insertions(+) (limited to 'src') diff --git a/src/librc/librc-misc.c b/src/librc/librc-misc.c index 2e9de801..1eedc968 100644 --- a/src/librc/librc-misc.c +++ b/src/librc/librc-misc.c @@ -28,6 +28,8 @@ * SUCH DAMAGE. */ +#include + #include "queue.h" #include "librc.h" @@ -214,6 +216,69 @@ rc_config_list(const char *file) } librc_hidden_def(rc_config_list) +static void rc_config_set_value(RC_STRINGLIST *config, char *value) +{ + RC_STRING *cline; + char *entry; + size_t i = 0; + char *newline; + char *p = value; + bool replaced; + char *token; + + if (! p) + return; + if (strncmp(p, "export ", 7) == 0) + p += 7; + if (! (token = strsep(&p, "="))) + return; + + entry = xstrdup(token); + /* Preserve shell coloring */ + if (*p == '$') + token = value; + else + do { + /* Bash variables are usually quoted */ + token = strsep(&p, "\"\'"); + } while (token && *token == '\0'); + + /* Drop a newline if that's all we have */ + if (token) { + i = strlen(token) - 1; + if (token[i] == '\n') + token[i] = 0; + + i = strlen(entry) + strlen(token) + 2; + newline = xmalloc(sizeof(char) * i); + snprintf(newline, i, "%s=%s", entry, token); + } else { + i = strlen(entry) + 2; + newline = xmalloc(sizeof(char) * i); + snprintf(newline, i, "%s=", entry); + } + + replaced = false; + /* In shells the last item takes precedence, so we need to remove + any prior values we may already have */ + TAILQ_FOREACH(cline, config, entries) { + i = strlen(entry); + if (strncmp(entry, cline->value, i) == 0 && cline->value[i] == '=') { + /* We have a match now - to save time we directly replace it */ + free(cline->value); + cline->value = newline; + replaced = true; + break; + } + } + + if (!replaced) { + rc_stringlist_add(config, newline); + free(newline); + } + free(entry); +} + /* * Override some specific rc.conf options on the kernel command line */ @@ -272,6 +337,42 @@ static RC_STRINGLIST *rc_config_override(RC_STRINGLIST *config) } #endif +static RC_STRINGLIST * rc_config_directory(RC_STRINGLIST *config) +{ + DIR *dp; + struct dirent *d; + RC_STRINGLIST *rc_conf_d_files = rc_stringlist_new(); + RC_STRING *fname; + RC_STRINGLIST *rc_conf_d_list; + char path[PATH_MAX]; + RC_STRING *line; + + if ((dp = opendir(RC_CONF_D)) != NULL) { + while ((d = readdir(dp)) != NULL) { + if (fnmatch("*.conf", d->d_name, FNM_PATHNAME) == 0) { + rc_stringlist_addu(rc_conf_d_files, d->d_name); + } + } + closedir(dp); + + if (rc_conf_d_files) { + rc_stringlist_sort(&rc_conf_d_files); + TAILQ_FOREACH(fname, rc_conf_d_files, entries) { + if (! fname->value) + continue; + sprintf(path, "%s/%s", RC_CONF_D, fname->value); + rc_conf_d_list = rc_config_list(path); + TAILQ_FOREACH(line, rc_conf_d_list, entries) + if (line->value) + rc_config_set_value(config, line->value); + rc_stringlist_free(rc_conf_d_list); + } + rc_stringlist_free(rc_conf_d_files); + } + } + return config; +} + RC_STRINGLIST * rc_config_load(const char *file) { @@ -401,6 +502,8 @@ rc_conf_value(const char *setting) #endif } + rc_conf = rc_config_directory(rc_conf); + /* Convert old uppercase to lowercase */ TAILQ_FOREACH(s, rc_conf, entries) { p = s->value; diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in index 13e1b5b9..e3a586f6 100644 --- a/src/librc/rc.h.in +++ b/src/librc/rc.h.in @@ -56,6 +56,7 @@ extern "C" { #define RC_SYS_WHITELIST RC_LIBEXECDIR "/conf.d/env_whitelist" #define RC_USR_WHITELIST RC_SYSCONFDIR "/conf.d/env_whitelist" #define RC_CONF RC_SYSCONFDIR "/rc.conf" +#define RC_CONF_D RC_SYSCONFDIR "/rc.conf.d" #define RC_CONF_OLD RC_SYSCONFDIR "/conf.d/rc" #define RC_PATH_PREFIX RC_LIBEXECDIR "/bin:/bin:/sbin:/usr/bin:/usr/sbin" -- cgit v1.2.3