openrc/src/rc/rc-update.c

352 lines
9.0 KiB
C
Raw Normal View History

/*
2009-04-24 03:01:22 +05:30
rc-update
Manage init scripts and runlevels
*/
2008-01-14 10:35:22 +05:30
/*
2009-05-01 19:41:40 +05:30
* Copyright (c) 2007-2009 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtins.h"
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
2008-02-12 01:44:09 +05:30
extern const char *applet;
/* Return the number of changes made:
* -1 = no changes (error)
* 0 = no changes (nothing to do)
* 1+ = number of runlevels updated
*/
2009-01-10 17:48:00 +05:30
static int
add(const char *runlevel, const char *service)
{
int retval = -1;
2007-04-11 18:14:47 +05:30
2009-02-28 19:42:19 +05:30
if (!rc_service_exists(service)) {
if (errno == ENOEXEC)
2009-04-24 03:01:22 +05:30
eerror("%s: service `%s' is not executeable",
applet, service);
2009-02-28 19:42:19 +05:30
else
2009-04-24 03:01:22 +05:30
eerror("%s: service `%s' does not exist",
applet, service);
2009-02-28 19:42:19 +05:30
} else if (rc_service_in_runlevel(service, runlevel)) {
ewarn("%s: %s already installed in runlevel `%s'; skipping",
2009-04-24 03:01:22 +05:30
applet, service, runlevel);
retval = 0;
2009-02-28 19:42:19 +05:30
} else if (rc_service_add(runlevel, service)) {
einfo("service %s added to runlevel %s", service, runlevel);
retval = 1;
} else
2009-02-28 19:42:19 +05:30
eerror("%s: failed to add service `%s' to runlevel `%s': %s",
2009-04-24 03:01:22 +05:30
applet, service, runlevel, strerror (errno));
2007-04-11 18:14:47 +05:30
return retval;
}
2009-01-10 17:48:00 +05:30
static int
delete(const char *runlevel, const char *service)
{
int retval = -1;
errno = 0;
2009-02-28 19:42:19 +05:30
if (rc_service_delete(runlevel, service)) {
einfo("service %s removed from runlevel %s",
service, runlevel);
return 1;
}
if (errno == ENOENT)
eerror("%s: service `%s' is not in the runlevel `%s'",
2009-04-24 03:01:22 +05:30
applet, service, runlevel);
2008-01-14 10:35:22 +05:30
else
eerror("%s: failed to remove service `%s' from runlevel `%s': %s",
2009-04-24 03:01:22 +05:30
applet, service, runlevel, strerror (errno));
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;
}
2009-01-10 17:48:00 +05:30
static void
show(RC_STRINGLIST *runlevels, bool verbose)
{
RC_STRINGLIST *services = rc_services_in_runlevel(NULL);
RC_STRING *service;
RC_STRING *runlevel;
RC_STRINGLIST *in;
bool inone;
char buffer[PATH_MAX];
size_t l;
rc_stringlist_sort(&services);
TAILQ_FOREACH(service, services, entries) {
in = rc_stringlist_new();
inone = false;
TAILQ_FOREACH(runlevel, runlevels, entries) {
if (rc_service_in_runlevel(service->value,
2009-04-24 03:01:22 +05:30
runlevel->value))
{
rc_stringlist_add(in, runlevel->value);
inone = true;
} else {
l = strlen(runlevel->value);
memset (buffer, ' ', l);
buffer[l] = 0;
rc_stringlist_add (in, buffer);
}
}
if (inone || verbose) {
printf(" %20s |", service->value);
TAILQ_FOREACH(runlevel, in, entries)
2009-04-24 03:01:22 +05:30
printf (" %s", runlevel->value);
printf ("\n");
}
rc_stringlist_free(in);
}
rc_stringlist_free (services);
}
#include "_usage.h"
2009-04-24 03:01:22 +05:30
#define usagestring "" \
"Usage: rc-update [options] add service <runlevel>\n" \
" rc-update [options] del service <runlevel>\n" \
" rc-update [options] show"
#define getoptstring "su" getoptstring_COMMON
static const struct option longopts[] = {
{ "stack", 0, NULL, 's' },
{ "update", 0, NULL, 'u' },
longopts_COMMON
};
static const char * const longopts_help[] = {
"Stack a runlevel instead of a service",
"Force an update of the dependency tree",
longopts_help_COMMON
};
#include "_usage.c"
#define DOADD (1 << 1)
#define DODELETE (1 << 2)
#define DOSHOW (1 << 3)
2009-01-10 17:48:00 +05:30
int
rc_update(int argc, char **argv)
{
2010-03-18 02:07:47 +05:30
RC_DEPTREE *deptree;
RC_STRINGLIST *runlevels;
RC_STRING *runlevel;
char *service = NULL;
char *p;
int action = 0;
bool verbose = false, stack = false;
int opt;
int retval = EXIT_FAILURE;
int num_updated = 0;
int (*actfunc)(const char *, const char *);
int ret;
2007-04-11 18:14:47 +05:30
while ((opt = getopt_long(argc, argv, getoptstring,
2009-04-24 03:01:22 +05:30
longopts, (int *)0)) != -1)
switch (opt) {
case 's':
stack = true;
break;
case 'u':
2010-03-18 02:07:47 +05:30
deptree = _rc_deptree_load(-1, &ret);
if (deptree)
rc_deptree_free(deptree);
return ret;
case_RC_COMMON_GETOPT;
}
verbose = rc_yesno(getenv ("EINFO_VERBOSE"));
2007-09-26 04:47:25 +05:30
if ((action & DOSHOW && action != DOSHOW) ||
2008-01-11 21:21:40 +05:30
(action & DOADD && action != DOADD) ||
(action & DODELETE && action != DODELETE))
eerrorx("%s: cannot mix commands", applet);
/* We need to be backwards compatible */
if (optind < argc) {
if (strcmp(argv[optind], "add") == 0)
action = DOADD;
else if (strcmp(argv[optind], "delete") == 0 ||
2009-04-24 03:01:22 +05:30
strcmp(argv[optind], "del") == 0)
action = DODELETE;
else if (strcmp(argv[optind], "show") == 0)
action = DOSHOW;
if (action)
optind++;
else
2009-04-24 03:01:22 +05:30
eerrorx("%s: invalid command `%s'",
applet, argv[optind]);
}
2009-01-10 17:48:00 +05:30
if (!action)
action = DOSHOW;
runlevels = rc_stringlist_new();
if (optind >= argc) {
2009-04-30 21:15:18 +05:30
if (!(action & DOSHOW))
eerrorx("%s: no service specified", applet);
} else {
service = argv[optind];
optind++;
while (optind < argc)
if (rc_runlevel_exists(argv[optind]))
rc_stringlist_add(runlevels, argv[optind++]);
else {
rc_stringlist_free(runlevels);
eerrorx ("%s: `%s' is not a valid runlevel",
2009-04-24 03:01:22 +05:30
applet, argv[optind]);
}
}
2007-04-11 18:14:47 +05:30
retval = EXIT_SUCCESS;
if (action & DOSHOW) {
if (service)
rc_stringlist_add(runlevels, service);
2009-01-10 17:48:00 +05:30
if (!TAILQ_FIRST(runlevels)) {
free(runlevels);
runlevels = rc_runlevel_list();
}
2007-04-11 18:14:47 +05:30
rc_stringlist_sort(&runlevels);
show (runlevels, verbose);
} else {
2009-01-10 17:48:00 +05:30
if (!service)
eerror ("%s: no service specified", applet);
else {
if (action & DOADD) {
actfunc = stack ? addstack : add;
} else if (action & DODELETE) {
actfunc = stack ? delstack : delete;
} else {
rc_stringlist_free(runlevels);
2009-01-10 17:48:00 +05:30
eerrorx("%s: invalid action", applet);
}
2009-01-10 17:48:00 +05:30
if (!TAILQ_FIRST(runlevels)) {
p = rc_runlevel_get();
rc_stringlist_add(runlevels, p);
free(p);
}
2009-01-10 17:48:00 +05:30
if (!TAILQ_FIRST(runlevels)) {
free(runlevels);
2009-04-24 03:01:22 +05:30
eerrorx("%s: no runlevels found", applet);
}
2009-01-10 17:48:00 +05:30
TAILQ_FOREACH(runlevel, runlevels, entries) {
if (!rc_runlevel_exists(runlevel->value)) {
eerror ("%s: runlevel `%s' does not exist",
2009-04-24 03:01:22 +05:30
applet, runlevel->value);
continue;
}
ret = actfunc(runlevel->value, service);
if (ret < 0)
retval = EXIT_FAILURE;
num_updated += ret;
2007-04-11 18:14:47 +05:30
}
if (retval == EXIT_SUCCESS &&
num_updated == 0 && action & DODELETE)
ewarnx("%s: service `%s' not found in any"
2009-04-24 03:01:22 +05:30
" of the specified runlevels",
applet, service);
2007-04-11 18:14:47 +05:30
}
}
rc_stringlist_free(runlevels);
return retval;
}