xref: /f-stack/tools/ngctl/main.c (revision d4a07e70)
13b2bd0f6Slogwang 
23b2bd0f6Slogwang /*
33b2bd0f6Slogwang  * main.c
43b2bd0f6Slogwang  *
53b2bd0f6Slogwang  * Copyright (c) 1996-1999 Whistle Communications, Inc.
63b2bd0f6Slogwang  * All rights reserved.
73b2bd0f6Slogwang  *
83b2bd0f6Slogwang  * Subject to the following obligations and disclaimer of warranty, use and
93b2bd0f6Slogwang  * redistribution of this software, in source or object code forms, with or
103b2bd0f6Slogwang  * without modifications are expressly permitted by Whistle Communications;
113b2bd0f6Slogwang  * provided, however, that:
123b2bd0f6Slogwang  * 1. Any and all reproductions of the source or object code must include the
133b2bd0f6Slogwang  *    copyright notice above and the following disclaimer of warranties; and
143b2bd0f6Slogwang  * 2. No rights are granted, in any manner or form, to use Whistle
153b2bd0f6Slogwang  *    Communications, Inc. trademarks, including the mark "WHISTLE
163b2bd0f6Slogwang  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
173b2bd0f6Slogwang  *    such appears in the above copyright notice or in the software.
183b2bd0f6Slogwang  *
193b2bd0f6Slogwang  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
203b2bd0f6Slogwang  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
213b2bd0f6Slogwang  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
223b2bd0f6Slogwang  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
233b2bd0f6Slogwang  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
243b2bd0f6Slogwang  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
253b2bd0f6Slogwang  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
263b2bd0f6Slogwang  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
273b2bd0f6Slogwang  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
283b2bd0f6Slogwang  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
293b2bd0f6Slogwang  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
303b2bd0f6Slogwang  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
313b2bd0f6Slogwang  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
323b2bd0f6Slogwang  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
333b2bd0f6Slogwang  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
343b2bd0f6Slogwang  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
353b2bd0f6Slogwang  * OF SUCH DAMAGE.
363b2bd0f6Slogwang  *
373b2bd0f6Slogwang  * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $
383b2bd0f6Slogwang  */
393b2bd0f6Slogwang 
403b2bd0f6Slogwang #include <sys/cdefs.h>
413b2bd0f6Slogwang __FBSDID("$FreeBSD$");
423b2bd0f6Slogwang 
433b2bd0f6Slogwang #include <sys/param.h>
443b2bd0f6Slogwang #include <sys/socket.h>
453b2bd0f6Slogwang #include <sys/select.h>
463b2bd0f6Slogwang 
473b2bd0f6Slogwang #include <ctype.h>
483b2bd0f6Slogwang #include <err.h>
493b2bd0f6Slogwang #include <errno.h>
503b2bd0f6Slogwang #include <limits.h>
513b2bd0f6Slogwang #include <stdio.h>
523b2bd0f6Slogwang #include <stdlib.h>
533b2bd0f6Slogwang #include <string.h>
543b2bd0f6Slogwang #include <sysexits.h>
553b2bd0f6Slogwang #include <unistd.h>
563b2bd0f6Slogwang #ifdef EDITLINE
573b2bd0f6Slogwang #include <signal.h>
583b2bd0f6Slogwang #include <histedit.h>
593b2bd0f6Slogwang #include <pthread.h>
603b2bd0f6Slogwang #endif
613b2bd0f6Slogwang 
623b2bd0f6Slogwang #include <netgraph.h>
633b2bd0f6Slogwang 
643b2bd0f6Slogwang #include "ngctl.h"
653b2bd0f6Slogwang 
66*d4a07e70Sfengbojiang #ifdef FSTACK
67*d4a07e70Sfengbojiang #include "ff_ipc.h"
68*d4a07e70Sfengbojiang #endif
69*d4a07e70Sfengbojiang 
703b2bd0f6Slogwang #define PROMPT			"+ "
713b2bd0f6Slogwang #define MAX_ARGS		512
723b2bd0f6Slogwang #define WHITESPACE		" \t\r\n\v\f"
733b2bd0f6Slogwang #define DUMP_BYTES_PER_LINE	16
743b2bd0f6Slogwang 
753b2bd0f6Slogwang /* Internal functions */
763b2bd0f6Slogwang static int	ReadFile(FILE *fp);
77*d4a07e70Sfengbojiang #ifndef FSTACK
783b2bd0f6Slogwang static void	ReadSockets(fd_set *);
79*d4a07e70Sfengbojiang #endif
803b2bd0f6Slogwang static int	DoParseCommand(const char *line);
813b2bd0f6Slogwang static int	DoCommand(int ac, char **av);
823b2bd0f6Slogwang static int	DoInteractive(void);
833b2bd0f6Slogwang static const	struct ngcmd *FindCommand(const char *string);
843b2bd0f6Slogwang static int	MatchCommand(const struct ngcmd *cmd, const char *s);
853b2bd0f6Slogwang static void	Usage(const char *msg);
863b2bd0f6Slogwang static int	ReadCmd(int ac, char **av);
873b2bd0f6Slogwang static int	HelpCmd(int ac, char **av);
883b2bd0f6Slogwang static int	QuitCmd(int ac, char **av);
893b2bd0f6Slogwang #ifdef EDITLINE
90*d4a07e70Sfengbojiang #ifndef FSTACK
913b2bd0f6Slogwang static volatile sig_atomic_t unblock;
923b2bd0f6Slogwang static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
933b2bd0f6Slogwang static pthread_cond_t	cond = PTHREAD_COND_INITIALIZER;
943b2bd0f6Slogwang #endif
95*d4a07e70Sfengbojiang #endif
963b2bd0f6Slogwang 
973b2bd0f6Slogwang /* List of commands */
983b2bd0f6Slogwang static const struct ngcmd *const cmds[] = {
993b2bd0f6Slogwang 	&config_cmd,
1003b2bd0f6Slogwang 	&connect_cmd,
1013b2bd0f6Slogwang 	&debug_cmd,
1023b2bd0f6Slogwang 	&dot_cmd,
1033b2bd0f6Slogwang 	&help_cmd,
1043b2bd0f6Slogwang 	&list_cmd,
1053b2bd0f6Slogwang 	&mkpeer_cmd,
1063b2bd0f6Slogwang 	&msg_cmd,
1073b2bd0f6Slogwang 	&name_cmd,
1083b2bd0f6Slogwang 	&read_cmd,
1093b2bd0f6Slogwang 	&rmhook_cmd,
1103b2bd0f6Slogwang 	&show_cmd,
1113b2bd0f6Slogwang 	&shutdown_cmd,
1123b2bd0f6Slogwang 	&status_cmd,
1133b2bd0f6Slogwang 	&types_cmd,
1143b2bd0f6Slogwang 	&write_cmd,
1153b2bd0f6Slogwang 	&quit_cmd,
1163b2bd0f6Slogwang 	NULL
1173b2bd0f6Slogwang };
1183b2bd0f6Slogwang 
1193b2bd0f6Slogwang /* Commands defined in this file */
1203b2bd0f6Slogwang const struct ngcmd read_cmd = {
1213b2bd0f6Slogwang 	ReadCmd,
1223b2bd0f6Slogwang 	"read <filename>",
1233b2bd0f6Slogwang 	"Read and execute commands from a file",
1243b2bd0f6Slogwang 	NULL,
1253b2bd0f6Slogwang 	{ "source", "." }
1263b2bd0f6Slogwang };
1273b2bd0f6Slogwang const struct ngcmd help_cmd = {
1283b2bd0f6Slogwang 	HelpCmd,
1293b2bd0f6Slogwang 	"help [command]",
1303b2bd0f6Slogwang 	"Show command summary or get more help on a specific command",
1313b2bd0f6Slogwang 	NULL,
1323b2bd0f6Slogwang 	{ "?" }
1333b2bd0f6Slogwang };
1343b2bd0f6Slogwang const struct ngcmd quit_cmd = {
1353b2bd0f6Slogwang 	QuitCmd,
1363b2bd0f6Slogwang 	"quit",
1373b2bd0f6Slogwang 	"Exit program",
1383b2bd0f6Slogwang 	NULL,
1393b2bd0f6Slogwang 	{ "exit" }
1403b2bd0f6Slogwang };
1413b2bd0f6Slogwang 
1423b2bd0f6Slogwang /* Our control and data sockets */
143*d4a07e70Sfengbojiang #ifndef FSTACK
1443b2bd0f6Slogwang int	csock, dsock;
145*d4a07e70Sfengbojiang #else
146*d4a07e70Sfengbojiang int csock = -1, dsock = -1;
147*d4a07e70Sfengbojiang 
148*d4a07e70Sfengbojiang void
close_ng_socks()149*d4a07e70Sfengbojiang __attribute__((destructor)) close_ng_socks()
150*d4a07e70Sfengbojiang {
151*d4a07e70Sfengbojiang 	if (csock >= 0) {
152*d4a07e70Sfengbojiang 		ng_close(csock);
153*d4a07e70Sfengbojiang 	}
154*d4a07e70Sfengbojiang 
155*d4a07e70Sfengbojiang 	if (dsock >= 0) {
156*d4a07e70Sfengbojiang 		ng_close(dsock);
157*d4a07e70Sfengbojiang 	}
158*d4a07e70Sfengbojiang }
159*d4a07e70Sfengbojiang #endif
1603b2bd0f6Slogwang 
1613b2bd0f6Slogwang /*
1623b2bd0f6Slogwang  * main()
1633b2bd0f6Slogwang  */
1643b2bd0f6Slogwang int
main(int ac,char * av[])1653b2bd0f6Slogwang main(int ac, char *av[])
1663b2bd0f6Slogwang {
1673b2bd0f6Slogwang 	char	name[NG_NODESIZ];
1683b2bd0f6Slogwang 	int	interactive = isatty(0) && isatty(1);
1693b2bd0f6Slogwang 	FILE	*fp = NULL;
1703b2bd0f6Slogwang 	int	ch, rtn = 0;
1713b2bd0f6Slogwang 
1723b2bd0f6Slogwang 	/* Set default node name */
1733b2bd0f6Slogwang 	snprintf(name, sizeof(name), "ngctl%d", getpid());
1743b2bd0f6Slogwang 
1753b2bd0f6Slogwang 	/* Parse command line */
176*d4a07e70Sfengbojiang #ifndef FSTACK
1773b2bd0f6Slogwang 	while ((ch = getopt(ac, av, "df:n:")) != -1) {
178*d4a07e70Sfengbojiang #else
179*d4a07e70Sfengbojiang 	ff_ipc_init();
180*d4a07e70Sfengbojiang 	while ((ch = getopt(ac, av, "df:n:p:")) != -1) {
181*d4a07e70Sfengbojiang #endif
1823b2bd0f6Slogwang 		switch (ch) {
1833b2bd0f6Slogwang 		case 'd':
1843b2bd0f6Slogwang 			NgSetDebug(NgSetDebug(-1) + 1);
1853b2bd0f6Slogwang 			break;
1863b2bd0f6Slogwang 		case 'f':
1873b2bd0f6Slogwang 			if (strcmp(optarg, "-") == 0)
1883b2bd0f6Slogwang 				fp = stdin;
1893b2bd0f6Slogwang 			else if ((fp = fopen(optarg, "r")) == NULL)
1903b2bd0f6Slogwang 				err(EX_NOINPUT, "%s", optarg);
1913b2bd0f6Slogwang 			break;
1923b2bd0f6Slogwang 		case 'n':
1933b2bd0f6Slogwang 			snprintf(name, sizeof(name), "%s", optarg);
1943b2bd0f6Slogwang 			break;
195*d4a07e70Sfengbojiang #ifdef FSTACK
196*d4a07e70Sfengbojiang 		case 'p':
197*d4a07e70Sfengbojiang 			ff_set_proc_id(atoi(optarg));
198*d4a07e70Sfengbojiang 			break;
199*d4a07e70Sfengbojiang #endif
2003b2bd0f6Slogwang 		case '?':
2013b2bd0f6Slogwang 		default:
2023b2bd0f6Slogwang 			Usage((char *)NULL);
2033b2bd0f6Slogwang 			break;
2043b2bd0f6Slogwang 		}
2053b2bd0f6Slogwang 	}
2063b2bd0f6Slogwang 	ac -= optind;
2073b2bd0f6Slogwang 	av += optind;
2083b2bd0f6Slogwang 
2093b2bd0f6Slogwang 	/* Create a new socket node */
2103b2bd0f6Slogwang 	if (NgMkSockNode(name, &csock, &dsock) < 0)
2113b2bd0f6Slogwang 		err(EX_OSERR, "can't create node");
2123b2bd0f6Slogwang 
2133b2bd0f6Slogwang 	/* Do commands as requested */
2143b2bd0f6Slogwang 	if (ac == 0) {
2153b2bd0f6Slogwang 		if (fp != NULL) {
2163b2bd0f6Slogwang 			rtn = ReadFile(fp);
2173b2bd0f6Slogwang 		} else if (interactive) {
2183b2bd0f6Slogwang 			rtn = DoInteractive();
2193b2bd0f6Slogwang 		} else
2203b2bd0f6Slogwang 			Usage("no command specified");
2213b2bd0f6Slogwang 	} else {
2223b2bd0f6Slogwang 		rtn = DoCommand(ac, av);
2233b2bd0f6Slogwang 	}
2243b2bd0f6Slogwang 
2253b2bd0f6Slogwang 	/* Convert command return code into system exit code */
2263b2bd0f6Slogwang 	switch (rtn) {
2273b2bd0f6Slogwang 	case CMDRTN_OK:
2283b2bd0f6Slogwang 	case CMDRTN_QUIT:
2293b2bd0f6Slogwang 		rtn = 0;
2303b2bd0f6Slogwang 		break;
2313b2bd0f6Slogwang 	case CMDRTN_USAGE:
2323b2bd0f6Slogwang 		rtn = EX_USAGE;
2333b2bd0f6Slogwang 		break;
2343b2bd0f6Slogwang 	case CMDRTN_ERROR:
2353b2bd0f6Slogwang 		rtn = EX_OSERR;
2363b2bd0f6Slogwang 		break;
2373b2bd0f6Slogwang 	}
238*d4a07e70Sfengbojiang #ifdef FSTACK
239*d4a07e70Sfengbojiang 	ff_ipc_exit();
240*d4a07e70Sfengbojiang #endif
2413b2bd0f6Slogwang 	return (rtn);
2423b2bd0f6Slogwang }
2433b2bd0f6Slogwang 
2443b2bd0f6Slogwang /*
2453b2bd0f6Slogwang  * Process commands from a file
2463b2bd0f6Slogwang  */
2473b2bd0f6Slogwang static int
2483b2bd0f6Slogwang ReadFile(FILE *fp)
2493b2bd0f6Slogwang {
2503b2bd0f6Slogwang 	char line[LINE_MAX];
2513b2bd0f6Slogwang 	int num, rtn;
2523b2bd0f6Slogwang 
2533b2bd0f6Slogwang 	for (num = 1; fgets(line, sizeof(line), fp) != NULL; num++) {
2543b2bd0f6Slogwang 		if (*line == '#')
2553b2bd0f6Slogwang 			continue;
2563b2bd0f6Slogwang 		if ((rtn = DoParseCommand(line)) != 0) {
2573b2bd0f6Slogwang 			warnx("line %d: error in file", num);
2583b2bd0f6Slogwang 			return (rtn);
2593b2bd0f6Slogwang 		}
2603b2bd0f6Slogwang 	}
2613b2bd0f6Slogwang 	return (CMDRTN_OK);
2623b2bd0f6Slogwang }
2633b2bd0f6Slogwang 
2643b2bd0f6Slogwang #ifdef EDITLINE
265*d4a07e70Sfengbojiang #ifndef FSTACK
2663b2bd0f6Slogwang /* Signal handler for Monitor() thread. */
2673b2bd0f6Slogwang static void
2683b2bd0f6Slogwang Unblock(int signal __unused)
2693b2bd0f6Slogwang {
2703b2bd0f6Slogwang 
2713b2bd0f6Slogwang 	unblock = 1;
2723b2bd0f6Slogwang }
2733b2bd0f6Slogwang 
2743b2bd0f6Slogwang /*
2753b2bd0f6Slogwang  * Thread that monitors csock and dsock while main thread
2763b2bd0f6Slogwang  * can be blocked in el_gets().
2773b2bd0f6Slogwang  */
2783b2bd0f6Slogwang static void *
2793b2bd0f6Slogwang Monitor(void *v __unused)
2803b2bd0f6Slogwang {
2813b2bd0f6Slogwang 	struct sigaction act;
2823b2bd0f6Slogwang 	const int maxfd = MAX(csock, dsock) + 1;
2833b2bd0f6Slogwang 
2843b2bd0f6Slogwang 	act.sa_handler = Unblock;
2853b2bd0f6Slogwang 	sigemptyset(&act.sa_mask);
2863b2bd0f6Slogwang 	act.sa_flags = 0;
2873b2bd0f6Slogwang 	sigaction(SIGUSR1, &act, NULL);
2883b2bd0f6Slogwang 
2893b2bd0f6Slogwang 	pthread_mutex_lock(&mutex);
2903b2bd0f6Slogwang 	for (;;) {
2913b2bd0f6Slogwang 		fd_set rfds;
2923b2bd0f6Slogwang 
2933b2bd0f6Slogwang 		/* See if any data or control messages are arriving. */
2943b2bd0f6Slogwang 		FD_ZERO(&rfds);
2953b2bd0f6Slogwang 		FD_SET(csock, &rfds);
2963b2bd0f6Slogwang 		FD_SET(dsock, &rfds);
2973b2bd0f6Slogwang 		unblock = 0;
2983b2bd0f6Slogwang 		if (select(maxfd, &rfds, NULL, NULL, NULL) <= 0) {
2993b2bd0f6Slogwang 			if (errno == EINTR) {
3003b2bd0f6Slogwang 				if (unblock == 1)
3013b2bd0f6Slogwang 					pthread_cond_wait(&cond, &mutex);
3023b2bd0f6Slogwang 				continue;
3033b2bd0f6Slogwang 			}
3043b2bd0f6Slogwang 			err(EX_OSERR, "select");
3053b2bd0f6Slogwang 		}
3063b2bd0f6Slogwang 		ReadSockets(&rfds);
3073b2bd0f6Slogwang 	}
3083b2bd0f6Slogwang 
3093b2bd0f6Slogwang 	return (NULL);
3103b2bd0f6Slogwang }
311*d4a07e70Sfengbojiang #endif
3123b2bd0f6Slogwang 
3133b2bd0f6Slogwang static char *
3143b2bd0f6Slogwang Prompt(EditLine *el __unused)
3153b2bd0f6Slogwang {
3163b2bd0f6Slogwang 
3173b2bd0f6Slogwang 	return (PROMPT);
3183b2bd0f6Slogwang }
3193b2bd0f6Slogwang 
3203b2bd0f6Slogwang /*
3213b2bd0f6Slogwang  * Here we start a thread, that will monitor the netgraph
3223b2bd0f6Slogwang  * sockets and catch any unexpected messages or data on them,
3233b2bd0f6Slogwang  * that can arrive while user edits his/her commands.
3243b2bd0f6Slogwang  *
3253b2bd0f6Slogwang  * Whenever we expect data on netgraph sockets, we send signal
3263b2bd0f6Slogwang  * to monitoring thread. The signal forces it to exit select()
3273b2bd0f6Slogwang  * system call and sleep on condvar until we wake it. While
3283b2bd0f6Slogwang  * monitoring thread sleeps, we can do our work with netgraph
3293b2bd0f6Slogwang  * sockets.
3303b2bd0f6Slogwang  */
3313b2bd0f6Slogwang static int
3323b2bd0f6Slogwang DoInteractive(void)
3333b2bd0f6Slogwang {
334*d4a07e70Sfengbojiang #ifndef FSTACK
3353b2bd0f6Slogwang 	pthread_t monitor;
336*d4a07e70Sfengbojiang #endif
3373b2bd0f6Slogwang 	EditLine *el;
3383b2bd0f6Slogwang 	History *hist;
3393b2bd0f6Slogwang 	HistEvent hev = { 0, "" };
3403b2bd0f6Slogwang 
3413b2bd0f6Slogwang 	(*help_cmd.func)(0, NULL);
342*d4a07e70Sfengbojiang #ifndef FSTACK
3433b2bd0f6Slogwang 	pthread_create(&monitor, NULL, Monitor, NULL);
344*d4a07e70Sfengbojiang #endif
3453b2bd0f6Slogwang 	el = el_init(getprogname(), stdin, stdout, stderr);
3463b2bd0f6Slogwang 	if (el == NULL)
3473b2bd0f6Slogwang 		return (CMDRTN_ERROR);
3483b2bd0f6Slogwang 	el_set(el, EL_PROMPT, Prompt);
3493b2bd0f6Slogwang 	el_set(el, EL_SIGNAL, 1);
3503b2bd0f6Slogwang 	el_set(el, EL_EDITOR, "emacs");
3513b2bd0f6Slogwang 	hist = history_init();
3523b2bd0f6Slogwang 	if (hist == NULL)
3533b2bd0f6Slogwang 		return (CMDRTN_ERROR);
3543b2bd0f6Slogwang 	history(hist, &hev, H_SETSIZE, 100);
3553b2bd0f6Slogwang 	history(hist, &hev, H_SETUNIQUE, 1);
3563b2bd0f6Slogwang 	el_set(el, EL_HIST, history, (const char *)hist);
3573b2bd0f6Slogwang 	el_source(el, NULL);
3583b2bd0f6Slogwang 
3593b2bd0f6Slogwang 	for (;;) {
3603b2bd0f6Slogwang 		const char *buf;
3613b2bd0f6Slogwang 		int count;
3623b2bd0f6Slogwang 
3633b2bd0f6Slogwang 		if ((buf = el_gets(el, &count)) == NULL) {
3643b2bd0f6Slogwang 			printf("\n");
3653b2bd0f6Slogwang 			break;
3663b2bd0f6Slogwang 		}
3673b2bd0f6Slogwang 		history(hist, &hev, H_ENTER, buf);
368*d4a07e70Sfengbojiang #ifndef FSTACK
3693b2bd0f6Slogwang 		pthread_kill(monitor, SIGUSR1);
3703b2bd0f6Slogwang 		pthread_mutex_lock(&mutex);
371*d4a07e70Sfengbojiang #endif
3723b2bd0f6Slogwang 		if (DoParseCommand(buf) == CMDRTN_QUIT) {
373*d4a07e70Sfengbojiang #ifndef FSTACK
3743b2bd0f6Slogwang 			pthread_mutex_unlock(&mutex);
375*d4a07e70Sfengbojiang #endif
3763b2bd0f6Slogwang 			break;
3773b2bd0f6Slogwang 		}
378*d4a07e70Sfengbojiang #ifndef FSTACK
3793b2bd0f6Slogwang 		pthread_cond_signal(&cond);
3803b2bd0f6Slogwang 		pthread_mutex_unlock(&mutex);
381*d4a07e70Sfengbojiang #endif
3823b2bd0f6Slogwang 	}
3833b2bd0f6Slogwang 
3843b2bd0f6Slogwang 	history_end(hist);
3853b2bd0f6Slogwang 	el_end(el);
386*d4a07e70Sfengbojiang #ifndef FSTACK
3873b2bd0f6Slogwang 	pthread_cancel(monitor);
388*d4a07e70Sfengbojiang #endif
3893b2bd0f6Slogwang 
3903b2bd0f6Slogwang 	return (CMDRTN_QUIT);
3913b2bd0f6Slogwang }
3923b2bd0f6Slogwang 
3933b2bd0f6Slogwang #else /* !EDITLINE */
3943b2bd0f6Slogwang 
3953b2bd0f6Slogwang /*
3963b2bd0f6Slogwang  * Interactive mode w/o libedit functionality.
3973b2bd0f6Slogwang  */
3983b2bd0f6Slogwang static int
3993b2bd0f6Slogwang DoInteractive(void)
4003b2bd0f6Slogwang {
4013b2bd0f6Slogwang 	const int maxfd = MAX(csock, dsock) + 1;
4023b2bd0f6Slogwang 
4033b2bd0f6Slogwang 	(*help_cmd.func)(0, NULL);
4043b2bd0f6Slogwang 	while (1) {
4053b2bd0f6Slogwang 		struct timeval tv;
4063b2bd0f6Slogwang 		fd_set rfds;
4073b2bd0f6Slogwang 
4083b2bd0f6Slogwang 		/* See if any data or control messages are arriving */
4093b2bd0f6Slogwang 		FD_ZERO(&rfds);
410*d4a07e70Sfengbojiang #ifndef FSTACK
4113b2bd0f6Slogwang 		FD_SET(csock, &rfds);
4123b2bd0f6Slogwang 		FD_SET(dsock, &rfds);
413*d4a07e70Sfengbojiang #endif
4143b2bd0f6Slogwang 		memset(&tv, 0, sizeof(tv));
4153b2bd0f6Slogwang 		if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) {
4163b2bd0f6Slogwang 
4173b2bd0f6Slogwang 			/* Issue prompt and wait for anything to happen */
4183b2bd0f6Slogwang 			printf("%s", PROMPT);
4193b2bd0f6Slogwang 			fflush(stdout);
4203b2bd0f6Slogwang 			FD_ZERO(&rfds);
4213b2bd0f6Slogwang 			FD_SET(0, &rfds);
422*d4a07e70Sfengbojiang #ifndef FSTACK
4233b2bd0f6Slogwang 			FD_SET(csock, &rfds);
4243b2bd0f6Slogwang 			FD_SET(dsock, &rfds);
425*d4a07e70Sfengbojiang #endif
4263b2bd0f6Slogwang 			if (select(maxfd, &rfds, NULL, NULL, NULL) < 0)
4273b2bd0f6Slogwang 				err(EX_OSERR, "select");
4283b2bd0f6Slogwang 
4293b2bd0f6Slogwang 			/* If not user input, print a newline first */
4303b2bd0f6Slogwang 			if (!FD_ISSET(0, &rfds))
4313b2bd0f6Slogwang 				printf("\n");
4323b2bd0f6Slogwang 		}
4333b2bd0f6Slogwang 
434*d4a07e70Sfengbojiang #ifndef FSTACK
4353b2bd0f6Slogwang 		ReadSockets(&rfds);
436*d4a07e70Sfengbojiang #endif
4373b2bd0f6Slogwang 
4383b2bd0f6Slogwang 		/* Get any user input */
4393b2bd0f6Slogwang 		if (FD_ISSET(0, &rfds)) {
4403b2bd0f6Slogwang 			char buf[LINE_MAX];
4413b2bd0f6Slogwang 
4423b2bd0f6Slogwang 			if (fgets(buf, sizeof(buf), stdin) == NULL) {
4433b2bd0f6Slogwang 				printf("\n");
4443b2bd0f6Slogwang 				break;
4453b2bd0f6Slogwang 			}
4463b2bd0f6Slogwang 			if (DoParseCommand(buf) == CMDRTN_QUIT)
4473b2bd0f6Slogwang 				break;
4483b2bd0f6Slogwang 		}
4493b2bd0f6Slogwang 	}
4503b2bd0f6Slogwang 	return (CMDRTN_QUIT);
4513b2bd0f6Slogwang }
4523b2bd0f6Slogwang #endif /* !EDITLINE */
4533b2bd0f6Slogwang 
454*d4a07e70Sfengbojiang #ifndef FSTACK
4553b2bd0f6Slogwang /*
4563b2bd0f6Slogwang  * Read and process data on netgraph control and data sockets.
4573b2bd0f6Slogwang  */
4583b2bd0f6Slogwang static void
4593b2bd0f6Slogwang ReadSockets(fd_set *rfds)
4603b2bd0f6Slogwang {
4613b2bd0f6Slogwang 	/* Display any incoming control message. */
4623b2bd0f6Slogwang 	if (FD_ISSET(csock, rfds))
4633b2bd0f6Slogwang 		MsgRead();
4643b2bd0f6Slogwang 
4653b2bd0f6Slogwang 	/* Display any incoming data packet. */
4663b2bd0f6Slogwang 	if (FD_ISSET(dsock, rfds)) {
4673b2bd0f6Slogwang 		char hook[NG_HOOKSIZ];
4683b2bd0f6Slogwang 		u_char *buf;
4693b2bd0f6Slogwang 		int rl;
4703b2bd0f6Slogwang 
4713b2bd0f6Slogwang 		/* Read packet from socket. */
4723b2bd0f6Slogwang 		if ((rl = NgAllocRecvData(dsock, &buf, hook)) < 0)
4733b2bd0f6Slogwang 			err(EX_OSERR, "reading hook \"%s\"", hook);
4743b2bd0f6Slogwang 		if (rl == 0)
4753b2bd0f6Slogwang 			errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
4763b2bd0f6Slogwang 
4773b2bd0f6Slogwang 		/* Write packet to stdout. */
4783b2bd0f6Slogwang 		printf("Rec'd data packet on hook \"%s\":\n", hook);
4793b2bd0f6Slogwang 		DumpAscii(buf, rl);
4803b2bd0f6Slogwang 		free(buf);
4813b2bd0f6Slogwang 	}
4823b2bd0f6Slogwang }
483*d4a07e70Sfengbojiang #endif
4843b2bd0f6Slogwang 
4853b2bd0f6Slogwang /*
4863b2bd0f6Slogwang  * Parse a command line and execute the command
4873b2bd0f6Slogwang  */
4883b2bd0f6Slogwang static int
4893b2bd0f6Slogwang DoParseCommand(const char *line)
4903b2bd0f6Slogwang {
4913b2bd0f6Slogwang 	char *av[MAX_ARGS];
4923b2bd0f6Slogwang 	int ac;
4933b2bd0f6Slogwang 
4943b2bd0f6Slogwang 	/* Parse line */
4953b2bd0f6Slogwang 	for (ac = 0, av[0] = strtok((char *)line, WHITESPACE);
4963b2bd0f6Slogwang 	    ac < MAX_ARGS - 1 && av[ac];
4973b2bd0f6Slogwang 	    av[++ac] = strtok(NULL, WHITESPACE));
4983b2bd0f6Slogwang 
4993b2bd0f6Slogwang 	/* Do command */
5003b2bd0f6Slogwang 	return (DoCommand(ac, av));
5013b2bd0f6Slogwang }
5023b2bd0f6Slogwang 
5033b2bd0f6Slogwang /*
5043b2bd0f6Slogwang  * Execute the command
5053b2bd0f6Slogwang  */
5063b2bd0f6Slogwang static int
5073b2bd0f6Slogwang DoCommand(int ac, char **av)
5083b2bd0f6Slogwang {
5093b2bd0f6Slogwang 	const struct ngcmd *cmd;
5103b2bd0f6Slogwang 	int rtn;
5113b2bd0f6Slogwang 
5123b2bd0f6Slogwang 	if (ac == 0 || *av[0] == 0)
5133b2bd0f6Slogwang 		return (CMDRTN_OK);
5143b2bd0f6Slogwang 	if ((cmd = FindCommand(av[0])) == NULL)
5153b2bd0f6Slogwang 		return (CMDRTN_ERROR);
5163b2bd0f6Slogwang 	if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE)
5173b2bd0f6Slogwang 		warnx("usage: %s", cmd->cmd);
5183b2bd0f6Slogwang 	return (rtn);
5193b2bd0f6Slogwang }
5203b2bd0f6Slogwang 
5213b2bd0f6Slogwang /*
5223b2bd0f6Slogwang  * Find a command
5233b2bd0f6Slogwang  */
5243b2bd0f6Slogwang static const struct ngcmd *
5253b2bd0f6Slogwang FindCommand(const char *string)
5263b2bd0f6Slogwang {
5273b2bd0f6Slogwang 	int k, found = -1;
5283b2bd0f6Slogwang 
5293b2bd0f6Slogwang 	for (k = 0; cmds[k] != NULL; k++) {
5303b2bd0f6Slogwang 		if (MatchCommand(cmds[k], string)) {
5313b2bd0f6Slogwang 			if (found != -1) {
5323b2bd0f6Slogwang 				warnx("\"%s\": ambiguous command", string);
5333b2bd0f6Slogwang 				return (NULL);
5343b2bd0f6Slogwang 			}
5353b2bd0f6Slogwang 			found = k;
5363b2bd0f6Slogwang 		}
5373b2bd0f6Slogwang 	}
5383b2bd0f6Slogwang 	if (found == -1) {
5393b2bd0f6Slogwang 		warnx("\"%s\": unknown command", string);
5403b2bd0f6Slogwang 		return (NULL);
5413b2bd0f6Slogwang 	}
5423b2bd0f6Slogwang 	return (cmds[found]);
5433b2bd0f6Slogwang }
5443b2bd0f6Slogwang 
5453b2bd0f6Slogwang /*
5463b2bd0f6Slogwang  * See if string matches a prefix of "cmd" (or an alias) case insensitively
5473b2bd0f6Slogwang  */
5483b2bd0f6Slogwang static int
5493b2bd0f6Slogwang MatchCommand(const struct ngcmd *cmd, const char *s)
5503b2bd0f6Slogwang {
5513b2bd0f6Slogwang 	int a;
5523b2bd0f6Slogwang 
5533b2bd0f6Slogwang 	/* Try to match command, ignoring the usage stuff */
5543b2bd0f6Slogwang 	if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) {
5553b2bd0f6Slogwang 		if (strncasecmp(s, cmd->cmd, strlen(s)) == 0)
5563b2bd0f6Slogwang 			return (1);
5573b2bd0f6Slogwang 	}
5583b2bd0f6Slogwang 
5593b2bd0f6Slogwang 	/* Try to match aliases */
5603b2bd0f6Slogwang 	for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) {
5613b2bd0f6Slogwang 		if (strlen(cmd->aliases[a]) >= strlen(s)) {
5623b2bd0f6Slogwang 			if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0)
5633b2bd0f6Slogwang 				return (1);
5643b2bd0f6Slogwang 		}
5653b2bd0f6Slogwang 	}
5663b2bd0f6Slogwang 
5673b2bd0f6Slogwang 	/* No match */
5683b2bd0f6Slogwang 	return (0);
5693b2bd0f6Slogwang }
5703b2bd0f6Slogwang 
5713b2bd0f6Slogwang /*
5723b2bd0f6Slogwang  * ReadCmd()
5733b2bd0f6Slogwang  */
5743b2bd0f6Slogwang static int
5753b2bd0f6Slogwang ReadCmd(int ac, char **av)
5763b2bd0f6Slogwang {
5773b2bd0f6Slogwang 	FILE *fp;
5783b2bd0f6Slogwang 	int rtn;
5793b2bd0f6Slogwang 
5803b2bd0f6Slogwang 	/* Open file */
5813b2bd0f6Slogwang 	switch (ac) {
5823b2bd0f6Slogwang 	case 2:
5833b2bd0f6Slogwang 		if ((fp = fopen(av[1], "r")) == NULL) {
5843b2bd0f6Slogwang 			warn("%s", av[1]);
5853b2bd0f6Slogwang 			return (CMDRTN_ERROR);
5863b2bd0f6Slogwang 		}
5873b2bd0f6Slogwang 		break;
5883b2bd0f6Slogwang 	default:
5893b2bd0f6Slogwang 		return (CMDRTN_USAGE);
5903b2bd0f6Slogwang 	}
5913b2bd0f6Slogwang 
5923b2bd0f6Slogwang 	/* Process it */
5933b2bd0f6Slogwang 	rtn = ReadFile(fp);
5943b2bd0f6Slogwang 	fclose(fp);
5953b2bd0f6Slogwang 	return (rtn);
5963b2bd0f6Slogwang }
5973b2bd0f6Slogwang 
5983b2bd0f6Slogwang /*
5993b2bd0f6Slogwang  * HelpCmd()
6003b2bd0f6Slogwang  */
6013b2bd0f6Slogwang static int
6023b2bd0f6Slogwang HelpCmd(int ac, char **av)
6033b2bd0f6Slogwang {
6043b2bd0f6Slogwang 	const struct ngcmd *cmd;
6053b2bd0f6Slogwang 	int k;
6063b2bd0f6Slogwang 
6073b2bd0f6Slogwang 	switch (ac) {
6083b2bd0f6Slogwang 	case 0:
6093b2bd0f6Slogwang 	case 1:
6103b2bd0f6Slogwang 		/* Show all commands */
6113b2bd0f6Slogwang 		printf("Available commands:\n");
6123b2bd0f6Slogwang 		for (k = 0; cmds[k] != NULL; k++) {
6133b2bd0f6Slogwang 			char *s, buf[100];
6143b2bd0f6Slogwang 
6153b2bd0f6Slogwang 			cmd = cmds[k];
6163b2bd0f6Slogwang 			snprintf(buf, sizeof(buf), "%s", cmd->cmd);
6173b2bd0f6Slogwang 			for (s = buf; *s != '\0' && !isspace(*s); s++);
6183b2bd0f6Slogwang 			*s = '\0';
6193b2bd0f6Slogwang 			printf("  %-10s %s\n", buf, cmd->desc);
6203b2bd0f6Slogwang 		}
6213b2bd0f6Slogwang 		return (CMDRTN_OK);
6223b2bd0f6Slogwang 	default:
6233b2bd0f6Slogwang 		/* Show help on a specific command */
6243b2bd0f6Slogwang 		if ((cmd = FindCommand(av[1])) != NULL) {
6253b2bd0f6Slogwang 			printf("usage:    %s\n", cmd->cmd);
6263b2bd0f6Slogwang 			if (cmd->aliases[0] != NULL) {
6273b2bd0f6Slogwang 				int a = 0;
6283b2bd0f6Slogwang 
6293b2bd0f6Slogwang 				printf("Aliases:  ");
6303b2bd0f6Slogwang 				while (1) {
6313b2bd0f6Slogwang 					printf("%s", cmd->aliases[a++]);
6323b2bd0f6Slogwang 					if (a == MAX_CMD_ALIAS
6333b2bd0f6Slogwang 					    || cmd->aliases[a] == NULL) {
6343b2bd0f6Slogwang 						printf("\n");
6353b2bd0f6Slogwang 						break;
6363b2bd0f6Slogwang 					}
6373b2bd0f6Slogwang 					printf(", ");
6383b2bd0f6Slogwang 				}
6393b2bd0f6Slogwang 			}
6403b2bd0f6Slogwang 			printf("Summary:  %s\n", cmd->desc);
6413b2bd0f6Slogwang 			if (cmd->help != NULL) {
6423b2bd0f6Slogwang 				const char *s;
6433b2bd0f6Slogwang 				char buf[65];
6443b2bd0f6Slogwang 				int tot, len, done;
6453b2bd0f6Slogwang 
6463b2bd0f6Slogwang 				printf("Description:\n");
6473b2bd0f6Slogwang 				for (s = cmd->help; *s != '\0'; s += len) {
6483b2bd0f6Slogwang 					while (isspace(*s))
6493b2bd0f6Slogwang 						s++;
6503b2bd0f6Slogwang 					tot = snprintf(buf,
6513b2bd0f6Slogwang 					    sizeof(buf), "%s", s);
6523b2bd0f6Slogwang 					len = strlen(buf);
6533b2bd0f6Slogwang 					done = len == tot;
6543b2bd0f6Slogwang 					if (!done) {
6553b2bd0f6Slogwang 						while (len > 0
6563b2bd0f6Slogwang 						    && !isspace(buf[len-1]))
6573b2bd0f6Slogwang 							buf[--len] = '\0';
6583b2bd0f6Slogwang 					}
6593b2bd0f6Slogwang 					printf("  %s\n", buf);
6603b2bd0f6Slogwang 				}
6613b2bd0f6Slogwang 			}
6623b2bd0f6Slogwang 		}
6633b2bd0f6Slogwang 	}
6643b2bd0f6Slogwang 	return (CMDRTN_OK);
6653b2bd0f6Slogwang }
6663b2bd0f6Slogwang 
6673b2bd0f6Slogwang /*
6683b2bd0f6Slogwang  * QuitCmd()
6693b2bd0f6Slogwang  */
6703b2bd0f6Slogwang static int
6713b2bd0f6Slogwang QuitCmd(int ac __unused, char **av __unused)
6723b2bd0f6Slogwang {
6733b2bd0f6Slogwang 	return (CMDRTN_QUIT);
6743b2bd0f6Slogwang }
6753b2bd0f6Slogwang 
6763b2bd0f6Slogwang /*
6773b2bd0f6Slogwang  * Dump data in hex and ASCII form
6783b2bd0f6Slogwang  */
6793b2bd0f6Slogwang void
6803b2bd0f6Slogwang DumpAscii(const u_char *buf, int len)
6813b2bd0f6Slogwang {
6823b2bd0f6Slogwang 	char ch, sbuf[100];
6833b2bd0f6Slogwang 	int k, count;
6843b2bd0f6Slogwang 
6853b2bd0f6Slogwang 	for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) {
6863b2bd0f6Slogwang 		snprintf(sbuf, sizeof(sbuf), "%04x:  ", count);
6873b2bd0f6Slogwang 		for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
6883b2bd0f6Slogwang 			if (count + k < len) {
6893b2bd0f6Slogwang 				snprintf(sbuf + strlen(sbuf),
6903b2bd0f6Slogwang 				    sizeof(sbuf) - strlen(sbuf),
6913b2bd0f6Slogwang 				    "%02x ", buf[count + k]);
6923b2bd0f6Slogwang 			} else {
6933b2bd0f6Slogwang 				snprintf(sbuf + strlen(sbuf),
6943b2bd0f6Slogwang 				    sizeof(sbuf) - strlen(sbuf), "   ");
6953b2bd0f6Slogwang 			}
6963b2bd0f6Slogwang 		}
6973b2bd0f6Slogwang 		snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
6983b2bd0f6Slogwang 		for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
6993b2bd0f6Slogwang 			if (count + k < len) {
7003b2bd0f6Slogwang 				ch = isprint(buf[count + k]) ?
7013b2bd0f6Slogwang 				    buf[count + k] : '.';
7023b2bd0f6Slogwang 				snprintf(sbuf + strlen(sbuf),
7033b2bd0f6Slogwang 				    sizeof(sbuf) - strlen(sbuf), "%c", ch);
7043b2bd0f6Slogwang 			} else {
7053b2bd0f6Slogwang 				snprintf(sbuf + strlen(sbuf),
7063b2bd0f6Slogwang 				    sizeof(sbuf) - strlen(sbuf), " ");
7073b2bd0f6Slogwang 			}
7083b2bd0f6Slogwang 		}
7093b2bd0f6Slogwang 		printf("%s\n", sbuf);
7103b2bd0f6Slogwang 	}
7113b2bd0f6Slogwang }
7123b2bd0f6Slogwang 
7133b2bd0f6Slogwang /*
7143b2bd0f6Slogwang  * Usage()
7153b2bd0f6Slogwang  */
7163b2bd0f6Slogwang static void
7173b2bd0f6Slogwang Usage(const char *msg)
7183b2bd0f6Slogwang {
7193b2bd0f6Slogwang 	if (msg)
7203b2bd0f6Slogwang 		warnx("%s", msg);
7213b2bd0f6Slogwang 	fprintf(stderr,
722*d4a07e70Sfengbojiang #ifndef FSTACK
7233b2bd0f6Slogwang 		"usage: ngctl [-d] [-f file] [-n name] [command ...]\n");
724*d4a07e70Sfengbojiang #else
725*d4a07e70Sfengbojiang 		"usage: ngctl -p <f-stack proc_id>  [-d] [-f file] [-n name] [command ...]\n");
726*d4a07e70Sfengbojiang 
727*d4a07e70Sfengbojiang 	ff_ipc_exit();
728*d4a07e70Sfengbojiang #endif
7293b2bd0f6Slogwang 	exit(EX_USAGE);
7303b2bd0f6Slogwang }
731