From b0b19cfc571423eca0694c0da8ed1fe3b875105a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 31 May 2012 04:11:57 +0200 Subject: [PATCH] systemctl: introduce "systemctl man" to show man page for unit For now this only reads man: URLs, but later on we might want to support info: too. http/https is probably out of focus. (cherry picked from commit 256425cc10d74c13602527eb86b4ba0938964565) Conflicts: TODO --- man/systemctl.xml | 8 +++++ src/systemctl/systemctl.c | 79 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 7ae5add..425bfd5 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -633,7 +633,15 @@ looking for formatted human-readable output. + + man [NAME...|PID...] + Show manual pages for + one or more units, if available. If a + PID is passed the manual pages for the + unit the process of the PID belongs to + is shown. + reset-failed [NAME...] diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 7f03a65..377a878 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2449,6 +2449,73 @@ static void print_status_info(UnitStatusInfo *i) { arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); } +static void man_status_info(UnitStatusInfo *i) { + char **p; + + assert(i); + + if (!i->documentation) { + log_info("Documentation for %s not known.", i->id); + return; + } + + STRV_FOREACH(p, i->documentation) { + + if (startswith(*p, "man:")) { + size_t k; + char *e = NULL; + char *page = NULL, *section = NULL; + const char *args[4] = { "man", NULL, NULL, NULL }; + pid_t pid; + + k = strlen(*p); + + if ((*p)[k-1] == ')') + e = strrchr(*p, '('); + + if (e) { + page = strndup((*p) + 4, e - *p - 4); + if (!page) { + log_error("Out of memory."); + return; + } + + section = strndup(e + 1, *p + k - e - 2); + if (!section) { + free(page); + log_error("Out of memory"); + return; + } + + args[1] = section; + args[2] = page; + } else + args[1] = *p + 4; + + pid = fork(); + if (pid < 0) { + log_error("Failed to fork: %m"); + free(page); + free(section); + continue; + } + + if (pid == 0) { + /* Child */ + execvp(args[0], (char**) args); + log_error("Failed to execute man: %m"); + _exit(EXIT_FAILURE); + } + + free(page); + free(section); + + wait_for_terminate(pid, NULL); + } else + log_info("Can't show %s.", *p); + } +} + static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) { assert(name); @@ -2947,8 +3014,12 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo r = 0; - if (!show_properties) - print_status_info(&info); + if (!show_properties) { + if (streq(verb, "man")) + man_status_info(&info); + else + print_status_info(&info); + } strv_free(info.documentation); @@ -3039,7 +3110,7 @@ static int show(DBusConnection *bus, char **args) { assert(bus); assert(args); - show_properties = !streq(args[0], "status"); + show_properties = streq(args[0], "show"); if (show_properties) pager_open_if_enabled(); @@ -4209,6 +4280,7 @@ static int systemctl_help(void) { " status [NAME...|PID...] Show runtime status of one or more units\n" " show [NAME...|JOB...] Show properties of one or more\n" " units/jobs or the manager\n" + " man [NAME...|PID...] Show manual for one or more units\n" " reset-failed [NAME...] Reset failed state for all, one, or more\n" " units\n" " load [NAME...] Load one or more units\n\n" @@ -5172,6 +5244,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "check", MORE, 2, check_unit }, { "show", MORE, 1, show }, { "status", MORE, 2, show }, + { "man", MORE, 2, show }, { "dump", EQUAL, 1, dump }, { "dot", EQUAL, 1, dot }, { "snapshot", LESS, 2, snapshot },