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