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