diff options
author | William Hubbs <w.d.hubbs@gmail.com> | 2015-10-30 12:32:32 -0500 |
---|---|---|
committer | William Hubbs <w.d.hubbs@gmail.com> | 2015-11-05 10:40:24 -0600 |
commit | c09eeca49145b034df6527c500099ba22f28e824 (patch) | |
tree | 255b5cf66de8257a44eb5f799c6e3b03c7871ca5 /src/librc/librc-misc.c | |
parent | 4cf6b0ecf7f5b35a6d80d76c60e77c3e1c7fee5c (diff) |
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.
Diffstat (limited to 'src/librc/librc-misc.c')
-rw-r--r-- | src/librc/librc-misc.c | 103 |
1 files changed, 103 insertions, 0 deletions
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 <fnmatch.h> + #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; |