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>