/* * 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 #include #include #include #include #include #include #include #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 [...]\n" \ " or: rc-update [options] del [...]\n" \ " or: rc-update [options] [show [...]]"; 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; }