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>