main.c revision 61880
1
2/*
3 * main.c
4 *
5 * Copyright (c) 1996-1999 Whistle Communications, Inc.
6 * All rights reserved.
7 *
8 * Subject to the following obligations and disclaimer of warranty, use and
9 * redistribution of this software, in source or object code forms, with or
10 * without modifications are expressly permitted by Whistle Communications;
11 * provided, however, that:
12 * 1. Any and all reproductions of the source or object code must include the
13 *    copyright notice above and the following disclaimer of warranties; and
14 * 2. No rights are granted, in any manner or form, to use Whistle
15 *    Communications, Inc. trademarks, including the mark "WHISTLE
16 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17 *    such appears in the above copyright notice or in the software.
18 *
19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 *
37 * $FreeBSD: head/usr.sbin/ngctl/main.c 61880 2000-06-20 18:51:38Z archie $
38 * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $
39 */
40
41#include "ngctl.h"
42
43#define PROMPT			"+ "
44#define MAX_ARGS		512
45#define WHITESPACE		" \t\r\n\v\f"
46#define DUMP_BYTES_PER_LINE	16
47
48/* Internal functions */
49static int	ReadFile(FILE *fp);
50static int	DoParseCommand(char *line);
51static int	DoCommand(int ac, char **av);
52static int	DoInteractive(void);
53static const	struct ngcmd *FindCommand(const char *string);
54static int	MatchCommand(const struct ngcmd *cmd, const char *s);
55static void	Usage(const char *msg);
56static int	ReadCmd(int ac, char **av);
57static int	HelpCmd(int ac, char **av);
58static int	QuitCmd(int ac, char **av);
59
60/* List of commands */
61static const struct ngcmd *const cmds[] = {
62	&connect_cmd,
63	&debug_cmd,
64	&help_cmd,
65	&list_cmd,
66	&mkpeer_cmd,
67	&msg_cmd,
68	&name_cmd,
69	&read_cmd,
70	&rmhook_cmd,
71	&show_cmd,
72	&shutdown_cmd,
73	&status_cmd,
74	&types_cmd,
75	&quit_cmd,
76	NULL
77};
78
79/* Commands defined in this file */
80const struct ngcmd read_cmd = {
81	ReadCmd,
82	"read <filename>",
83	"Read and execute commands from a file",
84	NULL,
85	{ "source", "." }
86};
87const struct ngcmd help_cmd = {
88	HelpCmd,
89	"help [command]",
90	"Show command summary or get more help on a specific command",
91	NULL,
92	{ "?" }
93};
94const struct ngcmd quit_cmd = {
95	QuitCmd,
96	"quit",
97	"Exit program",
98	NULL,
99	{ "exit" }
100};
101
102/* Our control and data sockets */
103int	csock, dsock;
104
105/*
106 * main()
107 */
108int
109main(int ac, char *av[])
110{
111	char	name[NG_NODELEN + 1];
112	int	interactive = isatty(0) && isatty(1);
113	FILE	*fp = NULL;
114	int	ch, rtn = 0;
115
116	/* Set default node name */
117	snprintf(name, sizeof(name), "ngctl%d", getpid());
118
119	/* Parse command line */
120	while ((ch = getopt(ac, av, "df:n:")) != EOF) {
121		switch (ch) {
122		case 'd':
123			NgSetDebug(NgSetDebug(-1) + 1);
124			break;
125		case 'f':
126			if (strcmp(optarg, "-") == 0)
127				fp = stdin;
128			else if ((fp = fopen(optarg, "r")) == NULL)
129				err(EX_NOINPUT, "%s", optarg);
130			break;
131		case 'n':
132			snprintf(name, sizeof(name), "%s", optarg);
133			break;
134		case '?':
135		default:
136			Usage((char *)NULL);
137			break;
138		}
139	}
140	ac -= optind;
141	av += optind;
142
143	/* Create a new socket node */
144	if (NgMkSockNode(name, &csock, &dsock) < 0)
145		err(EX_OSERR, "can't create node");
146
147	/* Do commands as requested */
148	if (ac == 0) {
149		if (fp != NULL) {
150			rtn = ReadFile(fp);
151		} else if (interactive) {
152			rtn = DoInteractive();
153		} else
154			Usage("no command specified");
155	} else {
156		rtn = DoCommand(ac, av);
157	}
158
159	/* Convert command return code into system exit code */
160	switch (rtn) {
161	case CMDRTN_OK:
162	case CMDRTN_QUIT:
163		rtn = 0;
164		break;
165	case CMDRTN_USAGE:
166		rtn = EX_USAGE;
167		break;
168	case CMDRTN_ERROR:
169		rtn = EX_OSERR;
170		break;
171	}
172	return(rtn);
173}
174
175/*
176 * Process commands from a file
177 */
178static int
179ReadFile(FILE *fp)
180{
181	char line[LINE_MAX];
182	int num, rtn;
183
184	for (num = 1; fgets(line, sizeof(line), fp) != NULL; num++) {
185		if (*line == '#')
186			continue;
187		if ((rtn = DoParseCommand(line)) != 0) {
188			warnx("line %d: error in file", num);
189			return(rtn);
190		}
191	}
192	return(CMDRTN_OK);
193}
194
195/*
196 * Interactive mode
197 */
198static int
199DoInteractive(void)
200{
201	const int maxfd = MAX(csock, dsock) + 1;
202
203	(*help_cmd.func)(0, NULL);
204	while (1) {
205		struct timeval tv;
206		fd_set rfds;
207
208		/* See if any data or control messages are arriving */
209		FD_ZERO(&rfds);
210		FD_SET(csock, &rfds);
211		FD_SET(dsock, &rfds);
212		memset(&tv, 0, sizeof(tv));
213		if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) {
214
215			/* Issue prompt and wait for anything to happen */
216			printf("%s", PROMPT);
217			fflush(stdout);
218			FD_ZERO(&rfds);
219			FD_SET(0, &rfds);
220			FD_SET(csock, &rfds);
221			FD_SET(dsock, &rfds);
222			if (select(maxfd, &rfds, NULL, NULL, NULL) < 0)
223				err(EX_OSERR, "select");
224
225			/* If not user input, print a newline first */
226			if (!FD_ISSET(0, &rfds))
227				printf("\n");
228		}
229
230		/* Display any incoming control message */
231		if (FD_ISSET(csock, &rfds))
232			MsgRead();
233
234		/* Display any incoming data packet */
235		if (FD_ISSET(dsock, &rfds)) {
236			u_char buf[8192];
237			char hook[NG_HOOKLEN + 1];
238			int rl;
239
240			/* Read packet from socket */
241			if ((rl = NgRecvData(dsock,
242			    buf, sizeof(buf), hook)) < 0)
243				err(EX_OSERR, "reading hook \"%s\"", hook);
244			if (rl == 0)
245				errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
246
247			/* Write packet to stdout */
248			printf("Rec'd data packet on hook \"%s\":\n", hook);
249			DumpAscii(buf, rl);
250		}
251
252		/* Get any user input */
253		if (FD_ISSET(0, &rfds)) {
254			char buf[LINE_MAX];
255
256			if (fgets(buf, sizeof(buf), stdin) == NULL) {
257				printf("\n");
258				break;
259			}
260			if (DoParseCommand(buf) == CMDRTN_QUIT)
261				break;
262		}
263	}
264	return(CMDRTN_QUIT);
265}
266
267/*
268 * Parse a command line and execute the command
269 */
270static int
271DoParseCommand(char *line)
272{
273	char *av[MAX_ARGS];
274	int ac;
275
276	/* Parse line */
277	for (ac = 0, av[0] = strtok(line, WHITESPACE);
278	    ac < MAX_ARGS - 1 && av[ac];
279	    av[++ac] = strtok(NULL, WHITESPACE));
280
281	/* Do command */
282	return(DoCommand(ac, av));
283}
284
285/*
286 * Execute the command
287 */
288static int
289DoCommand(int ac, char **av)
290{
291	const struct ngcmd *cmd;
292	int rtn;
293
294	if (ac == 0 || *av[0] == 0)
295		return(CMDRTN_OK);
296	if ((cmd = FindCommand(av[0])) == NULL)
297		return(CMDRTN_ERROR);
298	if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE)
299		warnx("usage: %s", cmd->cmd);
300	return(rtn);
301}
302
303/*
304 * Find a command
305 */
306static const struct ngcmd *
307FindCommand(const char *string)
308{
309	int k, found = -1;
310
311	for (k = 0; cmds[k] != NULL; k++) {
312		if (MatchCommand(cmds[k], string)) {
313			if (found != -1) {
314				warnx("\"%s\": ambiguous command", string);
315				return(NULL);
316			}
317			found = k;
318		}
319	}
320	if (found == -1) {
321		warnx("\"%s\": unknown command", string);
322		return(NULL);
323	}
324	return(cmds[found]);
325}
326
327/*
328 * See if string matches a prefix of "cmd" (or an alias) case insensitively
329 */
330static int
331MatchCommand(const struct ngcmd *cmd, const char *s)
332{
333	int a;
334
335	/* Try to match command, ignoring the usage stuff */
336	if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) {
337		if (strncasecmp(s, cmd->cmd, strlen(s)) == 0)
338			return (1);
339	}
340
341	/* Try to match aliases */
342	for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) {
343		if (strlen(cmd->aliases[a]) >= strlen(s)) {
344			if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0)
345				return (1);
346		}
347	}
348
349	/* No match */
350	return (0);
351}
352
353/*
354 * ReadCmd()
355 */
356static int
357ReadCmd(int ac, char **av)
358{
359	FILE *fp;
360	int rtn;
361
362	/* Open file */
363	switch (ac) {
364	case 2:
365		if ((fp = fopen(av[1], "r")) == NULL)
366			warn("%s", av[1]);
367		return(CMDRTN_ERROR);
368	default:
369		return(CMDRTN_USAGE);
370	}
371
372	/* Process it */
373	rtn = ReadFile(fp);
374	fclose(fp);
375	return(rtn);
376}
377
378/*
379 * HelpCmd()
380 */
381static int
382HelpCmd(int ac, char **av)
383{
384	const struct ngcmd *cmd;
385	int k;
386
387	switch (ac) {
388	case 0:
389	case 1:
390		/* Show all commands */
391		printf("Available commands:\n");
392		for (k = 0; cmds[k] != NULL; k++) {
393			char *s, buf[100];
394
395			cmd = cmds[k];
396			snprintf(buf, sizeof(buf), "%s", cmd->cmd);
397			for (s = buf; *s != '\0' && !isspace(*s); s++);
398			*s = '\0';
399			printf("  %-10s %s\n", buf, cmd->desc);
400		}
401		return(CMDRTN_OK);
402	default:
403		/* Show help on a specific command */
404		if ((cmd = FindCommand(av[1])) != NULL) {
405			printf("Usage:    %s\n", cmd->cmd);
406			if (cmd->aliases[0] != NULL) {
407				int a = 0;
408
409				printf("Aliases:  ");
410				while (1) {
411					printf("%s", cmd->aliases[a++]);
412					if (a == MAX_CMD_ALIAS
413					    || cmd->aliases[a] == NULL) {
414						printf("\n");
415						break;
416					}
417					printf(", ");
418				}
419			}
420			printf("Summary:  %s\n", cmd->desc);
421			if (cmd->help != NULL) {
422				const char *s;
423				char buf[65];
424				int tot, len, done;
425
426				printf("Description:\n");
427				for (s = cmd->help; *s != '\0'; s += len) {
428					while (isspace(*s))
429						s++;
430					tot = snprintf(buf,
431					    sizeof(buf), "%s", s);
432					len = strlen(buf);
433					done = len == tot;
434					if (!done) {
435						while (len > 0
436						    && !isspace(buf[len-1]))
437							buf[--len] = '\0';
438					}
439					printf("  %s\n", buf);
440				}
441			}
442		}
443	}
444	return(CMDRTN_OK);
445}
446
447/*
448 * QuitCmd()
449 */
450static int
451QuitCmd(int ac, char **av)
452{
453	return(CMDRTN_QUIT);
454}
455
456/*
457 * Dump data in hex and ASCII form
458 */
459void
460DumpAscii(const u_char *buf, int len)
461{
462	char ch, sbuf[100];
463	int k, count;
464
465	for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) {
466		snprintf(sbuf, sizeof(sbuf), "%04x:  ", count);
467		for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
468			if (count + k < len) {
469				snprintf(sbuf + strlen(sbuf),
470				    sizeof(sbuf) - strlen(sbuf),
471				    "%02x ", buf[count + k]);
472			} else {
473				snprintf(sbuf + strlen(sbuf),
474				    sizeof(sbuf) - strlen(sbuf), "   ");
475			}
476		}
477		snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
478		for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
479			if (count + k < len) {
480				ch = isprint(buf[count + k]) ?
481				    buf[count + k] : '.';
482				snprintf(sbuf + strlen(sbuf),
483				    sizeof(sbuf) - strlen(sbuf), "%c", ch);
484			} else {
485				snprintf(sbuf + strlen(sbuf),
486				    sizeof(sbuf) - strlen(sbuf), " ");
487			}
488		}
489		printf("%s\n", sbuf);
490	}
491}
492
493/*
494 * Usage()
495 */
496static void
497Usage(const char *msg)
498{
499	if (msg)
500		warnx("%s", msg);
501	errx(EX_USAGE, "usage: ngctl [-d] [-f file] [-n name] [command ...]");
502}
503
504