aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/librc/librc-daemon.c45
-rw-r--r--src/librc/librc-depend.c133
-rw-r--r--src/librc/librc.c420
-rw-r--r--src/openrc-run/meson.build4
-rw-r--r--src/openrc-run/openrc-run.c77
-rw-r--r--src/openrc/meson.build4
-rw-r--r--src/openrc/rc.c137
-rw-r--r--src/rc-service/meson.build2
-rw-r--r--src/rc-service/rc-service.c16
-rw-r--r--src/rc-status/meson.build2
-rw-r--r--src/rc-status/rc-status.c13
-rw-r--r--src/rc-update/meson.build2
-rw-r--r--src/rc-update/rc-update.c15
-rw-r--r--src/shared/_usage.h10
-rw-r--r--src/shared/misc.c91
-rw-r--r--src/shared/misc.h12
-rw-r--r--src/supervise-daemon/supervise-daemon.c23
17 files changed, 866 insertions, 140 deletions
diff --git a/src/librc/librc-daemon.c b/src/librc/librc-daemon.c
index 50245129..10bf883a 100644
--- a/src/librc/librc-daemon.c
+++ b/src/librc/librc-daemon.c
@@ -405,13 +405,26 @@ rc_service_daemon_set(const char *service, const char *exec,
RC_STRINGLIST *match;
int i = 0;
FILE *fp;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
if (!exec && !pidfile) {
errno = EINVAL;
return false;
}
- xasprintf(&dirpath, RC_SVCDIR "/daemons/%s", basename_c(service));
+ xasprintf(&dirpath, "%s/daemons/%s", svcdir, basename_c(service));
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
/* Regardless, erase any existing daemon info */
if ((dp = opendir(dirpath))) {
@@ -477,13 +490,25 @@ rc_service_started_daemon(const char *service,
bool retval = false;
DIR *dp;
struct dirent *d;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
if (!service || !exec)
return false;
- xasprintf(&dirpath, RC_SVCDIR "/daemons/%s", basename_c(service));
+ xasprintf(&dirpath, "%s/daemons/%s", svcdir, basename_c(service));
match = _match_list(exec, argv, NULL);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
+
if (indx > 0) {
xasprintf(&file, "%03d", indx);
retval = _match_daemon(dirpath, file, match);
@@ -532,9 +557,21 @@ rc_service_daemons_crashed(const char *service)
size_t i;
char *ch_root;
char *spidfile;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
+ path += snprintf(dirpath, sizeof(dirpath), "%s/daemons/%s",
+ svcdir, basename_c(service));
- path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
- basename_c(service));
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
if (!(dp = opendir(dirpath)))
return false;
diff --git a/src/librc/librc-depend.c b/src/librc/librc-depend.c
index d584af2c..dfb275bf 100644
--- a/src/librc/librc-depend.c
+++ b/src/librc/librc-depend.c
@@ -35,7 +35,8 @@
#define GENDEP RC_LIBEXECDIR "/sh/gendepends.sh"
-#define RC_DEPCONFIG RC_SVCDIR "/depconfig"
+#define RC_DEPCONFIG_FILE "/depconfig"
+#define RC_DEPCONFIG RC_SVCDIR RC_DEPCONFIG_FILE
static const char *bootlevel = NULL;
@@ -121,7 +122,26 @@ get_deptype(const RC_DEPINFO *depinfo, const char *type)
RC_DEPTREE *
rc_deptree_load(void) {
- return rc_deptree_load_file(RC_DEPTREE_CACHE);
+
+ char *cache = RC_DEPTREE_CACHE;
+#ifdef RC_USER_SERVICES
+ char *user_svcdir;
+ RC_DEPTREE *tree;
+
+ if (rc_is_user()) {
+ user_svcdir = rc_user_svcdir();
+ xasprintf(&cache, "%s/%s", user_svcdir, RC_DEPTREE_CACHE_FILE);
+
+ tree = rc_deptree_load_file(cache);
+
+ free(cache);
+ free(user_svcdir);
+
+ return tree;
+ }
+#endif
+
+ return rc_deptree_load_file(cache);
}
RC_DEPTREE *
@@ -668,19 +688,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
};
@@ -693,16 +713,31 @@ rc_deptree_update_needed(time_t *newest, char *file)
int i;
struct stat buf;
time_t mtime;
+ char *depconfig = RC_DEPCONFIG, *cache = RC_DEPTREE_CACHE;
+ char *depdir = NULL;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ char *user_sysconfdir, *tmp;
+
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ user_sysconfdir = rc_user_sysconfdir();
+ xasprintf(&cache, "%s/%s", svcdir, RC_DEPTREE_CACHE_FILE);
+ }
+#endif
/* 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 (i = 0; depdirs[i]; i++) {
+ xasprintf(&depdir, "%s/%s", svcdir, depdirs[i]);
+ if (mkdir(depdir, 0755) != 0 && errno != EEXIST)
+ fprintf(stderr, "mkdir `%s': %s\n", depdir, strerror(errno));
+ free(depdir);
+ }
/* Quick test to see if anything we use has changed and we have
* data in our deptree. */
- if (stat(RC_DEPTREE_CACHE, &buf) == 0) {
+ if (stat(cache, &buf) == 0) {
mtime = buf.st_mtime;
} else {
/* No previous cache found.
@@ -727,11 +762,38 @@ rc_deptree_update_needed(time_t *newest, char *file)
#ifdef RC_LOCAL_CONFDIR
newer |= !deep_mtime_check(RC_LOCAL_CONFDIR,true,&mtime,file);
#endif
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ newer |= !deep_mtime_check(RC_SYS_USER_INITDIR,true,&mtime,file);
+ newer |= !deep_mtime_check(RC_SYS_USER_CONFDIR,true,&mtime,file);
+
+ xasprintf(&tmp, "%s/%s", user_sysconfdir, RC_USER_INITDIR_FOLDER);
+ newer |= !deep_mtime_check(tmp,true,&mtime,file);
+ free(tmp);
+
+ xasprintf(&tmp, "%s/%s", user_sysconfdir, RC_USER_CONFDIR_FOLDER);
+ newer |= !deep_mtime_check(tmp,true,&mtime,file);
+ free(tmp);
+
+ free(user_sysconfdir);
+ }
+#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);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ xasprintf(&depconfig, "%s/%s", svcdir, RC_DEPCONFIG_FILE);
+ free(svcdir);
+ }
+#endif
+ config = rc_config_list(depconfig);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(depconfig);
+ }
+#endif
TAILQ_FOREACH(s, config, entries) {
newer |= !deep_mtime_check(s->value, true, &mtime, file);
}
@@ -768,6 +830,11 @@ rc_deptree_update(void)
char *line = NULL;
size_t len = 0;
char *depend, *depends, *service, *type, *nosys, *onosys;
+ char *cache = RC_DEPTREE_CACHE;
+ char *depconfig = RC_DEPCONFIG;
+#ifdef RC_USER_SERVICES
+ char *user_svcdir;
+#endif
size_t i, k, l;
bool retval = true;
const char *sys = rc_sys();
@@ -1048,7 +1115,16 @@ 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"))) {
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ user_svcdir = rc_user_svcdir();
+ xasprintf(&cache, "%s/%s", user_svcdir, RC_DEPTREE_CACHE_FILE);
+ xasprintf(&depconfig, "%s/%s", user_svcdir, RC_DEPCONFIG_FILE);
+ free(user_svcdir);
+ }
+#endif
+ if ((fp = fopen(cache, "w"))) {
i = 0;
TAILQ_FOREACH(depinfo, deptree, entries) {
fprintf(fp, "depinfo_%zu_service='%s'\n",
@@ -1067,25 +1143,32 @@ rc_deptree_update(void)
fclose(fp);
} else {
fprintf(stderr, "fopen `%s': %s\n",
- RC_DEPTREE_CACHE, strerror(errno));
+ cache, strerror(errno));
retval = false;
}
/* Save our external config files to disk */
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);
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(cache);
+ free(depconfig);
+ }
+#endif
+
rc_stringlist_free(config);
free(deptree);
return retval;
diff --git a/src/librc/librc.c b/src/librc/librc.c
index b3c98adb..9063b934 100644
--- a/src/librc/librc.c
+++ b/src/librc/librc.c
@@ -40,7 +40,8 @@
# include <sys/sysctl.h>
#endif
-#define RC_RUNLEVEL RC_SVCDIR "/softlevel"
+#define RC_RUNLEVEL_FOLDER "/softlevel"
+#define RC_RUNLEVEL RC_SVCDIR RC_RUNLEVEL_FOLDER
#ifndef S_IXUGO
# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
@@ -477,6 +478,7 @@ get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list, RC_STRINGLIS
RC_STRINGLIST *dirs;
RC_STRING *d, *parent;
const char *nextlevel;
+ char *runlevel_dir = RC_RUNLEVELDIR;
/*
* If we haven't been passed a runlevel or a level list, or
@@ -498,7 +500,15 @@ get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list, RC_STRINGLIS
* We can now do exactly the above procedure for our chained
* runlevels.
*/
- snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
+
+ snprintf(path, sizeof(path), "%s/%s", runlevel_dir, runlevel);
dirs = ls_dir(path, LS_DIR);
TAILQ_FOREACH(d, dirs, entries) {
nextlevel = d->value;
@@ -521,33 +531,78 @@ get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list, RC_STRINGLIS
rc_stringlist_delete(ancestor_list, nextlevel);
}
rc_stringlist_free(dirs);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
}
bool
rc_runlevel_starting(void)
{
- return exists(RC_STARTING);
+ char *rc_starting = RC_STARTING;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_svcdir = rc_user_svcdir();
+ xasprintf(&rc_starting, "%s/%s", user_svcdir, RC_STARTING_FOLDER);
+ free(user_svcdir);
+ }
+#endif
+ return exists(rc_starting);
}
bool
rc_runlevel_stopping(void)
{
- return exists(RC_STOPPING);
+ char *rc_stopping = RC_STOPPING;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_svcdir = rc_user_svcdir();
+ xasprintf(&rc_stopping, "%s/%s", user_svcdir, RC_STOPPING_FOLDER);
+ free(user_svcdir);
+ }
+#endif
+ return exists(rc_stopping);
}
RC_STRINGLIST *rc_runlevel_list(void)
{
- return ls_dir(RC_RUNLEVELDIR, LS_DIR);
+ char *runlevel_dir = RC_RUNLEVELDIR;
+ RC_STRINGLIST *runlevels;
+#ifdef RC_USER_SERVICES
+ char *user_sysconfdir;
+ if (rc_is_user()) {
+ user_sysconfdir = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconfdir, RC_USER_RUNLEVELS_FOLDER);
+ }
+#endif
+ runlevels = ls_dir(runlevel_dir, LS_DIR);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
+ return runlevels;
}
char *
rc_runlevel_get(void)
{
FILE *fp;
- char *runlevel = NULL;
+ char *runlevel = NULL, *runlevel_path = RC_RUNLEVEL;
size_t i;
- if ((fp = fopen(RC_RUNLEVEL, "r"))) {
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *path = rc_user_svcdir();
+ xasprintf(&runlevel_path, "%s/%s", path, RC_RUNLEVEL_FOLDER);
+ free(path);
+ }
+#endif
+
+ if ((fp = fopen(runlevel_path, "r"))) {
runlevel = xmalloc(sizeof(char) * PATH_MAX);
if (fgets(runlevel, PATH_MAX, fp)) {
i = strlen(runlevel) - 1;
@@ -558,6 +613,13 @@ rc_runlevel_get(void)
fclose(fp);
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_path);
+ }
+#endif
+
+
if (!runlevel || !*runlevel) {
free(runlevel);
runlevel = xstrdup(RC_LEVEL_SYSINIT);
@@ -569,12 +631,30 @@ rc_runlevel_get(void)
bool
rc_runlevel_set(const char *runlevel)
{
- FILE *fp = fopen(RC_RUNLEVEL, "w");
+ char *runlevel_path = RC_RUNLEVEL;
+ FILE *fp;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *path = rc_user_svcdir();
+ xasprintf(&runlevel_path, "%s/%s", path, RC_RUNLEVEL_FOLDER);
+ free(path);
+ }
+#endif
+
+ fp = fopen(runlevel_path, "w");
if (!fp)
return false;
fprintf(fp, "%s", runlevel);
fclose(fp);
+
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_path);
+ }
+#endif
return true;
}
@@ -587,7 +667,19 @@ rc_runlevel_exists(const char *runlevel)
if (!runlevel || strcmp(runlevel, "") == 0 || strcmp(runlevel, ".") == 0 ||
strcmp(runlevel, "..") == 0)
return false;
- snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_path = rc_user_sysconfdir();
+ snprintf(path, sizeof(path), "%s/%s/%s",
+ user_path, RC_USER_RUNLEVELS_FOLDER, runlevel);
+ } else {
+#endif
+ snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
+#ifdef RC_USER_SERVICES
+ }
+#endif
+
if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode))
return true;
return false;
@@ -597,11 +689,28 @@ bool
rc_runlevel_stack(const char *dst, const char *src)
{
char d[PATH_MAX], s[PATH_MAX];
+ char *runlevel_dir = RC_RUNLEVELDIR;
if (!rc_runlevel_exists(dst) || !rc_runlevel_exists(src))
return false;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
+
snprintf(s, sizeof(s), "../%s", src);
- snprintf(d, sizeof(s), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
+ snprintf(d, sizeof(s), "%s/%s/%s", runlevel_dir, dst, src);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
+
return (symlink(s, d) == 0 ? true : false);
}
@@ -609,8 +718,23 @@ bool
rc_runlevel_unstack(const char *dst, const char *src)
{
char path[PATH_MAX];
+ char *runlevel_dir = RC_RUNLEVELDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
+
+ snprintf(path, sizeof(path), "%s/%s/%s", runlevel_dir, dst, src);
- snprintf(path, sizeof(path), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
return (unlink(path) == 0 ? true : false);
}
@@ -632,9 +756,16 @@ rc_service_resolve(const char *service)
{
char buffer[PATH_MAX];
char file[PATH_MAX];
+ char *svcdir = RC_SVCDIR;
int r;
struct stat buf;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
if (!service)
return NULL;
@@ -642,14 +773,20 @@ rc_service_resolve(const char *service)
return xstrdup(service);
/* First check started services */
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", "started", service);
+ snprintf(file, sizeof(file), "%s/%s/%s", svcdir, "started", service);
if (lstat(file, &buf) || !S_ISLNK(buf.st_mode)) {
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- "inactive", service);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir, "inactive", service);
if (lstat(file, &buf) || !S_ISLNK(buf.st_mode))
*file = '\0';
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
+
if (*file) {
memset(buffer, 0, sizeof(buffer));
r = readlink(file, buffer, sizeof(buffer)-1);
@@ -657,6 +794,24 @@ rc_service_resolve(const char *service)
return xstrdup(buffer);
}
+#ifdef RC_USER_SERVICES
+ /* If we're in user services mode, try user services*/
+ if (rc_is_user()) {
+ /* User defined services have preference to system provided */
+ char *user_sysconfdir = rc_user_sysconfdir();
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ user_sysconfdir, RC_USER_INITDIR_FOLDER, service);
+ free(user_sysconfdir);
+ if (stat(file, &buf) == 0)
+ return xstrdup(file);
+
+ snprintf(file, sizeof(file), "%s/%s",
+ RC_SYS_USER_INITDIR, service);
+ if (stat(file, &buf) == 0)
+ return xstrdup(file);
+ }
+#endif
+
#ifdef RC_LOCAL_INITDIR
/* Nope, so lets see if the user has written it */
snprintf(file, sizeof(file), RC_LOCAL_INITDIR "/%s", service);
@@ -791,9 +946,24 @@ bool
rc_service_in_runlevel(const char *service, const char *runlevel)
{
char file[PATH_MAX];
+ char *runlevel_dir = RC_RUNLEVELDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ runlevel_dir, runlevel, basename_c(service));
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
- snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
- runlevel, basename_c(service));
return exists(file);
}
@@ -811,6 +981,12 @@ rc_service_mark(const char *service, const RC_SERVICE state)
RC_STRINGLIST *dirs;
RC_STRING *dir;
int serrno;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
if (!init)
return false;
@@ -822,8 +998,8 @@ rc_service_mark(const char *service, const RC_SERVICE state)
return false;
}
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- rc_parse_service_state(state), base);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir,rc_parse_service_state(state), base);
if (exists(file))
unlink(file);
i = symlink(init, file);
@@ -849,15 +1025,16 @@ rc_service_mark(const char *service, const RC_SERVICE state)
s != RC_SERVICE_SCHEDULED) &&
(!skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
{
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- rc_service_state_names[i].name, base);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir, rc_service_state_names[i].name, base);
if (exists(file)) {
if ((state == RC_SERVICE_STARTING ||
state == RC_SERVICE_STOPPING) &&
s == RC_SERVICE_INACTIVE)
{
snprintf(was, sizeof(was),
- RC_SVCDIR "/%s/%s",
+ "%s/%s/%s",
+ svcdir,
rc_parse_service_state(RC_SERVICE_WASINACTIVE),
base);
if (symlink(init, was) == -1) {
@@ -879,19 +1056,19 @@ rc_service_mark(const char *service, const RC_SERVICE state)
state == RC_SERVICE_STOPPED ||
state == RC_SERVICE_INACTIVE)
{
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- "exclusive", base);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir, "exclusive", base);
unlink(file);
}
/* Remove any options and daemons the service may have stored */
if (state == RC_SERVICE_STOPPED) {
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- "options", base);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir, "options", base);
rm_dir(file, true);
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- "daemons", base);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir, "daemons", base);
rm_dir(file, true);
rc_service_schedule_clear(service);
@@ -899,7 +1076,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) {
- snprintf(file, sizeof(file), RC_SVCDIR "/%s", "scheduled");
+ snprintf(file, sizeof(file), "%s/%s", svcdir, "scheduled");
dirs = ls_dir(file, 0);
TAILQ_FOREACH(dir, dirs, entries) {
snprintf(was, sizeof(was), "%s/%s/%s",
@@ -927,10 +1104,16 @@ rc_service_state(const char *service)
RC_STRINGLIST *dirs;
RC_STRING *dir;
const char *base = basename_c(service);
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
for (i = 0; rc_service_state_names[i].name; i++) {
- snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
- rc_service_state_names[i].name, base);
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ svcdir, 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;
@@ -944,11 +1127,14 @@ rc_service_state(const char *service)
state |= RC_SERVICE_CRASHED;
}
if (state & RC_SERVICE_STOPPED) {
- dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
+ char *scheduled;
+ xasprintf(&scheduled, "%s/%s", svcdir, "/scheduled");
+ dirs = ls_dir(scheduled, 0);
+ free(scheduled);
TAILQ_FOREACH(dir, dirs, entries) {
snprintf(file, sizeof(file),
- RC_SVCDIR "/scheduled/%s/%s",
- dir->value, service);
+ "%s/scheduled/%s/%s",
+ svcdir, dir->value, service);
if (exists(file)) {
state |= RC_SERVICE_SCHEDULED;
break;
@@ -966,9 +1152,15 @@ rc_service_value_get(const char *service, const char *option)
char *buffer = NULL;
size_t len = 0;
char file[PATH_MAX];
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
- snprintf(file, sizeof(file), RC_SVCDIR "/options/%s/%s",
- service, option);
+ snprintf(file, sizeof(file), "%s/options/%s/%s",
+ svcdir, service, option);
rc_getfile(file, &buffer, &len);
return buffer;
@@ -981,8 +1173,14 @@ rc_service_value_set(const char *service, const char *option,
FILE *fp;
char file[PATH_MAX];
char *p = file;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
- p += snprintf(file, sizeof(file), RC_SVCDIR "/options/%s", service);
+ p += snprintf(file, sizeof(file), "%s/options/%s", svcdir, service);
if (mkdir(file, 0755) != 0 && errno != EEXIST)
return false;
@@ -1006,13 +1204,19 @@ rc_service_schedule_start(const char *service, const char *service_to_start)
char *p = file;
char *init;
bool retval;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
/* service may be a provided service, like net */
if (!service || !rc_service_exists(service_to_start))
return false;
- p += snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s",
- basename_c(service));
+ p += snprintf(file, sizeof(file), "%s/scheduled/%s",
+ svcdir, basename_c(service));
if (mkdir(file, 0755) != 0 && errno != EEXIST)
return false;
@@ -1028,9 +1232,15 @@ bool
rc_service_schedule_clear(const char *service)
{
char dir[PATH_MAX];
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
- snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s",
- basename_c(service));
+ snprintf(dir, sizeof(dir), "%s/scheduled/%s",
+ svcdir, basename_c(service));
if (!rm_dir(dir, true) && errno == ENOENT)
return true;
return false;
@@ -1041,6 +1251,7 @@ rc_services_in_runlevel(const char *runlevel)
{
char dir[PATH_MAX];
RC_STRINGLIST *list = NULL;
+ char *runlevel_dir = RC_RUNLEVELDIR;
if (!runlevel) {
#ifdef RC_PKG_INITDIR
@@ -1049,9 +1260,28 @@ rc_services_in_runlevel(const char *runlevel)
#ifdef RC_LOCAL_INITDIR
RC_STRINGLIST *local = ls_dir(RC_LOCAL_INITDIR, LS_INITD);
#endif
+#ifdef RC_USER_SERVICES
+ RC_STRINGLIST *local_user;
+ RC_STRINGLIST *sys_user;
+ char *user_sysconf;
+ char *user_initdir;
+ if (rc_is_user()) {
+ user_sysconf = rc_user_sysconfdir();
+ xasprintf(&user_initdir, "%s/%s", user_sysconf, RC_USER_INITDIR_FOLDER);
+
+ local_user = ls_dir(user_initdir, LS_INITD);
+ sys_user = ls_dir(RC_SYS_USER_INITDIR, LS_INITD);
+
+ list = rc_stringlist_new();
+ } else {
+#endif
list = ls_dir(RC_INITDIR, LS_INITD);
+#ifdef RC_USER_SERVICES
+ }
+#endif
+
#ifdef RC_PKG_INITDIR
TAILQ_CONCAT(list, pkg, entries);
rc_stringlist_free(pkg);
@@ -1060,13 +1290,37 @@ rc_services_in_runlevel(const char *runlevel)
TAILQ_CONCAT(list, local, entries);
rc_stringlist_free(local);
#endif
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ TAILQ_CONCAT(list, local_user, entries);
+ TAILQ_CONCAT(list, sys_user, entries);
+ rc_stringlist_free(local_user);
+ rc_stringlist_free(sys_user);
+ free(user_sysconf);
+ free(user_initdir);
+ }
+#endif
return list;
}
/* These special levels never contain any services */
if (strcmp(runlevel, RC_LEVEL_SINGLE) != 0) {
- snprintf(dir, sizeof(dir), RC_RUNLEVELDIR "/%s", runlevel);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
+ snprintf(dir, sizeof(dir), "%s/%s", runlevel_dir, runlevel);
list = ls_dir(dir, LS_INITD);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
+
}
if (!list)
list = rc_stringlist_new();
@@ -1099,9 +1353,16 @@ rc_services_in_state(RC_SERVICE state)
RC_STRING *d;
char dir[PATH_MAX];
char *p = dir;
+ char *svcdir = RC_SVCDIR;
- p += snprintf(dir, sizeof(dir), RC_SVCDIR "/%s",
- rc_parse_service_state(state));
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
+ p += snprintf(dir, sizeof(dir), "%s/%s",
+ svcdir, rc_parse_service_state(state));
if (state != RC_SERVICE_SCHEDULED)
return ls_dir(dir, LS_INITD);
@@ -1120,6 +1381,13 @@ rc_services_in_state(RC_SERVICE state)
}
}
rc_stringlist_free(dirs);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
+
return list;
}
@@ -1131,7 +1399,15 @@ rc_service_add(const char *runlevel, const char *service)
char file[PATH_MAX];
char path[MAXPATHLEN] = { '\0' };
char binit[PATH_MAX];
+ char *runlevel_dir = RC_RUNLEVELDIR;
char *i;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
if (!rc_runlevel_exists(runlevel)) {
errno = ENOENT;
@@ -1144,8 +1420,14 @@ rc_service_add(const char *runlevel, const char *service)
}
i = init = rc_service_resolve(service);
- snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
- runlevel, basename_c(service));
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ runlevel_dir, runlevel, basename_c(service));
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
/* We need to ensure that only things in /etc/init.d are added
* to the boot runlevel */
@@ -1172,9 +1454,25 @@ bool
rc_service_delete(const char *runlevel, const char *service)
{
char file[PATH_MAX];
+ char *runlevel_dir = RC_RUNLEVELDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *user_sysconf = rc_user_sysconfdir();
+ xasprintf(&runlevel_dir, "%s/%s", user_sysconf, RC_USER_RUNLEVELS_FOLDER);
+ free(user_sysconf);
+ }
+#endif
+
+ snprintf(file, sizeof(file), "%s/%s/%s",
+ runlevel_dir, runlevel, basename_c(service));
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(runlevel_dir);
+ }
+#endif
- snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
- runlevel, basename_c(service));
if (unlink(file) == 0)
return true;
return false;
@@ -1183,14 +1481,25 @@ 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[PATH_MAX];
+ char *scheduled;
+ char *svcdir = RC_SVCDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+ xasprintf(&scheduled, "%s/%s", svcdir, "/scheduled");
+ dirs = ls_dir(scheduled, 0);
+ free(scheduled);
TAILQ_FOREACH(dir, dirs, entries) {
- snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s/%s",
- dir->value, service);
+ snprintf(file, sizeof(file), "%s/scheduled/%s/%s",
+ svcdir, dir->value, service);
if (exists(file))
rc_stringlist_add(list, file);
}
@@ -1202,8 +1511,15 @@ RC_STRINGLIST *
rc_services_scheduled(const char *service)
{
char dir[PATH_MAX];
+ char *svcdir = RC_SVCDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
- snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s",
- basename_c(service));
+ snprintf(dir, sizeof(dir), "%s/scheduled/%s",
+ svcdir, basename_c(service));
return ls_dir(dir, LS_INITD);
}
diff --git a/src/openrc-run/meson.build b/src/openrc-run/meson.build
index 2e4b29a0..5de60456 100644
--- a/src/openrc-run/meson.build
+++ b/src/openrc-run/meson.build
@@ -1,6 +1,6 @@
executable('openrc-run',
['openrc-run.c', misc_c, plugin_c, selinux_c, usage_c, version_h],
- c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags],
+ c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, selinux_dep, util_dep, crypt_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
@@ -9,7 +9,7 @@ executable('openrc-run',
executable('runscript',
['openrc-run.c', misc_c, plugin_c, selinux_c, usage_c, version_h],
- c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags],
+ c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, selinux_dep, util_dep, crypt_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
diff --git a/src/openrc-run/openrc-run.c b/src/openrc-run/openrc-run.c
index 6b28a809..6ce5ddec 100644
--- a/src/openrc-run/openrc-run.c
+++ b/src/openrc-run/openrc-run.c
@@ -54,7 +54,7 @@
#include "_usage.h"
#include "helpers.h"
-#define PREFIX_LOCK RC_SVCDIR "/prefix.lock"
+#define PREFIX_LOCK_FILE "/prefix.lock"
#define WAIT_INTERVAL 20000000 /* usecs to poll the lock file */
#define WAIT_TIMEOUT 60 /* seconds until we timeout */
@@ -164,8 +164,21 @@ static void
unhotplug(void)
{
char *file = NULL;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
+ xasprintf(&file, "%s/hotplugged/%s", svcdir, applet);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
- xasprintf(&file, RC_SVCDIR "/hotplugged/%s", applet);
if (exists(file) && unlink(file) != 0)
eerror("%s: unlink `%s': %s", applet, file, strerror(errno));
free(file);
@@ -286,12 +299,25 @@ 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 = RC_SVCDIR PREFIX_LOCK_FILE;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ char *svcdir = rc_user_svcdir();
+ xasprintf(&prefix_lock, "%s/%s", svcdir, PREFIX_LOCK_FILE);
+ free(svcdir);
+ }
+#endif
/*
* Lock the prefix.
- * open() may fail here when running as user, as RC_SVCDIR may not be writable.
*/
- lock_fd = open(PREFIX_LOCK, O_WRONLY | O_CREAT, 0664);
+ lock_fd = open(prefix_lock, O_WRONLY | O_CREAT, 0664);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(prefix_lock);
+ }
+#endif
if (lock_fd != -1) {
while (flock(lock_fd, LOCK_EX) != 0) {
@@ -350,6 +376,13 @@ svc_exec(const char *arg1, const char *arg2)
int slave_tty;
sigset_t sigchldmask;
sigset_t oldmask;
+ char *runsh;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
/* Setup our signal pipe */
if (pipe(signal_pipe) == -1)
@@ -390,20 +423,19 @@ svc_exec(const char *arg1, const char *arg2)
dup2(slave_tty, STDERR_FILENO);
}
- if (exists(RC_SVCDIR "/openrc-run.sh")) {
+ xasprintf(&runsh, "%s/%s", svcdir, "openrc-run.sh");
+ if (exists(runsh)) {
if (arg2)
einfov("Executing: %s %s %s %s %s",
- RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh",
+ runsh, runsh,
service, arg1, arg2);
else
einfov("Executing: %s %s %s %s",
- RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh",
+ runsh, runsh,
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(runsh, runsh, service, arg1, arg2, (char *) NULL);
+ eerror("%s: exec '%s': %s",
+ service, runsh, strerror(errno));
_exit(EXIT_FAILURE);
} else {
if (arg2)
@@ -423,8 +455,15 @@ svc_exec(const char *arg1, const char *arg2)
service, strerror(errno));
_exit(EXIT_FAILURE);
}
+ free(runsh);
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
+
buffer = xmalloc(sizeof(char) * BUFSIZ);
fd[0].fd = signal_pipe[0];
fd[0].events = fd[1].events = POLLIN;
@@ -500,6 +539,12 @@ svc_wait(const char *svc)
bool forever = false;
RC_STRINGLIST *keywords;
struct timespec interval, timeout, warn;
+ char *svcdir = RC_SVCDIR;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
/* Some services don't have a timeout, like fsck */
keywords = rc_deptree_depend(deptree, svc, "keyword");
@@ -508,7 +553,13 @@ 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", svcdir, basename_c(svc));
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
interval.tv_sec = 0;
interval.tv_nsec = WAIT_INTERVAL;
diff --git a/src/openrc/meson.build b/src/openrc/meson.build
index 9ab32ef4..3ad0d62b 100644
--- a/src/openrc/meson.build
+++ b/src/openrc/meson.build
@@ -1,6 +1,6 @@
executable('openrc',
['rc.c', 'rc-logger.c', misc_c, plugin_c, usage_c, version_h],
- c_args : cc_branding_flags,
+ c_args : [cc_branding_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
dependencies: [dl_dep, util_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
@@ -9,7 +9,7 @@ executable('openrc',
executable('rc',
['rc.c', 'rc-logger.c', misc_c, plugin_c, usage_c, version_h],
- c_args : cc_branding_flags,
+ c_args : [cc_branding_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
dependencies: [dl_dep, util_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
diff --git a/src/openrc/rc.c b/src/openrc/rc.c
index 36b2e2e2..dca94a7f 100644
--- a/src/openrc/rc.c
+++ b/src/openrc/rc.c
@@ -49,12 +49,20 @@
#include "helpers.h"
const char *extraopts = NULL;
-const char getoptstring[] = "a:no:s:S" getoptstring_COMMON;
+const char getoptstring[] = "a:no:s:S" getoptstring_COMMON
+#ifdef RC_USER_SERVICES
+getoptstring_USER_SERVICES
+#endif
+;
+
const struct option longopts[] = {
{ "no-stop", 0, NULL, 'n' },
{ "override", 1, NULL, 'o' },
{ "service", 1, NULL, 's' },
{ "sys", 0, NULL, 'S' },
+#ifdef RC_USER_SERVICES
+ longopts_USER_SERVICES
+#endif
longopts_COMMON
};
const char * const longopts_help[] = {
@@ -62,6 +70,9 @@ const char * const longopts_help[] = {
"override the next runlevel to change into\nwhen leaving single user or boot runlevels",
"runs the service specified with the rest\nof the arguments",
"output the RC system type, if any",
+#ifdef RC_USER_SERVICES
+ longopts_help_USER_SERVICES
+#endif
longopts_help_COMMON
};
const char *usagestring = "" \
@@ -70,7 +81,8 @@ 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_FILE "/interactive"
+#define INTERACTIVE RC_SVCDIR INTERACTIVE_FILE
#define DEVBOOT "/dev/.rcboot"
@@ -95,16 +107,24 @@ clean_failed(void)
DIR *dp;
struct dirent *d;
char *path;
+ char *svcdir = RC_SVCDIR;
+ char *failed_path;
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+ xasprintf(&failed_path, "%s/%s", svcdir, "/failed");
/* Clean the failed services state dir now */
- if ((dp = opendir(RC_SVCDIR "/failed"))) {
+ if ((dp = opendir(failed_path))) {
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_path, d->d_name);
if (unlink(path))
eerror("%s: unlink `%s': %s",
applet, path, strerror(errno));
@@ -112,12 +132,24 @@ clean_failed(void)
}
closedir(dp);
}
+ free(failed_path);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
}
static void
cleanup(void)
{
RC_PID *p, *tmp;
+ char *rc_starting = RC_STARTING;
+ char *rc_stopping = RC_STOPPING;
+
+#ifdef RC_USER_SERVICES
+ char *user_svcdir;
+#endif
if (!rc_in_logger && !rc_in_plugin &&
applet && (strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0))
@@ -132,11 +164,27 @@ cleanup(void)
free(termios_orig);
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ user_svcdir = rc_user_svcdir();
+ xasprintf(&rc_starting, "%s/%s", user_svcdir, RC_STARTING_FOLDER);
+ xasprintf(&rc_stopping, "%s/%s", user_svcdir, RC_STOPPING_FOLDER);
+ free(user_svcdir);
+ }
+#endif
+
/* Clean runlevel start, stop markers */
- rmdir(RC_STARTING);
- rmdir(RC_STOPPING);
+ rmdir(rc_starting);
+ rmdir(rc_stopping);
clean_failed();
rc_logger_close();
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(rc_starting);
+ free(rc_stopping);
+ }
+#endif
}
LIST_FOREACH_SAFE(p, &service_pids, entries, tmp) {
@@ -211,9 +259,24 @@ want_interactive(void)
static void
mark_interactive(void)
{
- FILE *fp = fopen(INTERACTIVE, "w");
+ FILE *fp;
+ char *interactive_path = INTERACTIVE;
+#ifdef RC_USER_SERVICES
+ char *svcdir;
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ xasprintf(&interactive_path, "%s/%s", svcdir, INTERACTIVE_FILE);
+ free(svcdir);
+ }
+#endif
+ fp = fopen(interactive_path, "w");
if (fp)
fclose(fp);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(interactive_path);
+ }
+#endif
}
static void
@@ -649,9 +712,18 @@ do_start_services(const RC_STRINGLIST *start_services, bool parallel)
bool interactive = false;
RC_SERVICE state;
bool crashed = false;
+ char *interactive_path = INTERACTIVE;
+#ifdef RC_USER_SERVICES
+ char *svcdir;
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ xasprintf(&interactive_path, "%s/%s", svcdir, INTERACTIVE_FILE);
+ free(svcdir);
+ }
+#endif
if (!rc_yesno(getenv("EINFO_QUIET")))
- interactive = exists(INTERACTIVE);
+ interactive = exists(interactive_path);
errno = 0;
crashed = rc_conf_yesno("rc_crashed_start");
if (errno == ENOENT)
@@ -710,10 +782,15 @@ 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_path))
+ unlink(interactive_path);
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(interactive_path);
+ }
+#endif
}
#ifdef RC_DEBUG
@@ -754,10 +831,16 @@ int main(int argc, char **argv)
int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
char *krunlevel = NULL;
char *pidstr = NULL;
+ char *rc_starting = RC_STARTING;
+ char *rc_stopping = RC_STOPPING;
int opt;
bool parallel;
int regen = 0;
bool nostop = false;
+#ifdef RC_USER_SERVICES
+ char *user_svcdir = NULL;
+ char *user_sysconfdir = NULL;
+#endif
#ifdef __linux__
char *proc;
char *p;
@@ -784,11 +867,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)) {
ewarn("%s still exists on your system and should be removed.",
@@ -833,10 +911,18 @@ int main(int argc, char **argv)
printf("%s\n", systype);
exit(EXIT_SUCCESS);
/* NOTREACHED */
+#ifdef RC_USER_SERVICES
+ case_RC_USER_SERVICES
+#endif
case_RC_COMMON_GETOPT
}
}
+ /* Ensure our environment is pure
+ * Also, add our configuration to it */
+ env_filter();
+ env_config();
+
if (strcmp(applet, "rc") == 0)
ewarn("rc is deprecated, please use openrc instead.");
newlevel = argv[optind++];
@@ -858,6 +944,15 @@ int main(int argc, char **argv)
setenv("RC_PID", pidstr, 1);
free(pidstr);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ user_svcdir = rc_user_svcdir();
+ xasprintf(&rc_stopping, "%s/%s", user_svcdir, RC_STOPPING_FOLDER);
+ xasprintf(&rc_starting, "%s/%s", user_svcdir, RC_STARTING_FOLDER);
+ free(user_svcdir);
+ }
+#endif
+
/* Create a list of all services which should be started for the new or
* current runlevel including those in boot, sysinit and hotplugged
* runlevels. Clearly, some of these will already be started so we
@@ -960,7 +1055,7 @@ int main(int argc, char **argv)
/* Clean the failed services state dir */
clean_failed();
- if (mkdir(RC_STOPPING, 0755) != 0) {
+ if (mkdir(rc_stopping, 0755) != 0) {
if (errno == EACCES)
eerrorx("%s: superuser access required", applet);
eerrorx("%s: failed to create stopping dir `%s': %s",
@@ -1046,7 +1141,7 @@ int main(int argc, char **argv)
going_down ? newlevel : runlevel);
hook_out = 0;
- rmdir(RC_STOPPING);
+ rmdir(rc_stopping);
/* Store the new runlevel */
if (newlevel) {
@@ -1063,7 +1158,7 @@ int main(int argc, char **argv)
rc_logger_close();
#endif
- mkdir(RC_STARTING, 0755);
+ mkdir(rc_starting, 0755);
rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, runlevel);
hook_out = RC_HOOK_RUNLEVEL_START_OUT;
@@ -1111,6 +1206,12 @@ int main(int argc, char **argv)
}
rc_stringlist_free(runlevel_chain);
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(rc_stopping);
+ free(rc_starting);
+ }
+#endif
#ifdef __linux__
/* If the "noinit" parameter was passed on the kernel command line then
diff --git a/src/rc-service/meson.build b/src/rc-service/meson.build
index 3a109d8e..543daf45 100644
--- a/src/rc-service/meson.build
+++ b/src/rc-service/meson.build
@@ -1,6 +1,6 @@
executable('rc-service',
['rc-service.c', misc_c, usage_c, version_h],
- c_args : cc_branding_flags,
+ c_args : [cc_branding_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
include_directories: [incdir, einfo_incdir, rc_incdir],
install: true,
diff --git a/src/rc-service/rc-service.c b/src/rc-service/rc-service.c
index 3d2eb730..5127e75f 100644
--- a/src/rc-service/rc-service.c
+++ b/src/rc-service/rc-service.c
@@ -31,7 +31,11 @@
const char *applet = NULL;
const char *extraopts = NULL;
-const char getoptstring[] = "cdDe:ilr:INsSZ" getoptstring_COMMON;
+const char getoptstring[] = "cdDe:ilr:INsSZ" getoptstring_COMMON
+#ifdef RC_USER_SERVICES
+getoptstring_USER_SERVICES
+#endif
+;
const struct option longopts[] = {
{ "debug", 0, NULL, 'd' },
{ "nodeps", 0, NULL, 'D' },
@@ -45,6 +49,9 @@ const struct option longopts[] = {
{ "list", 0, NULL, 'l' },
{ "resolve", 1, NULL, 'r' },
{ "dry-run", 0, NULL, 'Z' },
+#ifdef RC_USER_SERVICES
+ longopts_USER_SERVICES
+#endif
longopts_COMMON
};
const char * const longopts_help[] = {
@@ -60,6 +67,9 @@ const char * const longopts_help[] = {
"list all available services",
"resolve the service name to an init script",
"dry run (show what would happen)",
+#ifdef RC_USER_SERVICES
+ longopts_help_USER_SERVICES
+#endif
longopts_help_COMMON
};
const char *usagestring = "" \
@@ -141,7 +151,9 @@ int main(int argc, char **argv)
case 'Z':
setenv("IN_DRYRUN", "yes", 1);
break;
-
+#ifdef RC_USER_SERVICES
+ case_RC_USER_SERVICES
+#endif
case_RC_COMMON_GETOPT
}
}
diff --git a/src/rc-status/meson.build b/src/rc-status/meson.build
index 7377d837..ee3f3782 100644
--- a/src/rc-status/meson.build
+++ b/src/rc-status/meson.build
@@ -1,6 +1,6 @@
executable('rc-status',
['rc-status.c', misc_c, usage_c, version_h],
- c_args : cc_branding_flags,
+ c_args : [cc_branding_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
dependencies: [util_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
diff --git a/src/rc-status/rc-status.c b/src/rc-status/rc-status.c
index 78d5a6af..d56fe830 100644
--- a/src/rc-status/rc-status.c
+++ b/src/rc-status/rc-status.c
@@ -39,7 +39,11 @@ enum format_t {
const char *applet = NULL;
const char *extraopts = NULL;
+#ifdef RC_USER_SERVICES
+const char getoptstring[] = "acf:lmrsSu" getoptstring_COMMON getoptstring_USER_SERVICES;
+#else
const char getoptstring[] = "acf:lmrsSu" getoptstring_COMMON;
+#endif
const struct option longopts[] = {
{"all", 0, NULL, 'a'},
{"crashed", 0, NULL, 'c'},
@@ -50,6 +54,9 @@ const struct option longopts[] = {
{"servicelist", 0, NULL, 's'},
{"supervised", 0, NULL, 'S'},
{"unused", 0, NULL, 'u'},
+#ifdef RC_USER_SERVICES
+ longopts_USER_SERVICES
+#endif
longopts_COMMON
};
const char * const longopts_help[] = {
@@ -62,6 +69,9 @@ const char * const longopts_help[] = {
"Show service list",
"show supervised services",
"Show services not assigned to any runlevel",
+#ifdef RC_USER_SERVICES
+ longopts_help_USER_SERVICES
+#endif
longopts_help_COMMON
};
const char *usagestring = "" \
@@ -354,6 +364,9 @@ int main(int argc, char **argv)
goto exit;
/* NOTREACHED */
+#ifdef RC_USER_SERVICES
+ case_RC_USER_SERVICES
+#endif
case_RC_COMMON_GETOPT
}
diff --git a/src/rc-update/meson.build b/src/rc-update/meson.build
index 7d284979..4d3b9015 100644
--- a/src/rc-update/meson.build
+++ b/src/rc-update/meson.build
@@ -1,6 +1,6 @@
executable('rc-update',
['rc-update.c', misc_c, usage_c, version_h],
- c_args : cc_branding_flags,
+ c_args : [cc_branding_flags, cc_user_services_flags],
link_with: [libeinfo, librc],
include_directories: [incdir, einfo_incdir, rc_incdir],
install: true,
diff --git a/src/rc-update/rc-update.c b/src/rc-update/rc-update.c
index 64d781a5..f4bbbe88 100644
--- a/src/rc-update/rc-update.c
+++ b/src/rc-update/rc-update.c
@@ -36,17 +36,27 @@ const char *usagestring = "" \
"Usage: rc-update [options] add <service> [<runlevel>...]\n" \
" or: rc-update [options] del <service> [<runlevel>...]\n" \
" or: rc-update [options] [show [<runlevel>...]]";
-const char getoptstring[] = "asu" getoptstring_COMMON;
+const char getoptstring[] = "asu" getoptstring_COMMON
+#ifdef RC_USER_SERVICES
+getoptstring_USER_SERVICES
+#endif
+;
const struct option longopts[] = {
{ "all", 0, NULL, 'a' },
{ "stack", 0, NULL, 's' },
{ "update", 0, NULL, 'u' },
+#ifdef RC_USER_SERVICES
+ longopts_USER_SERVICES
+#endif
longopts_COMMON
};
const char * const longopts_help[] = {
"Process all runlevels",
"Stack a runlevel instead of a service",
"Force an update of the dependency tree",
+#ifdef RC_USER_SERVICES
+ longopts_help_USER_SERVICES
+#endif
longopts_help_COMMON
};
@@ -236,6 +246,9 @@ int main(int argc, char **argv)
if (deptree)
rc_deptree_free(deptree);
return ret;
+#ifdef RC_USER_SERVICES
+ case_RC_USER_SERVICES
+#endif
case_RC_COMMON_GETOPT
}
diff --git a/src/shared/_usage.h b/src/shared/_usage.h
index 91b956e0..3264ad6f 100644
--- a/src/shared/_usage.h
+++ b/src/shared/_usage.h
@@ -45,6 +45,16 @@
case 'q': case_RC_COMMON_getopt_case_q; break; \
default: case_RC_COMMON_getopt_default; break;
+#ifdef RC_USER_SERVICES
+#define case_RC_USER_SERVICES \
+ case 'U': rc_set_user(); break;
+#define getoptstring_USER_SERVICES "U"
+#define longopts_USER_SERVICES \
+ { "user", 0, NULL, 'U'},
+#define longopts_help_USER_SERVICES \
+ "use user facing services",
+#endif
+
extern const char *applet;
extern const char *extraopts;
extern const char getoptstring[];
diff --git a/src/shared/misc.c b/src/shared/misc.c
index 28f4e235..6970513f 100644
--- a/src/shared/misc.c
+++ b/src/shared/misc.c
@@ -79,6 +79,16 @@ env_filter(void)
/* Add the user defined list of vars */
env_allow = rc_stringlist_split(rc_conf_value("rc_env_allow"), " ");
+
+#ifdef RC_USER_SERVICES
+ /* Needed for local user services to be found */
+ if (rc_is_user()) {
+ rc_stringlist_addu(env_allow, "HOME");
+ rc_stringlist_addu(env_allow, "XDG_RUNTIME_DIR");
+ rc_stringlist_addu(env_allow, "XDG_CONFIG_HOME");
+ rc_stringlist_addu(env_allow, "RC_USER_SERVICES");
+ }
+#endif
/*
* If '*' is an entry in rc_env_allow, do nothing as we are to pass
* through all environment variables.
@@ -247,8 +257,22 @@ svc_lock(const char *applet, bool ignore_lock_failure)
{
char *file = NULL;
int fd;
+ char *svcdir = RC_SVCDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
+ xasprintf(&file, "%s/exclusive/%s", svcdir, applet);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
- xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet);
fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
free(file);
if (fd == -1)
@@ -273,8 +297,22 @@ int
svc_unlock(const char *applet, int fd)
{
char *file = NULL;
+ char *svcdir = RC_SVCDIR;
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ }
+#endif
+
+ xasprintf(&file, "%s/exclusive/%s", svcdir, applet);
+
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
- xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet);
close(fd);
unlink(file);
free(file);
@@ -385,15 +423,33 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen)
struct stat st;
struct utimbuf ut;
FILE *fp;
+ char *cache = RC_DEPTREE_CACHE;
+ char *skew = RC_DEPTREE_SKEWED;
+#ifdef RC_USER_SERVICES
+ char *user_svcdir;
+ if (rc_is_user()) {
+ user_svcdir = rc_user_svcdir();
+ xasprintf(&cache, "%s%s", user_svcdir, RC_DEPTREE_CACHE_FILE);
+ xasprintf(&skew, "%s%s", user_svcdir, RC_DEPTREE_SKEWED_FILE);
+ free(user_svcdir);
+ }
+#endif
t = 0;
if (rc_deptree_update_needed(&t, file) || force != 0) {
/* Test if we have permission to update the deptree */
- fd = open(RC_DEPTREE_CACHE, O_WRONLY);
+ fd = open(cache, O_WRONLY);
merrno = errno;
errno = serrno;
- if (fd == -1 && merrno == EACCES)
+ if (fd == -1 && merrno == EACCES) {
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(cache);
+ free(skew);
+ }
+#endif
return rc_deptree_load();
+ }
close(fd);
if (regen)
@@ -403,30 +459,41 @@ 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));
+ if (stat(cache, &st) != 0) {
+ eerror("stat(%s): %s", cache, strerror(errno));
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(cache);
+ free(skew);
+ }
+#endif
return NULL;
}
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", cache, ctime(&t));
+ fp = fopen(skew, "w");
if (fp != NULL) {
fprintf(fp, "%s\n", file);
fclose(fp);
}
ut.actime = t;
ut.modtime = t;
- utime(RC_DEPTREE_CACHE, &ut);
+ utime(cache, &ut);
} else {
- if (exists(RC_DEPTREE_SKEWED))
- unlink(RC_DEPTREE_SKEWED);
+ if (exists(skew))
+ unlink(skew);
}
}
if (force == -1 && regen != NULL)
*regen = retval;
}
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(cache);
+ free(skew);
+ }
+#endif
return rc_deptree_load();
}
diff --git a/src/shared/misc.h b/src/shared/misc.h
index b158a786..132f4b80 100644
--- a/src/shared/misc.h
+++ b/src/shared/misc.h
@@ -33,11 +33,15 @@
#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_FILE "/deptree"
+#define RC_DEPTREE_CACHE RC_SVCDIR RC_DEPTREE_CACHE_FILE
+#define RC_DEPTREE_SKEWED_FILE "/clock-skewed"
+#define RC_DEPTREE_SKEWED RC_SVCDIR RC_DEPTREE_SKEWED_FILE
#define RC_KRUNLEVEL RC_SVCDIR "/krunlevel"
-#define RC_STARTING RC_SVCDIR "/rc.starting"
-#define RC_STOPPING RC_SVCDIR "/rc.stopping"
+#define RC_STARTING_FOLDER "/rc.starting"
+#define RC_STARTING RC_SVCDIR RC_STARTING_FOLDER
+#define RC_STOPPING_FOLDER "/rc.stopping"
+#define RC_STOPPING RC_SVCDIR RC_STOPPING_FOLDER
#define RC_SVCDIR_STARTING RC_SVCDIR "/starting"
#define RC_SVCDIR_INACTIVE RC_SVCDIR "/inactive"
diff --git a/src/supervise-daemon/supervise-daemon.c b/src/supervise-daemon/supervise-daemon.c
index adf35630..845b118c 100644
--- a/src/supervise-daemon/supervise-daemon.c
+++ b/src/supervise-daemon/supervise-daemon.c
@@ -817,6 +817,19 @@ int main(int argc, char **argv)
char **child_argv = NULL;
char *str = NULL;
char *cmdline = NULL;
+ char *svcdir = RC_SVCDIR;
+ char *pidfile_path = "/var/run";
+#ifdef RC_USER_SERVICES
+ char *env;
+ if (rc_is_user()) {
+ svcdir = rc_user_svcdir();
+ if ((env = getenv("XDG_RUNTIME_DIR"))) {
+ pidfile_path = env;
+ } else {
+ pidfile_path = "/tmp/"
+ }
+ }
+#endif
applet = basename_c(argv[0]);
atexit(cleanup);
@@ -1085,8 +1098,14 @@ int main(int argc, char **argv)
umask(numask);
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", pidfile_path, svcname);
+ xasprintf(&fifopath, "%s/supervise-%s.ctl", svcdir, svcname);
+#ifdef RC_USER_SERVICES
+ if (rc_is_user()) {
+ free(svcdir);
+ }
+#endif
+
if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST)
eerrorx("%s: unable to create control fifo: %s",
applet, strerror(errno));