commit bd030151f607f80ab726bd272be914feda51c4a5 from: Stefan Sperling date: Fri Oct 25 08:07:41 2024 UTC introduce support for running externals commands installed from packages nsh can now support a set of known external commands installed from packages. The commands will only be offered while the corresponding package is installed, and nsh knows how to complete arguments for such commands, how to edit their configurations files, etc. commit - 15a2fabe4d49f7c280b90dcce141c15804dbc37f commit + bd030151f607f80ab726bd272be914feda51c4a5 blob - 0ab38bf23486728192a964f13a9d9d33e842b254 blob + 60a25858fc624dd0a79a85c65b918cdbf21efa9c --- Makefile +++ Makefile @@ -29,7 +29,7 @@ SRCS+=ctl.c show.c if.c version.c route.c conf.c compl SRCS+=bridge.c tunnel.c media.c sysctl.c passwd.c pfsync.c carp.c SRCS+=trunk.c who.c more.c stringlist.c utils.c sqlite3.c ppp.c prompt.c SRCS+=nopt.c pflow.c wg.c nameserver.c ndp.c umb.c utf8.c cmdargs.c ctlargs.c -SRCS+=helpcommands.c makeargv.c hashtable.c mantab.c +SRCS+=helpcommands.c makeargv.c hashtable.c mantab.c findprog.c CLEANFILES+=compile.c mantab.c LDADD=-lutil -ledit -ltermcap blob - 2a3c298673f93f97a419f91e4124069c9e698f95 blob + bcf87e1d7524857c6f43950fbea021e464704975 --- bgpnsh/bgpnsh.c +++ bgpnsh/bgpnsh.c @@ -124,6 +124,25 @@ Command cmdtab[] = { }; size_t cmdtab_nitems = nitems(cmdtab); +Command extcmdtab[] = { + { NULL, NULL, CMPL0 NULL, 0, NULL, 0, 0, 0, 0 } +}; +size_t extcmdtab_nitems = nitems(cmdtab); + +/* satisify the linker */ +int +findprog(char *prog, char *path, char *filename, size_t filename_size) +{ + return 0; +} + +/* satisify the linker */ +struct cmd * +get_cmdtable(void) +{ + return cmdtab; +} + Command * getcmd(char *name) { blob - 715e9630c77e4973a5922efc12dba14edc052442 blob + 7fccb8d5190cff6eab599c628ef2f93fd8662758 --- commands.c +++ commands.c @@ -2044,15 +2044,152 @@ static Command cmdtab2[] = { { "su", enablehelp, CMPL(ta) (char **)enabletab, sizeof(Menu), enable, 0, 0, 0, 0 }, { 0, 0, CMPL0 0, 0, 0, 0, 0, 0, 0 } }; + +/* + * These commands require externals tools from packages and only appear in + * completion lists if the corresponding package is installed. + */ +static const char \ + mtrhelp[] = "network diagnostic tool, similar to ping and traceroute"; + +struct ghs mtrtab[] = { + { "", "Type Enter to run command", CMPL0 NULL, 0 }, + { "", "IPv4 address parameter", CMPL0 NULL, 0 }, + { "", "IPv6 address parameter", CMPL0 NULL, 0 }, + { "", "hostname parameter", CMPL0 NULL, 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +static int +extcmd(int argc, char **argv) +{ + char prog[PATH_MAX]; + + if (!findprog(argv[0], getenv("PATH"), prog, sizeof(prog))) { + printf("%% command not found: %s\n", argv[0]); + return 1; + } + + cmdargs(prog, argv); +} + +Command extcmdtab[] = { + { "mtr", mtrhelp, CMPL(h) (char **)mtrtab, sizeof(struct ghs), extcmd, 0, 0, 0, 0 }, + { NULL, NULL, CMPL0 NULL, 0, NULL, 0, 0, 0, 0 } +}; +size_t extcmdtab_nitems = nitems(extcmdtab); + +static size_t +count_extcmds(void) +{ + struct cmd *c; + int i; + size_t count = 0; + char prog[PATH_MAX]; + + for (i = 0; i < extcmdtab_nitems - 1; i++) { + c = &extcmdtab[i]; + if (findprog(c->name, getenv("PATH"), prog, sizeof(prog))) + count++; + } + + return count; +} + +/* Return a command table with available external commands merged in. */ +struct cmd * +get_cmdtable(void) +{ + static struct cmd *table; + static size_t cmd_count_cur; + size_t extcmd_count = count_extcmds(); + size_t total_cmd_count = cmdtab_nitems - 1 + extcmd_count; + int i, j; + + if (table && cmd_count_cur == total_cmd_count) + return table; /* cached table is still valid */ + + table = reallocarray(table, total_cmd_count + 1, sizeof(*table)); + if (table == NULL) { + printf("%% reallocarray: %s", strerror(errno)); + return cmdtab; /* fall back on not using external commands */ + } + cmd_count_cur = total_cmd_count; + + /* Copy all standard commands into table, excluding sentinel. */ + for (i = 0; i < cmdtab_nitems - 1; i++) { + struct cmd *c = &cmdtab[i]; + table[i] = *c; + } + + /* Add external commands. */ + for (i = 0, j = 0; i < extcmdtab_nitems - 1 && j < extcmd_count; i++) { + struct cmd *c = &extcmdtab[i]; + char prog[PATH_MAX]; + if (findprog(c->name, getenv("PATH"), prog, sizeof(prog))) { + table[cmdtab_nitems - 1 + j] = *c; + j++; + } + } + + /* Add sentinel. */ + memset(&table[total_cmd_count], 0, sizeof(*table)); + + return table; +} + +struct pkgname { + const char *progname; + const char *pkgname; +} pkgnames[] = { + { "mtr", "mtr" }, + { NULL, NULL } +}; + +static const char * +findpkgname(const char *progname) +{ + struct pkgname *p = &pkgnames[0]; + while (p->progname) { + if (strcmp(progname, p->progname) == 0) + return p->pkgname; + p++; + } + + return NULL; +} + Command * getcmd(char *name) { Command *cm; + const char *pkgname; - if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command)))) + cm = (Command *)genget(name, (char **)cmdtab, sizeof(*cm)); + if (cm) return cm; - return (Command *) genget(name, (char **) cmdtab2, sizeof(Command)); + + cm = (Command *)genget(name, (char **)cmdtab2, sizeof(*cm)); + if (cm) + return cm; + + /* extcmdtab should always be checked last */ + cm = (Command *)genget(name, (char **)extcmdtab, sizeof(*cm)); + if (cm) { + char prog[PATH_MAX]; + + if (findprog(cm->name, getenv("PATH"), prog, sizeof(prog))) + return cm; + + pkgname = findpkgname(cm->name); + printf("%% external command %s not found%s%s\n", cm->name, + pkgname ? "; install the package with: pkg_add " : "", + pkgname ? pkgname : ""); + + } + + return NULL; } void blob - 0131d0c2dc2b29c0da7575923f5f205a8868bf19 blob + 36506a8866715dff35945fb82608807533b73dc3 --- complete.c +++ complete.c @@ -346,7 +346,8 @@ complt_i(EditLine *el, int ch) unsigned char complt_c(EditLine *el, int ch) { - return(complete(el, (char **)cmdtab, sizeof(struct cmd))); + struct cmd *table = get_cmdtable(); + return complete(el, (char **)table, sizeof(struct cmd)); } unsigned char blob - 3eed39cfe7a6df63939115a4588ca1b1a515f480 blob + 9333644c81b3fde83a680a6284c081f645b09313 --- externs.h +++ externs.h @@ -196,6 +196,7 @@ void sigalarm(int); void command(void); int argvtostring(int, char **, char *, int); int cmdrc(char rcname[FILENAME_MAX]); +struct cmd *get_cmdtable(void); /* prompt.c */ char *iprompt(void); @@ -262,6 +263,8 @@ extern struct intlist Bridgelist[]; extern struct intlist *whichlist; extern size_t Intlist_nitems; extern size_t Bridgelist_nitems; +extern Command extcmdtab[]; +extern size_t extcmdtab_nitems; /* ieee80211.c */ #define NWID 0 @@ -593,3 +596,9 @@ int hashtable_foreach(struct hashtable *, int (*cb)(void *, size_t, void *, size_t, void *), void *cb_arg); int hashtable_num_entries(struct hashtable *); + +/* findprog.c */ +int findprog(char *, char *, char *, size_t); + +/* extcommands.c */ +int cmdmtr(int argc, char **argv); blob - /dev/null blob + 67c4bb0e82f07fcb5781a38e5c14659f5aeb3732 (mode 644) --- /dev/null +++ findprog.c @@ -0,0 +1,98 @@ +/* $OpenBSD: which.c,v 1.27 2019/01/25 00:19:27 millert Exp $ */ + +/* + * Copyright (c) 1997 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "externs.h" + +char *default_path = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:" + "/usr/local/bin:/usr/local/sbin:/usr/games"; + +int +findprog(char *prog, char *path, char *filename, size_t filename_size) +{ + char *p, *mypath; + int len, rval = 0; + struct stat sbuf; + char *pathcpy; + + if (filename_size < PATH_MAX) { + printf("%% internal error: findprog buffer too small\n"); + return (0); + } + + /* Special case if prog contains '/' */ + if (strchr(prog, '/')) { + if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && + access(prog, X_OK) == 0) { + if (strlcpy(filename, prog, filename_size) >= + filename_size) { + printf("%% program path too long: %s\n", + filename); + return (0); + } + return (1); + } else { + return (0); + } + } + + mypath = strdup(path ? path : default_path); + if (mypath == NULL) { + printf("%% strdup: %s", strerror(errno)); + return (0); + } + pathcpy = mypath; + + while ((p = strsep(&pathcpy, ":")) != NULL) { + if (*p == '\0') + p = "."; + + len = strlen(p); + while (len > 0 && p[len-1] == '/') + p[--len] = '\0'; /* strip trailing '/' */ + + len = snprintf(filename, filename_size, "%s/%s", p, prog); + if (len < 0) { + printf("%% snprintf: %s\n", strerror(errno)); + break; + } + if (len >= filename_size) { + printf("%% program path too long: %s\n", filename); + break; + } + if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && + access(filename, X_OK) == 0) { + rval = 1; + break; + } + } + free(mypath); + + return (rval); +} blob - 9564ad11cfdf44c8f81f31ff2f0d694bd6248954 blob + c3eb197920e31efc9b9328a7b69a3702b0401c07 --- helpcommands.c +++ helpcommands.c @@ -79,7 +79,7 @@ gen_help(char **x, char *cmdprefix, char *descrsuffix, int help(int argc, char **argv) { - Command *c; + Command *c, *table = get_cmdtable(); if (argc == 1) { u_int z = 0; @@ -87,11 +87,11 @@ help(int argc, char **argv) printf("%% Commands may be abbreviated.\n"); printf("%% Commands are:\n\n"); - for (c = cmdtab; c->name; c++) + for (c = table; c->name; c++) if (((c->needpriv && priv) || !c->needpriv) && strlen(c->name) > z) z = strlen(c->name); - for (c = cmdtab; c->name; c++) { + for (c = table; c->name; c++) { if (c->help && ((c->needpriv && priv) || !c->needpriv) && ((c->needconfig && config_mode) || !c->needconfig))