From 391d12db48754861b5cecac92ee3321597ee02c1 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Wed, 6 Apr 2022 10:51:55 -0500 Subject: migrate fully to meson build system - drop old build system - move shared include and source files to common directory - drop "rc-" prefix from shared include and source files - move executable-specific code to individual directories under src - adjust top-level .gitignore file for new build system This closes #489. --- src/rc-status/rc-status.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 src/rc-status/rc-status.c (limited to 'src/rc-status/rc-status.c') diff --git a/src/rc-status/rc-status.c b/src/rc-status/rc-status.c new file mode 100644 index 00000000..cb701e24 --- /dev/null +++ b/src/rc-status/rc-status.c @@ -0,0 +1,465 @@ +/* + * rc-status.c + * Display the status of the services in 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 "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +enum format_t { + FORMAT_DEFAULT, + FORMAT_INI, +}; + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "acf:lmrsSu" getoptstring_COMMON; +const struct option longopts[] = { + {"all", 0, NULL, 'a'}, + {"crashed", 0, NULL, 'c'}, + {"format", 1, NULL, 'f'}, + {"list", 0, NULL, 'l'}, + {"manual", 0, NULL, 'm'}, + {"runlevel", 0, NULL, 'r'}, + {"servicelist", 0, NULL, 's'}, + {"supervised", 0, NULL, 'S'}, + {"unused", 0, NULL, 'u'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Show services from all run levels", + "Show crashed services", + "format status to be parsable (currently arg must be ini)", + "Show list of run levels", + "Show manually started services", + "Show the name of the current runlevel", + "Show service list", + "show supervised services", + "Show services not assigned to any runlevel", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: rc-status [options] -f ini ...\n" \ + " or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]"; + +static RC_DEPTREE *deptree; +static RC_STRINGLIST *types; + +static RC_STRINGLIST *levels, *services, *tmp, *alist; +static RC_STRINGLIST *sservices, *nservices, *needsme; + +static void print_level(const char *prefix, const char *level, + enum format_t format) +{ + switch (format) { + case FORMAT_DEFAULT: + if (prefix) + printf("%s ", prefix); + printf ("Runlevel: "); + if (isatty(fileno(stdout))) + printf("%s%s%s\n", + ecolor(ECOLOR_HILITE), level, ecolor(ECOLOR_NORMAL)); + else + printf("%s\n", level); + break; + case FORMAT_INI: + printf("%s", "["); + if (prefix) + printf("%s ", prefix); + printf("%s]\n", level); + break; + } +} + +static char *get_uptime(const char *service) +{ + RC_SERVICE state = rc_service_state(service); + char *start_count; + char *start_time_string; + time_t start_time; + int64_t diff_days; + int64_t diff_hours; + int64_t diff_mins; + int64_t diff_secs; + char *uptime = NULL; + + if (state & RC_SERVICE_STARTED) { + start_count = rc_service_value_get(service, "start_count"); + start_time_string = rc_service_value_get(service, "start_time"); + if (start_count && start_time_string) { + start_time = to_time_t(start_time_string); + diff_secs = (int64_t) difftime(time(NULL), start_time); + diff_days = diff_secs / 86400; + diff_secs = diff_secs % 86400; + diff_hours = diff_secs / 3600; + diff_secs = diff_secs % 3600; + diff_mins = diff_secs / 60; + diff_secs = diff_secs % 60; + if (diff_days > 0) + xasprintf(&uptime, + "%"PRId64" day(s) %02"PRId64":%02"PRId64":%02"PRId64" (%s)", + diff_days, diff_hours, diff_mins, diff_secs, + start_count); + else + xasprintf(&uptime, + "%02"PRId64":%02"PRId64":%02"PRId64" (%s)", + diff_hours, diff_mins, diff_secs, start_count); + } + } + return uptime; +} + +static void print_service(const char *service, enum format_t format) +{ + char *status = NULL; + char *uptime = NULL; + char *child_pid = NULL; + char *start_time = NULL; + int cols; + const char *c = ecolor(ECOLOR_GOOD); + RC_SERVICE state = rc_service_state(service); + ECOLOR color = ECOLOR_BAD; + + if (state & RC_SERVICE_STOPPING) + xasprintf(&status, "stopping "); + else if (state & RC_SERVICE_STARTING) { + xasprintf(&status, "starting "); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_INACTIVE) { + xasprintf(&status, "inactive "); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_STARTED) { + errno = 0; + if (rc_service_daemons_crashed(service) && errno != EACCES) + { + child_pid = rc_service_value_get(service, "child_pid"); + start_time = rc_service_value_get(service, "start_time"); + if (start_time && child_pid) + xasprintf(&status, " unsupervised "); + else + xasprintf(&status, " crashed "); + free(child_pid); + free(start_time); + } else { + uptime = get_uptime(service); + if (uptime) { + xasprintf(&status, " started %s", uptime); + free(uptime); + } else + xasprintf(&status, " started "); + color = ECOLOR_GOOD; + } + } else if (state & RC_SERVICE_SCHEDULED) { + xasprintf(&status, "scheduled"); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_FAILED) { + xasprintf(&status, "failed"); + color = ECOLOR_WARN; + } else + xasprintf(&status, " stopped "); + + errno = 0; + switch (format) { + case FORMAT_DEFAULT: + cols = printf(" %s", service); + if (c && *c && isatty(fileno(stdout))) + printf("\n"); + ebracket(cols, color, status); + break; + case FORMAT_INI: + printf("%s = %s\n", service, status); + break; + } + free(status); +} + +static void print_services(const char *runlevel, RC_STRINGLIST *svcs, + enum format_t format) +{ + RC_STRINGLIST *l = NULL; + RC_STRING *s; + char *r = NULL; + + if (!svcs) + return; + if (!deptree) + deptree = _rc_deptree_load(0, NULL); + if (!deptree) { + TAILQ_FOREACH(s, svcs, entries) + if (!runlevel || + rc_service_in_runlevel(s->value, runlevel)) + print_service(s->value, format); + return; + } + if (!types) { + types = rc_stringlist_new(); + rc_stringlist_add(types, "ineed"); + rc_stringlist_add(types, "iuse"); + rc_stringlist_add(types, "iafter"); + } + if (!runlevel) + r = rc_runlevel_get(); + l = rc_deptree_depends(deptree, types, svcs, r ? r : runlevel, + RC_DEP_STRICT | RC_DEP_TRACE | RC_DEP_START); + free(r); + if (!l) + return; + TAILQ_FOREACH(s, l, entries) { + if (!rc_stringlist_find(svcs, s->value)) + continue; + if (!runlevel || rc_service_in_runlevel(s->value, runlevel)) + print_service(s->value, format); + } + rc_stringlist_free(l); +} + +static void print_stacked_services(const char *runlevel, enum format_t format) +{ + 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, format); + servicelist = rc_services_in_runlevel(stackedlevel->value); + print_services(stackedlevel->value, servicelist, format); + rc_stringlist_free(servicelist); + } + rc_stringlist_free(stackedlevels); + stackedlevels = NULL; +} + +int main(int argc, char **argv) +{ + RC_SERVICE state; + RC_STRING *s, *l, *t, *level; + enum format_t format = FORMAT_DEFAULT; + bool levels_given = false; + bool show_all = false; + char *p, *runlevel = NULL; + int opt, retval = 0; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, longopts, + (int *) 0)) != -1) + switch (opt) { + case 'a': + show_all = true; + levels = rc_runlevel_list(); + break; + case 'c': + services = rc_services_in_state(RC_SERVICE_STARTED); + retval = 1; + TAILQ_FOREACH(s, services, entries) + if (rc_service_daemons_crashed(s->value)) { + printf("%s\n", s->value); + retval = 0; + } + goto exit; + /* NOTREACHED */ + case 'f': + if (strcasecmp(optarg, "ini") == 0) { + format = FORMAT_INI; + setenv("EINFO_QUIET", "YES", 1); + } else + eerrorx("%s: invalid argument to --format switch\n", applet); + break; + case 'l': + levels = rc_runlevel_list(); + TAILQ_FOREACH(l, levels, entries) + printf("%s\n", l->value); + goto exit; + case 'm': + services = rc_services_in_runlevel(NULL); + levels = rc_runlevel_list(); + TAILQ_FOREACH_SAFE(s, services, entries, t) { + TAILQ_FOREACH(l, levels, entries) + if (rc_service_in_runlevel(s->value, l->value)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + break; + } + } + TAILQ_FOREACH_SAFE(s, services, entries, t) + if (rc_service_state(s->value) & + (RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + } + print_services(NULL, services, FORMAT_DEFAULT); + goto exit; + case 'r': + runlevel = rc_runlevel_get(); + printf("%s\n", runlevel); + goto exit; + /* NOTREACHED */ + case 'S': + services = rc_services_in_state(RC_SERVICE_STARTED); + TAILQ_FOREACH_SAFE(s, services, entries, t) + if (!rc_service_value_get(s->value, "child_pid")) + TAILQ_REMOVE(services, s, entries); + print_services(NULL, services, FORMAT_DEFAULT); + goto exit; + /* NOTREACHED */ + case 's': + services = rc_services_in_runlevel(NULL); + print_services(NULL, services, FORMAT_DEFAULT); + goto exit; + /* NOTREACHED */ + case 'u': + services = rc_services_in_runlevel(NULL); + levels = rc_runlevel_list(); + TAILQ_FOREACH_SAFE(s, services, entries, t) { + TAILQ_FOREACH(l, levels, entries) + if (rc_service_in_runlevel(s->value, l->value)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + break; + } + } + print_services(NULL, services, FORMAT_DEFAULT); + goto exit; + /* NOTREACHED */ + + case_RC_COMMON_GETOPT + } + + if (!levels) + levels = rc_stringlist_new(); + opt = (optind < argc) ? 0 : 1; + while (optind < argc) { + if (rc_runlevel_exists(argv[optind])) { + levels_given = true; + rc_stringlist_add(levels, argv[optind++]); + opt++; + } else + eerror("runlevel `%s' does not exist", argv[optind++]); + } + if (opt == 0) + exit(EXIT_FAILURE); + if (!TAILQ_FIRST(levels)) { + runlevel = rc_runlevel_get(); + rc_stringlist_add(levels, runlevel); + } + + /* Output the services in the order in which they would start */ + deptree = _rc_deptree_load(0, NULL); + + TAILQ_FOREACH(l, levels, entries) { + print_level(NULL, l->value, format); + services = rc_services_in_runlevel(l->value); + print_services(l->value, services, format); + print_stacked_services(l->value, format); + rc_stringlist_free(nservices); + nservices = NULL; + rc_stringlist_free(services); + services = NULL; + } + + if (show_all || !levels_given) { + /* Show hotplugged services */ + print_level("Dynamic", "hotplugged", format); + services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); + print_services(NULL, services, format); + rc_stringlist_free(services); + services = NULL; + + /* Show manually started and unassigned depended services */ + if (show_all) { + rc_stringlist_free(levels); + levels = rc_stringlist_new(); + if (!runlevel) + runlevel = rc_runlevel_get(); + rc_stringlist_add(levels, runlevel); + } + rc_stringlist_add(levels, RC_LEVEL_SYSINIT); + rc_stringlist_add(levels, RC_LEVEL_BOOT); + services = rc_services_in_runlevel(NULL); + sservices = rc_stringlist_new(); + TAILQ_FOREACH(l, levels, entries) { + nservices = rc_services_in_runlevel_stacked(l->value); + TAILQ_CONCAT(sservices, nservices, entries); + free(nservices); + } + TAILQ_FOREACH_SAFE(s, services, entries, t) { + state = rc_service_state(s->value); + if ((rc_stringlist_find(sservices, s->value) || + (state & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) { + if (!(state & RC_SERVICE_FAILED)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + } + } + } + needsme = rc_stringlist_new(); + rc_stringlist_add(needsme, "needsme"); + rc_stringlist_add(needsme, "wantsme"); + nservices = rc_stringlist_new(); + alist = rc_stringlist_new(); + l = rc_stringlist_add(alist, ""); + p = l->value; + TAILQ_FOREACH(level, levels, entries) { + TAILQ_FOREACH_SAFE(s, services, entries, t) { + l->value = s->value; + setenv("RC_SVCNAME", l->value, 1); + tmp = rc_deptree_depends(deptree, needsme, alist, level->value, RC_DEP_TRACE); + if (TAILQ_FIRST(tmp)) { + TAILQ_REMOVE(services, s, entries); + TAILQ_INSERT_TAIL(nservices, s, entries); + } + rc_stringlist_free(tmp); + } + } + l->value = p; + /* + * we are unsetting RC_SVCNAME because last loaded service will not + * be added to the list + */ + unsetenv("RC_SVCNAME"); + print_level("Dynamic", "needed/wanted", format); + print_services(NULL, nservices, format); + print_level("Dynamic", "manual", format); + print_services(NULL, services, format); + } + +exit: + free(runlevel); + rc_stringlist_free(alist); + rc_stringlist_free(needsme); + rc_stringlist_free(sservices); + rc_stringlist_free(nservices); + rc_stringlist_free(services); + rc_stringlist_free(types); + rc_stringlist_free(levels); + rc_deptree_free(deptree); + + return retval; +} -- cgit v1.2.3