File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / client / client.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Aug 22 12:33:54 2017 UTC (6 years, 10 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

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

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