Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ if get_option('pkgconfig')
libraries : rc,
variables: {
'pluginsdir': pluginsdir,
'initdir': rc_libexecdir / 'init.d',
'confdir': rc_libexecdir / 'conf.d',
}
)
endif
Expand Down
53 changes: 22 additions & 31 deletions sh/openrc-run.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -225,47 +225,38 @@ status()
# Start debug output
yesno $RC_DEBUG && set -x

# Parse RC_PATH in reverse order, so later conf.d files override earlier ones
IFS=:
for _f in $RC_PATH; do
_conf_d="$_f $_conf_d"
done
unset IFS

# Load configuration settings. First the global ones, then any
# service-specific settings.
sourcex -e "@SYSCONFDIR@/rc.conf"
if [ -d "@SYSCONFDIR@/rc.conf.d" ]; then
for _f in "@SYSCONFDIR@"/rc.conf.d/*.conf; do
for _f in $_conf_d; do
sourcex -e "$_f/rc.conf"
for _f in "$_f"/rc.conf.d/*.conf; do
sourcex -e "$_f"
done
fi

_usr_conf=${XDG_CONFIG_HOME:-${HOME}/.config}/rc
if yesno "$RC_USER_SERVICES"; then
sourcex -e "$_usr_conf/rc.conf"
if [ -d "$_usr_conf/rc.conf.d" ]; then
for _f in "$_usr_conf"/rc.conf.d/*.conf; do
sourcex -e "$_f"
done
fi
fi
done

_conf_d=${RC_SERVICE%/*}/../conf.d
# If we're net.eth0 or openvpn.work then load net or openvpn config
_c=${RC_SVCNAME%%.*}
if [ -n "$_c" -a "$_c" != "$RC_SVCNAME" ]; then
if ! sourcex -e "$_conf_d/$_c.$RC_RUNLEVEL"; then
sourcex -e "$_conf_d/$_c"
fi
if [ "$_c" != "$RC_SVCNAME" ]; then
# Overlay with our specific config
_c="$_c $RC_SVCNAME"
fi
unset _c

# Overlay with our specific config
if ! sourcex -e "$_conf_d/$RC_SVCNAME.$RC_RUNLEVEL"; then
sourcex -e "$_conf_d/$RC_SVCNAME"
fi
unset _conf_d
for _c in $_c; do
for _f in $_conf_d; do
if ! sourcex -e "$_f/$_c.$RC_RUNLEVEL"; then
sourcex -e "$_f/$_c"
fi
done
done

if yesno "$RC_USER_SERVICES" && [ "$_usr_conf/init.d" != "${RC_SERVICE%/*}" ]; then
if ! sourcex -e "$_usr_conf/conf.d/$RC_SVCNAME.$RC_RUNLEVEL"; then
sourcex -e "$_usr_conf/conf.d/$RC_SVCNAME"
fi
fi
unset _usr_conf
unset _c _f _conf_d

# load service supervisor functions
sourcex "@LIBEXECDIR@/sh/runit.sh"
Expand Down
208 changes: 142 additions & 66 deletions src/librc/librc.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,57 +555,128 @@ rc_runlevel_stacks(const char *runlevel)
return stack;
}

enum scriptdirs_entries {
SCRIPTDIR_USR,
#ifdef RC_LOCAL_PREFIX
SCRIPTDIR_LOCAL,
#endif
SCRIPTDIR_SYS,
#ifdef RC_PKG_PREFIX
SCRIPTDIR_PKG,
#endif
SCRIPTDIR_SVC,
SCRIPTDIR_MAX,
SCRIPTDIR_CAP
};

static const char * const scriptdirs[SCRIPTDIR_CAP] = {
#ifdef RC_LOCAL_PREFIX
RC_LOCAL_PREFIX "/etc",
#endif
RC_SYSCONFDIR,
#ifdef RC_PKG_PREFIX
RC_PKG_PREFIX "/etc",
#endif
RC_SVCDIR
};

static struct {
bool set;
char *svcdir;
char *usrconfdir;
char *runleveldir;
const char *scriptdirs[ARRAY_SIZE(scriptdirs)];
} rc_dirs = {
.scriptdirs = {
char *svcdir, *confdir, *datadir, *runleveldir;
} rc_dirs;

static struct {
char *buffer;
size_t count;
int *fds;
const char **entries;
} rc_path;

static void
free_rc_dirs(void)
{
#define X(ptr) ptr = (free(ptr), NULL)
X(rc_dirs.svcdir);
X(rc_dirs.confdir);
X(rc_dirs.datadir);
X(rc_dirs.runleveldir);

X(rc_path.buffer);
X(rc_path.entries);
X(rc_path.fds);
#undef X
}

static inline size_t
append_path(FILE *buf, int path_len, const char path[static path_len], const char *suffix)
{
fprintf(buf, "%.*s%s%c", path_len, path, suffix ? suffix : "", '\0');
return 1;
}

static size_t
append_path_string(FILE *buf, const char *path, const char *suffix)
{
size_t count = 0;

for (const char *entry = path; (path = strchrnul(entry, ':')); entry = path + 1) {
size_t entry_len = path - entry;

if (!entry_len)
continue;
count += append_path(buf, entry_len, entry, suffix);
if (!*path)
break;
}

return count;
}

static inline size_t
append_path_xdg(FILE *buf, const char *home, const char *dirs, const char *fallback, bool user)
{
const char *path;
size_t count = 0;

if (!(path = getenv(dirs)))
path = fallback;

if (home)
count += append_path(buf, strlen(home), home, NULL);
return count + append_path_string(buf, path, user ? "/rc/user" : "/rc");
}

static inline size_t
append_path_array(FILE *buf, size_t path_len, const char **path, bool user)
{
size_t count = 0;
while (count < path_len) {
const char *entry = path[count++];
append_path(buf, strlen(entry), entry, user ? "/user" : "");
}
return count;
}

static void
load_dynamic_path(bool user)
{
static const char *config_path[] = {
RC_SYSCONFDIR "/rc",
RC_SYSCONFDIR,
#ifdef RC_LOCAL_PREFIX
[SCRIPTDIR_LOCAL] = RC_LOCAL_PREFIX "/etc/user",
RC_LOCAL_PREFIX "/etc/rc",
RC_LOCAL_PREFIX "/etc",
#endif
[SCRIPTDIR_SYS] = RC_SYSCONFDIR "/user",
#ifdef RC_PKG_PREFIX
[SCRIPTDIR_PKG] = RC_PKG_PREFIX "/etc/user",
RC_PKG_PREFIX "/etc/rc",
RC_PKG_PREFIX "/etc",
#endif
RC_LIBEXECDIR,
};

const char *path;
size_t size;
FILE *fp = xopen_memstream(&rc_path.buffer, &size);

rc_path.count = 0;
if ((path = getenv("RC_PATH"))) {
rc_path.count += append_path_string(fp, path, NULL);
} else {
const char *svcdir = rc_dirs.svcdir ? rc_dirs.svcdir : RC_SVCDIR;

/* first check config paths, then data paths.
* and within those, check dynamic paths first, then hardcoded ones */
rc_path.count += append_path_xdg(fp, rc_dirs.confdir, "XDG_CONFIG_DIRS", "/etc/xdg", user);
rc_path.count += append_path_array(fp, ARRAY_SIZE(config_path), config_path, user);
rc_path.count += append_path(fp, strlen(svcdir), svcdir, NULL);
}
};
xclose_memstream(fp);

static void
free_rc_dirs(void)
{
free(rc_dirs.runleveldir);
rc_dirs.runleveldir = NULL;
free(rc_dirs.svcdir);
rc_dirs.svcdir = NULL;
rc_dirs.scriptdirs[0] = NULL;
rc_path.entries = calloc(rc_path.count + 1, sizeof(*rc_path.entries));

for (size_t count = 0, i = 0; i < size && count < rc_path.count; i += strlen(&rc_path.buffer[i]) + 1)
rc_path.entries[count++] = &rc_path.buffer[i];
rc_path.entries[rc_path.count] = NULL;

if (!rc_dirs.set) {
atexit(free_rc_dirs);
rc_dirs.set = true;
}
}

static bool is_user = false;
Expand All @@ -620,23 +691,29 @@ void
rc_set_user(void)
{
char *env;

if (is_user)
return;

if (!rc_dirs.set)
atexit(free_rc_dirs);
else
free_rc_dirs();

is_user = true;
rc_dirs.set = true;
setenv("RC_USER_SERVICES", "yes", true);

if ((env = getenv("XDG_CONFIG_HOME"))) {
xasprintf(&rc_dirs.usrconfdir, "%s/rc", env);
xasprintf(&rc_dirs.confdir, "%s/rc", env);
} else if ((env = getenv("HOME"))) {
xasprintf(&rc_dirs.usrconfdir, "%s/.config/rc", env);
xasprintf(&rc_dirs.confdir, "%s/.config/rc", env);
} else {
fprintf(stderr, "XDG_CONFIG_HOME and HOME unset.\n");
exit(EXIT_FAILURE);
}

xasprintf(&rc_dirs.runleveldir, "%s/runlevels", rc_dirs.usrconfdir);
xasprintf(&rc_dirs.runleveldir, "%s/runlevels", rc_dirs.confdir);

if (!(env = getenv("XDG_RUNTIME_DIR"))) {
/* FIXME: fallback to something else? */
Expand All @@ -645,41 +722,41 @@ rc_set_user(void)
}

xasprintf(&rc_dirs.svcdir, "%s/openrc", env);
atexit(free_rc_dirs);

rc_dirs.scriptdirs[SCRIPTDIR_USR] = rc_dirs.usrconfdir;
rc_dirs.scriptdirs[SCRIPTDIR_SVC] = rc_dirs.svcdir;
load_dynamic_path(true);

clear_dirfds();
}

const char * const *
rc_scriptdirs(void)
{
if (rc_dirs.set)
return rc_dirs.scriptdirs;
return scriptdirs;
if (!rc_path.entries)
load_dynamic_path(false);
return rc_path.entries;
}

size_t
rc_scriptdirfds(const int **fds)
{
static int scriptdirfds[SCRIPTDIR_MAX];
static size_t len = 0;

if (len == 0) {
const char * const *current_scriptdirs = rc_scriptdirs();
for (size_t i = 0; current_scriptdirs[i]; i++) {
int fd = open(current_scriptdirs[i], O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (!rc_path.entries)
load_dynamic_path(false);

rc_path.fds = xmalloc(rc_path.count * sizeof(*rc_path.fds));
for (size_t i = 0; i < rc_path.count; i++) {
int fd = open(rc_path.entries[i], O_RDONLY | O_DIRECTORY | O_CLOEXEC);

if (fd == -1)
continue;

scriptdirfds[len++] = fd;
rc_path.fds[len++] = fd;
}
}

*fds = scriptdirfds;
*fds = rc_path.fds;
return len;
}

Expand All @@ -692,24 +769,23 @@ rc_sysconfdir(void)
const char *
rc_usrconfdir(void)
{
if (rc_dirs.set)
return rc_dirs.usrconfdir;

return NULL;
return rc_dirs.confdir ? rc_dirs.confdir : NULL;
}

const char *
rc_runleveldir(void)
{
if (rc_dirs.set)
return rc_dirs.runleveldir;
return RC_RUNLEVELDIR;
return rc_dirs.runleveldir ? rc_dirs.runleveldir : RC_RUNLEVELDIR;
}

const char *
rc_svcdir(void)
{
return rc_dirs.set ? rc_dirs.svcdir : RC_SVCDIR;
static const char *svcdir;

if (!rc_dirs.svcdir && !svcdir && !(svcdir = getenv("RC_SVCDIR")))
svcdir = RC_SVCDIR;
return rc_dirs.svcdir ? rc_dirs.svcdir : svcdir;
}

static ssize_t
Expand Down
11 changes: 11 additions & 0 deletions src/shared/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ env_config(void)
char *tok;
const char *sys = rc_sys();
const char *svcdir = rc_svcdir();
const char *const *init_path = rc_scriptdirs();
char *buffer = NULL;
char *tmpdir;
size_t size = 0;
Expand Down Expand Up @@ -226,6 +227,16 @@ env_config(void)
setenv("RC_BOOTLEVEL", RC_LEVEL_BOOT, 1);
setenv("RC_RUNLEVEL", e, 1);

fp = xopen_memstream(&path, &size);
fputs("RC_PATH=", fp);
for (bool first = true; *init_path; init_path++, first = false) {
if (!first)
fputc(':', fp);
fputs(*init_path, fp);
}
xclose_memstream(fp);
putenv(path);

free(e);
free(tmpdir);

Expand Down
Loading