From c34fcd63f05044f9034b26c52f19c91e04668da7 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Wed, 20 Mar 2024 23:45:47 +0100 Subject: 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 --- src/librc/librc-daemon.c | 10 +- src/librc/librc-depend.c | 115 +++++++++----- src/librc/librc-misc.c | 56 ++++--- src/librc/librc.c | 268 +++++++++++++++++++++++++------- src/librc/meson.build | 1 + src/librc/rc.h.in | 41 ++++- src/mark_service/mark_service.c | 2 + src/openrc-run/openrc-run.c | 36 +++-- src/openrc/rc-logger.c | 25 +-- src/openrc/rc.c | 83 +++++++--- src/service/service.c | 3 + src/shared/_usage.h | 9 +- src/shared/misc.c | 44 ++++-- src/shared/misc.h | 13 +- src/supervise-daemon/supervise-daemon.c | 6 +- src/value/value.c | 3 + 16 files changed, 512 insertions(+), 203 deletions(-) (limited to 'src') 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 #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 diff --git a/src/mark_service/mark_service.c b/src/mark_service/mark_service.c index 7d4d9d97..36f32c28 100644 --- a/src/mark_service/mark_service.c +++ b/src/mark_service/mark_service.c @@ -37,6 +37,8 @@ int main(int argc, char **argv) /* size_t l; */ applet = basename_c(argv[0]); + if (rc_yesno(getenv("RC_USER_SERVICES"))) + rc_set_user(); if (argc > 1) service = argv[1]; else diff --git a/src/openrc-run/openrc-run.c b/src/openrc-run/openrc-run.c index 7c5d50a1..5856a368 100644 --- a/src/openrc-run/openrc-run.c +++ b/src/openrc-run/openrc-run.c @@ -54,7 +54,8 @@ #include "_usage.h" #include "helpers.h" -#define PREFIX_LOCK RC_SVCDIR "/prefix.lock" +#define PREFIX_LOCK_FILE "prefix.lock" +#define OPENRC_SH_FILE "openrc-run.sh" #define WAIT_INTERVAL 20000000 /* usecs to poll the lock file */ #define WAIT_TIMEOUT 60 /* seconds until we timeout */ @@ -165,7 +166,7 @@ unhotplug(void) { char *file = NULL; - xasprintf(&file, RC_SVCDIR "/hotplugged/%s", applet); + xasprintf(&file, "%s/hotplugged/%s", rc_service_dir(), applet); if (exists(file) && unlink(file) != 0) eerror("%s: unlink `%s': %s", applet, file, strerror(errno)); free(file); @@ -175,7 +176,7 @@ static void start_services(RC_STRINGLIST *list) { RC_STRING *svc; - RC_SERVICE state = rc_service_state (service); + RC_SERVICE state = rc_service_state(service); if (!list) return; @@ -286,12 +287,16 @@ write_prefix(const char *buffer, size_t bytes, bool *prefixed) const char *ec_normal = ecolor(ECOLOR_NORMAL); ssize_t ret = 0; int fd = fileno(stdout), lock_fd = -1; + char *prefix_lock; + /* * Lock the prefix. - * open() may fail here when running as user, as RC_SVCDIR may not be writable. + * open() may fail here when running as user, as RC_SVCDIR may not be writable. FIXME: Inaccurate comment? */ - lock_fd = open(PREFIX_LOCK, O_WRONLY | O_CREAT, 0664); + xasprintf(&prefix_lock, "%s/%s", rc_service_dir(), PREFIX_LOCK_FILE); + lock_fd = open(prefix_lock, O_WRONLY | O_CREAT, 0664); + free(prefix_lock); if (lock_fd != -1) { while (flock(lock_fd, LOCK_EX) != 0) { @@ -385,25 +390,24 @@ svc_exec(const char *arg1, const char *arg2) if (service_pid == -1) eerrorx("%s: fork: %s", service, strerror(errno)); if (service_pid == 0) { + char *openrc_sh_path; + xasprintf(&openrc_sh_path, "%s/%s", rc_service_dir(), OPENRC_SH_FILE); if (slave_tty >= 0) { dup2(slave_tty, STDOUT_FILENO); dup2(slave_tty, STDERR_FILENO); } - if (exists(RC_SVCDIR "/openrc-run.sh")) { + if (exists(openrc_sh_path)) { if (arg2) einfov("Executing: %s %s %s %s %s", - RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", + openrc_sh_path, openrc_sh_path, service, arg1, arg2); else einfov("Executing: %s %s %s %s", - RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", + openrc_sh_path, openrc_sh_path, service, arg1); - execl(RC_SVCDIR "/openrc-run.sh", - RC_SVCDIR "/openrc-run.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_SVCDIR "/openrc-run.sh': %s", - service, strerror(errno)); + execl(openrc_sh_path, openrc_sh_path, service, arg1, arg2, (char *) NULL); + eerror("%s: exec %s: %s", openrc_sh_path, service, strerror(errno)); _exit(EXIT_FAILURE); } else { if (arg2) @@ -423,6 +427,7 @@ svc_exec(const char *arg1, const char *arg2) service, strerror(errno)); _exit(EXIT_FAILURE); } + free(openrc_sh_path); /* UNREACHED: only for safe keeping */ } buffer = xmalloc(sizeof(char) * BUFSIZ); @@ -508,7 +513,7 @@ svc_wait(const char *svc) forever = true; rc_stringlist_free(keywords); - xasprintf(&file, RC_SVCDIR "/exclusive/%s", basename_c(svc)); + xasprintf(&file, "%s/exclusive/%s", rc_service_dir(), basename_c(svc)); interval.tv_sec = 0; interval.tv_nsec = WAIT_INTERVAL; @@ -1115,6 +1120,9 @@ int main(int argc, char **argv) applet = basename_c(argv[0]); + if (rc_yesno(getenv("RC_USER_SERVICES"))) + rc_set_user(); + if (stat(argv[1], &stbuf) != 0) { fprintf(stderr, "openrc-run `%s': %s\n", argv[1], strerror(errno)); diff --git a/src/openrc/rc-logger.c b/src/openrc/rc-logger.c index 974147f9..34cf2e82 100644 --- a/src/openrc/rc-logger.c +++ b/src/openrc/rc-logger.c @@ -46,7 +46,7 @@ #include "misc.h" #include "helpers.h" -#define TMPLOG RC_SVCDIR "/rc.log" +#define TMPLOG_FILE "rc.log" #define DEFAULTLOG "/var/log/rc.log" static int signal_pipe[2] = { -1, -1 }; @@ -145,6 +145,7 @@ rc_logger_open(const char *level) int i; FILE *log = NULL; FILE *plog = NULL; + char *logfile_path; const char *logfile; int log_error = 0; @@ -184,7 +185,8 @@ rc_logger_open(const char *level) signal_pipe[1] = -1; runlevel = level; - if ((log = fopen(TMPLOG, "ae"))) + xasprintf(&logfile_path, "%s/%s", rc_service_dir(), TMPLOG_FILE); + if ((log = fopen(logfile_path, "ae"))) write_time(log, "started"); else { free(logbuf); @@ -234,7 +236,7 @@ rc_logger_open(const char *level) break; } if (logbuf) { - if ((log = fopen(TMPLOG, "ae"))) { + if ((log = fopen(logfile_path, "ae"))) { write_time(log, "started"); write_log(fileno(log), logbuf, logbuf_len); } @@ -249,13 +251,13 @@ rc_logger_open(const char *level) logfile = rc_conf_value("rc_log_path"); if (logfile == NULL) logfile = DEFAULTLOG; - if (!strcmp(logfile, TMPLOG)) { + if (!strcmp(logfile, logfile_path)) { eerror("Cowardly refusing to concatenate a logfile into itself."); - eerrorx("Please change rc_log_path to something other than %s to get rid of this message", TMPLOG); + eerrorx("Please change rc_log_path to something other than %s to get rid of this message", logfile_path); } if ((plog = fopen(logfile, "ae"))) { - if ((log = fopen(TMPLOG, "re"))) { + if ((log = fopen(logfile_path, "re"))) { while ((bytes = fread(buffer, sizeof(*buffer), BUFSIZ, log)) > 0) { if (fwrite(buffer, sizeof(*buffer), bytes, plog) < bytes) { log_error = 1; @@ -266,7 +268,7 @@ rc_logger_open(const char *level) fclose(log); } else { log_error = 1; - eerror("Error: fopen(%s) failed: %s", TMPLOG, strerror(errno)); + eerror("Error: fopen(%s) failed: %s", logfile_path, strerror(errno)); } fclose(plog); @@ -284,11 +286,12 @@ rc_logger_open(const char *level) /* Try to keep the temporary log in case of errors */ if (!log_error) { if (errno != EROFS && ((strcmp(level, RC_LEVEL_SHUTDOWN) != 0) && (strcmp(level, RC_LEVEL_SYSINIT) != 0))) - if (unlink(TMPLOG) == -1) - eerror("Error: unlink(%s) failed: %s", TMPLOG, strerror(errno)); - } else if (exists(TMPLOG)) - eerrorx("Warning: temporary logfile left behind: %s", TMPLOG); + if (unlink(logfile_path) == -1) + eerror("Error: unlink(%s) failed: %s", logfile_path, strerror(errno)); + } else if (exists(logfile_path)) + eerrorx("Warning: temporary logfile left behind: %s", logfile_path); + free(logfile_path); exit(0); /* NOTREACHED */ diff --git a/src/openrc/rc.c b/src/openrc/rc.c index 15777c60..46e3022d 100644 --- a/src/openrc/rc.c +++ b/src/openrc/rc.c @@ -70,7 +70,7 @@ const char *usagestring = "" \ #define INITSH RC_LIBEXECDIR "/sh/init.sh" #define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh" -#define INTERACTIVE RC_SVCDIR "/interactive" +#define INTERACTIVE_SUBDIR "/interactive" #define DEVBOOT "/dev/.rcboot" @@ -95,16 +95,18 @@ clean_failed(void) DIR *dp; struct dirent *d; char *path; + char *failed_dir; + xasprintf(&failed_dir, "%s/failed", rc_service_dir()); /* Clean the failed services state dir now */ - if ((dp = opendir(RC_SVCDIR "/failed"))) { + if ((dp = opendir(failed_dir))) { while ((d = readdir(dp))) { if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) continue; - xasprintf(&path, RC_SVCDIR "/failed/%s", d->d_name); + xasprintf(&path, "%s/%s", failed_dir, d->d_name); if (unlink(path)) eerror("%s: unlink `%s': %s", applet, path, strerror(errno)); @@ -112,16 +114,17 @@ clean_failed(void) } closedir(dp); } + free(failed_dir); } static void cleanup(void) { RC_PID *p, *tmp; + char *dir; - if (!rc_in_logger && !rc_in_plugin && - applet && (strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0)) - { + if (!rc_in_logger && !rc_in_plugin && applet && + (strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0)) { if (hook_out) rc_plugin_run(hook_out, runlevel); @@ -133,8 +136,14 @@ cleanup(void) } /* Clean runlevel start, stop markers */ - rmdir(RC_STARTING); - rmdir(RC_STOPPING); + xasprintf(&dir, "%s/%s", rc_service_dir(), RC_STARTING); + rmdir(dir); + free(dir); + + xasprintf(&dir, "%s/%s", rc_service_dir(), RC_STOPPING); + rmdir(dir); + free(dir); + clean_failed(); rc_logger_close(); } @@ -211,9 +220,14 @@ want_interactive(void) static void mark_interactive(void) { - FILE *fp = fopen(INTERACTIVE, "w"); + char *interactive_dir; + FILE *fp; + + xasprintf(&interactive_dir, "%s/%s", rc_service_dir(), INTERACTIVE_SUBDIR); + fp = fopen(interactive_dir, "w"); if (fp) fclose(fp); + free(interactive_dir); } static void @@ -649,9 +663,11 @@ do_start_services(const RC_STRINGLIST *start_services, bool parallel) bool interactive = false; RC_SERVICE state; bool crashed = false; + char *interactive_dir; + xasprintf(&interactive_dir, "%s/%s", rc_service_dir(), INTERACTIVE_SUBDIR); if (!rc_yesno(getenv("EINFO_QUIET"))) - interactive = exists(INTERACTIVE); + interactive = exists(interactive_dir); errno = 0; crashed = rc_conf_yesno("rc_crashed_start"); if (errno == ENOENT) @@ -710,10 +726,10 @@ do_start_services(const RC_STRINGLIST *start_services, bool parallel) strcmp(runlevel, getenv("RC_BOOTLEVEL")) == 0)) mark_interactive(); else { - if (exists(INTERACTIVE)) - unlink(INTERACTIVE); + if (exists(interactive_dir)) + unlink(interactive_dir); } - + free(interactive_dir); } #ifdef RC_DEBUG @@ -754,6 +770,9 @@ int main(int argc, char **argv) int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; char *krunlevel = NULL; char *pidstr = NULL; + char *deptree_skewed; + char *stopping_dir; + char *starting_dir; int opt; bool parallel; int regen = 0; @@ -773,7 +792,6 @@ int main(int argc, char **argv) applet = basename_c(argv[0]); LIST_INIT(&service_pids); LIST_INIT(&free_these_pids); - atexit(cleanup); if (!applet) eerrorx("arguments required"); @@ -784,10 +802,6 @@ int main(int argc, char **argv) if (chdir("/") == -1) eerror("chdir: %s", strerror(errno)); - /* Ensure our environment is pure - * Also, add our configuration to it */ - env_filter(); - env_config(); /* complain about old configuration settings if they exist */ if (exists(RC_CONF_OLD)) { @@ -836,6 +850,14 @@ int main(int argc, char **argv) case_RC_COMMON_GETOPT } } + /* Register cleanup and set up env after flag parsing + * to assure user paths are still valid */ + atexit(cleanup); + + /* Ensure our environment is pure + * Also, add our configuration to it */ + env_filter(); + env_config(); newlevel = argv[optind++]; /* To make life easier, we only have the shutdown runlevel as @@ -952,17 +974,21 @@ int main(int argc, char **argv) /* Load our deptree */ if ((main_deptree = _rc_deptree_load(0, ®en)) == NULL) eerrorx("failed to load deptree"); - if (exists(RC_DEPTREE_SKEWED)) + + xasprintf(&deptree_skewed, "%s/%s", rc_service_dir(), RC_DEPTREE_SKEWED); + if (exists(deptree_skewed)) ewarn("WARNING: clock skew detected!"); + free(deptree_skewed); /* Clean the failed services state dir */ clean_failed(); - if (mkdir(RC_STOPPING, 0755) != 0) { + xasprintf(&stopping_dir, "%s/%s", rc_service_dir(), RC_STOPPING); + if (mkdir(stopping_dir, 0755) != 0) { if (errno == EACCES) eerrorx("%s: superuser access required", applet); eerrorx("%s: failed to create stopping dir `%s': %s", - applet, RC_STOPPING, strerror(errno)); + applet, stopping_dir, strerror(errno)); } /* Create a list of all services which we could stop (assuming @@ -1044,7 +1070,8 @@ int main(int argc, char **argv) going_down ? newlevel : runlevel); hook_out = 0; - rmdir(RC_STOPPING); + rmdir(stopping_dir); + free(stopping_dir); /* Store the new runlevel */ if (newlevel) { @@ -1061,7 +1088,9 @@ int main(int argc, char **argv) rc_logger_close(); #endif - mkdir(RC_STARTING, 0755); + xasprintf(&starting_dir, "%s/%s", rc_service_dir(), RC_STARTING); + mkdir(starting_dir, 0755); + free(starting_dir); rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, runlevel); hook_out = RC_HOOK_RUNLEVEL_START_OUT; @@ -1130,8 +1159,12 @@ int main(int argc, char **argv) * we need to delete them so that they are regenerated again in the * default runlevel as they may depend on things that are now * available */ - if (regen && strcmp(runlevel, bootlevel) == 0) - unlink(RC_DEPTREE_CACHE); + if (regen && strcmp(runlevel, bootlevel) == 0) { + char *deptree_cache; + xasprintf(&deptree_cache, "%s/%s", rc_service_dir(), RC_DEPTREE_CACHE); + unlink(deptree_cache); + free(deptree_cache); + } return EXIT_SUCCESS; } diff --git a/src/service/service.c b/src/service/service.c index 2545a022..18da90cd 100644 --- a/src/service/service.c +++ b/src/service/service.c @@ -32,6 +32,9 @@ int main(int argc, char **argv) RC_SERVICE state, bit; applet = basename_c(argv[0]); + if (rc_yesno(getenv("RC_USER_SERVICES"))) + rc_set_user(); + if (argc > 1) service = argv[1]; else diff --git a/src/shared/_usage.h b/src/shared/_usage.h index 91b956e0..eb80bb3c 100644 --- a/src/shared/_usage.h +++ b/src/shared/_usage.h @@ -12,8 +12,9 @@ #include #include +#include -#define getoptstring_COMMON "ChqVv" +#define getoptstring_COMMON "ChqVvU" #define longopts_COMMON \ { "help", 0, NULL, 'h'}, \ @@ -21,6 +22,7 @@ { "version", 0, NULL, 'V'}, \ { "verbose", 0, NULL, 'v'}, \ { "quiet", 0, NULL, 'q'}, \ + { "user", 0, NULL, 'U'}, \ { NULL, 0, NULL, 0 } #define longopts_help_COMMON \ @@ -28,13 +30,15 @@ "Disable color output", \ "Display software version", \ "Run verbosely", \ - "Run quietly (repeat to suppress errors)" + "Run quietly (repeat to suppress errors)", \ + "Run in user mode" #define case_RC_COMMON_getopt_case_C setenv ("EINFO_COLOR", "NO", 1); #define case_RC_COMMON_getopt_case_h usage (EXIT_SUCCESS); #define case_RC_COMMON_getopt_case_V if (argc == 2) show_version(); #define case_RC_COMMON_getopt_case_v setenv ("EINFO_VERBOSE", "YES", 1); #define case_RC_COMMON_getopt_case_q set_quiet_options(); +#define case_RC_COMMON_getopt_case_U rc_set_user(); #define case_RC_COMMON_getopt_default usage (EXIT_FAILURE); #define case_RC_COMMON_GETOPT \ @@ -43,6 +47,7 @@ case 'V': case_RC_COMMON_getopt_case_V; break; \ case 'v': case_RC_COMMON_getopt_case_v; break; \ case 'q': case_RC_COMMON_getopt_case_q; break; \ + case 'U': case_RC_COMMON_getopt_case_U; break; \ default: case_RC_COMMON_getopt_default; break; extern const char *applet; diff --git a/src/shared/misc.c b/src/shared/misc.c index 28f4e235..e946c911 100644 --- a/src/shared/misc.c +++ b/src/shared/misc.c @@ -64,6 +64,8 @@ static const char *const env_whitelist[] = { "RC_DEBUG", "RC_NODEPS", "LANG", "LC_MESSAGES", "TERM", "EINFO_COLOR", "EINFO_VERBOSE", + "RC_USER_SERVICES", "HOME", + "XDG_RUNTIME_DIR", "XDG_CONFIG_HOME", NULL }; @@ -142,9 +144,11 @@ env_config(void) char *np; char *npp; char *tok; + char *dir; const char *sys = rc_sys(); char *buffer = NULL; size_t size = 0; + const char *svc_dir = rc_service_dir(); /* Ensure our PATH is prefixed with the system locations first for a little extra security */ @@ -175,8 +179,10 @@ env_config(void) setenv("RC_VERSION", VERSION, 1); setenv("RC_LIBEXECDIR", RC_LIBEXECDIR, 1); - setenv("RC_SVCDIR", RC_SVCDIR, 1); - setenv("RC_TMPDIR", RC_SVCDIR "/tmp", 1); + setenv("RC_SVCDIR", svc_dir, 1); + xasprintf(&dir, "%s/tmp", svc_dir); + setenv("RC_TMPDIR", dir, 1); + free(dir); setenv("RC_BOOTLEVEL", RC_LEVEL_BOOT, 1); e = rc_runlevel_get(); setenv("RC_RUNLEVEL", e, 1); @@ -248,7 +254,7 @@ svc_lock(const char *applet, bool ignore_lock_failure) char *file = NULL; int fd; - xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); + xasprintf(&file, "%s/exclusive/%s", rc_service_dir(), applet); fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664); free(file); if (fd == -1) @@ -274,10 +280,11 @@ svc_unlock(const char *applet, int fd) { char *file = NULL; - xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); + xasprintf(&file, "%s/exclusive/%s", rc_service_dir(), applet); close(fd); unlink(file); free(file); + return -1; } @@ -388,12 +395,16 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen) t = 0; if (rc_deptree_update_needed(&t, file) || force != 0) { + char *deptree_cache; + xasprintf(&deptree_cache, "%s/%s", rc_service_dir(), RC_DEPTREE_CACHE); /* Test if we have permission to update the deptree */ - fd = open(RC_DEPTREE_CACHE, O_WRONLY); + fd = open(deptree_cache, O_WRONLY); merrno = errno; errno = serrno; - if (fd == -1 && merrno == EACCES) + if (fd == -1 && merrno == EACCES) { + free(deptree_cache); return rc_deptree_load(); + } close(fd); if (regen) @@ -403,29 +414,34 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen) eend (retval, "Failed to update the dependency tree"); if (retval == 0) { - if (stat(RC_DEPTREE_CACHE, &st) != 0) { - eerror("stat(%s): %s", RC_DEPTREE_CACHE, strerror(errno)); + char *deptree_skewed; + if (stat(deptree_cache, &st) != 0) { + eerror("stat(%s): %s", deptree_cache, strerror(errno)); + free(deptree_cache); return NULL; } + + xasprintf(&deptree_skewed, "%s/%s", rc_service_dir(), RC_DEPTREE_SKEWED); if (st.st_mtime < t) { eerror("Clock skew detected with `%s'", file); - eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE - "' to %s", ctime(&t)); - fp = fopen(RC_DEPTREE_SKEWED, "w"); + eerrorn("Adjusting mtime of %s to %s", deptree_cache, ctime(&t)); + fp = fopen(deptree_skewed, "w"); if (fp != NULL) { fprintf(fp, "%s\n", file); fclose(fp); } ut.actime = t; ut.modtime = t; - utime(RC_DEPTREE_CACHE, &ut); + utime(deptree_cache, &ut); } else { - if (exists(RC_DEPTREE_SKEWED)) - unlink(RC_DEPTREE_SKEWED); + if (exists(deptree_skewed)) + unlink(deptree_skewed); } + free(deptree_skewed); } if (force == -1 && regen != NULL) *regen = retval; + free(deptree_cache); } return rc_deptree_load(); } diff --git a/src/shared/misc.h b/src/shared/misc.h index b158a786..9171a881 100644 --- a/src/shared/misc.h +++ b/src/shared/misc.h @@ -33,16 +33,11 @@ #define RC_LEVEL_BOOT "boot" #define RC_LEVEL_DEFAULT "default" -#define RC_DEPTREE_CACHE RC_SVCDIR "/deptree" -#define RC_DEPTREE_SKEWED RC_SVCDIR "/clock-skewed" +#define RC_DEPTREE_CACHE "deptree" +#define RC_DEPTREE_SKEWED "clock-skewed" #define RC_KRUNLEVEL RC_SVCDIR "/krunlevel" -#define RC_STARTING RC_SVCDIR "/rc.starting" -#define RC_STOPPING RC_SVCDIR "/rc.stopping" - -#define RC_SVCDIR_STARTING RC_SVCDIR "/starting" -#define RC_SVCDIR_INACTIVE RC_SVCDIR "/inactive" -#define RC_SVCDIR_STARTED RC_SVCDIR "/started" -#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "/coldplugged" +#define RC_STARTING "rc.starting" +#define RC_STOPPING "rc.stopping" char *rc_conf_value(const char *var); bool rc_conf_yesno(const char *var); diff --git a/src/supervise-daemon/supervise-daemon.c b/src/supervise-daemon/supervise-daemon.c index adf35630..9bc3c029 100644 --- a/src/supervise-daemon/supervise-daemon.c +++ b/src/supervise-daemon/supervise-daemon.c @@ -817,6 +817,7 @@ int main(int argc, char **argv) char **child_argv = NULL; char *str = NULL; char *cmdline = NULL; + const char *svc_dir; applet = basename_c(argv[0]); atexit(cleanup); @@ -1084,9 +1085,10 @@ int main(int argc, char **argv) ch_root = expand_home(home, ch_root); umask(numask); + svc_dir = rc_service_dir(); if (!pidfile) - xasprintf(&pidfile, "/var/run/supervise-%s.pid", svcname); - xasprintf(&fifopath, "%s/supervise-%s.ctl", RC_SVCDIR, svcname); + xasprintf(&pidfile, "%s/supervise-%s.pid", rc_is_user() ? svc_dir : "/var/run", svcname); /* FIXME: improve */ + xasprintf(&fifopath, "%s/supervise-%s.ctl", svc_dir, svcname); if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST) eerrorx("%s: unable to create control fifo: %s", applet, strerror(errno)); diff --git a/src/value/value.c b/src/value/value.c index e5190379..7cd17a31 100644 --- a/src/value/value.c +++ b/src/value/value.c @@ -30,6 +30,9 @@ int main(int argc, char **argv) char *option; applet = basename_c(argv[0]); + if (rc_yesno(getenv("RC_USER_SERVICES"))) + rc_set_user(); + if (service == NULL) eerrorx("%s: no service specified", applet); -- cgit v1.2.3