aboutsummaryrefslogtreecommitdiff
path: root/src/librc/librc-misc.c
diff options
context:
space:
mode:
authorWilliam Hubbs <w.d.hubbs@gmail.com>2015-10-30 12:32:32 -0500
committerWilliam Hubbs <w.d.hubbs@gmail.com>2015-11-05 10:40:24 -0600
commitc09eeca49145b034df6527c500099ba22f28e824 (patch)
tree255b5cf66de8257a44eb5f799c6e3b03c7871ca5 /src/librc/librc-misc.c
parent4cf6b0ecf7f5b35a6d80d76c60e77c3e1c7fee5c (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.c103
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;