Annotation of embedaddon/bird2/client/client.c, revision 1.1.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/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>