aboutsummaryrefslogtreecommitdiff
path: root/src/librc
diff options
context:
space:
mode:
authorAnna (navi) Figueiredo Gomes <navi@vlhl.dev>2024-03-20 23:45:47 +0100
committerAnna (navi) Figueiredo Gomes <navi@vlhl.dev>2024-07-19 20:40:28 +0200
commitc34fcd63f05044f9034b26c52f19c91e04668da7 (patch)
tree8ef449218cf736057197cd39fba1dac6117216c3 /src/librc
parent917a7031d946c0b608517936fab0f54689167265 (diff)
openrc: dynamic paths for user services
add two api functions, `rc_service_dir` and `rc_sysconf_dir`, both are generate paths (and sub-paths) for resources, and meant to replace the hardcoded variables like `RC_SVCDIR`. those functions differ by dynamically switching between the system path, or the user path, set in their home folder or runtime directory. this lays out the intial support for user services. Signed-off-by: Anna (navi) Figueiredo Gomes <navi@vlhl.dev>
Diffstat (limited to 'src/librc')
-rw-r--r--src/librc/librc-daemon.c10
-rw-r--r--src/librc/librc-depend.c115
-rw-r--r--src/librc/librc-misc.c56
-rw-r--r--src/librc/librc.c268
-rw-r--r--src/librc/meson.build1
-rw-r--r--src/librc/rc.h.in41
6 files changed, 365 insertions, 126 deletions
diff --git a/src/librc/librc-daemon.c b/src/librc/librc-daemon.c
index e23593b9..96609786 100644
--- a/src/librc/librc-daemon.c
+++ b/src/librc/librc-daemon.c
@@ -411,9 +411,8 @@ rc_service_daemon_set(const char *service, const char *exec,
return false;
}
- xasprintf(&dirpath, RC_SVCDIR "/daemons/%s", basename_c(service));
-
/* Regardless, erase any existing daemon info */
+ xasprintf(&dirpath, "%s/daemons/%s", rc_service_dir(), basename_c(service));
if ((dp = opendir(dirpath))) {
match = _match_list(exec, argv, pidfile);
renamelist = rc_stringlist_new();
@@ -491,7 +490,7 @@ rc_service_started_daemon(const char *service,
if (!service || !exec)
return false;
- xasprintf(&dirpath, RC_SVCDIR "/daemons/%s", basename_c(service));
+ xasprintf(&dirpath, "%s/daemons/%s", rc_service_dir(), basename_c(service));
match = _match_list(exec, argv, NULL);
if (indx > 0) {
@@ -543,9 +542,8 @@ rc_service_daemons_crashed(const char *service)
char *ch_root;
char *spidfile;
- path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
- basename_c(service));
-
+ path += snprintf(dirpath, sizeof(dirpath),
+ "%s/daemons/%s", rc_service_dir(), basename_c(service));
if (!(dp = opendir(dirpath)))
return false;
diff --git a/src/librc/librc-depend.c b/src/librc/librc-depend.c
index d584af2c..32286c51 100644
--- a/src/librc/librc-depend.c
+++ b/src/librc/librc-depend.c
@@ -35,10 +35,10 @@
#define GENDEP RC_LIBEXECDIR "/sh/gendepends.sh"
-#define RC_DEPCONFIG RC_SVCDIR "/depconfig"
-
static const char *bootlevel = NULL;
+#define RC_DEPCONFIG "depconfig"
+
static char *
get_shell_value(char *string)
{
@@ -120,8 +120,16 @@ get_deptype(const RC_DEPINFO *depinfo, const char *type)
}
RC_DEPTREE *
-rc_deptree_load(void) {
- return rc_deptree_load_file(RC_DEPTREE_CACHE);
+rc_deptree_load(void)
+{
+ char *deptree_cache;
+ RC_DEPTREE *deptree;
+
+ xasprintf(&deptree_cache, "%s/%s", rc_service_dir(), RC_DEPTREE_CACHE);
+ deptree = rc_deptree_load_file(deptree_cache);
+ free(deptree_cache);
+
+ return deptree;
}
RC_DEPTREE *
@@ -668,19 +676,19 @@ static const DEPPAIR deppairs[] = {
static const char *const depdirs[] =
{
- RC_SVCDIR,
- RC_SVCDIR "/starting",
- RC_SVCDIR "/started",
- RC_SVCDIR "/stopping",
- RC_SVCDIR "/inactive",
- RC_SVCDIR "/wasinactive",
- RC_SVCDIR "/failed",
- RC_SVCDIR "/hotplugged",
- RC_SVCDIR "/daemons",
- RC_SVCDIR "/options",
- RC_SVCDIR "/exclusive",
- RC_SVCDIR "/scheduled",
- RC_SVCDIR "/tmp",
+ "",
+ "starting",
+ "started",
+ "stopping",
+ "inactive",
+ "wasinactive",
+ "failed",
+ "hotplugged",
+ "daemons",
+ "options",
+ "exclusive",
+ "scheduled",
+ "tmp",
NULL
};
@@ -690,19 +698,27 @@ rc_deptree_update_needed(time_t *newest, char *file)
bool newer = false;
RC_STRINGLIST *config;
RC_STRING *s;
- int i;
struct stat buf;
time_t mtime;
+ char *depconfig;
+ char *deptree_cache;
+ const char * const dirs[] = { RC_INIT_SUBDIR, RC_CONF_SUBDIR, RC_CONF_FILE, NULL };
+ char *dir;
+ const char *service_dir = rc_service_dir();
+ const char *sysconf_dir = rc_sysconf_dir();
/* Create base directories if needed */
- for (i = 0; depdirs[i]; i++)
- if (mkdir(depdirs[i], 0755) != 0 && errno != EEXIST)
- fprintf(stderr, "mkdir `%s': %s\n", depdirs[i], strerror(errno));
+ for (int i = 0; depdirs[i]; i++) {
+ xasprintf(&dir, "%s/%s", service_dir, depdirs[i]);
+ if (mkdir(dir, 0755) != 0 && errno != EEXIST)
+ fprintf(stderr, "mkdir '%s': %s\n", dir, strerror(errno));
+ free(dir);
+ }
/* Quick test to see if anything we use has changed and we have
* data in our deptree. */
-
- if (stat(RC_DEPTREE_CACHE, &buf) == 0) {
+ xasprintf(&deptree_cache, "%s/%s", service_dir, RC_DEPTREE_CACHE);
+ if (stat(deptree_cache, &buf) == 0) {
mtime = buf.st_mtime;
} else {
/* No previous cache found.
@@ -712,30 +728,48 @@ rc_deptree_update_needed(time_t *newest, char *file)
newer = true;
mtime = time(NULL);
}
+ free(deptree_cache);
+
+ for (int i = 0; dirs[i]; i++) {
+ xasprintf(&dir, "%s/%s", sysconf_dir, dirs[i]);
+ newer |= !deep_mtime_check(dir, true, &mtime, file);
+ free(dir);
+ }
- newer |= !deep_mtime_check(RC_INITDIR,true,&mtime,file);
- newer |= !deep_mtime_check(RC_CONFDIR,true,&mtime,file);
+ /* Test user configs */
+ if (rc_is_user()) {
+ const char *userconf_dir = rc_userconf_dir();
+ for (int i = 0; dirs[i]; i++) {
+ xasprintf(&dir, "%s/%s", userconf_dir, dirs[i]);
+ newer |= !deep_mtime_check(dir, true, &mtime, file);
+ free(dir);
+ }
+ newer |= !deep_mtime_check(RC_CONF, true, &mtime, file);
+ /* RC_USER doesn't have those paths */
+ } else {
#ifdef RC_PKG_INITDIR
- newer |= !deep_mtime_check(RC_PKG_INITDIR,true,&mtime,file);
+ newer |= !deep_mtime_check(RC_PKG_INITDIR, true, &mtime, file);
#endif
#ifdef RC_PKG_CONFDIR
- newer |= !deep_mtime_check(RC_PKG_CONFDIR,true,&mtime,file);
+ newer |= !deep_mtime_check(RC_PKG_CONFDIR, true, &mtime, file);
#endif
-#ifdef RC_LOCAL_INITDIRs
- newer |= !deep_mtime_check(RC_LOCAL_INITDIR,true,&mtime,file);
+#ifdef RC_LOCAL_INITDIR
+ newer |= !deep_mtime_check(RC_LOCAL_INITDIR, true, &mtime, file);
#endif
#ifdef RC_LOCAL_CONFDIR
- newer |= !deep_mtime_check(RC_LOCAL_CONFDIR,true,&mtime,file);
+ newer |= !deep_mtime_check(RC_LOCAL_CONFDIR, true, &mtime, file);
#endif
- newer |= !deep_mtime_check(RC_CONF,true,&mtime,file);
+ }
/* Some init scripts dependencies change depending on config files
* outside of baselayout, like syslog-ng, so we check those too. */
- config = rc_config_list(RC_DEPCONFIG);
+ xasprintf(&depconfig, "%s/depconfig", service_dir);
+ config = rc_config_list(depconfig);
TAILQ_FOREACH(s, config, entries) {
newer |= !deep_mtime_check(s->value, true, &mtime, file);
}
rc_stringlist_free(config);
+ free(depconfig);
/* Return newest file time, if requested */
if ((newer) && (newest != NULL)) {
@@ -768,6 +802,8 @@ rc_deptree_update(void)
char *line = NULL;
size_t len = 0;
char *depend, *depends, *service, *type, *nosys, *onosys;
+ char *deptree_cache;
+ char *depconfig;
size_t i, k, l;
bool retval = true;
const char *sys = rc_sys();
@@ -1048,7 +1084,8 @@ rc_deptree_update(void)
This works and should be entirely shell parseable provided that depend
names don't have any non shell variable characters in
*/
- if ((fp = fopen(RC_DEPTREE_CACHE, "w"))) {
+ xasprintf(&deptree_cache, "%s/%s", rc_service_dir(), RC_DEPTREE_CACHE);
+ if ((fp = fopen(deptree_cache, "w"))) {
i = 0;
TAILQ_FOREACH(depinfo, deptree, entries) {
fprintf(fp, "depinfo_%zu_service='%s'\n",
@@ -1066,27 +1103,29 @@ rc_deptree_update(void)
}
fclose(fp);
} else {
- fprintf(stderr, "fopen `%s': %s\n",
- RC_DEPTREE_CACHE, strerror(errno));
+ fprintf(stderr, "fopen `%s': %s\n", deptree_cache, strerror(errno));
retval = false;
}
+ free(deptree_cache);
/* Save our external config files to disk */
+ xasprintf(&depconfig, "%s/depconfig", rc_service_dir());
if (TAILQ_FIRST(config)) {
- if ((fp = fopen(RC_DEPCONFIG, "w"))) {
+ if ((fp = fopen(depconfig, "w"))) {
TAILQ_FOREACH(s, config, entries)
fprintf(fp, "%s\n", s->value);
fclose(fp);
} else {
fprintf(stderr, "fopen `%s': %s\n",
- RC_DEPCONFIG, strerror(errno));
+ depconfig, strerror(errno));
retval = false;
}
} else {
- unlink(RC_DEPCONFIG);
+ unlink(depconfig);
}
rc_stringlist_free(config);
+ free(depconfig);
free(deptree);
return retval;
}
diff --git a/src/librc/librc-misc.c b/src/librc/librc-misc.c
index ce658f5c..538a67c2 100644
--- a/src/librc/librc-misc.c
+++ b/src/librc/librc-misc.c
@@ -358,9 +358,10 @@ static RC_STRINGLIST * rc_config_directory(RC_STRINGLIST *config)
rc_config_set_value(config, line->value);
rc_stringlist_free(rc_conf_d_list);
}
- rc_stringlist_free(rc_conf_d_files);
}
}
+
+ rc_stringlist_free(rc_conf_d_files);
return config;
}
@@ -412,32 +413,47 @@ _free_rc_conf(void)
char *
rc_conf_value(const char *setting)
{
- RC_STRINGLIST *old;
+ RC_STRINGLIST *tmp;
RC_STRING *s;
char *p;
+ const char *userconf_dir;
+
+ if (rc_conf)
+ return rc_config_value(rc_conf, setting);
+
+ rc_conf = rc_config_load(RC_CONF);
+ atexit(_free_rc_conf);
+
+ /* Support old configs. */
+ if (exists(RC_CONF_OLD)) {
+ tmp = rc_config_load(RC_CONF_OLD);
+ TAILQ_CONCAT(rc_conf, tmp, entries);
+ rc_stringlist_free(tmp);
+ }
- if (!rc_conf) {
- rc_conf = rc_config_load(RC_CONF);
- atexit(_free_rc_conf);
+ /* Overlay user-specific configuration */
+ if ((userconf_dir = rc_userconf_dir())) {
+ char *user_config;
+ xasprintf(&user_config, "%s/%s", userconf_dir, RC_CONF_FILE);
- /* Support old configs. */
- if (exists(RC_CONF_OLD)) {
- old = rc_config_load(RC_CONF_OLD);
- TAILQ_CONCAT(rc_conf, old, entries);
- rc_stringlist_free(old);
+ if (exists(user_config)) {
+ tmp = rc_config_load(user_config);
+ TAILQ_CONCAT(rc_conf, tmp, entries);
+ rc_stringlist_free(tmp);
}
+ free(user_config);
+ }
- rc_conf = rc_config_directory(rc_conf);
- rc_conf = rc_config_kcl(rc_conf);
+ rc_conf = rc_config_directory(rc_conf);
+ rc_conf = rc_config_kcl(rc_conf);
- /* Convert old uppercase to lowercase */
- TAILQ_FOREACH(s, rc_conf, entries) {
- p = s->value;
- while (p && *p && *p != '=') {
- if (isupper((unsigned char)*p))
- *p = tolower((unsigned char)*p);
- p++;
- }
+ /* Convert old uppercase to lowercase */
+ TAILQ_FOREACH(s, rc_conf, entries) {
+ p = s->value;
+ while (p && *p && *p != '=') {
+ if (isupper((unsigned char)*p))
+ *p = tolower((unsigned char)*p);
+ p++;
}
}
diff --git a/src/librc/librc.c b/src/librc/librc.c
index 118726df..d218c8be 100644
--- a/src/librc/librc.c
+++ b/src/librc/librc.c
@@ -35,12 +35,11 @@
#include "librc.h"
#include "misc.h"
#include "rc.h"
+#include "einfo.h"
#ifdef __FreeBSD__
# include <sys/sysctl.h>
#endif
-#define RC_RUNLEVEL RC_SVCDIR "/softlevel"
-
#ifndef S_IXUGO
# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
#endif
@@ -412,7 +411,7 @@ get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list, RC_STRINGLIS
* We can now do exactly the above procedure for our chained
* runlevels.
*/
- xasprintf(&path, "%s/%s", RC_RUNLEVELDIR, runlevel);
+ xasprintf(&path, "%s/%s", rc_runlevel_dir(), runlevel);
dirs = ls_dir(path, LS_DIR);
TAILQ_FOREACH(d, dirs, entries) {
nextlevel = d->value;
@@ -441,28 +440,44 @@ get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list, RC_STRINGLIS
bool
rc_runlevel_starting(void)
{
- return exists(RC_STARTING);
+ char *stopping_dir;
+ bool found;
+
+ xasprintf(&stopping_dir, "%s/%s", rc_service_dir(), RC_STARTING);
+ found = exists(stopping_dir);
+ free(stopping_dir);
+
+ return found;
}
bool
rc_runlevel_stopping(void)
{
- return exists(RC_STOPPING);
+ char *stopping_dir;
+ bool found;
+
+ xasprintf(&stopping_dir, "%s/%s", rc_service_dir(), RC_STOPPING);
+ found = exists(stopping_dir);
+ free(stopping_dir);
+
+ return found;
}
RC_STRINGLIST *rc_runlevel_list(void)
{
- return ls_dir(RC_RUNLEVELDIR, LS_DIR);
+ return ls_dir(rc_runlevel_dir(), LS_DIR);
}
char *
rc_runlevel_get(void)
{
FILE *fp;
+ char *softlevel;
char *runlevel = NULL;
size_t i;
- if ((fp = fopen(RC_RUNLEVEL, "r"))) {
+ xasprintf(&softlevel, "%s/softlevel", rc_service_dir());
+ if ((fp = fopen(softlevel, "r"))) {
runlevel = xmalloc(sizeof(char) * PATH_MAX);
if (fgets(runlevel, PATH_MAX, fp)) {
i = strlen(runlevel) - 1;
@@ -472,6 +487,7 @@ rc_runlevel_get(void)
*runlevel = '\0';
fclose(fp);
}
+ free(softlevel);
if (!runlevel || !*runlevel) {
free(runlevel);
@@ -484,12 +500,16 @@ rc_runlevel_get(void)
bool
rc_runlevel_set(const char *runlevel)
{
- FILE *fp = fopen(RC_RUNLEVEL, "w");
+ char *softlevel;
+ FILE *fp;
+ xasprintf(&softlevel, "%s/softlevel", rc_service_dir());
+ fp = fopen(softlevel, "w");
if (!fp)
return false;
fprintf(fp, "%s", runlevel);
fclose(fp);
+ free(softlevel);
return true;
}
@@ -503,7 +523,7 @@ rc_runlevel_exists(const char *runlevel)
if (!runlevel || strcmp(runlevel, "") == 0 || strcmp(runlevel, ".") == 0 ||
strcmp(runlevel, "..") == 0)
return false;
- xasprintf(&path, "%s/%s", RC_RUNLEVELDIR, runlevel);
+ xasprintf(&path, "%s/%s", rc_runlevel_dir(), runlevel);
r = stat(path, &buf);
free(path);
if (r == 0 && S_ISDIR(buf.st_mode))
@@ -520,7 +540,7 @@ rc_runlevel_stack(const char *dst, const char *src)
if (!rc_runlevel_exists(dst) || !rc_runlevel_exists(src))
return false;
xasprintf(&s, "../%s", src);
- xasprintf(&d, "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
+ xasprintf(&d, "%s/%s/%s", rc_runlevel_dir(), dst, src);
r = symlink(s, d);
free(d);
free(s);
@@ -533,7 +553,7 @@ rc_runlevel_unstack(const char *dst, const char *src)
char *path;
int r;
- xasprintf(&path, "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
+ xasprintf(&path, "%s/%s/%s", rc_runlevel_dir(), dst, src);
r = unlink(path);
free(path);
return (r == 0);
@@ -551,6 +571,105 @@ rc_runlevel_stacks(const char *runlevel)
return stack;
}
+static struct {
+ char *userconf;
+ char *runlevels;
+ char *service;
+} rc_user_dirs;
+
+static void
+free_rc_user_dirs(void)
+{
+ free(rc_user_dirs.userconf);
+ rc_user_dirs.userconf = NULL;
+ free(rc_user_dirs.runlevels);
+ rc_user_dirs.runlevels = NULL;
+ free(rc_user_dirs.service);
+ rc_user_dirs.service = NULL;
+}
+
+static void
+init_rc_user_dirs(void)
+{
+ char *env;
+
+ if ((env = getenv("XDG_CONFIG_HOME")))
+ xasprintf(&rc_user_dirs.userconf, "%s/openrc", env);
+ else if ((env = getenv("HOME")))
+ xasprintf(&rc_user_dirs.userconf, "%s/.config/openrc", env);
+ else
+ eerrorx("XDG_CONFIG_HOME and HOME unset");
+
+ xasprintf(&rc_user_dirs.runlevels, "%s/%s", rc_user_dirs.userconf, RC_RUNLEVEL_SUBDIR);
+
+ if (!(env = getenv("XDG_RUNTIME_DIR")))
+ eerrorx("XDG_RUNTIME_DIR unset."); /* FIXME: fallback to something else? */
+ xasprintf(&rc_user_dirs.service, "%s/openrc", env);
+ atexit(free_rc_user_dirs);
+}
+
+static bool is_user = false;
+
+void
+rc_set_user(void)
+{
+ if (!is_user)
+ init_rc_user_dirs();
+ is_user = true;
+ setenv("RC_USER_SERVICES", "yes", true);
+}
+
+bool
+rc_is_user(void)
+{
+ return is_user;
+}
+
+const char *
+rc_sysconf_dir(void)
+{
+ if (rc_is_user())
+ return RC_SYSCONFDIR "/" RC_USER_SUBDIR;
+ else
+ return RC_SYSCONFDIR;
+}
+
+const char *
+rc_userconf_dir(void)
+{
+ if (!rc_is_user())
+ return NULL;
+
+ if (!rc_user_dirs.userconf)
+ eerrorx("rc_userconf_dir called in user mode without paths set");
+
+ return rc_user_dirs.userconf;
+}
+
+const char *
+rc_runlevel_dir(void)
+{
+ if (!rc_is_user())
+ return RC_RUNLEVELDIR;
+
+ if (!rc_user_dirs.runlevels)
+ eerrorx("rc_runlevel_dir called in user mode without paths set");
+
+ return rc_user_dirs.runlevels;
+}
+
+const char *
+rc_service_dir(void)
+{
+ if (!rc_is_user())
+ return RC_SVCDIR;
+
+ if (!rc_user_dirs.service)
+ eerrorx("rc_service_dir called in user mode without paths set");
+
+ return rc_user_dirs.service;
+}
+
static ssize_t
safe_readlink(const char *path, char **buf, size_t bufsiz)
{
@@ -582,6 +701,7 @@ rc_service_resolve(const char *service)
char *buffer;
char *file = NULL;
struct stat buf;
+ const char *sysconf_dir = rc_sysconf_dir();
if (!service)
return NULL;
@@ -590,10 +710,10 @@ rc_service_resolve(const char *service)
return xstrdup(service);
/* First check started services */
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR, "started", service);
+ xasprintf(&file, "%s/%s/%s", rc_service_dir(), "started", service);
if (lstat(file, &buf) || !S_ISLNK(buf.st_mode)) {
free(file);
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR, "inactive", service);
+ xasprintf(&file, "%s/%s/%s", rc_service_dir(), "inactive", service);
if (lstat(file, &buf) || !S_ISLNK(buf.st_mode)) {
free(file);
file = NULL;
@@ -605,6 +725,22 @@ rc_service_resolve(const char *service)
return buffer;
}
+ /* Check user specific scripts */
+ if (rc_is_user()) {
+ /* Local user config takes priority */
+ xasprintf(&file, "%s/%s/%s", rc_userconf_dir(), RC_INIT_SUBDIR, service);
+ if (stat(file, &buf) == 0)
+ return file;
+
+ free(file);
+ xasprintf(&file, "%s/%s/%s", sysconf_dir, RC_INIT_SUBDIR, service);
+ if (stat(file, &buf) == 0)
+ return file;
+
+ free(file);
+ return NULL;
+ }
+
#ifdef RC_LOCAL_INITDIR
/* Nope, so lets see if the user has written it */
free(file);
@@ -615,7 +751,7 @@ rc_service_resolve(const char *service)
/* System scripts take precedence over 3rd party ones */
free(file);
- xasprintf(&file, "%s/%s", RC_INITDIR, service);
+ xasprintf(&file, "%s/%s/%s", sysconf_dir, RC_INIT_SUBDIR, service);
if (stat(file, &buf) == 0)
return file;
@@ -745,8 +881,7 @@ rc_service_in_runlevel(const char *service, const char *runlevel)
char *file;
bool r;
- xasprintf(&file, "%s/%s/%s", RC_RUNLEVELDIR, runlevel,
- basename_c(service));
+ xasprintf(&file, "%s/%s/%s", rc_runlevel_dir(), runlevel, basename_c(service));
r = exists(file);
free(file);
return r;
@@ -760,6 +895,7 @@ rc_service_mark(const char *service, const RC_SERVICE state)
int skip_state = -1;
const char *base;
char *init = rc_service_resolve(service);
+ const char *svc_dir = rc_service_dir();
bool skip_wasinactive = false;
int s;
RC_STRINGLIST *dirs;
@@ -776,7 +912,7 @@ rc_service_mark(const char *service, const RC_SERVICE state)
return false;
}
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR,
+ xasprintf(&file, "%s/%s/%s", svc_dir,
rc_parse_service_state(state), base);
if (exists(file))
unlink(file);
@@ -804,14 +940,14 @@ rc_service_mark(const char *service, const RC_SERVICE state)
s != RC_SERVICE_SCHEDULED) &&
(!skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
{
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR,
+ xasprintf(&file, "%s/%s/%s", rc_service_dir(),
rc_service_state_names[i].name, base);
if (exists(file)) {
if ((state == RC_SERVICE_STARTING ||
state == RC_SERVICE_STOPPING) &&
s == RC_SERVICE_INACTIVE)
{
- xasprintf(&was, "%s/%s/%s", RC_SVCDIR,
+ xasprintf(&was, "%s/%s/%s", rc_service_dir(),
rc_parse_service_state(RC_SERVICE_WASINACTIVE),
base);
i = symlink(init, was);
@@ -838,18 +974,18 @@ rc_service_mark(const char *service, const RC_SERVICE state)
state == RC_SERVICE_STOPPED ||
state == RC_SERVICE_INACTIVE)
{
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR, "exclusive", base);
+ xasprintf(&file, "%s/%s/%s", svc_dir, "exclusive", base);
unlink(file);
free(file);
}
/* Remove any options and daemons the service may have stored */
if (state == RC_SERVICE_STOPPED) {
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR, "options", base);
+ xasprintf(&file, "%s/%s/%s", svc_dir, "options", base);
rm_dir(file, true);
free(file);
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR, "daemons", base);
+ xasprintf(&file, "%s/%s/%s", svc_dir, "daemons", base);
rm_dir(file, true);
free(file);
@@ -858,7 +994,7 @@ rc_service_mark(const char *service, const RC_SERVICE state)
/* These are final states, so remove us from scheduled */
if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) {
- xasprintf(&file, "%s/%s", RC_SVCDIR, "scheduled");
+ xasprintf(&file, "%s/%s", svc_dir, "scheduled");
dirs = ls_dir(file, 0);
TAILQ_FOREACH(dir, dirs, entries) {
xasprintf(&was, "%s/%s/%s", file, dir->value, base);
@@ -888,10 +1024,10 @@ rc_service_state(const char *service)
RC_STRINGLIST *dirs;
RC_STRING *dir;
const char *base = basename_c(service);
+ const char *svc_dir = rc_service_dir();
for (i = 0; rc_service_state_names[i].name; i++) {
- xasprintf(&file, "%s/%s/%s", RC_SVCDIR,
- rc_service_state_names[i].name, base);
+ xasprintf(&file, "%s/%s/%s", svc_dir, rc_service_state_names[i].name, base);
if (exists(file)) {
if (rc_service_state_names[i].state <= 0x10)
state = rc_service_state_names[i].state;
@@ -906,11 +1042,12 @@ rc_service_state(const char *service)
state |= RC_SERVICE_CRASHED;
}
if (state & RC_SERVICE_STOPPED) {
- dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
+ char *path;
+ xasprintf(&path, "%s/scheduled", svc_dir);
+ dirs = ls_dir(path, 0);
+ free(path);
TAILQ_FOREACH(dir, dirs, entries) {
- xasprintf(&file,
- "%s/scheduled/%s/%s", RC_SVCDIR,
- dir->value, service);
+ xasprintf(&file, "%s/scheduled/%s/%s", svc_dir, dir->value, service);
i = exists(file);
free(file);
if (i) {
@@ -931,7 +1068,7 @@ rc_service_value_get(const char *service, const char *option)
size_t len = 0;
char *file;
- xasprintf(&file, "%s/options/%s/%s", RC_SVCDIR, service, option);
+ xasprintf(&file, "%s/options/%s/%s", rc_service_dir(), service, option);
rc_getfile(file, &buffer, &len);
free(file);
@@ -945,7 +1082,7 @@ rc_service_value_set(const char *service, const char *option,
FILE *fp;
char *file1, *file2;
- xasprintf(&file1, "%s/options/%s", RC_SVCDIR, service);
+ xasprintf(&file1, "%s/options/%s", rc_service_dir(), service);
if (mkdir(file1, 0755) != 0 && errno != EEXIST) {
free(file1);
return false;
@@ -978,7 +1115,7 @@ rc_service_schedule_start(const char *service, const char *service_to_start)
if (!service || !rc_service_exists(service_to_start))
return false;
- xasprintf(&file1, "%s/scheduled/%s", RC_SVCDIR, basename_c(service));
+ xasprintf(&file1, "%s/scheduled/%s", rc_service_dir(), basename_c(service));
if (mkdir(file1, 0755) != 0 && errno != EEXIST) {
free(file1);
return false;
@@ -999,7 +1136,7 @@ rc_service_schedule_clear(const char *service)
char *dir;
bool r;
- xasprintf(&dir, "%s/scheduled/%s", RC_SVCDIR, basename_c(service));
+ xasprintf(&dir, "%s/scheduled/%s", rc_service_dir(), basename_c(service));
r = rm_dir(dir, true);
free(dir);
if (!r && errno == ENOENT)
@@ -1012,34 +1149,52 @@ rc_services_in_runlevel(const char *runlevel)
{
char *dir;
RC_STRINGLIST *list = NULL;
+ const char *sysconf_dir = rc_sysconf_dir();
if (!runlevel) {
-#ifdef RC_PKG_INITDIR
- RC_STRINGLIST *pkg = ls_dir(RC_PKG_INITDIR, LS_INITD);
-#endif
-#ifdef RC_LOCAL_INITDIR
- RC_STRINGLIST *local = ls_dir(RC_LOCAL_INITDIR, LS_INITD);
-#endif
+ const char *userconf_dir;
+ char *init_dir;
- list = ls_dir(RC_INITDIR, LS_INITD);
+ xasprintf(&init_dir, "%s/%s", sysconf_dir, RC_INIT_SUBDIR);
+ list = ls_dir(init_dir, LS_INITD);
+ free(init_dir);
+
+ if ((userconf_dir = rc_userconf_dir())) {
+ RC_STRINGLIST *usr = NULL;
+ xasprintf(&init_dir, "%s/%s", userconf_dir, RC_INIT_SUBDIR);
+
+ usr = ls_dir(init_dir, LS_INITD);
+ TAILQ_CONCAT(list, usr, entries);
+ rc_stringlist_free(usr);
+
+ free(init_dir);
+ return list;
+ }
#ifdef RC_PKG_INITDIR
- TAILQ_CONCAT(list, pkg, entries);
- rc_stringlist_free(pkg);
+ {
+ RC_STRINGLIST *pkg = ls_dir(RC_PKG_INITDIR, LS_INITD);
+ TAILQ_CONCAT(list, pkg, entries);
+ rc_stringlist_free(pkg);
+ }
#endif
#ifdef RC_LOCAL_INITDIR
- TAILQ_CONCAT(list, local, entries);
- rc_stringlist_free(local);
+ {
+ RC_STRINGLIST *local = ls_dir(RC_LOCAL_INITDIR, LS_INITD);
+ TAILQ_CONCAT(list, local, entries);
+ rc_stringlist_free(local);
+ }
#endif
return list;
}
/* These special levels never contain any services */
if (strcmp(runlevel, RC_LEVEL_SINGLE) != 0) {
- xasprintf(&dir, "%s/%s", RC_RUNLEVELDIR, runlevel);
+ xasprintf(&dir, "%s/%s", rc_runlevel_dir(), runlevel);
list = ls_dir(dir, LS_INITD);
free(dir);
}
+
if (!list)
list = rc_stringlist_new();
return list;
@@ -1071,8 +1226,7 @@ rc_services_in_state(RC_SERVICE state)
RC_STRING *d;
char *dir, *dir2;
- xasprintf(&dir, "%s/%s", RC_SVCDIR,
- rc_parse_service_state(state));
+ xasprintf(&dir, "%s/%s", rc_service_dir(), rc_parse_service_state(state));
if (state != RC_SERVICE_SCHEDULED) {
dirs = ls_dir(dir, LS_INITD);
@@ -1125,7 +1279,7 @@ rc_service_add(const char *runlevel, const char *service)
/* We need to ensure that only things in /etc/init.d are added
* to the boot runlevel */
- if (strcmp(runlevel, RC_LEVEL_BOOT) == 0) {
+ if (!rc_is_user() && strcmp(runlevel, RC_LEVEL_BOOT) == 0) {
path = realpath(dirname(init), NULL);
if (path == NULL) {
free(init);
@@ -1138,7 +1292,7 @@ rc_service_add(const char *runlevel, const char *service)
errno = EPERM;
return false;
}
- xasprintf(&binit, "%s/%s", RC_INITDIR, service);
+ xasprintf(&binit, "%s/%s/%s", rc_service_dir(), RC_INIT_SUBDIR, service);
i = binit;
}
@@ -1157,8 +1311,7 @@ rc_service_delete(const char *runlevel, const char *service)
char *file;
int r;
- xasprintf(&file, "%s/%s/%s", RC_RUNLEVELDIR, runlevel,
- basename_c(service));
+ xasprintf(&file, "%s/%s/%s", rc_runlevel_dir(), runlevel, basename_c(service));
r = unlink(file);
free(file);
return (r == 0);
@@ -1167,14 +1320,17 @@ rc_service_delete(const char *runlevel, const char *service)
RC_STRINGLIST *
rc_services_scheduled_by(const char *service)
{
- RC_STRINGLIST *dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
+ RC_STRINGLIST *dirs;
RC_STRINGLIST *list = rc_stringlist_new();
RC_STRING *dir;
- char *file;
+ char *file, *scheduled_dir;
+
+ xasprintf(&scheduled_dir, "%s/scheduled", rc_service_dir());
+ dirs = ls_dir(scheduled_dir, 0);
+ free(scheduled_dir);
TAILQ_FOREACH(dir, dirs, entries) {
- xasprintf(&file, "%s/scheduled/%s/%s", RC_SVCDIR, dir->value,
- service);
+ xasprintf(&file, "%s/scheduled/%s/%s", rc_service_dir(), dir->value, service);
if (exists(file))
rc_stringlist_add(list, file);
free(file);
@@ -1189,7 +1345,7 @@ rc_services_scheduled(const char *service)
char *dir;
RC_STRINGLIST *list;
- xasprintf(&dir, "%s/scheduled/%s", RC_SVCDIR, basename_c(service));
+ xasprintf(&dir, "%s/scheduled/%s", rc_service_dir(), basename_c(service));
list = ls_dir(dir, LS_INITD);
free(dir);
return list;
diff --git a/src/librc/meson.build b/src/librc/meson.build
index 30caa1f3..8d963208 100644
--- a/src/librc/meson.build
+++ b/src/librc/meson.build
@@ -27,6 +27,7 @@ librc = library('rc', librc_sources,
dependencies: kvm_dep,
include_directories : [incdir, einfo_incdir],
link_depends : 'rc.map',
+ link_with : libeinfo,
version : librc_version,
install : true,
install_dir : libdir)
diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in
index 69d9d0e4..59fdfda3 100644
--- a/src/librc/rc.h.in
+++ b/src/librc/rc.h.in
@@ -34,18 +34,27 @@ extern "C" {
#else
#define RC_SVCDIR RC_LIBEXECDIR "/init.d"
#endif
-#define RC_RUNLEVELDIR RC_SYSCONFDIR "/runlevels"
-#define RC_INITDIR RC_SYSCONFDIR "/init.d"
-#define RC_CONFDIR RC_SYSCONFDIR "/conf.d"
+
+#define RC_RUNLEVEL_SUBDIR "runlevels"
+#define RC_INIT_SUBDIR "init.d"
+#define RC_CONF_SUBDIR "conf.d"
+#define RC_USER_SUBDIR "user.d"
+
+#define RC_RUNLEVELDIR RC_SYSCONFDIR "/" RC_RUNLEVEL_SUBDIR
+#define RC_INITDIR RC_SYSCONFDIR "/" RC_INIT_SUBDIR
+#define RC_CONFDIR RC_SYSCONFDIR "/" RC_CONF_SUBDIR
#define RC_PLUGINDIR RC_LIBDIR "/plugins"
+#define RC_CONF_FILE "rc.conf"
+#define RC_CONF_D_SUBDIR "rc.conf.d"
+
#define RC_INIT_FIFO RC_SVCDIR"/init.ctl"
#define RC_PROFILE_ENV RC_SYSCONFDIR "/profile.env"
#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_CONF RC_SYSCONFDIR "/" RC_CONF_FILE
+#define RC_CONF_D RC_SYSCONFDIR "/" RC_CONF_D_SUBDIR
+#define RC_CONF_OLD RC_SYSCONFDIR "/conf.d/rc"
#define RC_PATH_PREFIX RC_LIBEXECDIR "/bin:/bin:/sbin:/usr/bin:/usr/sbin"
@@ -123,6 +132,10 @@ typedef TAILQ_HEAD(rc_stringlist, rc_string) RC_STRINGLIST;
#define RC_LEVEL_SINGLE "single"
#define RC_LEVEL_SHUTDOWN "shutdown"
+/* !TODO: documentation */
+void rc_set_user(void);
+bool rc_is_user(void);
+
/*! Return the current runlevel.
* @return the current runlevel */
char *rc_runlevel_get(void);
@@ -192,6 +205,22 @@ typedef enum
RC_SERVICE_CRASHED = 0x1000,
} RC_SERVICE;
+/*! Returns a path to the system configuration directory
+ * @return path string */
+const char *rc_sysconf_dir(void);
+
+/*! Returns a path to the current user's configuration directory
+ * @return path string */
+const char *rc_userconf_dir(void);
+
+/*! Returns a path to the runlevel directory, systemwide or for the current user
+ * @return path string */
+const char *rc_runlevel_dir(void);
+
+/*! Returns a path to the services directory, systemwide or for the current user
+ * @return path string */
+const char *rc_service_dir(void);
+
/*! Add the service to the runlevel
* @param runlevel to add to
* @param service to add