diff options
Diffstat (limited to 'src/rc-update')
-rw-r--r-- | src/rc-update/meson.build | 7 | ||||
-rw-r--r-- | src/rc-update/rc-update.c | 353 |
2 files changed, 360 insertions, 0 deletions
diff --git a/src/rc-update/meson.build b/src/rc-update/meson.build new file mode 100644 index 00000000..7d284979 --- /dev/null +++ b/src/rc-update/meson.build @@ -0,0 +1,7 @@ +executable('rc-update', + ['rc-update.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + link_with: [libeinfo, librc], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) diff --git a/src/rc-update/rc-update.c b/src/rc-update/rc-update.c new file mode 100644 index 00000000..445db4e8 --- /dev/null +++ b/src/rc-update/rc-update.c @@ -0,0 +1,353 @@ +/* + * rc-update + * Manage init scripts and runlevels + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#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 "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *usagestring = "" \ + "Usage: rc-update [options] add <service> [<runlevel>...]\n" \ + " or: rc-update [options] del <service> [<runlevel>...]\n" \ + " or: rc-update [options] [show [<runlevel>...]]"; +const char getoptstring[] = "asu" getoptstring_COMMON; +const struct option longopts[] = { + { "all", 0, NULL, 'a' }, + { "stack", 0, NULL, 's' }, + { "update", 0, NULL, 'u' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Process all runlevels", + "Stack a runlevel instead of a service", + "Force an update of the dependency tree", + longopts_help_COMMON +}; + +/* Return the number of changes made: + * -1 = no changes (error) + * 0 = no changes (nothing to do) + * 1+ = number of runlevels updated + */ +static int +add(const char *runlevel, const char *service) +{ + int retval = -1; + + if (!rc_service_exists(service)) { + if (errno == ENOEXEC) + eerror("%s: service `%s' is not executable", + applet, service); + else + eerror("%s: service `%s' does not exist", + applet, service); + } else if (rc_service_in_runlevel(service, runlevel)) { + einfo("%s: %s already installed in runlevel `%s'; skipping", + applet, service, runlevel); + retval = 0; + } else if (rc_service_add(runlevel, service)) { + einfo("service %s added to runlevel %s", service, runlevel); + retval = 1; + } else + eerror("%s: failed to add service `%s' to runlevel `%s': %s", + applet, service, runlevel, strerror (errno)); + + return retval; +} + +static int +delete(const char *runlevel, const char *service) +{ + int retval = -1; + + errno = 0; + 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'", + applet, service, runlevel); + else + eerror("%s: failed to remove service `%s' from runlevel `%s': %s", + 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; +} + +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 = NULL; + 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, + runlevel->value)) + { + rc_stringlist_add(in, runlevel->value); + inone = true; + } else { + l = strlen(runlevel->value); + buffer = xmalloc(l+1); + memset (buffer, ' ', l); + buffer[l] = 0; + rc_stringlist_add (in, buffer); + free(buffer); + } + } + + if (inone || verbose) { + printf(" %20s |", service->value); + TAILQ_FOREACH(runlevel, in, entries) + printf (" %s", runlevel->value); + printf ("\n"); + } + rc_stringlist_free(in); + } + + rc_stringlist_free (services); +} + +#define DOADD (1 << 1) +#define DODELETE (1 << 2) +#define DOSHOW (1 << 3) + +int main(int argc, char **argv) +{ + RC_DEPTREE *deptree; + RC_STRINGLIST *runlevels; + RC_STRING *runlevel; + char *service = NULL; + char *p; + int action = 0; + bool verbose = false, stack = false, all_runlevels = false; + int opt; + int retval = EXIT_FAILURE; + int num_updated = 0; + int (*actfunc)(const char *, const char *); + int ret; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *)0)) != -1) + switch (opt) { + case 'a': + all_runlevels = true; + break; + case 's': + stack = true; + break; + case 'u': + deptree = _rc_deptree_load(-1, &ret); + if (deptree) + rc_deptree_free(deptree); + return ret; + case_RC_COMMON_GETOPT + } + + verbose = rc_yesno(getenv ("EINFO_VERBOSE")); + + if ((action & DOSHOW && action != DOSHOW) || + (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 || + strcmp(argv[optind], "del") == 0) + action = DODELETE; + else if (strcmp(argv[optind], "show") == 0) + action = DOSHOW; + if (action) + optind++; + else + eerrorx("%s: invalid command `%s'", + applet, argv[optind]); + } + if (!action) + action = DOSHOW; + + runlevels = rc_stringlist_new(); + + if (optind >= argc) { + 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", + applet, argv[optind]); + } + } + + retval = EXIT_SUCCESS; + if (action & DOSHOW) { + if (service) + rc_stringlist_add(runlevels, service); + if (!TAILQ_FIRST(runlevels)) { + free(runlevels); + runlevels = rc_runlevel_list(); + } + + rc_stringlist_sort(&runlevels); + show (runlevels, verbose); + } else { + if (!service) + eerror ("%s: no service specified", applet); + else { + if (action & DOADD) { + if (all_runlevels) { + rc_stringlist_free(runlevels); + eerrorx("%s: the -a option is invalid with add", applet); + } + actfunc = stack ? addstack : add; + } else if (action & DODELETE) { + actfunc = stack ? delstack : delete; + } else { + rc_stringlist_free(runlevels); + eerrorx("%s: invalid action", applet); + } + + if (!TAILQ_FIRST(runlevels)) { + if (all_runlevels) { + free(runlevels); + runlevels = rc_runlevel_list(); + } else { + p = rc_runlevel_get(); + rc_stringlist_add(runlevels, p); + free(p); + } + } + + if (!TAILQ_FIRST(runlevels)) { + free(runlevels); + eerrorx("%s: no runlevels found", applet); + } + + TAILQ_FOREACH(runlevel, runlevels, entries) { + if (!rc_runlevel_exists(runlevel->value)) { + eerror ("%s: runlevel `%s' does not exist", + applet, runlevel->value); + continue; + } + + ret = actfunc(runlevel->value, service); + if (ret < 0) + retval = EXIT_FAILURE; + num_updated += ret; + } + + if (retval == EXIT_SUCCESS && + num_updated == 0 && action & DODELETE) + ewarnx("%s: service `%s' not found in any" + " of the specified runlevels", + applet, service); + } + } + + rc_stringlist_free(runlevels); + return retval; +} |