aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander V Vershilov <qnikst@gentoo.org>2013-08-07 11:03:51 +0400
committerWilliam Hubbs <w.d.hubbs@gmail.com>2013-09-20 14:27:31 -0500
commit7716bf31de5030b761613834e11e4e62f36403a5 (patch)
tree60daee3287bbadf7143291945c2a0411d1491878
parent445b297360b85c03b4509458f194a0d964c1d71a (diff)
Fix stacked runlevel support
Patch was provided by Max Hacking <max.gentoo.bugzilla@hacking.co.uk> and slightly fixed by Alexander Vershilov <qnikst@gentoo.org> and William Hubbs <williamh@gentoo.org>. Fixes: 1). Rebase to newest OpenRC version. 2). Remove code style fixes. Port to currect code style. 3). Fix rc_runlevel_stack instead of introducing new function. 4). Make get_runlevel_chain a private function. X-Gentoo-Bug: 467368 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=467368
-rw-r--r--src/librc/librc.c64
-rw-r--r--src/rc/rc-status.c65
-rw-r--r--src/rc/rc.c94
3 files changed, 140 insertions, 83 deletions
diff --git a/src/librc/librc.c b/src/librc/librc.c
index 40b975a9..839fcd8a 100644
--- a/src/librc/librc.c
+++ b/src/librc/librc.c
@@ -323,6 +323,42 @@ rc_parse_service_state(RC_SERVICE state)
return NULL;
}
+/* Returns a list of all the chained runlevels used by the
+ * specified runlevel in dependency order, including the
+ * specified runlevel. */
+static void
+get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list)
+{
+ char path[PATH_MAX];
+ RC_STRINGLIST *dirs;
+ RC_STRING *d, *dn;
+
+ /*
+ * If we haven't been passed a runlevel or a level list, or
+ * if the passed runlevel doesn't exist then we're done already!
+ */
+ if (!runlevel || !level_list || !rc_runlevel_exists(runlevel))
+ return;
+
+ /*
+ * We want to add this runlevel to the list but if
+ * it is already in the list it needs to go at the
+ * end again.
+ */
+ if (rc_stringlist_find(level_list, runlevel))
+ rc_stringlist_delete(level_list, runlevel);
+ rc_stringlist_add(level_list, runlevel);
+
+ /*
+ * We can now do exactly the above procedure for our chained
+ * runlevels.
+ */
+ snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
+ dirs = ls_dir(path, LS_DIR);
+ TAILQ_FOREACH_SAFE(d, dirs, entries, dn)
+ get_runlevel_chain(d->value, level_list);
+}
+
bool
rc_runlevel_starting(void)
{
@@ -424,22 +460,10 @@ librc_hidden_def(rc_runlevel_unstack)
RC_STRINGLIST *
rc_runlevel_stacks(const char *runlevel)
{
- char path[PATH_MAX];
- RC_STRINGLIST *dirs;
- RC_STRING *d, *dn;
-
- if (!runlevel)
- return false;
- snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
- dirs = ls_dir(path, LS_DIR);
- TAILQ_FOREACH_SAFE(d, dirs, entries, dn) {
- if (!rc_runlevel_exists(d->value)) {
- TAILQ_REMOVE(dirs, d, entries);
- free(d->value);
- free(d);
- }
- }
- return dirs;
+ RC_STRINGLIST *stack;
+ stack = rc_stringlist_new();
+ get_runlevel_chain(runlevel, stack);
+ return stack;
}
librc_hidden_def(rc_runlevel_stacks)
@@ -903,17 +927,13 @@ rc_services_in_runlevel_stacked(const char *runlevel)
stacks = rc_runlevel_stacks(runlevel);
TAILQ_FOREACH(stack, stacks, entries) {
sl = rc_services_in_runlevel(stack->value);
- if (list != NULL) {
- TAILQ_CONCAT(list, sl, entries);
- free(sl);
- } else
- list = sl;
+ TAILQ_CONCAT(list, sl, entries);
+ free(sl);
}
return list;
}
librc_hidden_def(rc_services_in_runlevel_stacked)
-
RC_STRINGLIST *
rc_services_in_state(RC_SERVICE state)
{
diff --git a/src/rc/rc-status.c b/src/rc/rc-status.c
index d0492168..d14dd63a 100644
--- a/src/rc/rc-status.c
+++ b/src/rc/rc-status.c
@@ -171,6 +171,26 @@ print_services(const char *runlevel, RC_STRINGLIST *svcs)
rc_stringlist_free(l);
}
+static void
+print_stacked_services(const char *runlevel)
+{
+ RC_STRINGLIST *stackedlevels, *servicelist;
+ RC_STRING *stackedlevel;
+
+ stackedlevels = rc_runlevel_stacks(runlevel);
+ TAILQ_FOREACH(stackedlevel, stackedlevels, entries) {
+ if (rc_stringlist_find(levels, stackedlevel->value) != NULL)
+ continue;
+ print_level("Stacked", stackedlevel->value);
+ servicelist = rc_services_in_runlevel(stackedlevel->value);
+ print_services(stackedlevel->value, servicelist);
+ rc_stringlist_free(servicelist);
+ print_stacked_services(stackedlevel->value);
+ }
+ rc_stringlist_free(stackedlevels);
+ stackedlevels = NULL;
+}
+
#include "_usage.h"
#define usagestring "" \
"Usage: rc-status [options] <runlevel>...\n" \
@@ -199,7 +219,8 @@ static const char * const longopts_help[] = {
int
rc_status(int argc, char **argv)
{
- RC_STRING *s, *l, *t;
+ RC_STRING *s, *l, *t, *level;
+
char *p, *runlevel = NULL;
int opt, aflag = 0, retval = 0;
@@ -280,16 +301,7 @@ rc_status(int argc, char **argv)
print_level(NULL, l->value);
services = rc_services_in_runlevel(l->value);
print_services(l->value, services);
- nservices = rc_runlevel_stacks(l->value);
- TAILQ_FOREACH(s, nservices, entries) {
- if (rc_stringlist_find(levels, s->value) != NULL)
- continue;
- print_level("Stacked", s->value);
- sservices = rc_services_in_runlevel(s->value);
- print_services(s->value, sservices);
- rc_stringlist_free(sservices);
- }
- sservices = NULL;
+ print_stacked_services(l->value);
rc_stringlist_free(nservices);
nservices = NULL;
rc_stringlist_free(services);
@@ -317,16 +329,14 @@ rc_status(int argc, char **argv)
services = rc_services_in_runlevel(NULL);
sservices = rc_stringlist_new();
TAILQ_FOREACH(l, levels, entries) {
- nservices = rc_services_in_runlevel(l->value);
+ nservices = rc_services_in_runlevel_stacked(l->value);
TAILQ_CONCAT(sservices, nservices, entries);
free(nservices);
}
TAILQ_FOREACH_SAFE(s, services, entries, t) {
- if (rc_stringlist_find(sservices, s->value) ||
- rc_service_state(s->value) &
- (RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED))
- {
- TAILQ_REMOVE(services, s, entries);
+ if ((rc_stringlist_find(sservices, s->value) ||
+ (rc_service_state(s->value) & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) {
+ TAILQ_REMOVE(services, s, entries);
free(s->value);
free(s);
}
@@ -337,18 +347,17 @@ rc_status(int argc, char **argv)
alist = rc_stringlist_new();
l = rc_stringlist_add(alist, "");
p = l->value;
- if (!runlevel)
- runlevel = rc_runlevel_get();
- TAILQ_FOREACH_SAFE(s, services, entries, t) {
- l->value = s->value;
- unsetenv("RC_SVCNAME");
- setenv("RC_SVCNAME", l->value, 1);
- tmp = rc_deptree_depends(deptree, needsme, alist, runlevel, RC_DEP_TRACE);
- if (TAILQ_FIRST(tmp)) {
- TAILQ_REMOVE(services, s, entries);
- TAILQ_INSERT_TAIL(nservices, s, entries);
+ TAILQ_FOREACH(level, levels, entries) {
+ TAILQ_FOREACH_SAFE(s, services, entries, t) {
+ l->value = s->value;
+ setenv("RC_SVCNAME", l->value, 1);
+ tmp = rc_deptree_depends(deptree, needsme, alist, level->value, RC_DEP_TRACE);
+ if (TAILQ_FIRST(tmp)) {
+ TAILQ_REMOVE(services, s, entries);
+ TAILQ_INSERT_TAIL(nservices, s, entries);
+ }
+ rc_stringlist_free(tmp);
}
- rc_stringlist_free(tmp);
}
l->value = p;
/* we are unsetting RC_SVCNAME because last loaded service
diff --git a/src/rc/rc.c b/src/rc/rc.c
index 0e37182c..0c983721 100644
--- a/src/rc/rc.c
+++ b/src/rc/rc.c
@@ -79,12 +79,6 @@ const char rc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
const char *applet = NULL;
static char *runlevel;
-static RC_STRINGLIST *hotplugged_services;
-static RC_STRINGLIST *stop_services;
-static RC_STRINGLIST *start_services;
-static RC_STRINGLIST *types_n;
-static RC_STRINGLIST *types_nua;
-static RC_DEPTREE *deptree;
static RC_HOOK hook_out;
struct termios *termios_orig = NULL;
@@ -524,7 +518,9 @@ runlevel_config(const char *service, const char *level)
}
static void
-do_stop_services(const char *newlevel, bool parallel, bool going_down)
+do_stop_services(const RC_STRINGLIST *types_n, const RC_STRINGLIST *start_services,
+ const RC_STRINGLIST *stop_services, const RC_DEPTREE *deptree,
+ const char *newlevel, bool parallel, bool going_down)
{
pid_t pid;
RC_STRING *service, *svc1, *svc2;
@@ -627,7 +623,7 @@ stop:
}
static void
-do_start_services(bool parallel)
+do_start_services(const RC_STRINGLIST *start_services, bool parallel)
{
RC_STRING *service;
pid_t pid;
@@ -754,6 +750,12 @@ main(int argc, char **argv)
{
const char *bootlevel = NULL;
char *newlevel = NULL;
+ static RC_STRINGLIST *hotplugged_services;
+ static RC_STRINGLIST *stop_services;
+ static RC_STRINGLIST *start_services;
+ static RC_STRINGLIST *types_n;
+ static RC_STRINGLIST *types_nua;
+ static RC_DEPTREE *deptree;
RC_STRINGLIST *deporder = NULL;
RC_STRINGLIST *tmplist;
RC_STRING *service;
@@ -868,7 +870,11 @@ main(int argc, char **argv)
snprintf(pidstr, sizeof(pidstr), "%d", getpid());
setenv("RC_PID", pidstr, 1);
- /* Load current runlevel */
+ /* 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
+ * won't actually be starting them all.
+ */
bootlevel = getenv("RC_BOOTLEVEL");
runlevel = rc_runlevel_get();
@@ -972,8 +978,13 @@ main(int argc, char **argv)
applet, RC_STOPPING, strerror(errno));
}
- /* Build a list of all services to stop and then work out the
- * correct order for stopping them */
+ /* Create a list of all services which we could stop (assuming
+ * they won't be active in the new or current runlevel) including
+ * all those services which have been started, are inactive or
+ * are currently starting. Clearly, some of these will be listed
+ * in the new or current runlevel so we won't actually be stopping
+ * them all.
+ */
stop_services = rc_services_in_state(RC_SERVICE_STARTED);
tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
TAILQ_CONCAT(stop_services, tmplist, entries);
@@ -996,7 +1007,11 @@ main(int argc, char **argv)
stop_services = tmplist;
}
- /* Load our list of start services */
+ /* 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
+ * won't actually be starting them all.
+ */
hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
start_services = rc_services_in_runlevel_stacked(newlevel ?
newlevel : runlevel);
@@ -1006,9 +1021,11 @@ main(int argc, char **argv)
tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT);
TAILQ_CONCAT(start_services, tmplist, entries);
free(tmplist);
+ /* If we are NOT headed for the single-user runlevel... */
if (strcmp(newlevel ? newlevel : runlevel,
RC_LEVEL_SINGLE) != 0)
{
+ /* If we are NOT headed for the boot runlevel... */
if (strcmp(newlevel ? newlevel : runlevel,
bootlevel) != 0)
{
@@ -1029,7 +1046,7 @@ main(int argc, char **argv)
/* Now stop the services that shouldn't be running */
if (stop_services && !nostop)
- do_stop_services(newlevel, parallel, going_down);
+ do_stop_services(types_n, start_services, stop_services, deptree, newlevel, parallel, going_down);
/* Wait for our services to finish */
wait_for_services();
@@ -1065,18 +1082,10 @@ main(int argc, char **argv)
TAILQ_FOREACH(service, hotplugged_services, entries)
rc_service_mark(service->value, RC_SERVICE_HOTPLUGGED);
- /* Order the services to start */
- if (start_services) {
- rc_stringlist_sort(&start_services);
- deporder = rc_deptree_depends(deptree, types_nua,
- start_services, runlevel,
- depoptions | RC_DEP_START);
- rc_stringlist_free(start_services);
- start_services = deporder;
- }
-
#ifdef __linux__
- /* mark any services skipped as started */
+ /* If the "noinit" parameter was passed on the kernel command line then
+ * mark the specified services as started so they will not be started
+ * by us. */
proc = p = rc_proc_getent("noinit");
if (proc) {
while ((token = strsep(&p, ",")))
@@ -1085,19 +1094,38 @@ main(int argc, char **argv)
}
#endif
+ /* If we have a list of services to start then... */
if (start_services) {
- do_start_services(parallel);
- /* FIXME: If we skip the boot runlevel and go straight
- * to default from sysinit, we should now re-evaluate our
- * start services + hotplugged services and call
- * do_start_services a second time. */
-
- /* Wait for our services to finish */
- wait_for_services();
+ /* Get a list of the chained runlevels which compose the target runlevel */
+ RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel);
+
+ /* Loop through them in reverse order. */
+ RC_STRING *rlevel;
+ TAILQ_FOREACH_REVERSE(rlevel, runlevel_chain, rc_stringlist, entries)
+ {
+ /* Get a list of all the services in that runlevel */
+ RC_STRINGLIST *run_services = rc_services_in_runlevel(rlevel->value);
+
+ /* Start those services. */
+ rc_stringlist_sort(&run_services);
+ deporder = rc_deptree_depends(deptree, types_nua, run_services, rlevel->value, depoptions | RC_DEP_START);
+ rc_stringlist_free(run_services);
+ run_services = deporder;
+ do_start_services(run_services, parallel);
+
+ /* Wait for our services to finish */
+ wait_for_services();
+
+ /* Free the list of services, we're done with it. */
+ rc_stringlist_free(run_services);
+ }
+ rc_stringlist_free(runlevel_chain);
}
#ifdef __linux__
- /* mark any services skipped as stopped */
+ /* If the "noinit" parameter was passed on the kernel command line then
+ * mark the specified services as stopped so that our records reflect
+ * reality. */
proc = p = rc_proc_getent("noinit");
if (proc) {
while ((token = strsep(&p, ",")))