Annotation of embedaddon/bird/client/client.c, revision 1.1

1.1     ! misho       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>