Annotation of embedaddon/bird/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/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>