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>