From c4b7a25b3781dd6ac645fef5cf8e0262d077da26 Mon Sep 17 00:00:00 2001
From: "Anna (navi) Figueiredo Gomes" <navi@vlhl.dev>
Date: Sat, 18 Mar 2023 01:03:28 -0300
Subject: librc: Allow user to override system-wide rc.conf

This change read a user version of rc.conf, to be located in
`~/.config/rc.conf`. The user version is loaded first, so it has
priority, thus overriding the system settings.

Signed-off-by: Anna (navi) Figueiredo Gomes <navi@vlhl.dev>
---
 src/librc/librc-misc.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/librc/rc.h.in      |  4 +++
 2 files changed, 77 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/librc/librc-misc.c b/src/librc/librc-misc.c
index ce658f5c..2dab1249 100644
--- a/src/librc/librc-misc.c
+++ b/src/librc/librc-misc.c
@@ -364,6 +364,52 @@ static RC_STRINGLIST * rc_config_directory(RC_STRINGLIST *config)
 	return config;
 }
 
+#ifdef RC_USER_SERVICES
+static RC_STRINGLIST * rc_user_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;
+	char *sysconf = rc_user_sysconfdir();
+	char *user_conf_d;
+
+	xasprintf(&user_conf_d, "%s/%s", sysconf, RC_USER_CONF_D);
+
+	if ((dp = opendir(user_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", user_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);
+		}
+	}
+
+	free(sysconf);
+	free(user_conf_d);
+	return config;
+}
+
+#endif
+
 RC_STRINGLIST *
 rc_config_load(const char *file)
 {
@@ -412,12 +458,37 @@ _free_rc_conf(void)
 char *
 rc_conf_value(const char *setting)
 {
-	RC_STRINGLIST *old;
+	RC_STRINGLIST *system, *old;
 	RC_STRING *s;
 	char *p;
+#ifdef RC_USER_SERVICES
+	RC_STRINGLIST *user;
+	char *userconf, *user_sysconf;
+#endif
 
 	if (!rc_conf) {
-		rc_conf = rc_config_load(RC_CONF);
+		rc_conf = rc_stringlist_new();
+
+#ifdef RC_USER_SERVICES
+		if (rc_is_user()) {
+			user_sysconf = rc_user_sysconfdir();
+			xasprintf(&userconf, "%s/%s", user_sysconf, RC_USER_CONF);
+
+			user = rc_config_load(userconf);
+			TAILQ_CONCAT(rc_conf, user, entries);
+			rc_stringlist_free(user);
+
+			free(userconf);
+			free(user_sysconf);
+
+			rc_user_config_directory(rc_conf);
+		}
+#endif
+
+		system = rc_config_load(RC_CONF);
+		TAILQ_CONCAT(rc_conf, system, entries);
+		rc_stringlist_free(system);
+
 		atexit(_free_rc_conf);
 
 		/* Support old configs. */
diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in
index 02e7d53f..1ffeba5a 100644
--- a/src/librc/rc.h.in
+++ b/src/librc/rc.h.in
@@ -56,6 +56,10 @@ extern "C" {
 #define RC_USER_RUNLEVELS_FOLDER         "/runlevels"
 #define RC_USER_RUNTIME_FOLDER           "/openrc"
 
+
+#define RC_USER_CONF                     "/rc.conf"
+#define RC_USER_CONF_D                   "/rc.conf.d"
+
 /*! Is openrc being ran in usermode?
  * @return true if yes, otherwise false */
 bool rc_is_user(void);
-- 
cgit v1.2.3