Add runlevel stacking, #88

This implementation has the limitation that you cannot have a stacked
runlevel and service of the same name in a runlevel.
This commit is contained in:
Roy Marples 2009-05-02 12:26:45 +01:00
parent e040bd77e9
commit 6615eb4b68
11 changed files with 217 additions and 26 deletions

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2007-2009 Roy Marples 4.\" Copyright (c) 2007-2009 Roy Marples
.\" All rights reserved .\" All rights reserved
.\" .\"
.\" Redistribution and use in source and binary forms, with or without .\" Redistribution and use in source and binary forms, with or without
@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd Jan 10, 2009 .Dd May 2, 2009
.Dt RC-UPDATE 8 SMM .Dt RC-UPDATE 8 SMM
.Os OpenRC .Os OpenRC
.Sh NAME .Sh NAME
@ -30,10 +30,12 @@
.Nd add and remove services to and from a runlevel .Nd add and remove services to and from a runlevel
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl s , -stack
.Ar add .Ar add
.Ar service .Ar service
.Op Ar runlevel ... .Op Ar runlevel ...
.Nm .Nm
.Op Fl s , -stack
.Ar delete .Ar delete
.Ar service .Ar service
.Op Ar runlevel ... .Op Ar runlevel ...
@ -51,7 +53,8 @@ All services must reside in the
.Pa /etc/init.d .Pa /etc/init.d
or or
.Pa /usr/local/etc/init.d .Pa /usr/local/etc/init.d
directories. They must also conform to the OpenRC runscript standard. directories.
They must also conform to the OpenRC runscript standard.
.Pp .Pp
.Bl -tag -width "Fl a , -delete service" .Bl -tag -width "Fl a , -delete service"
.It Ar add Ar service .It Ar add Ar service
@ -78,6 +81,11 @@ Forces an update of the dependency tree cache.
This may be needed in the event of clock skew (a file in /etc is newer than the This may be needed in the event of clock skew (a file in /etc is newer than the
system clock). system clock).
.El .El
.Pp
If the
.Fl s , -stack
option is given then we either add or remove the runlevel from the runlevel.
This allows inheritance of runlevels.
.Sh SEE ALSO .Sh SEE ALSO
.Xr rc 8 , .Xr rc 8 ,
.Xr rc-status 8 .Xr rc-status 8

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd January 13, 2009 .Dd May 2, 2009
.Dt RC 8 SMM .Dt RC 8 SMM
.Os OpenRC .Os OpenRC
.Sh NAME .Sh NAME
@ -35,10 +35,11 @@
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
first stops any services that are not for the runlevel and then starts any first stops any services that are not for the runlevel and then starts any
services added by services in the runlevel and from stacked runlevels added by
.Nm rc-update .Nm rc-update
that are not currently started. If no runlevel is specified then we use the that are not currently started.
current runlevel the system is currently in. If no runlevel is specified then we use the current runlevel the system
is currently in.
.Pp .Pp
There are some special runlevels that you should be aware of: There are some special runlevels that you should be aware of:
.Bl -tag -width "shutdown" .Bl -tag -width "shutdown"

View File

@ -66,7 +66,7 @@ static const rc_service_state_name_t rc_service_state_names[] = {
}; };
#define LS_INITD 0x01 #define LS_INITD 0x01
#define LS_DIR 0x02 #define LS_DIR 0x02
static RC_STRINGLIST * static RC_STRINGLIST *
ls_dir(const char *dir, int options) ls_dir(const char *dir, int options)
{ {
@ -102,7 +102,7 @@ ls_dir(const char *dir, int options)
} }
if (options & LS_DIR) { if (options & LS_DIR) {
if (stat(d->d_name, &buf) == 0 && if (stat(d->d_name, &buf) == 0 &&
! S_ISDIR(buf.st_mode)) !S_ISDIR(buf.st_mode))
continue; continue;
} }
rc_stringlist_add(list, d->d_name); rc_stringlist_add(list, d->d_name);
@ -330,6 +330,51 @@ rc_runlevel_exists(const char *runlevel)
} }
librc_hidden_def(rc_runlevel_exists) librc_hidden_def(rc_runlevel_exists)
bool
rc_runlevel_stack(const char *dst, const char *src)
{
char d[PATH_MAX], s[PATH_MAX];
if (!rc_runlevel_exists(dst) || !rc_runlevel_exists(src))
return false;
snprintf(s, sizeof(s), "../%s", src);
snprintf(d, sizeof(s), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
return (symlink(s, d) == 0 ? true : false);
}
librc_hidden_def(rc_runlevel_stack)
bool
rc_runlevel_unstack(const char *dst, const char *src)
{
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
return (unlink(path) == 0 ? true : false);
}
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;
}
librc_hidden_def(rc_runlevel_stacks)
/* Resolve a service name to it's full path */ /* Resolve a service name to it's full path */
char * char *
rc_service_resolve(const char *service) rc_service_resolve(const char *service)
@ -780,6 +825,27 @@ rc_services_in_runlevel(const char *runlevel)
} }
librc_hidden_def(rc_services_in_runlevel) librc_hidden_def(rc_services_in_runlevel)
RC_STRINGLIST *
rc_services_in_runlevel_stacked(const char *runlevel)
{
RC_STRINGLIST *list, *stacks, *sl;
RC_STRING *stack;
list = rc_services_in_runlevel(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;
}
return list;
}
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)
{ {

View File

@ -91,8 +91,11 @@ librc_hidden_proto(rc_runlevel_exists)
librc_hidden_proto(rc_runlevel_get) librc_hidden_proto(rc_runlevel_get)
librc_hidden_proto(rc_runlevel_list) librc_hidden_proto(rc_runlevel_list)
librc_hidden_proto(rc_runlevel_set) librc_hidden_proto(rc_runlevel_set)
librc_hidden_proto(rc_runlevel_stack)
librc_hidden_proto(rc_runlevel_stacks)
librc_hidden_proto(rc_runlevel_starting) librc_hidden_proto(rc_runlevel_starting)
librc_hidden_proto(rc_runlevel_stopping) librc_hidden_proto(rc_runlevel_stopping)
librc_hidden_proto(rc_runlevel_unstack)
librc_hidden_proto(rc_service_add) librc_hidden_proto(rc_service_add)
librc_hidden_proto(rc_service_daemons_crashed) librc_hidden_proto(rc_service_daemons_crashed)
librc_hidden_proto(rc_service_daemon_set) librc_hidden_proto(rc_service_daemon_set)
@ -106,6 +109,7 @@ librc_hidden_proto(rc_service_resolve)
librc_hidden_proto(rc_service_schedule_clear) librc_hidden_proto(rc_service_schedule_clear)
librc_hidden_proto(rc_service_schedule_start) librc_hidden_proto(rc_service_schedule_start)
librc_hidden_proto(rc_services_in_runlevel) librc_hidden_proto(rc_services_in_runlevel)
librc_hidden_proto(rc_services_in_runlevel_stacked)
librc_hidden_proto(rc_services_in_state) librc_hidden_proto(rc_services_in_state)
librc_hidden_proto(rc_services_scheduled) librc_hidden_proto(rc_services_scheduled)
librc_hidden_proto(rc_services_scheduled_by) librc_hidden_proto(rc_services_scheduled_by)

View File

@ -80,6 +80,22 @@ char *rc_runlevel_get(void);
* @return true if the runlevel exists, otherwise false */ * @return true if the runlevel exists, otherwise false */
bool rc_runlevel_exists(const char *); bool rc_runlevel_exists(const char *);
/*! Stack a runlevel onto another
* @param runlevel to stack onto
* @param runlevel being stacked
* @return true if successful, otherwise false */
bool rc_runlevel_stack(const char *, const char *);
/*! Unstack a runlevel from another
* @param runlevel to unstack from
* @param runlevel being unstacked
* @return true if successful, otherwise false */
bool rc_runlevel_unstack(const char *, const char *);
/*! Return a NULL terminated list of runlevel stacks in the runlevels
* @return a NULL terminated list of runlevels */
RC_STRINGLIST *rc_runlevel_stacks(const char *);
/*! Return a NULL terminated list of runlevels /*! Return a NULL terminated list of runlevels
* @return a NULL terminated list of runlevels */ * @return a NULL terminated list of runlevels */
RC_STRINGLIST *rc_runlevel_list(void); RC_STRINGLIST *rc_runlevel_list(void);
@ -225,6 +241,11 @@ bool rc_service_value_set(const char *, const char *, const char *);
* @return NULL terminated list of services */ * @return NULL terminated list of services */
RC_STRINGLIST *rc_services_in_runlevel(const char *); RC_STRINGLIST *rc_services_in_runlevel(const char *);
/*! List the stacked services in a runlevel
* @param runlevel to list
* @return NULL terminated list of services */
RC_STRINGLIST *rc_services_in_runlevel_stacked(const char *);
/*! List the services in a state /*! List the services in a state
* @param state to list * @param state to list
* @return NULL terminated list of services */ * @return NULL terminated list of services */

View File

@ -18,8 +18,11 @@ global:
rc_runlevel_get; rc_runlevel_get;
rc_runlevel_list; rc_runlevel_list;
rc_runlevel_set; rc_runlevel_set;
rc_runlevel_stack;
rc_runlevel_stacks;
rc_runlevel_starting; rc_runlevel_starting;
rc_runlevel_stopping; rc_runlevel_stopping;
rc_runlevel_unstack;
rc_service_add; rc_service_add;
rc_service_daemons_crashed; rc_service_daemons_crashed;
rc_service_daemon_set; rc_service_daemon_set;
@ -34,6 +37,7 @@ global:
rc_service_schedule_clear; rc_service_schedule_clear;
rc_service_schedule_start; rc_service_schedule_start;
rc_services_in_runlevel; rc_services_in_runlevel;
rc_services_in_runlevel_stacked;
rc_services_in_state; rc_services_in_state;
rc_services_scheduled; rc_services_scheduled;
rc_services_scheduled_by; rc_services_scheduled_by;

View File

@ -78,8 +78,10 @@ _rc_can_find_pids(void)
} }
static void static void
print_level(const char *level) print_level(const char *prefix, const char *level)
{ {
if (prefix)
printf("%s ", prefix);
printf ("Runlevel: "); printf ("Runlevel: ");
if (isatty(fileno(stdout))) if (isatty(fileno(stdout)))
printf("%s%s%s\n", printf("%s%s%s\n",
@ -274,16 +276,28 @@ rc_status(int argc, char **argv)
deptree = _rc_deptree_load(0, NULL); deptree = _rc_deptree_load(0, NULL);
TAILQ_FOREACH(l, levels, entries) { TAILQ_FOREACH(l, levels, entries) {
print_level(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);
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);
nservices = NULL;
rc_stringlist_free(services); rc_stringlist_free(services);
services = NULL; services = NULL;
} }
if (aflag || argc < 2) { if (aflag || argc < 2) {
/* Show hotplugged services */ /* Show hotplugged services */
print_level("hotplugged"); print_level("Dynamic", "hotplugged");
services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
print_services(NULL, services); print_services(NULL, services);
rc_stringlist_free(services); rc_stringlist_free(services);
@ -336,9 +350,9 @@ rc_status(int argc, char **argv)
rc_stringlist_free(tmp); rc_stringlist_free(tmp);
} }
l->value = p; l->value = p;
print_level("needed"); print_level("Dynamic", "needed");
print_services(NULL, nservices); print_services(NULL, nservices);
print_level("manual"); print_level("Dynamic", "manual");
print_services(NULL, services); print_services(NULL, services);
} }

View File

@ -63,11 +63,11 @@ add(const char *runlevel, const char *service)
eerror("%s: service `%s' does not exist", eerror("%s: service `%s' does not exist",
applet, service); applet, service);
} else if (rc_service_in_runlevel(service, runlevel)) { } else if (rc_service_in_runlevel(service, runlevel)) {
ewarn ("%s: %s already installed in runlevel `%s'; skipping", ewarn("%s: %s already installed in runlevel `%s'; skipping",
applet, service, runlevel); applet, service, runlevel);
retval = 0; retval = 0;
} else if (rc_service_add(runlevel, service)) { } else if (rc_service_add(runlevel, service)) {
einfo ("%s added to runlevel %s", service, runlevel); einfo("service %s added to runlevel %s", service, runlevel);
retval = 1; retval = 1;
} else } else
eerror("%s: failed to add service `%s' to runlevel `%s': %s", eerror("%s: failed to add service `%s' to runlevel `%s': %s",
@ -83,20 +83,76 @@ delete(const char *runlevel, const char *service)
errno = 0; errno = 0;
if (rc_service_delete(runlevel, service)) { if (rc_service_delete(runlevel, service)) {
einfo("%s removed from runlevel %s", service, runlevel); einfo("service %s removed from runlevel %s",
service, runlevel);
return 1; return 1;
} }
if (errno == ENOENT) if (errno == ENOENT)
eerror ("%s: service `%s' is not in the runlevel `%s'", eerror("%s: service `%s' is not in the runlevel `%s'",
applet, service, runlevel); applet, service, runlevel);
else else
eerror ("%s: failed to remove service `%s' from runlevel `%s': %s", eerror("%s: failed to remove service `%s' from runlevel `%s': %s",
applet, service, runlevel, strerror (errno)); applet, service, runlevel, strerror (errno));
return retval; return retval;
} }
static int
addstack(const char *runlevel, const char *stack)
{
if (!rc_runlevel_exists(runlevel)) {
eerror("%s: runlevel `%s' does not exist", applet, runlevel);
return -1;
}
if (!rc_runlevel_exists(stack)) {
eerror("%s: runlevel `%s' does not exist", applet, stack);
return -1;
}
if (strcmp(runlevel, stack) == 0) {
eerror("%s: cannot stack `%s' onto itself", applet, stack);
return -1;
}
if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 ||
strcmp(stack, RC_LEVEL_SYSINIT) == 0 ||
strcmp(runlevel, RC_LEVEL_BOOT) == 0 ||
strcmp(stack, RC_LEVEL_BOOT) == 0 ||
strcmp(runlevel, RC_LEVEL_SINGLE) == 0 ||
strcmp(stack, RC_LEVEL_SINGLE) == 0 ||
strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
strcmp(stack, RC_LEVEL_SHUTDOWN) == 0)
{
eerror("%s: cannot stack the %s runlevel",
applet, RC_LEVEL_SYSINIT);
return -1;
}
if (!rc_runlevel_stack(runlevel, stack)) {
eerror("%s: failed to stack `%s' to `%s': %s",
applet, stack, runlevel, strerror(errno));
return -1;
}
einfo("runlevel %s added to runlevel %s", stack, runlevel);
return 1;
}
static int
delstack(const char *runlevel, const char *stack)
{
if (rc_runlevel_unstack(runlevel, stack)) {
einfo("runlevel %s removed from runlevel %s", stack, runlevel);
return 1;
}
if (errno == ENOENT)
eerror("%s: runlevel `%s' is not in the runlevel `%s'",
applet, stack, runlevel);
else
eerror("%s: failed to remove runlevel `%s' from runlevel `%s': %s",
applet, stack, runlevel, strerror (errno));
return -1;
}
static void static void
show(RC_STRINGLIST *runlevels, bool verbose) show(RC_STRINGLIST *runlevels, bool verbose)
{ {
@ -143,12 +199,14 @@ show(RC_STRINGLIST *runlevels, bool verbose)
"Usage: rc-update [options] add service <runlevel>\n" \ "Usage: rc-update [options] add service <runlevel>\n" \
" rc-update [options] del service <runlevel>\n" \ " rc-update [options] del service <runlevel>\n" \
" rc-update [options] show" " rc-update [options] show"
#define getoptstring "u" getoptstring_COMMON #define getoptstring "su" getoptstring_COMMON
static const struct option longopts[] = { static const struct option longopts[] = {
{ "stack", 0, NULL, 's' },
{ "update", 0, NULL, 'u' }, { "update", 0, NULL, 'u' },
longopts_COMMON longopts_COMMON
}; };
static const char * const longopts_help[] = { static const char * const longopts_help[] = {
"Stack a runlevel instead of a service",
"Force an update of the dependency tree", "Force an update of the dependency tree",
longopts_help_COMMON longopts_help_COMMON
}; };
@ -166,7 +224,7 @@ rc_update(int argc, char **argv)
char *service = NULL; char *service = NULL;
char *p; char *p;
int action = 0; int action = 0;
bool verbose = false; bool verbose = false, stack = false;
int opt; int opt;
int retval = EXIT_FAILURE; int retval = EXIT_FAILURE;
int num_updated = 0; int num_updated = 0;
@ -176,11 +234,14 @@ rc_update(int argc, char **argv)
while ((opt = getopt_long(argc, argv, getoptstring, while ((opt = getopt_long(argc, argv, getoptstring,
longopts, (int *)0)) != -1) longopts, (int *)0)) != -1)
switch (opt) { switch (opt) {
case 's':
stack = true;
break;
case 'u': case 'u':
_rc_deptree_load(-1, &ret); _rc_deptree_load(-1, &ret);
return ret; return ret;
case_RC_COMMON_GETOPT case_RC_COMMON_GETOPT;
} }
verbose = rc_yesno(getenv ("EINFO_VERBOSE")); verbose = rc_yesno(getenv ("EINFO_VERBOSE"));
@ -241,9 +302,9 @@ rc_update(int argc, char **argv)
eerror ("%s: no service specified", applet); eerror ("%s: no service specified", applet);
else { else {
if (action & DOADD) { if (action & DOADD) {
actfunc = add; actfunc = stack ? addstack : add;
} else if (action & DODELETE) { } else if (action & DODELETE) {
actfunc = delete; actfunc = stack ? delstack : delete;
} else { } else {
rc_stringlist_free(runlevels); rc_stringlist_free(runlevels);
eerrorx("%s: invalid action", applet); eerrorx("%s: invalid action", applet);

View File

@ -1037,7 +1037,7 @@ main(int argc, char **argv)
/* Load our list of start services */ /* Load our list of start services */
hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
start_services = rc_services_in_runlevel(newlevel ? start_services = rc_services_in_runlevel_stacked(newlevel ?
newlevel : runlevel); newlevel : runlevel);
if (strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 && if (strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SYSINIT) != 0) strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SYSINIT) != 0)

View File

@ -16,8 +16,11 @@ rc_runlevel_exists
rc_runlevel_get rc_runlevel_get
rc_runlevel_list rc_runlevel_list
rc_runlevel_set rc_runlevel_set
rc_runlevel_stack
rc_runlevel_stacks
rc_runlevel_starting rc_runlevel_starting
rc_runlevel_stopping rc_runlevel_stopping
rc_runlevel_unstack
rc_service_add rc_service_add
rc_service_daemon_set rc_service_daemon_set
rc_service_daemons_crashed rc_service_daemons_crashed
@ -35,6 +38,7 @@ rc_service_state
rc_service_value_get rc_service_value_get
rc_service_value_set rc_service_value_set
rc_services_in_runlevel rc_services_in_runlevel
rc_services_in_runlevel_stacked
rc_services_in_state rc_services_in_state
rc_services_scheduled rc_services_scheduled
rc_services_scheduled_by rc_services_scheduled_by

View File

@ -32,10 +32,16 @@ rc_runlevel_list
rc_runlevel_list@@RC_1.0 rc_runlevel_list@@RC_1.0
rc_runlevel_set rc_runlevel_set
rc_runlevel_set@@RC_1.0 rc_runlevel_set@@RC_1.0
rc_runlevel_stack
rc_runlevel_stack@@RC_1.0
rc_runlevel_stacks
rc_runlevel_stacks@@RC_1.0
rc_runlevel_starting rc_runlevel_starting
rc_runlevel_starting@@RC_1.0 rc_runlevel_starting@@RC_1.0
rc_runlevel_stopping rc_runlevel_stopping
rc_runlevel_stopping@@RC_1.0 rc_runlevel_stopping@@RC_1.0
rc_runlevel_unstack
rc_runlevel_unstack@@RC_1.0
rc_service_add rc_service_add
rc_service_add@@RC_1.0 rc_service_add@@RC_1.0
rc_service_daemon_set rc_service_daemon_set
@ -70,6 +76,8 @@ rc_service_value_set
rc_service_value_set@@RC_1.0 rc_service_value_set@@RC_1.0
rc_services_in_runlevel rc_services_in_runlevel
rc_services_in_runlevel@@RC_1.0 rc_services_in_runlevel@@RC_1.0
rc_services_in_runlevel_stacked
rc_services_in_runlevel_stacked@@RC_1.0
rc_services_in_state rc_services_in_state
rc_services_in_state@@RC_1.0 rc_services_in_state@@RC_1.0
rc_services_scheduled rc_services_scheduled