File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / client / client.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 19:50:23 2021 UTC (3 years, 4 months ago) by misho
Branches: bird, MAIN
CVS tags: v1_6_8p3, HEAD
bird 1.6.8

    1: /*
    2:  *	BIRD Client
    3:  *
    4:  *	(c) 1999--2004 Martin Mares <mj@ucw.cz>
    5:  *	(c) 2013 Tomas Hlavacek <tmshlvck@gmail.com>
    6:  *
    7:  *	Can be freely distributed and used under the terms of the GNU GPL.
    8:  */
    9: 
   10: /**
   11:  * DOC: BIRD client
   12:  *
   13:  * There are two variants of BIRD client: regular and light. regular
   14:  * variant depends on readline and ncurses libraries, while light
   15:  * variant uses just libc. Most of the code and the main() is common
   16:  * for both variants (in client.c file) and just a few functions are
   17:  * different (in birdc.c for regular and birdcl.c for light). Two
   18:  * binaries are generated by linking common object files like client.o
   19:  * (which is compiled from client.c just once) with either birdc.o or
   20:  * birdcl.o for each variant.
   21:  */
   22: 
   23: #include <stdio.h>
   24: #include <stdlib.h>
   25: #include <fcntl.h>
   26: #include <unistd.h>
   27: #include <errno.h>
   28: #include <sys/select.h>
   29: #include <sys/socket.h>
   30: #include <sys/types.h>
   31: #include <sys/un.h>
   32: 
   33: #include "nest/bird.h"
   34: #include "lib/resource.h"
   35: #include "lib/string.h"
   36: #include "client/client.h"
   37: #include "sysdep/unix/unix.h"
   38: 
   39: #define SERVER_READ_BUF_LEN 4096
   40: 
   41: static char *opt_list = "s:vrl";
   42: static int verbose, restricted, once;
   43: static char *init_cmd;
   44: 
   45: static char *server_path = PATH_CONTROL_SOCKET;
   46: static int server_fd;
   47: static byte server_read_buf[SERVER_READ_BUF_LEN];
   48: static byte *server_read_pos = server_read_buf;
   49: 
   50: int init = 1;		/* During intial sequence */
   51: int busy = 1;		/* Executing BIRD command */
   52: int interactive;	/* Whether stdin is terminal */
   53: 
   54: static int num_lines, skip_input;
   55: int term_lns, term_cls;
   56: 
   57: 
   58: /*** Parsing of arguments ***/
   59: 
   60: static void
   61: usage(char *name)
   62: {
   63:   fprintf(stderr, "Usage: %s [-s <control-socket>] [-v] [-r] [-l]\n", name);
   64:   exit(1);
   65: }
   66: 
   67: static void
   68: parse_args(int argc, char **argv)
   69: {
   70:   int server_changed = 0;
   71:   int c;
   72: 
   73:   while ((c = getopt(argc, argv, opt_list)) >= 0)
   74:     switch (c)
   75:       {
   76:       case 's':
   77: 	server_path = optarg;
   78: 	server_changed = 1;
   79: 	break;
   80:       case 'v':
   81: 	verbose++;
   82: 	break;
   83:       case 'r':
   84: 	restricted = 1;
   85: 	break;
   86:       case 'l':
   87: 	if (!server_changed)
   88: 	  server_path = xbasename(server_path);
   89: 	break;
   90:       default:
   91: 	usage(argv[0]);
   92:       }
   93: 
   94:   /* If some arguments are not options, we take it as commands */
   95:   if (optind < argc)
   96:     {
   97:       char *tmp;
   98:       int i;
   99:       int len = 0;
  100: 
  101:       for (i = optind; i < argc; i++)
  102: 	len += strlen(argv[i]) + 1;
  103: 
  104:       tmp = init_cmd = malloc(len);
  105:       for (i = optind; i < argc; i++)
  106: 	{
  107: 	  strcpy(tmp, argv[i]);
  108: 	  tmp += strlen(tmp);
  109: 	  *tmp++ = ' ';
  110: 	}
  111:       tmp[-1] = 0;
  112: 
  113:       once = 1;
  114:       interactive = 0;
  115:     }
  116: }
  117: 
  118: 
  119: /*** Input ***/
  120: 
  121: static void server_send(char *cmd);
  122: 
  123: static int
  124: handle_internal_command(char *cmd)
  125: {
  126:   if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4))
  127:     {
  128:       cleanup();
  129:       exit(0);
  130:     }
  131:   if (!strncmp(cmd, "help", 4))
  132:     {
  133:       puts("Press `?' for context sensitive help.");
  134:       return 1;
  135:     }
  136:   return 0;
  137: }
  138: 
  139: static void
  140: submit_server_command(char *cmd)
  141: {
  142:   busy = 1;
  143:   num_lines = 2;
  144:   server_send(cmd);
  145: }
  146: 
  147: static inline void
  148: submit_init_command(char *cmd_raw)
  149: {
  150:   char *cmd = cmd_expand(cmd_raw);
  151: 
  152:   if (!cmd)
  153:   {
  154:     cleanup();
  155:     exit(0);
  156:   }
  157: 
  158:   submit_server_command(cmd);
  159:   free(cmd);
  160: }
  161: 
  162: void
  163: submit_command(char *cmd_raw)
  164: {
  165:   char *cmd = cmd_expand(cmd_raw);
  166: 
  167:   if (!cmd)
  168:     return;
  169: 
  170:   if (!handle_internal_command(cmd))
  171:     submit_server_command(cmd);
  172: 
  173:   free(cmd);
  174: }
  175: 
  176: static void
  177: init_commands(void)
  178: {
  179:   if (restricted)
  180:     {
  181:        submit_server_command("restrict");
  182:        restricted = 0;
  183:        return;
  184:     }
  185: 
  186:   if (init_cmd)
  187:     {
  188:       /* First transition - client received hello from BIRD
  189: 	 and there is waiting initial command */
  190:       submit_init_command(init_cmd);
  191:       init_cmd = NULL;
  192:       return;
  193:     }
  194: 
  195:   if (once)
  196:     {
  197:       /* Initial command is finished and we want to exit */
  198:       cleanup();
  199:       exit(0);
  200:     }
  201: 
  202:   input_init();
  203: 
  204:   term_lns = (term_lns > 0) ? term_lns : 25;
  205:   term_cls = (term_cls > 0) ? term_cls : 80;
  206: 
  207:   init = 0;
  208: }
  209: 
  210: 
  211: /*** Output ***/
  212: 
  213: void
  214: more(void)
  215: {
  216:   more_begin();
  217:   printf("--More--\015");
  218:   fflush(stdout);
  219: 
  220:  redo:
  221:   switch (getchar())
  222:     {
  223:     case ' ':
  224:       num_lines = 2;
  225:       break;
  226:     case '\n':
  227:     case '\r':
  228:       num_lines--;
  229:       break;
  230:     case 'q':
  231:       skip_input = 1;
  232:       break;
  233:     default:
  234:       goto redo;
  235:     }
  236: 
  237:   printf("        \015");
  238:   fflush(stdout);
  239:   more_end();
  240: }
  241: 
  242: 
  243: /*** Communication with server ***/
  244: 
  245: static void
  246: server_connect(void)
  247: {
  248:   struct sockaddr_un sa;
  249: 
  250:   server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
  251:   if (server_fd < 0)
  252:     DIE("Cannot create socket");
  253: 
  254:   if (strlen(server_path) >= sizeof(sa.sun_path))
  255:     die("server_connect: path too long");
  256: 
  257:   bzero(&sa, sizeof(sa));
  258:   sa.sun_family = AF_UNIX;
  259:   strcpy(sa.sun_path, server_path);
  260:   if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
  261:     DIE("Unable to connect to server control socket (%s)", server_path);
  262:   if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
  263:     DIE("fcntl");
  264: }
  265: 
  266: 
  267: #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
  268: 
  269: static void
  270: server_got_reply(char *x)
  271: {
  272:   int code;
  273:   int len = 0;
  274: 
  275:   if (*x == '+')                        /* Async reply */
  276:     PRINTF(len, ">>> %s\n", x+1);
  277:   else if (x[0] == ' ')                 /* Continuation */
  278:     PRINTF(len, "%s%s\n", verbose ? "     " : "", x+1);
  279:   else if (strlen(x) > 4 &&
  280:            sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
  281:            (x[4] == ' ' || x[4] == '-'))
  282:     {
  283:       if (code)
  284:         PRINTF(len, "%s\n", verbose ? x : x+5);
  285: 
  286:       if (x[4] == ' ')
  287:       {
  288:         busy = 0;
  289:         skip_input = 0;
  290:         return;
  291:       }
  292:     }
  293:   else
  294:     PRINTF(len, "??? <%s>\n", x);
  295: 
  296:   if (interactive && busy && !skip_input && !init && (len > 0))
  297:     {
  298:       num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */
  299:       if (num_lines >= term_lns)
  300:         more();
  301:     }
  302: }
  303: 
  304: static void
  305: server_read(void)
  306: {
  307:   int c;
  308:   byte *start, *p;
  309: 
  310:  redo:
  311:   c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos);
  312:   if (!c)
  313:     die("Connection closed by server");
  314:   if (c < 0)
  315:     {
  316:       if (errno == EINTR)
  317: 	goto redo;
  318:       else
  319: 	DIE("Server read error");
  320:     }
  321: 
  322:   start = server_read_buf;
  323:   p = server_read_pos;
  324:   server_read_pos += c;
  325:   while (p < server_read_pos)
  326:     if (*p++ == '\n')
  327:       {
  328: 	p[-1] = 0;
  329: 	server_got_reply(start);
  330: 	start = p;
  331:       }
  332:   if (start != server_read_buf)
  333:     {
  334:       int l = server_read_pos - start;
  335:       memmove(server_read_buf, start, l);
  336:       server_read_pos = server_read_buf + l;
  337:     }
  338:   else if (server_read_pos == server_read_buf + sizeof(server_read_buf))
  339:     {
  340:       strcpy(server_read_buf, "?<too-long>");
  341:       server_read_pos = server_read_buf + 11;
  342:     }
  343: }
  344: 
  345: static void
  346: select_loop(void)
  347: {
  348:   int rv;
  349:   while (1)
  350:     {
  351:       if (init && !busy)
  352: 	init_commands();
  353: 
  354:       if (!init)
  355: 	input_notify(!busy);
  356: 
  357:       fd_set select_fds;
  358:       FD_ZERO(&select_fds);
  359: 
  360:       FD_SET(server_fd, &select_fds);
  361:       if (!busy)
  362: 	FD_SET(0, &select_fds);
  363: 
  364:       rv = select(server_fd+1, &select_fds, NULL, NULL, NULL);
  365:       if (rv < 0)
  366: 	{
  367: 	  if (errno == EINTR)
  368: 	    continue;
  369: 	  else
  370: 	    DIE("select");
  371: 	}
  372: 
  373:       if (FD_ISSET(0, &select_fds))
  374: 	{
  375: 	  input_read();
  376: 	  continue;
  377: 	}
  378: 
  379:       if (FD_ISSET(server_fd, &select_fds))
  380: 	{
  381: 	  server_read();
  382: 	  continue;
  383: 	}
  384:     }
  385: }
  386: 
  387: static void
  388: wait_for_write(int fd)
  389: {
  390:   while (1)
  391:     {
  392:       int rv;
  393:       fd_set set;
  394:       FD_ZERO(&set);
  395:       FD_SET(fd, &set);
  396: 
  397:       rv = select(fd+1, NULL, &set, NULL, NULL);
  398:       if (rv < 0)
  399: 	{
  400: 	  if (errno == EINTR)
  401: 	    continue;
  402: 	  else
  403: 	    DIE("select");
  404: 	}
  405: 
  406:       if (FD_ISSET(server_fd, &set))
  407: 	return;
  408:     }
  409: }
  410: 
  411: static void
  412: server_send(char *cmd)
  413: {
  414:   int l = strlen(cmd);
  415:   byte *z = alloca(l + 1);
  416: 
  417:   memcpy(z, cmd, l);
  418:   z[l++] = '\n';
  419:   while (l)
  420:     {
  421:       int cnt = write(server_fd, z, l);
  422: 
  423:       if (cnt < 0)
  424: 	{
  425: 	  if (errno == EAGAIN)
  426: 	    wait_for_write(server_fd);
  427: 	  else if (errno == EINTR)
  428: 	    continue;
  429: 	  else
  430: 	    DIE("Server write error");
  431: 	}
  432:       else
  433: 	{
  434: 	  l -= cnt;
  435: 	  z += cnt;
  436: 	}
  437:     }
  438: }
  439: 
  440: int
  441: main(int argc, char **argv)
  442: {
  443:   interactive = isatty(0);
  444:   parse_args(argc, argv);
  445:   cmd_build_tree();
  446:   server_connect();
  447:   select_loop();
  448:   return 0;
  449: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>