Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.sbin / ngctl / main.c
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: src/usr.sbin/ngctl/main.c,v 1.4.2.4 2002/02/01 18:17:43 archie Exp $
38  * $DragonFly: src/usr.sbin/ngctl/main.c,v 1.2 2003/06/17 04:29:57 dillon Exp $
39  * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $
40  */
41
42 #include "ngctl.h"
43
44 #define PROMPT                  "+ "
45 #define MAX_ARGS                512
46 #define WHITESPACE              " \t\r\n\v\f"
47 #define DUMP_BYTES_PER_LINE     16
48
49 /* Internal functions */
50 static int      ReadFile(FILE *fp);
51 static int      DoParseCommand(char *line);
52 static int      DoCommand(int ac, char **av);
53 static int      DoInteractive(void);
54 static const    struct ngcmd *FindCommand(const char *string);
55 static int      MatchCommand(const struct ngcmd *cmd, const char *s);
56 static void     Usage(const char *msg);
57 static int      ReadCmd(int ac, char **av);
58 static int      HelpCmd(int ac, char **av);
59 static int      QuitCmd(int ac, char **av);
60
61 /* List of commands */
62 static const struct ngcmd *const cmds[] = {
63         &config_cmd,
64         &connect_cmd,
65         &debug_cmd,
66         &help_cmd,
67         &list_cmd,
68         &mkpeer_cmd,
69         &msg_cmd,
70         &name_cmd,
71         &read_cmd,
72         &rmhook_cmd,
73         &show_cmd,
74         &shutdown_cmd,
75         &status_cmd,
76         &types_cmd,
77         &write_cmd,
78         &quit_cmd,
79         NULL
80 };
81
82 /* Commands defined in this file */
83 const struct ngcmd read_cmd = {
84         ReadCmd,
85         "read <filename>",
86         "Read and execute commands from a file",
87         NULL,
88         { "source", "." }
89 };
90 const struct ngcmd help_cmd = {
91         HelpCmd,
92         "help [command]",
93         "Show command summary or get more help on a specific command",
94         NULL,
95         { "?" }
96 };
97 const struct ngcmd quit_cmd = {
98         QuitCmd,
99         "quit",
100         "Exit program",
101         NULL,
102         { "exit" }
103 };
104
105 /* Our control and data sockets */
106 int     csock, dsock;
107
108 /*
109  * main()
110  */
111 int
112 main(int ac, char *av[])
113 {
114         char    name[NG_NODELEN + 1];
115         int     interactive = isatty(0) && isatty(1);
116         FILE    *fp = NULL;
117         int     ch, rtn = 0;
118
119         /* Set default node name */
120         snprintf(name, sizeof(name), "ngctl%d", getpid());
121
122         /* Parse command line */
123         while ((ch = getopt(ac, av, "df:n:")) != EOF) {
124                 switch (ch) {
125                 case 'd':
126                         NgSetDebug(NgSetDebug(-1) + 1);
127                         break;
128                 case 'f':
129                         if (strcmp(optarg, "-") == 0)
130                                 fp = stdin;
131                         else if ((fp = fopen(optarg, "r")) == NULL)
132                                 err(EX_NOINPUT, "%s", optarg);
133                         break;
134                 case 'n':
135                         snprintf(name, sizeof(name), "%s", optarg);
136                         break;
137                 case '?':
138                 default:
139                         Usage((char *)NULL);
140                         break;
141                 }
142         }
143         ac -= optind;
144         av += optind;
145
146         /* Create a new socket node */
147         if (NgMkSockNode(name, &csock, &dsock) < 0)
148                 err(EX_OSERR, "can't create node");
149
150         /* Do commands as requested */
151         if (ac == 0) {
152                 if (fp != NULL) {
153                         rtn = ReadFile(fp);
154                 } else if (interactive) {
155                         rtn = DoInteractive();
156                 } else
157                         Usage("no command specified");
158         } else {
159                 rtn = DoCommand(ac, av);
160         }
161
162         /* Convert command return code into system exit code */
163         switch (rtn) {
164         case CMDRTN_OK:
165         case CMDRTN_QUIT:
166                 rtn = 0;
167                 break;
168         case CMDRTN_USAGE:
169                 rtn = EX_USAGE;
170                 break;
171         case CMDRTN_ERROR:
172                 rtn = EX_OSERR;
173                 break;
174         }
175         return(rtn);
176 }
177
178 /*
179  * Process commands from a file
180  */
181 static int
182 ReadFile(FILE *fp)
183 {
184         char line[LINE_MAX];
185         int num, rtn;
186
187         for (num = 1; fgets(line, sizeof(line), fp) != NULL; num++) {
188                 if (*line == '#')
189                         continue;
190                 if ((rtn = DoParseCommand(line)) != 0) {
191                         warnx("line %d: error in file", num);
192                         return(rtn);
193                 }
194         }
195         return(CMDRTN_OK);
196 }
197
198 /*
199  * Interactive mode
200  */
201 static int
202 DoInteractive(void)
203 {
204         const int maxfd = MAX(csock, dsock) + 1;
205
206         (*help_cmd.func)(0, NULL);
207         while (1) {
208                 struct timeval tv;
209                 fd_set rfds;
210
211                 /* See if any data or control messages are arriving */
212                 FD_ZERO(&rfds);
213                 FD_SET(csock, &rfds);
214                 FD_SET(dsock, &rfds);
215                 memset(&tv, 0, sizeof(tv));
216                 if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) {
217
218                         /* Issue prompt and wait for anything to happen */
219                         printf("%s", PROMPT);
220                         fflush(stdout);
221                         FD_ZERO(&rfds);
222                         FD_SET(0, &rfds);
223                         FD_SET(csock, &rfds);
224                         FD_SET(dsock, &rfds);
225                         if (select(maxfd, &rfds, NULL, NULL, NULL) < 0)
226                                 err(EX_OSERR, "select");
227
228                         /* If not user input, print a newline first */
229                         if (!FD_ISSET(0, &rfds))
230                                 printf("\n");
231                 }
232
233                 /* Display any incoming control message */
234                 if (FD_ISSET(csock, &rfds))
235                         MsgRead();
236
237                 /* Display any incoming data packet */
238                 if (FD_ISSET(dsock, &rfds)) {
239                         u_char buf[8192];
240                         char hook[NG_HOOKLEN + 1];
241                         int rl;
242
243                         /* Read packet from socket */
244                         if ((rl = NgRecvData(dsock,
245                             buf, sizeof(buf), hook)) < 0)
246                                 err(EX_OSERR, "reading hook \"%s\"", hook);
247                         if (rl == 0)
248                                 errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
249
250                         /* Write packet to stdout */
251                         printf("Rec'd data packet on hook \"%s\":\n", hook);
252                         DumpAscii(buf, rl);
253                 }
254
255                 /* Get any user input */
256                 if (FD_ISSET(0, &rfds)) {
257                         char buf[LINE_MAX];
258
259                         if (fgets(buf, sizeof(buf), stdin) == NULL) {
260                                 printf("\n");
261                                 break;
262                         }
263                         if (DoParseCommand(buf) == CMDRTN_QUIT)
264                                 break;
265                 }
266         }
267         return(CMDRTN_QUIT);
268 }
269
270 /*
271  * Parse a command line and execute the command
272  */
273 static int
274 DoParseCommand(char *line)
275 {
276         char *av[MAX_ARGS];
277         int ac;
278
279         /* Parse line */
280         for (ac = 0, av[0] = strtok(line, WHITESPACE);
281             ac < MAX_ARGS - 1 && av[ac];
282             av[++ac] = strtok(NULL, WHITESPACE));
283
284         /* Do command */
285         return(DoCommand(ac, av));
286 }
287
288 /*
289  * Execute the command
290  */
291 static int
292 DoCommand(int ac, char **av)
293 {
294         const struct ngcmd *cmd;
295         int rtn;
296
297         if (ac == 0 || *av[0] == 0)
298                 return(CMDRTN_OK);
299         if ((cmd = FindCommand(av[0])) == NULL)
300                 return(CMDRTN_ERROR);
301         if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE)
302                 warnx("usage: %s", cmd->cmd);
303         return(rtn);
304 }
305
306 /*
307  * Find a command
308  */
309 static const struct ngcmd *
310 FindCommand(const char *string)
311 {
312         int k, found = -1;
313
314         for (k = 0; cmds[k] != NULL; k++) {
315                 if (MatchCommand(cmds[k], string)) {
316                         if (found != -1) {
317                                 warnx("\"%s\": ambiguous command", string);
318                                 return(NULL);
319                         }
320                         found = k;
321                 }
322         }
323         if (found == -1) {
324                 warnx("\"%s\": unknown command", string);
325                 return(NULL);
326         }
327         return(cmds[found]);
328 }
329
330 /*
331  * See if string matches a prefix of "cmd" (or an alias) case insensitively
332  */
333 static int
334 MatchCommand(const struct ngcmd *cmd, const char *s)
335 {
336         int a;
337
338         /* Try to match command, ignoring the usage stuff */
339         if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) {
340                 if (strncasecmp(s, cmd->cmd, strlen(s)) == 0)
341                         return (1);
342         }
343
344         /* Try to match aliases */
345         for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) {
346                 if (strlen(cmd->aliases[a]) >= strlen(s)) {
347                         if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0)
348                                 return (1);
349                 }
350         }
351
352         /* No match */
353         return (0);
354 }
355
356 /*
357  * ReadCmd()
358  */
359 static int
360 ReadCmd(int ac, char **av)
361 {
362         FILE *fp;
363         int rtn;
364
365         /* Open file */
366         switch (ac) {
367         case 2:
368                 if ((fp = fopen(av[1], "r")) == NULL) {
369                         warn("%s", av[1]);
370                         return(CMDRTN_ERROR);
371                 }
372                 break;
373         default:
374                 return(CMDRTN_USAGE);
375         }
376
377         /* Process it */
378         rtn = ReadFile(fp);
379         fclose(fp);
380         return(rtn);
381 }
382
383 /*
384  * HelpCmd()
385  */
386 static int
387 HelpCmd(int ac, char **av)
388 {
389         const struct ngcmd *cmd;
390         int k;
391
392         switch (ac) {
393         case 0:
394         case 1:
395                 /* Show all commands */
396                 printf("Available commands:\n");
397                 for (k = 0; cmds[k] != NULL; k++) {
398                         char *s, buf[100];
399
400                         cmd = cmds[k];
401                         snprintf(buf, sizeof(buf), "%s", cmd->cmd);
402                         for (s = buf; *s != '\0' && !isspace(*s); s++);
403                         *s = '\0';
404                         printf("  %-10s %s\n", buf, cmd->desc);
405                 }
406                 return(CMDRTN_OK);
407         default:
408                 /* Show help on a specific command */
409                 if ((cmd = FindCommand(av[1])) != NULL) {
410                         printf("Usage:    %s\n", cmd->cmd);
411                         if (cmd->aliases[0] != NULL) {
412                                 int a = 0;
413
414                                 printf("Aliases:  ");
415                                 while (1) {
416                                         printf("%s", cmd->aliases[a++]);
417                                         if (a == MAX_CMD_ALIAS
418                                             || cmd->aliases[a] == NULL) {
419                                                 printf("\n");
420                                                 break;
421                                         }
422                                         printf(", ");
423                                 }
424                         }
425                         printf("Summary:  %s\n", cmd->desc);
426                         if (cmd->help != NULL) {
427                                 const char *s;
428                                 char buf[65];
429                                 int tot, len, done;
430
431                                 printf("Description:\n");
432                                 for (s = cmd->help; *s != '\0'; s += len) {
433                                         while (isspace(*s))
434                                                 s++;
435                                         tot = snprintf(buf,
436                                             sizeof(buf), "%s", s);
437                                         len = strlen(buf);
438                                         done = len == tot;
439                                         if (!done) {
440                                                 while (len > 0
441                                                     && !isspace(buf[len-1]))
442                                                         buf[--len] = '\0';
443                                         }
444                                         printf("  %s\n", buf);
445                                 }
446                         }
447                 }
448         }
449         return(CMDRTN_OK);
450 }
451
452 /*
453  * QuitCmd()
454  */
455 static int
456 QuitCmd(int ac, char **av)
457 {
458         return(CMDRTN_QUIT);
459 }
460
461 /*
462  * Dump data in hex and ASCII form
463  */
464 void
465 DumpAscii(const u_char *buf, int len)
466 {
467         char ch, sbuf[100];
468         int k, count;
469
470         for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) {
471                 snprintf(sbuf, sizeof(sbuf), "%04x:  ", count);
472                 for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
473                         if (count + k < len) {
474                                 snprintf(sbuf + strlen(sbuf),
475                                     sizeof(sbuf) - strlen(sbuf),
476                                     "%02x ", buf[count + k]);
477                         } else {
478                                 snprintf(sbuf + strlen(sbuf),
479                                     sizeof(sbuf) - strlen(sbuf), "   ");
480                         }
481                 }
482                 snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
483                 for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
484                         if (count + k < len) {
485                                 ch = isprint(buf[count + k]) ?
486                                     buf[count + k] : '.';
487                                 snprintf(sbuf + strlen(sbuf),
488                                     sizeof(sbuf) - strlen(sbuf), "%c", ch);
489                         } else {
490                                 snprintf(sbuf + strlen(sbuf),
491                                     sizeof(sbuf) - strlen(sbuf), " ");
492                         }
493                 }
494                 printf("%s\n", sbuf);
495         }
496 }
497
498 /*
499  * Usage()
500  */
501 static void
502 Usage(const char *msg)
503 {
504         if (msg)
505                 warnx("%s", msg);
506         errx(EX_USAGE, "usage: ngctl [-d] [-f file] [-n name] [command ...]");
507 }
508