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