]> git.ipfire.org Git - thirdparty/bird.git/blob - client/client.c
Use our own SUN_LEN if libc doesn't provide it.
[thirdparty/bird.git] / client / client.c
1 /*
2 * BIRD Client
3 *
4 * (c) 1999--2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <sys/time.h>
17 #include <sys/types.h>
18 #include <readline/readline.h>
19 #include <readline/history.h>
20
21 #include "nest/bird.h"
22 #include "lib/resource.h"
23 #include "lib/string.h"
24 #include "client/client.h"
25 #include "sysdep/unix/unix.h"
26
27 static char *opt_list = "s:v";
28 static int verbose;
29
30 static char *server_path = PATH_CONTROL_SOCKET;
31 static int server_fd;
32 static int server_reply;
33 static byte server_read_buf[4096];
34 static byte *server_read_pos = server_read_buf;
35
36 static int input_initialized;
37 static int input_hidden;
38 static int input_hidden_end;
39
40 /*** Parsing of arguments ***/
41
42 static void
43 usage(void)
44 {
45 fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v]\n");
46 exit(1);
47 }
48
49 static void
50 parse_args(int argc, char **argv)
51 {
52 int c;
53
54 while ((c = getopt(argc, argv, opt_list)) >= 0)
55 switch (c)
56 {
57 case 's':
58 server_path = optarg;
59 break;
60 case 'v':
61 verbose++;
62 break;
63 default:
64 usage();
65 }
66 if (optind < argc)
67 usage();
68 }
69
70 /*** Input ***/
71
72 static void server_send(char *);
73 static void select_loop(int);
74
75 /* HACK: libreadline internals we need to access */
76 extern int _rl_vis_botlin;
77 extern void _rl_move_vert(int);
78 extern Function *rl_last_func;
79
80 static int
81 handle_internal_command(char *cmd)
82 {
83 if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4))
84 {
85 cleanup();
86 exit(0);
87 }
88 if (!strncmp(cmd, "help", 4))
89 {
90 puts("Press `?' for context sensitive help.");
91 return 1;
92 }
93 return 0;
94 }
95
96 static void
97 got_line(char *cmd_buffer)
98 {
99 char *cmd;
100
101 if (!cmd_buffer)
102 {
103 cleanup();
104 exit(0);
105 }
106 if (cmd_buffer[0])
107 {
108 cmd = cmd_expand(cmd_buffer);
109 if (cmd)
110 {
111 add_history(cmd);
112 if (!handle_internal_command(cmd))
113 {
114 server_send(cmd);
115 input_hidden = -1;
116 select_loop(0);
117 input_hidden = 0;
118 }
119 free(cmd);
120 }
121 else
122 add_history(cmd_buffer);
123 }
124 free(cmd_buffer);
125 }
126
127 void
128 input_start_list(void) /* Leave the currently edited line and make space for listing */
129 {
130 _rl_move_vert(_rl_vis_botlin);
131 crlf();
132 }
133
134 void
135 input_stop_list(void) /* Reprint the currently edited line after listing */
136 {
137 rl_on_new_line();
138 rl_redisplay();
139 }
140
141 static int
142 input_complete(int arg, int key)
143 {
144 static int complete_flag;
145 char buf[256];
146
147 if (rl_last_func != input_complete)
148 complete_flag = 0;
149 switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag))
150 {
151 case 0:
152 complete_flag = 1;
153 break;
154 case 1:
155 rl_insert_text(buf);
156 break;
157 default:
158 complete_flag = 1;
159 ding();
160 }
161 return 0;
162 }
163
164 static int
165 input_help(int arg, int key)
166 {
167 int i = 0;
168
169 if (arg != 1)
170 return rl_insert(arg, '?');
171 while (i < rl_point)
172 {
173 if (rl_line_buffer[i++] == '"')
174 do
175 {
176 if (i >= rl_point) /* `?' inside quoted string -> insert */
177 return rl_insert(1, '?');
178 }
179 while (rl_line_buffer[i++] != '"');
180 }
181 rl_begin_undo_group(); /* HACK: We want to display `?' at point position */
182 rl_insert_text("?");
183 rl_redisplay();
184 rl_end_undo_group();
185 input_start_list();
186 cmd_help(rl_line_buffer, rl_point);
187 rl_undo_command(1, 0);
188 input_stop_list();
189 return 0;
190 }
191
192 static void
193 input_init(void)
194 {
195 rl_readline_name = "birdc";
196 rl_add_defun("bird-complete", input_complete, '\t');
197 rl_add_defun("bird-help", input_help, '?');
198 rl_callback_handler_install("bird> ", got_line);
199 input_initialized = 1;
200 if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
201 die("fcntl: %m");
202 }
203
204 static void
205 input_hide(void)
206 {
207 if (input_hidden)
208 return;
209 if (rl_line_buffer)
210 {
211 input_hidden_end = rl_end;
212 rl_end = 0;
213 rl_expand_prompt("");
214 rl_redisplay();
215 input_hidden = 1;
216 }
217 }
218
219 static void
220 input_reveal(void)
221 {
222 if (input_hidden <= 0)
223 return;
224 rl_end = input_hidden_end;
225 rl_expand_prompt("bird> ");
226 rl_forced_update_display();
227 input_hidden = 0;
228 }
229
230 void
231 cleanup(void)
232 {
233 if (input_initialized)
234 {
235 input_initialized = 0;
236 input_hide();
237 rl_callback_handler_remove();
238 }
239 }
240
241 /*** Communication with server ***/
242
243 static void
244 server_connect(void)
245 {
246 struct sockaddr_un sa;
247
248 server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
249 if (server_fd < 0)
250 die("Cannot create socket: %m");
251 bzero(&sa, sizeof(sa));
252 sa.sun_family = AF_UNIX;
253 strcpy(sa.sun_path, server_path);
254 if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
255 die("Unable to connect to server control socket (%s): %m", server_path);
256 if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
257 die("fcntl: %m");
258 }
259
260 static void
261 server_got_reply(char *x)
262 {
263 int code;
264
265 input_hide();
266 if (*x == '+') /* Async reply */
267 printf(">>> %s\n", x+1);
268 else if (x[0] == ' ') /* Continuation */
269 printf("%s%s\n", verbose ? " " : "", x+1);
270 else if (strlen(x) > 4 &&
271 sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
272 (x[4] == ' ' || x[4] == '-'))
273 {
274 if (code)
275 printf("%s\n", verbose ? x : x+5);
276 if (x[4] == ' ')
277 server_reply = code;
278 }
279 else
280 printf("??? <%s>\n", x);
281 }
282
283 static void
284 server_read(void)
285 {
286 int c;
287 byte *start, *p;
288
289 c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos);
290 if (!c)
291 die("Connection closed by server.");
292 if (c < 0)
293 die("Server read error: %m");
294 start = server_read_buf;
295 p = server_read_pos;
296 server_read_pos += c;
297 while (p < server_read_pos)
298 if (*p++ == '\n')
299 {
300 p[-1] = 0;
301 server_got_reply(start);
302 start = p;
303 }
304 if (start != server_read_buf)
305 {
306 int l = server_read_pos - start;
307 memmove(server_read_buf, start, l);
308 server_read_pos = server_read_buf + l;
309 }
310 else if (server_read_pos == server_read_buf + sizeof(server_read_buf))
311 {
312 strcpy(server_read_buf, "?<too-long>");
313 server_read_pos = server_read_buf + 11;
314 }
315 }
316
317 static fd_set select_fds;
318
319 static void
320 select_loop(int mode)
321 {
322 server_reply = -1;
323 while (mode || server_reply < 0)
324 {
325 FD_ZERO(&select_fds);
326 FD_SET(server_fd, &select_fds);
327 if (mode)
328 FD_SET(0, &select_fds);
329 select(server_fd+1, &select_fds, NULL, NULL, NULL);
330 if (FD_ISSET(server_fd, &select_fds))
331 {
332 server_read();
333 if (mode)
334 input_reveal();
335 }
336 if (FD_ISSET(0, &select_fds))
337 rl_callback_read_char();
338 }
339 input_reveal();
340 }
341
342 static void
343 server_send(char *cmd)
344 {
345 int l = strlen(cmd);
346 byte *z = alloca(l + 1);
347
348 memcpy(z, cmd, l);
349 z[l++] = '\n';
350 while (l)
351 {
352 int cnt = write(server_fd, z, l);
353 if (cnt < 0)
354 {
355 if (errno == -EAGAIN)
356 {
357 fd_set set;
358 FD_ZERO(&set);
359 do
360 {
361 FD_SET(server_fd, &set);
362 select(server_fd+1, NULL, &set, NULL, NULL);
363 }
364 while (!FD_ISSET(server_fd, &set));
365 }
366 else
367 die("Server write error: %m");
368 }
369 else
370 {
371 l -= cnt;
372 z += cnt;
373 }
374 }
375 }
376
377 int
378 main(int argc, char **argv)
379 {
380 #ifdef HAVE_LIBDMALLOC
381 if (!getenv("DMALLOC_OPTIONS"))
382 dmalloc_debug(0x2f03d00);
383 #endif
384
385 parse_args(argc, argv);
386 cmd_build_tree();
387 server_connect();
388 select_loop(0);
389
390 input_init();
391
392 select_loop(1);
393 return 0;
394 }