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