diff options
Diffstat (limited to 'src/librc')
-rw-r--r-- | src/librc/librc-daemon.c | 10 | ||||
-rw-r--r-- | src/librc/librc-depend.c | 115 | ||||
-rw-r--r-- | src/librc/librc-misc.c | 56 | ||||
-rw-r--r-- | src/librc/librc.c | 268 | ||||
-rw-r--r-- | src/librc/meson.build | 1 | ||||
-rw-r--r-- | src/librc/rc.h.in | 41 |
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 |