]> git.ipfire.org Git - thirdparty/bird.git/blame - client/client.c
We don't need bvsnprintf() in BIRD client
[thirdparty/bird.git] / client / client.c
CommitLineData
a5e9f3d2
OZ
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
f2ae2bad 40static char *opt_list = "s:vrl";
a5e9f3d2
OZ
41static int verbose, restricted, once;
42static char *init_cmd;
43
44static char *server_path = PATH_CONTROL_SOCKET;
45static int server_fd;
46static byte server_read_buf[SERVER_READ_BUF_LEN];
47static byte *server_read_pos = server_read_buf;
48
49int init = 1; /* During intial sequence */
50int busy = 1; /* Executing BIRD command */
51int interactive; /* Whether stdin is terminal */
52
53static int num_lines, skip_input;
54int term_lns, term_cls;
55
56
57/*** Parsing of arguments ***/
58
59static void
60usage(char *name)
61{
f2ae2bad 62 fprintf(stderr, "Usage: %s [-s <control-socket>] [-v] [-r] [-l]\n", name);
a5e9f3d2
OZ
63 exit(1);
64}
65
66static void
67parse_args(int argc, char **argv)
68{
f2ae2bad 69 int server_changed = 0;
a5e9f3d2
OZ
70 int c;
71
72 while ((c = getopt(argc, argv, opt_list)) >= 0)
73 switch (c)
74 {
75 case 's':
76 server_path = optarg;
f2ae2bad 77 server_changed = 1;
a5e9f3d2
OZ
78 break;
79 case 'v':
80 verbose++;
81 break;
82 case 'r':
83 restricted = 1;
84 break;
f2ae2bad
OZ
85 case 'l':
86 if (!server_changed)
87 server_path = xbasename(server_path);
88 break;
a5e9f3d2
OZ
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
120static void server_send(char *cmd);
121
122static int
123handle_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
138static void
139submit_server_command(char *cmd)
140{
141 busy = 1;
142 num_lines = 2;
143 server_send(cmd);
144}
145
8137fe6d
OZ
146static inline void
147submit_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
a5e9f3d2
OZ
161void
162submit_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
175static void
176init_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 */
8137fe6d 189 submit_init_command(init_cmd);
a5e9f3d2
OZ
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();
4d4979c6
OZ
202
203 term_lns = (term_lns > 0) ? term_lns : 25;
204 term_cls = (term_cls > 0) ? term_cls : 80;
205
a5e9f3d2
OZ
206 init = 0;
207}
208
209
210/*** Output ***/
211
212void
213more(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
244static void
245server_connect(void)
246{
247 struct sockaddr_un sa;
248
249 server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
250 if (server_fd < 0)
3f2c7600 251 DIE("Cannot create socket");
a5e9f3d2
OZ
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)
3f2c7600 260 DIE("Unable to connect to server control socket (%s)", server_path);
a5e9f3d2 261 if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
3f2c7600 262 DIE("fcntl");
a5e9f3d2
OZ
263}
264
265
266#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
267
268static void
269server_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
303static void
304server_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)
3f2c7600 312 die("Connection closed by server");
a5e9f3d2
OZ
313 if (c < 0)
314 {
315 if (errno == EINTR)
316 goto redo;
317 else
3f2c7600 318 DIE("Server read error");
a5e9f3d2
OZ
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
344static void
345select_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
3f2c7600 369 DIE("select");
a5e9f3d2
OZ
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
386static void
387wait_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
3f2c7600 402 DIE("select");
a5e9f3d2
OZ
403 }
404
405 if (FD_ISSET(server_fd, &set))
406 return;
407 }
408}
409
410static void
411server_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
3f2c7600 429 DIE("Server write error");
a5e9f3d2
OZ
430 }
431 else
432 {
433 l -= cnt;
434 z += cnt;
435 }
436 }
437}
438
a5e9f3d2
OZ
439int
440main(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}