From 427a1ce2995b376ed6d112c5c5b422217f815fbb Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Fri, 15 Feb 2019 14:21:43 -0600 Subject: [PATCH] rc-status: add -f option to allow formatting output The -f option can be used when showing the status of services in runlevels to allow making the output more easily parsable. Currently, the .ini format is the only one supported. --- NEWS.md | 5 +++ man/rc-status.8 | 4 ++ src/rc/rc-status.c | 110 +++++++++++++++++++++++++++++---------------- 3 files changed, 81 insertions(+), 38 deletions(-) diff --git a/NEWS.md b/NEWS.md index ffb74f97..a51069c6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,11 @@ the information in this file is in reverse order. ## OpenRC 0.41. +This version adds the ability to format the output of rc-status when +showing the status of services in a runlevel so that it may be parsed. +Currently, the -f switch only accepts ini as an argument which +causes the output to be in the .ini format. + This version adds an experimental build time switch to allow setting the default shell to use for service scripts. By default, this is set to /bin/sh if it is changed, the new shell must diff --git a/man/rc-status.8 b/man/rc-status.8 index 68ecc49a..bfe37c23 100644 --- a/man/rc-status.8 +++ b/man/rc-status.8 @@ -17,6 +17,7 @@ .Sh SYNOPSIS .Nm .Op Fl aclsuC +.Op Fl f Ar ini .Op Ar runlevel .Sh DESCRIPTION .Nm @@ -37,6 +38,9 @@ The options are as follows: Show all runlevels and their services. .It Fl c , -crashed List all services that have crashed. +.It Fl f , -format +Select a format for the output. Currently, the only one that can be +specified is ini, which outputs in *.ini format. .It Fl l , -list List all defined runlevels. .It Fl m , -manual diff --git a/src/rc/rc-status.c b/src/rc/rc-status.c index 5de97148..5051fc0f 100644 --- a/src/rc/rc-status.c +++ b/src/rc/rc-status.c @@ -27,12 +27,18 @@ #include "rc-misc.h" #include "_usage.h" +enum format_t { + FORMAT_DEFAULT, + FORMAT_INI, +}; + const char *applet = NULL; const char *extraopts = NULL; -const char *getoptstring = "aclmrsSu" getoptstring_COMMON; +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'}, @@ -44,6 +50,7 @@ const struct option longopts[] = { 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", @@ -53,7 +60,7 @@ const char * const longopts_help[] = { longopts_help_COMMON }; const char *usagestring = "" \ - "Usage: rc-status [options] ...\n" \ + "Usage: rc-status [options] -f ini ...\n" \ " or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]"; static RC_DEPTREE *deptree; @@ -62,18 +69,27 @@ 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) +static void print_level(const char *prefix, const char *level, + enum format_t format) { - 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); + 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) @@ -124,13 +140,13 @@ static char *get_uptime(const char *service) return uptime; } -static void print_service(const char *service) +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 = printf(" %s", service); + int cols; const char *c = ecolor(ECOLOR_GOOD); RC_SERVICE state = rc_service_state(service); ECOLOR color = ECOLOR_BAD; @@ -174,13 +190,22 @@ static void print_service(const char *service) xasprintf(&status, " stopped "); errno = 0; - if (c && *c && isatty(fileno(stdout))) - printf("\n"); - ebracket(cols, color, status); + 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) +static void print_services(const char *runlevel, RC_STRINGLIST *svcs, + enum format_t format) { RC_STRINGLIST *l = NULL; RC_STRING *s; @@ -194,7 +219,7 @@ static void print_services(const char *runlevel, RC_STRINGLIST *svcs) TAILQ_FOREACH(s, svcs, entries) if (!runlevel || rc_service_in_runlevel(s->value, runlevel)) - print_service(s->value); + print_service(s->value, format); return; } if (!types) { @@ -214,12 +239,12 @@ static void print_services(const char *runlevel, RC_STRINGLIST *svcs) if (!rc_stringlist_find(svcs, s->value)) continue; if (!runlevel || rc_service_in_runlevel(s->value, runlevel)) - print_service(s->value); + print_service(s->value, format); } rc_stringlist_free(l); } -static void print_stacked_services(const char *runlevel) +static void print_stacked_services(const char *runlevel, enum format_t format) { RC_STRINGLIST *stackedlevels, *servicelist; RC_STRING *stackedlevel; @@ -228,9 +253,9 @@ static void print_stacked_services(const char *runlevel) TAILQ_FOREACH(stackedlevel, stackedlevels, entries) { if (rc_stringlist_find(levels, stackedlevel->value) != NULL) continue; - print_level("Stacked", stackedlevel->value); + print_level("Stacked", stackedlevel->value, format); servicelist = rc_services_in_runlevel(stackedlevel->value); - print_services(stackedlevel->value, servicelist); + print_services(stackedlevel->value, servicelist, format); rc_stringlist_free(servicelist); } rc_stringlist_free(stackedlevels); @@ -241,6 +266,8 @@ 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; @@ -263,6 +290,12 @@ int main(int argc, char **argv) } goto exit; /* NOTREACHED */ + case 'f': + if (strcasecmp(optarg, "ini") == 0) + format = FORMAT_INI; + else + eerrorx("%s: invalid argument to --format switch\n", applet); + break; case 'l': levels = rc_runlevel_list(); TAILQ_FOREACH(l, levels, entries) @@ -287,7 +320,7 @@ int main(int argc, char **argv) free(s->value); free(s); } - print_services(NULL, services); + print_services(NULL, services, FORMAT_DEFAULT); goto exit; case 'r': runlevel = rc_runlevel_get(); @@ -299,12 +332,12 @@ int main(int argc, char **argv) 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); + print_services(NULL, services, FORMAT_DEFAULT); goto exit; /* NOTREACHED */ case 's': services = rc_services_in_runlevel(NULL); - print_services(NULL, services); + print_services(NULL, services, FORMAT_DEFAULT); goto exit; /* NOTREACHED */ case 'u': @@ -319,7 +352,7 @@ int main(int argc, char **argv) break; } } - print_services(NULL, services); + print_services(NULL, services, FORMAT_DEFAULT); goto exit; /* NOTREACHED */ @@ -331,6 +364,7 @@ int main(int argc, char **argv) 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 @@ -347,21 +381,21 @@ int main(int argc, char **argv) deptree = _rc_deptree_load(0, NULL); TAILQ_FOREACH(l, levels, entries) { - print_level(NULL, l->value); + print_level(NULL, l->value, format); services = rc_services_in_runlevel(l->value); - print_services(l->value, services); - print_stacked_services(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 || argc < 2) { + if (show_all || !levels_given) { /* Show hotplugged services */ - print_level("Dynamic", "hotplugged"); + print_level("Dynamic", "hotplugged", format); services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); - print_services(NULL, services); + print_services(NULL, services, format); rc_stringlist_free(services); services = NULL; @@ -418,10 +452,10 @@ int main(int argc, char **argv) * be added to the list */ unsetenv("RC_SVCNAME"); - print_level("Dynamic", "needed/wanted"); - print_services(NULL, nservices); - print_level("Dynamic", "manual"); - print_services(NULL, services); + print_level("Dynamic", "needed/wanted", format); + print_services(NULL, nservices, format); + print_level("Dynamic", "manual", format); + print_services(NULL, services, format); } exit: