Commit Diff


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[] = {
+	{ "<cr>", "Type Enter to run command", CMPL0 NULL, 0 },
+	{ "<IPv4-address>", "IPv4 address parameter", CMPL0 NULL, 0 },
+	{ "<IPv6-address>", "IPv6 address parameter", CMPL0 NULL, 0 },
+	{ "<hostname>", "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 <millert@openbsd.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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))