cmd: add 'help' subcommand to zpool and zfs

'program help subcommand' is a reasonably common pattern for
multifunction command-line programs. This commit adds support for that
style to the zpool and zfs commands.

When run as 'zpool help [<topic>]' or 'zfs help [<topic>]', executes the
'man' program on the PATH with the most likely manpage name for the
requested topic: "zpool-<topic>" or "zfs-<topic>" for subcommands, or
"zpool<topic>" or "zfs<topic>" for the "concepts" and "props" topics.
If no topic is supplied, uses the top "zpool" or "zfs" pages.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Kay Pedersen <mail@mkwg.de>
Signed-off-by: Rob Norris <robn@despairlabs.com>
Closes #15288
This commit is contained in:
Rob N 2023-09-20 02:06:47 +10:00 committed by Brian Behlendorf
parent 9aa1a2878e
commit 3af63683fe
3 changed files with 64 additions and 1 deletions

View File

@ -132,6 +132,8 @@ static int zfs_do_zone(int argc, char **argv);
static int zfs_do_unzone(int argc, char **argv); static int zfs_do_unzone(int argc, char **argv);
#endif #endif
static int zfs_do_help(int argc, char **argv);
/* /*
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds. * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
*/ */
@ -606,6 +608,9 @@ usage(boolean_t requested)
(void) fprintf(fp, (void) fprintf(fp,
gettext("\nFor the delegated permission list, run: %s\n"), gettext("\nFor the delegated permission list, run: %s\n"),
"zfs allow|unallow"); "zfs allow|unallow");
(void) fprintf(fp,
gettext("\nFor further help on a command or topic, "
"run: %s\n"), "zfs help [<topic>]");
} }
/* /*
@ -8726,6 +8731,25 @@ zfs_do_version(int argc, char **argv)
return (zfs_version_print() != 0); return (zfs_version_print() != 0);
} }
/* Display documentation */
static int
zfs_do_help(int argc, char **argv)
{
char page[MAXNAMELEN];
if (argc < 3 || strcmp(argv[2], "zfs") == 0)
strcpy(page, "zfs");
else if (strcmp(argv[2], "concepts") == 0 ||
strcmp(argv[2], "props") == 0)
snprintf(page, sizeof (page), "zfs%s", argv[2]);
else
snprintf(page, sizeof (page), "zfs-%s", argv[2]);
execlp("man", "man", page, NULL);
fprintf(stderr, "couldn't run man program: %s", strerror(errno));
return (-1);
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -8781,6 +8805,12 @@ main(int argc, char **argv)
if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0)) if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))
return (zfs_do_version(argc, argv)); return (zfs_do_version(argc, argv));
/*
* Special case 'help'
*/
if (strcmp(cmdname, "help") == 0)
return (zfs_do_help(argc, argv));
if ((g_zfs = libzfs_init()) == NULL) { if ((g_zfs = libzfs_init()) == NULL) {
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno)); (void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
return (1); return (1);

View File

@ -126,6 +126,8 @@ static int zpool_do_version(int, char **);
static int zpool_do_wait(int, char **); static int zpool_do_wait(int, char **);
static int zpool_do_help(int argc, char **argv);
static zpool_compat_status_t zpool_do_load_compat( static zpool_compat_status_t zpool_do_load_compat(
const char *, boolean_t *); const char *, boolean_t *);
@ -538,6 +540,10 @@ usage(boolean_t requested)
(void) fprintf(fp, "%s", (void) fprintf(fp, "%s",
get_usage(command_table[i].usage)); get_usage(command_table[i].usage));
} }
(void) fprintf(fp,
gettext("\nFor further help on a command or topic, "
"run: %s\n"), "zpool help [<topic>]");
} else { } else {
(void) fprintf(fp, gettext("usage:\n")); (void) fprintf(fp, gettext("usage:\n"));
(void) fprintf(fp, "%s", get_usage(current_command->usage)); (void) fprintf(fp, "%s", get_usage(current_command->usage));
@ -11051,6 +11057,25 @@ zpool_do_version(int argc, char **argv)
return (zfs_version_print() != 0); return (zfs_version_print() != 0);
} }
/* Display documentation */
static int
zpool_do_help(int argc, char **argv)
{
char page[MAXNAMELEN];
if (argc < 3 || strcmp(argv[2], "zpool") == 0)
strcpy(page, "zpool");
else if (strcmp(argv[2], "concepts") == 0 ||
strcmp(argv[2], "props") == 0)
snprintf(page, sizeof (page), "zpool%s", argv[2]);
else
snprintf(page, sizeof (page), "zpool-%s", argv[2]);
execlp("man", "man", page, NULL);
fprintf(stderr, "couldn't run man program: %s", strerror(errno));
return (-1);
}
/* /*
* Do zpool_load_compat() and print error message on failure * Do zpool_load_compat() and print error message on failure
*/ */
@ -11118,6 +11143,12 @@ main(int argc, char **argv)
if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0)) if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))
return (zpool_do_version(argc, argv)); return (zpool_do_version(argc, argv));
/*
* Special case 'help'
*/
if (strcmp(cmdname, "help") == 0)
return (zpool_do_help(argc, argv));
if ((g_zfs = libzfs_init()) == NULL) { if ((g_zfs = libzfs_init()) == NULL) {
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno)); (void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
return (1); return (1);

View File

@ -117,7 +117,9 @@ usage:
For the property list, run: zfs set|get For the property list, run: zfs set|get
For the delegated permission list, run: zfs allow|unallow") For the delegated permission list, run: zfs allow|unallow
For further help on a command or topic, run: zfs help [<topic>]")
cnt=0 cnt=0
for cmd in ${neg_cmds[@]}; do for cmd in ${neg_cmds[@]}; do
log_mustnot zfs program $cmd $TESTPOOL $TESTZCP $TESTDS 2>&1 log_mustnot zfs program $cmd $TESTPOOL $TESTZCP $TESTDS 2>&1