From 7716bf31de5030b761613834e11e4e62f36403a5 Mon Sep 17 00:00:00 2001 From: Alexander V Vershilov Date: Wed, 7 Aug 2013 11:03:51 +0400 Subject: [PATCH] Fix stacked runlevel support Patch was provided by Max Hacking and slightly fixed by Alexander Vershilov and William Hubbs . 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 --- src/librc/librc.c | 64 +++++++++++++++++++++----------- src/rc/rc-status.c | 65 ++++++++++++++++++-------------- src/rc/rc.c | 92 ++++++++++++++++++++++++++++++---------------- 3 files changed, 139 insertions(+), 82 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] ...\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. */ + /* Get a list of the chained runlevels which compose the target runlevel */ + RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel); - /* Wait for our services to finish */ - wait_for_services(); + /* 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, ",")))