5 * Copyright (c) 1996-1999 Whistle Communications, Inc.
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.
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
38 * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $
41 #include <sys/param.h>
42 #include <sys/socket.h>
43 #include <sys/select.h>
66 #define WHITESPACE " \t\r\n\v\f"
67 #define DUMP_BYTES_PER_LINE 16
69 /* Internal functions */
70 static int ReadFile(FILE *fp
);
71 static void ReadSockets(fd_set
*);
72 static int DoParseCommand(const char *line
);
73 static int DoCommand(int ac
, char **av
);
74 static int DoInteractive(void);
75 static const struct ngcmd
*FindCommand(const char *string
);
76 static int MatchCommand(const struct ngcmd
*cmd
, const char *s
);
77 static void Usage(const char *msg
);
78 static int ReadCmd(int ac
, char **av
);
79 static int HelpCmd(int ac
, char **av
);
80 static int QuitCmd(int ac
, char **av
);
82 static sig_atomic_t unblock
;
83 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
84 static pthread_cond_t cond
= PTHREAD_COND_INITIALIZER
;
87 /* List of commands */
88 static const struct ngcmd
*const cmds
[] = {
109 /* Commands defined in this file */
110 const struct ngcmd read_cmd
= {
113 "Read and execute commands from a file",
117 const struct ngcmd help_cmd
= {
120 "Show command summary or get more help on a specific command",
124 const struct ngcmd quit_cmd
= {
132 /* Our control and data sockets */
139 main(int ac
, char *av
[])
141 char name
[NG_NODESIZ
];
142 int interactive
= isatty(0) && isatty(1);
146 /* Set default node name */
147 snprintf(name
, sizeof(name
), "ngctl%d", getpid());
149 /* Parse command line */
150 while ((ch
= getopt(ac
, av
, "df:n:")) != -1) {
153 NgSetDebug(NgSetDebug(-1) + 1);
156 if (strcmp(optarg
, "-") == 0)
158 else if ((fp
= fopen(optarg
, "r")) == NULL
)
159 err(EX_NOINPUT
, "%s", optarg
);
162 snprintf(name
, sizeof(name
), "%s", optarg
);
173 /* Create a new socket node */
174 if (NgMkSockNode(name
, &csock
, &dsock
) < 0)
175 err(EX_OSERR
, "can't create node");
177 /* Do commands as requested */
181 } else if (interactive
) {
182 rtn
= DoInteractive();
184 Usage("no command specified");
186 rtn
= DoCommand(ac
, av
);
189 /* Convert command return code into system exit code */
206 * Process commands from a file
214 for (num
= 1; fgets(line
, sizeof(line
), fp
) != NULL
; num
++) {
217 if ((rtn
= DoParseCommand(line
)) != 0) {
218 warnx("line %d: error in file", num
);
226 /* Signal handler for Monitor() thread. */
235 * Thread that monitors csock and dsock while main thread
236 * can be blocked in el_gets().
241 struct sigaction act
;
242 const int maxfd
= MAX(csock
, dsock
) + 1;
244 act
.sa_handler
= Unblock
;
245 sigemptyset(&act
.sa_mask
);
247 sigaction(SIGUSR1
, &act
, NULL
);
249 pthread_mutex_lock(&mutex
);
253 /* See if any data or control messages are arriving. */
255 FD_SET(csock
, &rfds
);
256 FD_SET(dsock
, &rfds
);
258 if (select(maxfd
, &rfds
, NULL
, NULL
, NULL
) <= 0) {
259 if (errno
== EINTR
) {
261 pthread_cond_wait(&cond
, &mutex
);
264 err(EX_OSERR
, "select");
280 * Here we start a thread, that will monitor the netgraph
281 * sockets and catch any unexpected messages or data on them,
282 * that can arrive while user edits his/her commands.
284 * Whenever we expect data on netgraph sockets, we send signal
285 * to monitoring thread. The signal forces it to exit select()
286 * system call and sleep on condvar until we wake it. While
287 * monitoring thread sleeps, we can do our work with netgraph
296 HistEvent hev
= { 0, "" };
298 (*help_cmd
.func
)(0, NULL
);
299 pthread_create(&monitor
, NULL
, Monitor
, NULL
);
300 el
= el_init(getprogname(), stdin
, stdout
, stderr
);
302 return (CMDRTN_ERROR
);
303 el_set(el
, EL_PROMPT
, Prompt
);
304 el_set(el
, EL_SIGNAL
, 1);
305 el_set(el
, EL_EDITOR
, "emacs");
306 hist
= history_init();
308 return (CMDRTN_ERROR
);
309 history(hist
, &hev
, H_SETSIZE
, 100);
310 history(hist
, &hev
, H_SETUNIQUE
, 1);
311 el_set(el
, EL_HIST
, history
, (const char *)hist
);
318 if ((buf
= el_gets(el
, &count
)) == NULL
) {
322 history(hist
, &hev
, H_ENTER
, buf
);
323 pthread_kill(monitor
, SIGUSR1
);
324 pthread_mutex_lock(&mutex
);
325 if (DoParseCommand(buf
) == CMDRTN_QUIT
)
327 pthread_cond_signal(&cond
);
328 pthread_mutex_unlock(&mutex
);
333 pthread_cancel(monitor
);
335 return (CMDRTN_QUIT
);
338 #else /* !EDITLINE */
341 * Interactive mode w/o libedit functionality.
346 const int maxfd
= MAX(csock
, dsock
) + 1;
348 (*help_cmd
.func
)(0, NULL
);
353 /* See if any data or control messages are arriving */
355 FD_SET(csock
, &rfds
);
356 FD_SET(dsock
, &rfds
);
357 memset(&tv
, 0, sizeof(tv
));
358 if (select(maxfd
, &rfds
, NULL
, NULL
, &tv
) <= 0) {
360 /* Issue prompt and wait for anything to happen */
361 printf("%s", PROMPT
);
365 FD_SET(csock
, &rfds
);
366 FD_SET(dsock
, &rfds
);
367 if (select(maxfd
, &rfds
, NULL
, NULL
, NULL
) < 0)
368 err(EX_OSERR
, "select");
370 /* If not user input, print a newline first */
371 if (!FD_ISSET(0, &rfds
))
377 /* Get any user input */
378 if (FD_ISSET(0, &rfds
)) {
381 if (fgets(buf
, sizeof(buf
), stdin
) == NULL
) {
385 if (DoParseCommand(buf
) == CMDRTN_QUIT
)
389 return (CMDRTN_QUIT
);
391 #endif /* !EDITLINE */
394 * Read and process data on netgraph control and data sockets.
397 ReadSockets(fd_set
*rfds
)
399 /* Display any incoming control message. */
400 if (FD_ISSET(csock
, rfds
))
403 /* Display any incoming data packet. */
404 if (FD_ISSET(dsock
, rfds
)) {
405 char hook
[NG_HOOKSIZ
];
409 /* Read packet from socket. */
410 if ((rl
= NgAllocRecvData(dsock
, &buf
, hook
)) < 0)
411 err(EX_OSERR
, "reading hook \"%s\"", hook
);
413 errx(EX_OSERR
, "EOF from hook \"%s\"?", hook
);
415 /* Write packet to stdout. */
416 printf("Rec'd data packet on hook \"%s\":\n", hook
);
423 * Parse a command line and execute the command
426 DoParseCommand(const char *line
)
432 for (ac
= 0, av
[0] = strtok((char *)line
, WHITESPACE
);
433 ac
< MAX_ARGS
- 1 && av
[ac
];
434 av
[++ac
] = strtok(NULL
, WHITESPACE
));
437 return (DoCommand(ac
, av
));
441 * Execute the command
444 DoCommand(int ac
, char **av
)
446 const struct ngcmd
*cmd
;
449 if (ac
== 0 || *av
[0] == 0)
451 if ((cmd
= FindCommand(av
[0])) == NULL
)
452 return (CMDRTN_ERROR
);
453 if ((rtn
= (*cmd
->func
)(ac
, av
)) == CMDRTN_USAGE
)
454 warnx("usage: %s", cmd
->cmd
);
461 static const struct ngcmd
*
462 FindCommand(const char *string
)
466 for (k
= 0; cmds
[k
] != NULL
; k
++) {
467 if (MatchCommand(cmds
[k
], string
)) {
469 warnx("\"%s\": ambiguous command", string
);
476 warnx("\"%s\": unknown command", string
);
479 return (cmds
[found
]);
483 * See if string matches a prefix of "cmd" (or an alias) case insensitively
486 MatchCommand(const struct ngcmd
*cmd
, const char *s
)
490 /* Try to match command, ignoring the usage stuff */
491 if (strlen(s
) <= strcspn(cmd
->cmd
, WHITESPACE
)) {
492 if (strncasecmp(s
, cmd
->cmd
, strlen(s
)) == 0)
496 /* Try to match aliases */
497 for (a
= 0; a
< MAX_CMD_ALIAS
&& cmd
->aliases
[a
] != NULL
; a
++) {
498 if (strlen(cmd
->aliases
[a
]) >= strlen(s
)) {
499 if (strncasecmp(s
, cmd
->aliases
[a
], strlen(s
)) == 0)
512 ReadCmd(int ac
, char **av
)
520 if ((fp
= fopen(av
[1], "r")) == NULL
) {
522 return (CMDRTN_ERROR
);
526 return (CMDRTN_USAGE
);
539 HelpCmd(int ac
, char **av
)
541 const struct ngcmd
*cmd
;
547 /* Show all commands */
548 printf("Available commands:\n");
549 for (k
= 0; cmds
[k
] != NULL
; k
++) {
553 snprintf(buf
, sizeof(buf
), "%s", cmd
->cmd
);
554 for (s
= buf
; *s
!= '\0' && !isspace(*s
); s
++);
556 printf(" %-10s %s\n", buf
, cmd
->desc
);
560 /* Show help on a specific command */
561 if ((cmd
= FindCommand(av
[1])) != NULL
) {
562 printf("usage: %s\n", cmd
->cmd
);
563 if (cmd
->aliases
[0] != NULL
) {
568 printf("%s", cmd
->aliases
[a
++]);
569 if (a
== MAX_CMD_ALIAS
570 || cmd
->aliases
[a
] == NULL
) {
577 printf("Summary: %s\n", cmd
->desc
);
578 if (cmd
->help
!= NULL
) {
583 printf("Description:\n");
584 for (s
= cmd
->help
; *s
!= '\0'; s
+= len
) {
588 sizeof(buf
), "%s", s
);
593 && !isspace(buf
[len
-1]))
596 printf(" %s\n", buf
);
608 QuitCmd(int ac __unused
, char **av __unused
)
610 return (CMDRTN_QUIT
);
614 * Dump data in hex and ASCII form
617 DumpAscii(const u_char
*buf
, int len
)
622 for (count
= 0; count
< len
; count
+= DUMP_BYTES_PER_LINE
) {
623 snprintf(sbuf
, sizeof(sbuf
), "%04x: ", count
);
624 for (k
= 0; k
< DUMP_BYTES_PER_LINE
; k
++) {
625 if (count
+ k
< len
) {
626 snprintf(sbuf
+ strlen(sbuf
),
627 sizeof(sbuf
) - strlen(sbuf
),
628 "%02x ", buf
[count
+ k
]);
630 snprintf(sbuf
+ strlen(sbuf
),
631 sizeof(sbuf
) - strlen(sbuf
), " ");
634 snprintf(sbuf
+ strlen(sbuf
), sizeof(sbuf
) - strlen(sbuf
), " ");
635 for (k
= 0; k
< DUMP_BYTES_PER_LINE
; k
++) {
636 if (count
+ k
< len
) {
637 ch
= isprint(buf
[count
+ k
]) ?
638 buf
[count
+ k
] : '.';
639 snprintf(sbuf
+ strlen(sbuf
),
640 sizeof(sbuf
) - strlen(sbuf
), "%c", ch
);
642 snprintf(sbuf
+ strlen(sbuf
),
643 sizeof(sbuf
) - strlen(sbuf
), " ");
646 printf("%s\n", sbuf
);
654 Usage(const char *msg
)
659 "usage: ngctl [-d] [-f file] [-n name] [command ...]\n");