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
This commit is contained in:
parent
445b297360
commit
7716bf31de
@ -323,6 +323,42 @@ rc_parse_service_state(RC_SERVICE state)
|
|||||||
return NULL;
|
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
|
bool
|
||||||
rc_runlevel_starting(void)
|
rc_runlevel_starting(void)
|
||||||
{
|
{
|
||||||
@ -424,22 +460,10 @@ librc_hidden_def(rc_runlevel_unstack)
|
|||||||
RC_STRINGLIST *
|
RC_STRINGLIST *
|
||||||
rc_runlevel_stacks(const char *runlevel)
|
rc_runlevel_stacks(const char *runlevel)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
RC_STRINGLIST *stack;
|
||||||
RC_STRINGLIST *dirs;
|
stack = rc_stringlist_new();
|
||||||
RC_STRING *d, *dn;
|
get_runlevel_chain(runlevel, stack);
|
||||||
|
return stack;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
librc_hidden_def(rc_runlevel_stacks)
|
librc_hidden_def(rc_runlevel_stacks)
|
||||||
|
|
||||||
@ -903,17 +927,13 @@ rc_services_in_runlevel_stacked(const char *runlevel)
|
|||||||
stacks = rc_runlevel_stacks(runlevel);
|
stacks = rc_runlevel_stacks(runlevel);
|
||||||
TAILQ_FOREACH(stack, stacks, entries) {
|
TAILQ_FOREACH(stack, stacks, entries) {
|
||||||
sl = rc_services_in_runlevel(stack->value);
|
sl = rc_services_in_runlevel(stack->value);
|
||||||
if (list != NULL) {
|
TAILQ_CONCAT(list, sl, entries);
|
||||||
TAILQ_CONCAT(list, sl, entries);
|
free(sl);
|
||||||
free(sl);
|
|
||||||
} else
|
|
||||||
list = sl;
|
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
librc_hidden_def(rc_services_in_runlevel_stacked)
|
librc_hidden_def(rc_services_in_runlevel_stacked)
|
||||||
|
|
||||||
|
|
||||||
RC_STRINGLIST *
|
RC_STRINGLIST *
|
||||||
rc_services_in_state(RC_SERVICE state)
|
rc_services_in_state(RC_SERVICE state)
|
||||||
{
|
{
|
||||||
|
@ -171,6 +171,26 @@ print_services(const char *runlevel, RC_STRINGLIST *svcs)
|
|||||||
rc_stringlist_free(l);
|
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"
|
#include "_usage.h"
|
||||||
#define usagestring "" \
|
#define usagestring "" \
|
||||||
"Usage: rc-status [options] <runlevel>...\n" \
|
"Usage: rc-status [options] <runlevel>...\n" \
|
||||||
@ -199,7 +219,8 @@ static const char * const longopts_help[] = {
|
|||||||
int
|
int
|
||||||
rc_status(int argc, char **argv)
|
rc_status(int argc, char **argv)
|
||||||
{
|
{
|
||||||
RC_STRING *s, *l, *t;
|
RC_STRING *s, *l, *t, *level;
|
||||||
|
|
||||||
char *p, *runlevel = NULL;
|
char *p, *runlevel = NULL;
|
||||||
int opt, aflag = 0, retval = 0;
|
int opt, aflag = 0, retval = 0;
|
||||||
|
|
||||||
@ -280,16 +301,7 @@ rc_status(int argc, char **argv)
|
|||||||
print_level(NULL, l->value);
|
print_level(NULL, l->value);
|
||||||
services = rc_services_in_runlevel(l->value);
|
services = rc_services_in_runlevel(l->value);
|
||||||
print_services(l->value, services);
|
print_services(l->value, services);
|
||||||
nservices = rc_runlevel_stacks(l->value);
|
print_stacked_services(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;
|
|
||||||
rc_stringlist_free(nservices);
|
rc_stringlist_free(nservices);
|
||||||
nservices = NULL;
|
nservices = NULL;
|
||||||
rc_stringlist_free(services);
|
rc_stringlist_free(services);
|
||||||
@ -317,16 +329,14 @@ rc_status(int argc, char **argv)
|
|||||||
services = rc_services_in_runlevel(NULL);
|
services = rc_services_in_runlevel(NULL);
|
||||||
sservices = rc_stringlist_new();
|
sservices = rc_stringlist_new();
|
||||||
TAILQ_FOREACH(l, levels, entries) {
|
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);
|
TAILQ_CONCAT(sservices, nservices, entries);
|
||||||
free(nservices);
|
free(nservices);
|
||||||
}
|
}
|
||||||
TAILQ_FOREACH_SAFE(s, services, entries, t) {
|
TAILQ_FOREACH_SAFE(s, services, entries, t) {
|
||||||
if (rc_stringlist_find(sservices, s->value) ||
|
if ((rc_stringlist_find(sservices, s->value) ||
|
||||||
rc_service_state(s->value) &
|
(rc_service_state(s->value) & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) {
|
||||||
(RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED))
|
TAILQ_REMOVE(services, s, entries);
|
||||||
{
|
|
||||||
TAILQ_REMOVE(services, s, entries);
|
|
||||||
free(s->value);
|
free(s->value);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
@ -337,18 +347,17 @@ rc_status(int argc, char **argv)
|
|||||||
alist = rc_stringlist_new();
|
alist = rc_stringlist_new();
|
||||||
l = rc_stringlist_add(alist, "");
|
l = rc_stringlist_add(alist, "");
|
||||||
p = l->value;
|
p = l->value;
|
||||||
if (!runlevel)
|
TAILQ_FOREACH(level, levels, entries) {
|
||||||
runlevel = rc_runlevel_get();
|
TAILQ_FOREACH_SAFE(s, services, entries, t) {
|
||||||
TAILQ_FOREACH_SAFE(s, services, entries, t) {
|
l->value = s->value;
|
||||||
l->value = s->value;
|
setenv("RC_SVCNAME", l->value, 1);
|
||||||
unsetenv("RC_SVCNAME");
|
tmp = rc_deptree_depends(deptree, needsme, alist, level->value, RC_DEP_TRACE);
|
||||||
setenv("RC_SVCNAME", l->value, 1);
|
if (TAILQ_FIRST(tmp)) {
|
||||||
tmp = rc_deptree_depends(deptree, needsme, alist, runlevel, RC_DEP_TRACE);
|
TAILQ_REMOVE(services, s, entries);
|
||||||
if (TAILQ_FIRST(tmp)) {
|
TAILQ_INSERT_TAIL(nservices, s, entries);
|
||||||
TAILQ_REMOVE(services, s, entries);
|
}
|
||||||
TAILQ_INSERT_TAIL(nservices, s, entries);
|
rc_stringlist_free(tmp);
|
||||||
}
|
}
|
||||||
rc_stringlist_free(tmp);
|
|
||||||
}
|
}
|
||||||
l->value = p;
|
l->value = p;
|
||||||
/* we are unsetting RC_SVCNAME because last loaded service
|
/* we are unsetting RC_SVCNAME because last loaded service
|
||||||
|
92
src/rc/rc.c
92
src/rc/rc.c
@ -79,12 +79,6 @@ const char rc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
|
|||||||
|
|
||||||
const char *applet = NULL;
|
const char *applet = NULL;
|
||||||
static char *runlevel;
|
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;
|
static RC_HOOK hook_out;
|
||||||
|
|
||||||
struct termios *termios_orig = NULL;
|
struct termios *termios_orig = NULL;
|
||||||
@ -524,7 +518,9 @@ runlevel_config(const char *service, const char *level)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
pid_t pid;
|
||||||
RC_STRING *service, *svc1, *svc2;
|
RC_STRING *service, *svc1, *svc2;
|
||||||
@ -627,7 +623,7 @@ stop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
do_start_services(bool parallel)
|
do_start_services(const RC_STRINGLIST *start_services, bool parallel)
|
||||||
{
|
{
|
||||||
RC_STRING *service;
|
RC_STRING *service;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
@ -754,6 +750,12 @@ main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
const char *bootlevel = NULL;
|
const char *bootlevel = NULL;
|
||||||
char *newlevel = 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 *deporder = NULL;
|
||||||
RC_STRINGLIST *tmplist;
|
RC_STRINGLIST *tmplist;
|
||||||
RC_STRING *service;
|
RC_STRING *service;
|
||||||
@ -868,7 +870,11 @@ main(int argc, char **argv)
|
|||||||
snprintf(pidstr, sizeof(pidstr), "%d", getpid());
|
snprintf(pidstr, sizeof(pidstr), "%d", getpid());
|
||||||
setenv("RC_PID", pidstr, 1);
|
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");
|
bootlevel = getenv("RC_BOOTLEVEL");
|
||||||
runlevel = rc_runlevel_get();
|
runlevel = rc_runlevel_get();
|
||||||
|
|
||||||
@ -972,8 +978,13 @@ main(int argc, char **argv)
|
|||||||
applet, RC_STOPPING, strerror(errno));
|
applet, RC_STOPPING, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build a list of all services to stop and then work out the
|
/* Create a list of all services which we could stop (assuming
|
||||||
* correct order for stopping them */
|
* 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);
|
stop_services = rc_services_in_state(RC_SERVICE_STARTED);
|
||||||
tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
|
tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
|
||||||
TAILQ_CONCAT(stop_services, tmplist, entries);
|
TAILQ_CONCAT(stop_services, tmplist, entries);
|
||||||
@ -996,7 +1007,11 @@ main(int argc, char **argv)
|
|||||||
stop_services = tmplist;
|
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);
|
hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
|
||||||
start_services = rc_services_in_runlevel_stacked(newlevel ?
|
start_services = rc_services_in_runlevel_stacked(newlevel ?
|
||||||
newlevel : runlevel);
|
newlevel : runlevel);
|
||||||
@ -1006,9 +1021,11 @@ main(int argc, char **argv)
|
|||||||
tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT);
|
tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT);
|
||||||
TAILQ_CONCAT(start_services, tmplist, entries);
|
TAILQ_CONCAT(start_services, tmplist, entries);
|
||||||
free(tmplist);
|
free(tmplist);
|
||||||
|
/* If we are NOT headed for the single-user runlevel... */
|
||||||
if (strcmp(newlevel ? newlevel : runlevel,
|
if (strcmp(newlevel ? newlevel : runlevel,
|
||||||
RC_LEVEL_SINGLE) != 0)
|
RC_LEVEL_SINGLE) != 0)
|
||||||
{
|
{
|
||||||
|
/* If we are NOT headed for the boot runlevel... */
|
||||||
if (strcmp(newlevel ? newlevel : runlevel,
|
if (strcmp(newlevel ? newlevel : runlevel,
|
||||||
bootlevel) != 0)
|
bootlevel) != 0)
|
||||||
{
|
{
|
||||||
@ -1029,7 +1046,7 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
/* Now stop the services that shouldn't be running */
|
/* Now stop the services that shouldn't be running */
|
||||||
if (stop_services && !nostop)
|
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 our services to finish */
|
||||||
wait_for_services();
|
wait_for_services();
|
||||||
@ -1065,18 +1082,10 @@ main(int argc, char **argv)
|
|||||||
TAILQ_FOREACH(service, hotplugged_services, entries)
|
TAILQ_FOREACH(service, hotplugged_services, entries)
|
||||||
rc_service_mark(service->value, RC_SERVICE_HOTPLUGGED);
|
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__
|
#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");
|
proc = p = rc_proc_getent("noinit");
|
||||||
if (proc) {
|
if (proc) {
|
||||||
while ((token = strsep(&p, ",")))
|
while ((token = strsep(&p, ",")))
|
||||||
@ -1085,19 +1094,38 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* If we have a list of services to start then... */
|
||||||
if (start_services) {
|
if (start_services) {
|
||||||
do_start_services(parallel);
|
/* Get a list of the chained runlevels which compose the target runlevel */
|
||||||
/* FIXME: If we skip the boot runlevel and go straight
|
RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel);
|
||||||
* 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 */
|
/* Loop through them in reverse order. */
|
||||||
wait_for_services();
|
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__
|
#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");
|
proc = p = rc_proc_getent("noinit");
|
||||||
if (proc) {
|
if (proc) {
|
||||||
while ((token = strsep(&p, ",")))
|
while ((token = strsep(&p, ",")))
|
||||||
|
Loading…
Reference in New Issue
Block a user