From patchwork Thu Jan 28 22:52:18 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 8154811 Return-Path: X-Original-To: patchwork-linux-nvdimm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id EC4A8BEEED for ; Thu, 28 Jan 2016 22:52:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B855520328 for ; Thu, 28 Jan 2016 22:52:53 +0000 (UTC) Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 53CDA202FE for ; Thu, 28 Jan 2016 22:52:52 +0000 (UTC) Received: from ml01.vlan14.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 48E6E1A222A; Thu, 28 Jan 2016 14:52:52 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by ml01.01.org (Postfix) with ESMTP id 16BE91A222A for ; Thu, 28 Jan 2016 14:52:51 -0800 (PST) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga101.fm.intel.com with ESMTP; 28 Jan 2016 14:52:43 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.22,360,1449561600"; d="scan'208";a="900549252" Received: from dwillia2-desk3.jf.intel.com (HELO dwillia2-desk3.amr.corp.intel.com) ([10.54.39.136]) by orsmga002.jf.intel.com with ESMTP; 28 Jan 2016 14:52:43 -0800 Subject: [ndctl PATCH 04/13] ndctl: help command From: Dan Williams To: linux-nvdimm@lists.01.org Date: Thu, 28 Jan 2016 14:52:18 -0800 Message-ID: <20160128225218.17855.19239.stgit@dwillia2-desk3.amr.corp.intel.com> In-Reply-To: <20160128225157.17855.5190.stgit@dwillia2-desk3.amr.corp.intel.com> References: <20160128225157.17855.5190.stgit@dwillia2-desk3.amr.corp.intel.com> User-Agent: StGit/0.17.1-9-g687f MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Route "ndctl --help" to "man ndctl-" Signed-off-by: Dan Williams --- Makefile.am | 3 + builtin-help.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 26 +++++++++ ndctl.c | 81 +++++++++------------------- 4 files changed, 217 insertions(+), 56 deletions(-) create mode 100644 builtin-help.c create mode 100644 builtin.h diff --git a/Makefile.am b/Makefile.am index d768de1492cd..ffbbd044050f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,8 @@ AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + -DNDCTL_MAN_PATH=\""$(mandir)"\" \ -I${top_srcdir}/lib/ndctl \ -I${top_srcdir}/lib \ -I${top_srcdir}/ \ @@ -65,6 +67,7 @@ ndctl_SOURCES = ndctl.c \ builtin-xable-namespace.c \ builtin-xable-region.c \ builtin-test.c \ + builtin-help.c \ builtin-zero-labels.c \ util/parse-options.c \ util/parse-options.h \ diff --git a/builtin-help.c b/builtin-help.c new file mode 100644 index 000000000000..55d3114c4bf8 --- /dev/null +++ b/builtin-help.c @@ -0,0 +1,163 @@ +/* + * builtin-help.c + * + * Builtin help command + */ +#include +#include +#include +#include +#include +#include +#include + +#define pr_err(x, ...) fprintf(stderr, x, ##__VA_ARGS__) +#define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */ + +static void exec_man_konqueror(const char *path, const char *page) +{ + const char *display = getenv("DISPLAY"); + + if (display && *display) { + struct strbuf man_page = STRBUF_INIT; + const char *filename = "kfmclient"; + char sbuf[STRERR_BUFSIZE]; + + /* It's simpler to launch konqueror using kfmclient. */ + if (path) { + const char *file = strrchr(path, '/'); + if (file && !strcmp(file + 1, "konqueror")) { + char *new = strdup(path); + char *dest = strrchr(new, '/'); + + /* strlen("konqueror") == strlen("kfmclient") */ + strcpy(dest + 1, "kfmclient"); + path = new; + } + if (file) + filename = file; + } else + path = "kfmclient"; + strbuf_addf(&man_page, "man:%s(1)", page); + execlp(path, filename, "newTab", man_page.buf, NULL); + warning("failed to exec '%s': %s", path, + strerror_r(errno, sbuf, sizeof(sbuf))); + } +} + +static void exec_man_man(const char *path, const char *page) +{ + char sbuf[STRERR_BUFSIZE]; + + if (!path) + path = "man"; + execlp(path, "man", page, NULL); + warning("failed to exec '%s': %s", path, + strerror_r(errno, sbuf, sizeof(sbuf))); +} + +static char *cmd_to_page(const char *ndctl_cmd, char **page) +{ + int rc; + + if (!ndctl_cmd) + rc = asprintf(page, "ndctl"); + else if (!prefixcmp(ndctl_cmd, "ndctl")) + rc = asprintf(page, "%s", ndctl_cmd); + else + rc = asprintf(page, "ndctl-%s", ndctl_cmd); + + if (rc < 0) + return NULL; + return *page; +} + +static int is_absolute_path(const char *path) +{ + return path[0] == '/'; +} + +static const char *system_path(const char *path) +{ + static const char *prefix = PREFIX; + struct strbuf d = STRBUF_INIT; + + if (is_absolute_path(path)) + return path; + + strbuf_addf(&d, "%s/%s", prefix, path); + path = strbuf_detach(&d, NULL); + return path; +} + +static void setup_man_path(void) +{ + struct strbuf new_path = STRBUF_INIT; + const char *old_path = getenv("MANPATH"); + + /* We should always put ':' after our path. If there is no + * old_path, the ':' at the end will let 'man' to try + * system-wide paths after ours to find the manual page. If + * there is old_path, we need ':' as delimiter. */ + strbuf_addstr(&new_path, system_path(NDCTL_MAN_PATH)); + strbuf_addch(&new_path, ':'); + if (old_path) + strbuf_addstr(&new_path, old_path); + + setenv("MANPATH", new_path.buf, 1); + + strbuf_release(&new_path); +} + +static void exec_viewer(const char *name, const char *page) +{ + if (!strcasecmp(name, "man")) + exec_man_man(NULL, page); + else if (!strcasecmp(name, "konqueror")) + exec_man_konqueror(NULL, page); + else + warning("'%s': unknown man viewer.", name); +} + +static int show_man_page(const char *ndctl_cmd) +{ + const char *fallback = getenv("NDCTL_MAN_VIEWER"); + char *page; + + page = cmd_to_page(ndctl_cmd, &page); + if (!page) + return -1; + setup_man_path(); + if (fallback) + exec_viewer(fallback, page); + exec_viewer("man", page); + + pr_err("no man viewer handled the request"); + free(page); + return -1; +} + +int cmd_help(int argc, const char **argv) +{ + const char * const builtin_help_subcommands[] = { + "enable-region", "disable-region", "zero-labels", + "enable-namespace", "disable-namespace", NULL }; + struct option builtin_help_options[] = { + OPT_END(), + }; + const char *builtin_help_usage[] = { + "ndctl help [command]", + NULL + }; + + argc = parse_options_subcommand(argc, argv, builtin_help_options, + builtin_help_subcommands, builtin_help_usage, 0); + + if (!argv[0]) { + printf("\n usage: %s\n\n", ndctl_usage_string); + printf("\n %s\n\n", ndctl_more_info_string); + return 0; + } + + return show_man_page(argv[0]); +} diff --git a/builtin.h b/builtin.h new file mode 100644 index 000000000000..ed0b7e497a43 --- /dev/null +++ b/builtin.h @@ -0,0 +1,26 @@ +#ifndef _NDCTL_BUILTIN_H_ +#define _NDCTL_BUILTIN_H_ +extern const char ndctl_usage_string[]; +extern const char ndctl_more_info_string[]; + +struct cmd_struct { + const char *cmd; + int (*fn)(int, const char **); +}; + +int cmd_create_nfit(int argc, const char **argv); +int cmd_enable_namespace(int argc, const char **argv); +int cmd_disable_namespace(int argc, const char **argv); +int cmd_enable_region(int argc, const char **argv); +int cmd_disable_region(int argc, const char **argv); +int cmd_zero_labels(int argc, const char **argv); +int cmd_help(int argc, const char **argv); +#ifdef ENABLE_TEST +int cmd_test(int argc, const char **argv); +#endif +#ifdef ENABLE_DESTRUCTIVE +int cmd_bat(int argc, const char **argv); +#endif + +#endif /* _NDCTL_BUILTIN_H_ */ + diff --git a/ndctl.c b/ndctl.c index 242832af8249..f4e2746f85e3 100644 --- a/ndctl.c +++ b/ndctl.c @@ -5,46 +5,22 @@ #include #include #include +#include #include #include #include -static const char *ndctl_usage_string; - -static const char ndctl_more_info_string[] = +const char ndctl_usage_string[] = "ndctl [--version] [--help] COMMAND [ARGS]"; +const char ndctl_more_info_string[] = "See 'ndctl help COMMAND' for more information on a specific command."; -struct cmd_struct { - const char *cmd; - int (*fn)(int, const char **); -}; - static int cmd_version(int argc, const char **argv) { printf("%s\n", VERSION); return 0; } -static int cmd_help(int argc, const char **argv) -{ - printf("\n%s\n\n", ndctl_usage_string); - return 0; -} - -int cmd_create_nfit(int argc, const char **argv); -int cmd_enable_namespace(int argc, const char **argv); -int cmd_disable_namespace(int argc, const char **argv); -int cmd_enable_region(int argc, const char **argv); -int cmd_disable_region(int argc, const char **argv); -int cmd_zero_labels(int argc, const char **argv); -#ifdef ENABLE_TEST -int cmd_test(int argc, const char **argv); -#endif -#ifdef ENABLE_DESTRUCTIVE -int cmd_bat(int argc, const char **argv); -#endif - static struct cmd_struct commands[] = { { "version", cmd_version }, { "create-nfit", cmd_create_nfit }, @@ -53,6 +29,7 @@ static struct cmd_struct commands[] = { { "enable-region", cmd_enable_region }, { "disable-region", cmd_disable_region }, { "zero-labels", cmd_zero_labels }, + { "help", cmd_help }, #ifdef ENABLE_TEST { "test", cmd_test }, #endif @@ -61,29 +38,6 @@ static struct cmd_struct commands[] = { #endif }; -/* place holder until help system is implemented */ -static char *init_usage_string(void) -{ - char *def = "ndctl [--version] [--help] COMMAND [ARGS]"; - unsigned int len = strlen(def) + 1, i, p; - char *u; - - for (i = 0; i < ARRAY_SIZE(commands); i++) - len += strlen(commands[i].cmd) + 2; - u = calloc(1, len); - if (!u) - return def; - - p = sprintf(u, "%s", "ndctl [--version] [--help] "); - for (i = 0; i < ARRAY_SIZE(commands); i++) { - p += sprintf(&u[p], "%s", commands[i].cmd); - if ((i + 1) < ARRAY_SIZE(commands)) - p += sprintf(&u[p], "|"); - } - p = sprintf(&u[p], "%s", " [ARGS]"); - return u; -} - static int handle_options(const char ***argv, int *argc) { int handled = 0; @@ -93,11 +47,22 @@ static int handle_options(const char ***argv, int *argc) if (cmd[0] != '-') break; - if (!strcmp(cmd, "--help")) - exit(cmd_help(*argc, *argv)); + if (!strcmp(cmd, "--version") || !strcmp(cmd, "--help")) + break; - if (!strcmp(cmd, "--version")) + /* + * Shortcut for '-h' and '-v' options to invoke help + * and version command. + */ + if (!strcmp(cmd, "-h")) { + (*argv)[0] = "--help"; break; + } + + if (!strcmp(cmd, "-v")) { + (*argv)[0] = "--version"; + break; + } if (!strcmp(cmd, "--list-cmds")) { unsigned int i; @@ -160,6 +125,12 @@ static void handle_internal_command(int argc, const char **argv) const char *cmd = argv[0]; unsigned int i; + /* Turn "ndctl cmd --help" into "ndctl help cmd" */ + if (argc > 1 && !strcmp(argv[1], "--help")) { + argv[1] = argv[0]; + argv[0] = cmd = "help"; + } + for (i = 0; i < ARRAY_SIZE(commands); i++) { struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) @@ -170,8 +141,6 @@ static void handle_internal_command(int argc, const char **argv) int main(int argc, const char **argv) { - ndctl_usage_string = init_usage_string(); - /* Look for flags.. */ argv++; argc--; @@ -183,7 +152,7 @@ int main(int argc, const char **argv) } else { /* The user didn't specify a command; give them help */ printf("\n usage: %s\n\n", ndctl_usage_string); - /* printf("\n %s\n\n", ndctl_more_info_string); TODO */ + printf("\n %s\n\n", ndctl_more_info_string); goto out; } handle_internal_command(argc, argv);