main.c revision 61880
152419Sjulian 252419Sjulian/* 352419Sjulian * main.c 452419Sjulian * 552419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 652419Sjulian * All rights reserved. 752419Sjulian * 852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 952419Sjulian * redistribution of this software, in source or object code forms, with or 1052419Sjulian * without modifications are expressly permitted by Whistle Communications; 1152419Sjulian * provided, however, that: 1252419Sjulian * 1. Any and all reproductions of the source or object code must include the 1352419Sjulian * copyright notice above and the following disclaimer of warranties; and 1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1552419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1652419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1752419Sjulian * such appears in the above copyright notice or in the software. 1852419Sjulian * 1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3552419Sjulian * OF SUCH DAMAGE. 3652419Sjulian * 3752419Sjulian * $FreeBSD: head/usr.sbin/ngctl/main.c 61880 2000-06-20 18:51:38Z archie $ 3853913Sarchie * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $ 3952419Sjulian */ 4052419Sjulian 4152419Sjulian#include "ngctl.h" 4252419Sjulian 4353913Sarchie#define PROMPT "+ " 4453913Sarchie#define MAX_ARGS 512 4553913Sarchie#define WHITESPACE " \t\r\n\v\f" 4653913Sarchie#define DUMP_BYTES_PER_LINE 16 4752419Sjulian 4852419Sjulian/* Internal functions */ 4952419Sjulianstatic int ReadFile(FILE *fp); 5052419Sjulianstatic int DoParseCommand(char *line); 5152419Sjulianstatic int DoCommand(int ac, char **av); 5252419Sjulianstatic int DoInteractive(void); 5352419Sjulianstatic const struct ngcmd *FindCommand(const char *string); 5453913Sarchiestatic int MatchCommand(const struct ngcmd *cmd, const char *s); 5552419Sjulianstatic void Usage(const char *msg); 5652419Sjulianstatic int ReadCmd(int ac, char **av); 5752419Sjulianstatic int HelpCmd(int ac, char **av); 5852419Sjulianstatic int QuitCmd(int ac, char **av); 5952419Sjulian 6052419Sjulian/* List of commands */ 6152419Sjulianstatic const struct ngcmd *const cmds[] = { 6252419Sjulian &connect_cmd, 6352419Sjulian &debug_cmd, 6452419Sjulian &help_cmd, 6552419Sjulian &list_cmd, 6652419Sjulian &mkpeer_cmd, 6753913Sarchie &msg_cmd, 6852419Sjulian &name_cmd, 6952419Sjulian &read_cmd, 7052419Sjulian &rmhook_cmd, 7152419Sjulian &show_cmd, 7252419Sjulian &shutdown_cmd, 7352419Sjulian &status_cmd, 7452419Sjulian &types_cmd, 7552419Sjulian &quit_cmd, 7652419Sjulian NULL 7752419Sjulian}; 7852419Sjulian 7952419Sjulian/* Commands defined in this file */ 8052419Sjulianconst struct ngcmd read_cmd = { 8152419Sjulian ReadCmd, 8252419Sjulian "read <filename>", 8352419Sjulian "Read and execute commands from a file", 8453913Sarchie NULL, 8553913Sarchie { "source", "." } 8652419Sjulian}; 8752419Sjulianconst struct ngcmd help_cmd = { 8852419Sjulian HelpCmd, 8952419Sjulian "help [command]", 9052419Sjulian "Show command summary or get more help on a specific command", 9153913Sarchie NULL, 9253913Sarchie { "?" } 9352419Sjulian}; 9452419Sjulianconst struct ngcmd quit_cmd = { 9552419Sjulian QuitCmd, 9652419Sjulian "quit", 9752419Sjulian "Exit program", 9853913Sarchie NULL, 9953913Sarchie { "exit" } 10052419Sjulian}; 10152419Sjulian 10252419Sjulian/* Our control and data sockets */ 10352419Sjulianint csock, dsock; 10452419Sjulian 10552419Sjulian/* 10652419Sjulian * main() 10752419Sjulian */ 10852419Sjulianint 10952419Sjulianmain(int ac, char *av[]) 11052419Sjulian{ 11152419Sjulian char name[NG_NODELEN + 1]; 11252419Sjulian int interactive = isatty(0) && isatty(1); 11352419Sjulian FILE *fp = NULL; 11452419Sjulian int ch, rtn = 0; 11552419Sjulian 11652419Sjulian /* Set default node name */ 11752419Sjulian snprintf(name, sizeof(name), "ngctl%d", getpid()); 11852419Sjulian 11952419Sjulian /* Parse command line */ 12052419Sjulian while ((ch = getopt(ac, av, "df:n:")) != EOF) { 12152419Sjulian switch (ch) { 12252419Sjulian case 'd': 12352419Sjulian NgSetDebug(NgSetDebug(-1) + 1); 12452419Sjulian break; 12552419Sjulian case 'f': 12652419Sjulian if (strcmp(optarg, "-") == 0) 12752419Sjulian fp = stdin; 12852419Sjulian else if ((fp = fopen(optarg, "r")) == NULL) 12952419Sjulian err(EX_NOINPUT, "%s", optarg); 13052419Sjulian break; 13152419Sjulian case 'n': 13252419Sjulian snprintf(name, sizeof(name), "%s", optarg); 13352419Sjulian break; 13452419Sjulian case '?': 13552419Sjulian default: 13652419Sjulian Usage((char *)NULL); 13752419Sjulian break; 13852419Sjulian } 13952419Sjulian } 14052419Sjulian ac -= optind; 14152419Sjulian av += optind; 14252419Sjulian 14352419Sjulian /* Create a new socket node */ 14456709Sarchie if (NgMkSockNode(name, &csock, &dsock) < 0) 14552419Sjulian err(EX_OSERR, "can't create node"); 14652419Sjulian 14752419Sjulian /* Do commands as requested */ 14852419Sjulian if (ac == 0) { 14952419Sjulian if (fp != NULL) { 15052419Sjulian rtn = ReadFile(fp); 15152419Sjulian } else if (interactive) { 15252419Sjulian rtn = DoInteractive(); 15352419Sjulian } else 15452419Sjulian Usage("no command specified"); 15552419Sjulian } else { 15652419Sjulian rtn = DoCommand(ac, av); 15752419Sjulian } 15852419Sjulian 15952419Sjulian /* Convert command return code into system exit code */ 16052419Sjulian switch (rtn) { 16152419Sjulian case CMDRTN_OK: 16252419Sjulian case CMDRTN_QUIT: 16352419Sjulian rtn = 0; 16452419Sjulian break; 16552419Sjulian case CMDRTN_USAGE: 16652419Sjulian rtn = EX_USAGE; 16752419Sjulian break; 16852419Sjulian case CMDRTN_ERROR: 16952419Sjulian rtn = EX_OSERR; 17052419Sjulian break; 17152419Sjulian } 17252419Sjulian return(rtn); 17352419Sjulian} 17452419Sjulian 17552419Sjulian/* 17652419Sjulian * Process commands from a file 17752419Sjulian */ 17852419Sjulianstatic int 17952419SjulianReadFile(FILE *fp) 18052419Sjulian{ 18152419Sjulian char line[LINE_MAX]; 18252419Sjulian int num, rtn; 18352419Sjulian 18452419Sjulian for (num = 1; fgets(line, sizeof(line), fp) != NULL; num++) { 18552419Sjulian if (*line == '#') 18652419Sjulian continue; 18752419Sjulian if ((rtn = DoParseCommand(line)) != 0) { 18852419Sjulian warnx("line %d: error in file", num); 18952419Sjulian return(rtn); 19052419Sjulian } 19152419Sjulian } 19252419Sjulian return(CMDRTN_OK); 19352419Sjulian} 19452419Sjulian 19552419Sjulian/* 19652419Sjulian * Interactive mode 19752419Sjulian */ 19852419Sjulianstatic int 19952419SjulianDoInteractive(void) 20052419Sjulian{ 20153913Sarchie const int maxfd = MAX(csock, dsock) + 1; 20252419Sjulian 20352419Sjulian (*help_cmd.func)(0, NULL); 20453913Sarchie while (1) { 20553913Sarchie struct timeval tv; 20653913Sarchie fd_set rfds; 20753913Sarchie 20853913Sarchie /* See if any data or control messages are arriving */ 20953913Sarchie FD_ZERO(&rfds); 21053913Sarchie FD_SET(csock, &rfds); 21153913Sarchie FD_SET(dsock, &rfds); 21253913Sarchie memset(&tv, 0, sizeof(tv)); 21353913Sarchie if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) { 21453913Sarchie 21553913Sarchie /* Issue prompt and wait for anything to happen */ 21653913Sarchie printf("%s", PROMPT); 21753913Sarchie fflush(stdout); 21853913Sarchie FD_ZERO(&rfds); 21953913Sarchie FD_SET(0, &rfds); 22053913Sarchie FD_SET(csock, &rfds); 22153913Sarchie FD_SET(dsock, &rfds); 22253913Sarchie if (select(maxfd, &rfds, NULL, NULL, NULL) < 0) 22353913Sarchie err(EX_OSERR, "select"); 22453913Sarchie 22553913Sarchie /* If not user input, print a newline first */ 22653913Sarchie if (!FD_ISSET(0, &rfds)) 22753913Sarchie printf("\n"); 22853913Sarchie } 22953913Sarchie 23053913Sarchie /* Display any incoming control message */ 23161880Sarchie if (FD_ISSET(csock, &rfds)) 23261880Sarchie MsgRead(); 23353913Sarchie 23453913Sarchie /* Display any incoming data packet */ 23561880Sarchie if (FD_ISSET(dsock, &rfds)) { 23653913Sarchie u_char buf[8192]; 23753913Sarchie char hook[NG_HOOKLEN + 1]; 23853913Sarchie int rl; 23953913Sarchie 24053913Sarchie /* Read packet from socket */ 24153913Sarchie if ((rl = NgRecvData(dsock, 24253913Sarchie buf, sizeof(buf), hook)) < 0) 24361880Sarchie err(EX_OSERR, "reading hook \"%s\"", hook); 24453913Sarchie if (rl == 0) 24553913Sarchie errx(EX_OSERR, "EOF from hook \"%s\"?", hook); 24653913Sarchie 24753913Sarchie /* Write packet to stdout */ 24853913Sarchie printf("Rec'd data packet on hook \"%s\":\n", hook); 24953913Sarchie DumpAscii(buf, rl); 25053913Sarchie } 25153913Sarchie 25253913Sarchie /* Get any user input */ 25353913Sarchie if (FD_ISSET(0, &rfds)) { 25453913Sarchie char buf[LINE_MAX]; 25553913Sarchie 25653913Sarchie if (fgets(buf, sizeof(buf), stdin) == NULL) { 25753913Sarchie printf("\n"); 25853913Sarchie break; 25953913Sarchie } 26053913Sarchie if (DoParseCommand(buf) == CMDRTN_QUIT) 26153913Sarchie break; 26253913Sarchie } 26353913Sarchie } 26452419Sjulian return(CMDRTN_QUIT); 26552419Sjulian} 26652419Sjulian 26752419Sjulian/* 26852419Sjulian * Parse a command line and execute the command 26952419Sjulian */ 27052419Sjulianstatic int 27152419SjulianDoParseCommand(char *line) 27252419Sjulian{ 27352419Sjulian char *av[MAX_ARGS]; 27452419Sjulian int ac; 27552419Sjulian 27652419Sjulian /* Parse line */ 27752419Sjulian for (ac = 0, av[0] = strtok(line, WHITESPACE); 27852419Sjulian ac < MAX_ARGS - 1 && av[ac]; 27952419Sjulian av[++ac] = strtok(NULL, WHITESPACE)); 28052419Sjulian 28152419Sjulian /* Do command */ 28252419Sjulian return(DoCommand(ac, av)); 28352419Sjulian} 28452419Sjulian 28552419Sjulian/* 28652419Sjulian * Execute the command 28752419Sjulian */ 28852419Sjulianstatic int 28952419SjulianDoCommand(int ac, char **av) 29052419Sjulian{ 29152419Sjulian const struct ngcmd *cmd; 29252419Sjulian int rtn; 29352419Sjulian 29452419Sjulian if (ac == 0 || *av[0] == 0) 29552419Sjulian return(CMDRTN_OK); 29652419Sjulian if ((cmd = FindCommand(av[0])) == NULL) 29752419Sjulian return(CMDRTN_ERROR); 29852419Sjulian if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE) 29952419Sjulian warnx("usage: %s", cmd->cmd); 30052419Sjulian return(rtn); 30152419Sjulian} 30252419Sjulian 30352419Sjulian/* 30452419Sjulian * Find a command 30552419Sjulian */ 30652419Sjulianstatic const struct ngcmd * 30752419SjulianFindCommand(const char *string) 30852419Sjulian{ 30953913Sarchie int k, found = -1; 31052419Sjulian 31153913Sarchie for (k = 0; cmds[k] != NULL; k++) { 31253913Sarchie if (MatchCommand(cmds[k], string)) { 31352419Sjulian if (found != -1) { 31452419Sjulian warnx("\"%s\": ambiguous command", string); 31552419Sjulian return(NULL); 31652419Sjulian } 31752419Sjulian found = k; 31852419Sjulian } 31952419Sjulian } 32052419Sjulian if (found == -1) { 32152419Sjulian warnx("\"%s\": unknown command", string); 32252419Sjulian return(NULL); 32352419Sjulian } 32452419Sjulian return(cmds[found]); 32552419Sjulian} 32652419Sjulian 32752419Sjulian/* 32853913Sarchie * See if string matches a prefix of "cmd" (or an alias) case insensitively 32953913Sarchie */ 33053913Sarchiestatic int 33153913SarchieMatchCommand(const struct ngcmd *cmd, const char *s) 33253913Sarchie{ 33353913Sarchie int a; 33453913Sarchie 33553913Sarchie /* Try to match command, ignoring the usage stuff */ 33653913Sarchie if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) { 33753913Sarchie if (strncasecmp(s, cmd->cmd, strlen(s)) == 0) 33853913Sarchie return (1); 33953913Sarchie } 34053913Sarchie 34153913Sarchie /* Try to match aliases */ 34253913Sarchie for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) { 34353913Sarchie if (strlen(cmd->aliases[a]) >= strlen(s)) { 34453913Sarchie if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0) 34553913Sarchie return (1); 34653913Sarchie } 34753913Sarchie } 34853913Sarchie 34953913Sarchie /* No match */ 35053913Sarchie return (0); 35153913Sarchie} 35253913Sarchie 35353913Sarchie/* 35452419Sjulian * ReadCmd() 35552419Sjulian */ 35652419Sjulianstatic int 35752419SjulianReadCmd(int ac, char **av) 35852419Sjulian{ 35952419Sjulian FILE *fp; 36052419Sjulian int rtn; 36152419Sjulian 36252419Sjulian /* Open file */ 36352419Sjulian switch (ac) { 36452419Sjulian case 2: 36552419Sjulian if ((fp = fopen(av[1], "r")) == NULL) 36652419Sjulian warn("%s", av[1]); 36752419Sjulian return(CMDRTN_ERROR); 36852419Sjulian default: 36952419Sjulian return(CMDRTN_USAGE); 37052419Sjulian } 37152419Sjulian 37252419Sjulian /* Process it */ 37352419Sjulian rtn = ReadFile(fp); 37452419Sjulian fclose(fp); 37552419Sjulian return(rtn); 37652419Sjulian} 37752419Sjulian 37852419Sjulian/* 37952419Sjulian * HelpCmd() 38052419Sjulian */ 38152419Sjulianstatic int 38252419SjulianHelpCmd(int ac, char **av) 38352419Sjulian{ 38452419Sjulian const struct ngcmd *cmd; 38552419Sjulian int k; 38652419Sjulian 38752419Sjulian switch (ac) { 38852419Sjulian case 0: 38952419Sjulian case 1: 39052419Sjulian /* Show all commands */ 39152419Sjulian printf("Available commands:\n"); 39252419Sjulian for (k = 0; cmds[k] != NULL; k++) { 39352419Sjulian char *s, buf[100]; 39452419Sjulian 39552419Sjulian cmd = cmds[k]; 39652419Sjulian snprintf(buf, sizeof(buf), "%s", cmd->cmd); 39752419Sjulian for (s = buf; *s != '\0' && !isspace(*s); s++); 39852419Sjulian *s = '\0'; 39952419Sjulian printf(" %-10s %s\n", buf, cmd->desc); 40052419Sjulian } 40152419Sjulian return(CMDRTN_OK); 40252419Sjulian default: 40352419Sjulian /* Show help on a specific command */ 40452419Sjulian if ((cmd = FindCommand(av[1])) != NULL) { 40552419Sjulian printf("Usage: %s\n", cmd->cmd); 40653913Sarchie if (cmd->aliases[0] != NULL) { 40753913Sarchie int a = 0; 40853913Sarchie 40953913Sarchie printf("Aliases: "); 41053913Sarchie while (1) { 41153913Sarchie printf("%s", cmd->aliases[a++]); 41253913Sarchie if (a == MAX_CMD_ALIAS 41353913Sarchie || cmd->aliases[a] == NULL) { 41453913Sarchie printf("\n"); 41553913Sarchie break; 41653913Sarchie } 41753913Sarchie printf(", "); 41853913Sarchie } 41953913Sarchie } 42052419Sjulian printf("Summary: %s\n", cmd->desc); 42152419Sjulian if (cmd->help != NULL) { 42252419Sjulian const char *s; 42352419Sjulian char buf[65]; 42452419Sjulian int tot, len, done; 42552419Sjulian 42652419Sjulian printf("Description:\n"); 42752419Sjulian for (s = cmd->help; *s != '\0'; s += len) { 42852419Sjulian while (isspace(*s)) 42952419Sjulian s++; 43052419Sjulian tot = snprintf(buf, 43152419Sjulian sizeof(buf), "%s", s); 43252419Sjulian len = strlen(buf); 43352419Sjulian done = len == tot; 43452419Sjulian if (!done) { 43552419Sjulian while (len > 0 43652419Sjulian && !isspace(buf[len-1])) 43752419Sjulian buf[--len] = '\0'; 43852419Sjulian } 43952419Sjulian printf(" %s\n", buf); 44052419Sjulian } 44152419Sjulian } 44252419Sjulian } 44352419Sjulian } 44452419Sjulian return(CMDRTN_OK); 44552419Sjulian} 44652419Sjulian 44752419Sjulian/* 44852419Sjulian * QuitCmd() 44952419Sjulian */ 45052419Sjulianstatic int 45152419SjulianQuitCmd(int ac, char **av) 45252419Sjulian{ 45352419Sjulian return(CMDRTN_QUIT); 45452419Sjulian} 45552419Sjulian 45652419Sjulian/* 45753913Sarchie * Dump data in hex and ASCII form 45853913Sarchie */ 45961880Sarchievoid 46053913SarchieDumpAscii(const u_char *buf, int len) 46153913Sarchie{ 46253913Sarchie char ch, sbuf[100]; 46353913Sarchie int k, count; 46453913Sarchie 46553913Sarchie for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) { 46653913Sarchie snprintf(sbuf, sizeof(sbuf), "%04x: ", count); 46753913Sarchie for (k = 0; k < DUMP_BYTES_PER_LINE; k++) { 46853913Sarchie if (count + k < len) { 46953913Sarchie snprintf(sbuf + strlen(sbuf), 47053913Sarchie sizeof(sbuf) - strlen(sbuf), 47153913Sarchie "%02x ", buf[count + k]); 47253913Sarchie } else { 47353913Sarchie snprintf(sbuf + strlen(sbuf), 47453913Sarchie sizeof(sbuf) - strlen(sbuf), " "); 47553913Sarchie } 47653913Sarchie } 47753913Sarchie snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " "); 47853913Sarchie for (k = 0; k < DUMP_BYTES_PER_LINE; k++) { 47953913Sarchie if (count + k < len) { 48053913Sarchie ch = isprint(buf[count + k]) ? 48153913Sarchie buf[count + k] : '.'; 48253913Sarchie snprintf(sbuf + strlen(sbuf), 48353913Sarchie sizeof(sbuf) - strlen(sbuf), "%c", ch); 48453913Sarchie } else { 48553913Sarchie snprintf(sbuf + strlen(sbuf), 48653913Sarchie sizeof(sbuf) - strlen(sbuf), " "); 48753913Sarchie } 48853913Sarchie } 48953913Sarchie printf("%s\n", sbuf); 49053913Sarchie } 49153913Sarchie} 49253913Sarchie 49353913Sarchie/* 49452419Sjulian * Usage() 49552419Sjulian */ 49652419Sjulianstatic void 49752419SjulianUsage(const char *msg) 49852419Sjulian{ 49952419Sjulian if (msg) 50052419Sjulian warnx("%s", msg); 50152419Sjulian errx(EX_USAGE, "usage: ngctl [-d] [-f file] [-n name] [command ...]"); 50252419Sjulian} 50352419Sjulian 504