Revision d2c392d4
client/Makefile | ||
---|---|---|
1 |
source=commands.c util.c client_common.c
|
|
1 |
source=commands.c util.c common.c |
|
2 | 2 |
root-rel=../ |
3 | 3 |
dir-name=client |
4 | 4 |
|
5 |
clients := $(client) birdcl |
|
6 |
|
|
7 |
source-dep := $(source) $(addsuffix .c,$(clients)) |
|
8 |
|
|
9 |
subdir: $(addsuffix .o,$(clients)) |
|
10 |
|
|
5 | 11 |
include ../Rules |
client/birdc/Makefile | ||
---|---|---|
1 |
source=client.c |
|
2 |
root-rel=../../ |
|
3 |
dir-name=client/birdc |
|
4 |
|
|
5 |
include ../../Rules |
client/birdc/client.c | ||
---|---|---|
1 |
/* |
|
2 |
* BIRD Client |
|
3 |
* |
|
4 |
* (c) 1999--2004 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 <termios.h> |
|
14 |
#include <errno.h> |
|
15 |
#include <sys/socket.h> |
|
16 |
#include <sys/un.h> |
|
17 |
#include <sys/types.h> |
|
18 |
#include <readline/readline.h> |
|
19 |
#include <readline/history.h> |
|
20 |
#include <curses.h> |
|
21 |
|
|
22 |
#include "nest/bird.h" |
|
23 |
#include "lib/resource.h" |
|
24 |
#include "lib/string.h" |
|
25 |
#include "client/client.h" |
|
26 |
#include "sysdep/unix/unix.h" |
|
27 |
|
|
28 |
static char *opt_list = "s:vr"; |
|
29 |
static int verbose; |
|
30 |
static char *init_cmd; |
|
31 |
static int once; |
|
32 |
static int restricted; |
|
33 |
|
|
34 |
extern char *server_path; |
|
35 |
extern int server_fd; |
|
36 |
extern byte server_read_buf[SERVER_READ_BUF_LEN]; |
|
37 |
extern byte *server_read_pos; |
|
38 |
|
|
39 |
extern int input_initialized; |
|
40 |
extern int input_hidden_end; |
|
41 |
extern int cstate; |
|
42 |
extern int nstate; |
|
43 |
|
|
44 |
extern int num_lines, skip_input, interactive; |
|
45 |
|
|
46 |
/*** Parsing of arguments ***/ |
|
47 |
|
|
48 |
static void |
|
49 |
usage(void) |
|
50 |
{ |
|
51 |
fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v] [-r]\n"); |
|
52 |
exit(1); |
|
53 |
} |
|
54 |
|
|
55 |
static void |
|
56 |
parse_args(int argc, char **argv) |
|
57 |
{ |
|
58 |
int c; |
|
59 |
|
|
60 |
while ((c = getopt(argc, argv, opt_list)) >= 0) |
|
61 |
switch (c) |
|
62 |
{ |
|
63 |
case 's': |
|
64 |
server_path = optarg; |
|
65 |
break; |
|
66 |
case 'v': |
|
67 |
verbose++; |
|
68 |
break; |
|
69 |
case 'r': |
|
70 |
restricted = 1; |
|
71 |
break; |
|
72 |
default: |
|
73 |
usage(); |
|
74 |
} |
|
75 |
|
|
76 |
/* If some arguments are not options, we take it as commands */ |
|
77 |
if (optind < argc) |
|
78 |
{ |
|
79 |
char *tmp; |
|
80 |
int i; |
|
81 |
int len = 0; |
|
82 |
|
|
83 |
for (i = optind; i < argc; i++) |
|
84 |
len += strlen(argv[i]) + 1; |
|
85 |
|
|
86 |
tmp = init_cmd = malloc(len); |
|
87 |
for (i = optind; i < argc; i++) |
|
88 |
{ |
|
89 |
strcpy(tmp, argv[i]); |
|
90 |
tmp += strlen(tmp); |
|
91 |
*tmp++ = ' '; |
|
92 |
} |
|
93 |
tmp[-1] = 0; |
|
94 |
|
|
95 |
once = 1; |
|
96 |
} |
|
97 |
} |
|
98 |
|
|
99 |
/*** Input ***/ |
|
100 |
|
|
101 |
/* HACK: libreadline internals we need to access */ |
|
102 |
extern int _rl_vis_botlin; |
|
103 |
extern void _rl_move_vert(int); |
|
104 |
extern Function *rl_last_func; |
|
105 |
|
|
106 |
static void |
|
107 |
add_history_dedup(char *cmd) |
|
108 |
{ |
|
109 |
/* Add history line if it differs from the last one */ |
|
110 |
HIST_ENTRY *he = history_get(history_length); |
|
111 |
if (!he || strcmp(he->line, cmd)) |
|
112 |
add_history(cmd); |
|
113 |
} |
|
114 |
|
|
115 |
void got_line(char *cmd_buffer) |
|
116 |
{ |
|
117 |
char *cmd; |
|
118 |
|
|
119 |
if (!cmd_buffer) |
|
120 |
{ |
|
121 |
cleanup(); |
|
122 |
exit(0); |
|
123 |
} |
|
124 |
if (cmd_buffer[0]) |
|
125 |
{ |
|
126 |
cmd = cmd_expand(cmd_buffer); |
|
127 |
if (cmd) |
|
128 |
{ |
|
129 |
add_history_dedup(cmd); |
|
130 |
|
|
131 |
if (!handle_internal_command(cmd)) |
|
132 |
submit_server_command(cmd); |
|
133 |
|
|
134 |
free(cmd); |
|
135 |
} |
|
136 |
else |
|
137 |
add_history_dedup(cmd_buffer); |
|
138 |
} |
|
139 |
free(cmd_buffer); |
|
140 |
} |
|
141 |
|
|
142 |
void |
|
143 |
input_start_list(void) /* Leave the currently edited line and make space for listing */ |
|
144 |
{ |
|
145 |
_rl_move_vert(_rl_vis_botlin); |
|
146 |
#ifdef HAVE_RL_CRLF |
|
147 |
rl_crlf(); |
|
148 |
#endif |
|
149 |
} |
|
150 |
|
|
151 |
void |
|
152 |
input_stop_list(void) /* Reprint the currently edited line after listing */ |
|
153 |
{ |
|
154 |
rl_on_new_line(); |
|
155 |
rl_redisplay(); |
|
156 |
} |
|
157 |
|
|
158 |
static int |
|
159 |
input_complete(int arg UNUSED, int key UNUSED) |
|
160 |
{ |
|
161 |
static int complete_flag; |
|
162 |
char buf[256]; |
|
163 |
|
|
164 |
if (rl_last_func != input_complete) |
|
165 |
complete_flag = 0; |
|
166 |
switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) |
|
167 |
{ |
|
168 |
case 0: |
|
169 |
complete_flag = 1; |
|
170 |
break; |
|
171 |
case 1: |
|
172 |
rl_insert_text(buf); |
|
173 |
break; |
|
174 |
default: |
|
175 |
complete_flag = 1; |
|
176 |
#ifdef HAVE_RL_DING |
|
177 |
rl_ding(); |
|
178 |
#endif |
|
179 |
} |
|
180 |
return 0; |
|
181 |
} |
|
182 |
|
|
183 |
static int |
|
184 |
input_help(int arg, int key UNUSED) |
|
185 |
{ |
|
186 |
int i, in_string, in_bracket; |
|
187 |
|
|
188 |
if (arg != 1) |
|
189 |
return rl_insert(arg, '?'); |
|
190 |
|
|
191 |
in_string = in_bracket = 0; |
|
192 |
for (i = 0; i < rl_point; i++) |
|
193 |
{ |
|
194 |
|
|
195 |
if (rl_line_buffer[i] == '"') |
|
196 |
in_string = ! in_string; |
|
197 |
else if (! in_string) |
|
198 |
{ |
|
199 |
if (rl_line_buffer[i] == '[') |
|
200 |
in_bracket++; |
|
201 |
else if (rl_line_buffer[i] == ']') |
|
202 |
in_bracket--; |
|
203 |
} |
|
204 |
} |
|
205 |
|
|
206 |
/* `?' inside string or path -> insert */ |
|
207 |
if (in_string || in_bracket) |
|
208 |
return rl_insert(1, '?'); |
|
209 |
|
|
210 |
rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ |
|
211 |
rl_insert_text("?"); |
|
212 |
rl_redisplay(); |
|
213 |
rl_end_undo_group(); |
|
214 |
input_start_list(); |
|
215 |
cmd_help(rl_line_buffer, rl_point); |
|
216 |
rl_undo_command(1, 0); |
|
217 |
input_stop_list(); |
|
218 |
return 0; |
|
219 |
} |
|
220 |
|
|
221 |
static void |
|
222 |
input_init(void) |
|
223 |
{ |
|
224 |
rl_readline_name = "birdc"; |
|
225 |
rl_add_defun("bird-complete", input_complete, '\t'); |
|
226 |
rl_add_defun("bird-help", input_help, '?'); |
|
227 |
rl_callback_handler_install("bird> ", got_line); |
|
228 |
input_initialized = 1; |
|
229 |
// readline library does strange things when stdin is nonblocking. |
|
230 |
// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) |
|
231 |
// die("fcntl: %m"); |
|
232 |
} |
|
233 |
|
|
234 |
static void |
|
235 |
input_hide(void) |
|
236 |
{ |
|
237 |
input_hidden_end = rl_end; |
|
238 |
rl_end = 0; |
|
239 |
rl_expand_prompt(""); |
|
240 |
rl_redisplay(); |
|
241 |
} |
|
242 |
|
|
243 |
static void |
|
244 |
input_reveal(void) |
|
245 |
{ |
|
246 |
/* need this, otherwise some lib seems to eat pending output when |
|
247 |
the prompt is displayed */ |
|
248 |
fflush(stdout); |
|
249 |
tcdrain(fileno(stdout)); |
|
250 |
|
|
251 |
rl_end = input_hidden_end; |
|
252 |
rl_expand_prompt("bird> "); |
|
253 |
rl_forced_update_display(); |
|
254 |
} |
|
255 |
|
|
256 |
void |
|
257 |
update_state(void) |
|
258 |
{ |
|
259 |
if (nstate == cstate) |
|
260 |
return; |
|
261 |
|
|
262 |
if (restricted) |
|
263 |
{ |
|
264 |
submit_server_command("restrict"); |
|
265 |
restricted = 0; |
|
266 |
return; |
|
267 |
} |
|
268 |
|
|
269 |
if (init_cmd) |
|
270 |
{ |
|
271 |
/* First transition - client received hello from BIRD |
|
272 |
and there is waiting initial command */ |
|
273 |
submit_server_command(init_cmd); |
|
274 |
init_cmd = NULL; |
|
275 |
return; |
|
276 |
} |
|
277 |
|
|
278 |
if (!init_cmd && once) |
|
279 |
{ |
|
280 |
/* Initial command is finished and we want to exit */ |
|
281 |
cleanup(); |
|
282 |
exit(0); |
|
283 |
} |
|
284 |
|
|
285 |
if (nstate == STATE_PROMPT) |
|
286 |
{ |
|
287 |
if (input_initialized) |
|
288 |
input_reveal(); |
|
289 |
else |
|
290 |
input_init(); |
|
291 |
} |
|
292 |
|
|
293 |
if (nstate != STATE_PROMPT) |
|
294 |
input_hide(); |
|
295 |
|
|
296 |
cstate = nstate; |
|
297 |
} |
|
298 |
|
|
299 |
void |
|
300 |
more(void) |
|
301 |
{ |
|
302 |
printf("--More--\015"); |
|
303 |
fflush(stdout); |
|
304 |
|
|
305 |
redo: |
|
306 |
switch (getchar()) |
|
307 |
{ |
|
308 |
case 32: |
|
309 |
num_lines = 2; |
|
310 |
break; |
|
311 |
case 13: |
|
312 |
num_lines--; |
|
313 |
break; |
|
314 |
case 'q': |
|
315 |
skip_input = 1; |
|
316 |
break; |
|
317 |
default: |
|
318 |
goto redo; |
|
319 |
} |
|
320 |
|
|
321 |
printf(" \015"); |
|
322 |
fflush(stdout); |
|
323 |
} |
|
324 |
|
|
325 |
void cleanup(void) |
|
326 |
{ |
|
327 |
if (input_initialized) |
|
328 |
{ |
|
329 |
input_initialized = 0; |
|
330 |
input_hide(); |
|
331 |
rl_callback_handler_remove(); |
|
332 |
} |
|
333 |
} |
|
334 |
|
|
335 |
|
|
336 |
/*** Communication with server ***/ |
|
337 |
|
|
338 |
static fd_set select_fds; |
|
339 |
|
|
340 |
static void |
|
341 |
select_loop(void) |
|
342 |
{ |
|
343 |
int rv; |
|
344 |
while (1) |
|
345 |
{ |
|
346 |
FD_ZERO(&select_fds); |
|
347 |
|
|
348 |
if (cstate != STATE_CMD_USER) |
|
349 |
FD_SET(server_fd, &select_fds); |
|
350 |
if (cstate != STATE_CMD_SERVER) |
|
351 |
FD_SET(0, &select_fds); |
|
352 |
|
|
353 |
rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); |
|
354 |
if (rv < 0) |
|
355 |
{ |
|
356 |
if (errno == EINTR) |
|
357 |
continue; |
|
358 |
else |
|
359 |
die("select: %m"); |
|
360 |
} |
|
361 |
|
|
362 |
if (FD_ISSET(server_fd, &select_fds)) |
|
363 |
{ |
|
364 |
server_read(); |
|
365 |
update_state(); |
|
366 |
} |
|
367 |
|
|
368 |
if (FD_ISSET(0, &select_fds)) |
|
369 |
{ |
|
370 |
rl_callback_read_char(); |
|
371 |
update_state(); |
|
372 |
} |
|
373 |
} |
|
374 |
} |
|
375 |
|
|
376 |
#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) |
|
377 |
|
|
378 |
void server_got_reply(char *x) |
|
379 |
{ |
|
380 |
int code; |
|
381 |
int len = 0; |
|
382 |
|
|
383 |
if (*x == '+') /* Async reply */ |
|
384 |
PRINTF(len, ">>> %s\n", x+1); |
|
385 |
else if (x[0] == ' ') /* Continuation */ |
|
386 |
PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); |
|
387 |
else if (strlen(x) > 4 && |
|
388 |
sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && |
|
389 |
(x[4] == ' ' || x[4] == '-')) |
|
390 |
{ |
|
391 |
if (code) |
|
392 |
PRINTF(len, "%s\n", verbose ? x : x+5); |
|
393 |
if (x[4] == ' ') |
|
394 |
{ |
|
395 |
nstate = STATE_PROMPT; |
|
396 |
skip_input = 0; |
|
397 |
return; |
|
398 |
} |
|
399 |
} |
|
400 |
else |
|
401 |
PRINTF(len, "??? <%s>\n", x); |
|
402 |
|
|
403 |
if (skip_input) |
|
404 |
return; |
|
405 |
|
|
406 |
if (interactive && input_initialized && (len > 0)) |
|
407 |
{ |
|
408 |
int lns = LINES ? LINES : 25; |
|
409 |
int cls = COLS ? COLS : 80; |
|
410 |
num_lines += (len + cls - 1) / cls; /* Divide and round up */ |
|
411 |
if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) |
|
412 |
more(); |
|
413 |
} |
|
414 |
} |
|
415 |
|
|
416 |
int |
|
417 |
main(int argc, char **argv) |
|
418 |
{ |
|
419 |
#ifdef HAVE_LIBDMALLOC |
|
420 |
if (!getenv("DMALLOC_OPTIONS")) |
|
421 |
dmalloc_debug(0x2f03d00); |
|
422 |
#endif |
|
423 |
|
|
424 |
interactive = isatty(0); |
|
425 |
parse_args(argc, argv); |
|
426 |
cmd_build_tree(); |
|
427 |
server_connect(); |
|
428 |
select_loop(); |
|
429 |
return 0; |
|
430 |
} |
client/birdcl/Makefile | ||
---|---|---|
1 |
source=client.c |
|
2 |
root-rel=../../ |
|
3 |
dir-name=client/birdcl |
|
4 |
|
|
5 |
include ../../Rules |
client/birdcl/client.c | ||
---|---|---|
1 |
/* |
|
2 |
* BIRD Client |
|
3 |
* |
|
4 |
* (c) 1999--2004 Martin Mares <mj@ucw.cz> |
|
5 |
* (c) 2013 Tomas Hlavacek <tomas.hlavacek@nic.cz> |
|
6 |
* |
|
7 |
* Can be freely distributed and used under the terms of the GNU GPL. |
|
8 |
*/ |
|
9 |
|
|
10 |
#include <stdio.h> |
|
11 |
#include <stdlib.h> |
|
12 |
#include <fcntl.h> |
|
13 |
#include <unistd.h> |
|
14 |
#include <termios.h> |
|
15 |
#include <errno.h> |
|
16 |
#include <sys/socket.h> |
|
17 |
#include <sys/un.h> |
|
18 |
#include <sys/types.h> |
|
19 |
#include <sys/ioctl.h> |
|
20 |
#include <signal.h> |
|
21 |
#include <ctype.h> |
|
22 |
|
|
23 |
#include "nest/bird.h" |
|
24 |
#include "lib/resource.h" |
|
25 |
#include "lib/string.h" |
|
26 |
#include "client/client.h" |
|
27 |
#include "sysdep/unix/unix.h" |
|
28 |
|
|
29 |
#define INPUT_BUF_LEN 2048 |
|
30 |
|
|
31 |
static char *opt_list = "s:vr"; |
|
32 |
static int verbose; |
|
33 |
static char *init_cmd; |
|
34 |
static int once; |
|
35 |
|
|
36 |
extern char *server_path; |
|
37 |
extern int server_fd; |
|
38 |
|
|
39 |
extern int cstate; |
|
40 |
extern int num_lines, skip_input, interactive; |
|
41 |
|
|
42 |
static int term_lns=25; |
|
43 |
static int term_cls=80; |
|
44 |
struct termios tty_save; |
|
45 |
|
|
46 |
void |
|
47 |
input_start_list(void) |
|
48 |
{ |
|
49 |
/* Empty in non-ncurses version. */ |
|
50 |
} |
|
51 |
|
|
52 |
void |
|
53 |
input_stop_list(void) |
|
54 |
{ |
|
55 |
/* Empty in non-ncurses version. */ |
|
56 |
} |
|
57 |
|
|
58 |
/*** Parsing of arguments ***/ |
|
59 |
|
|
60 |
static void |
|
61 |
usage(void) |
|
62 |
{ |
|
63 |
fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v] [-r]\n"); |
|
64 |
exit(1); |
|
65 |
} |
|
66 |
|
|
67 |
static void |
|
68 |
parse_args(int argc, char **argv) |
|
69 |
{ |
|
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 |
break; |
|
78 |
case 'v': |
|
79 |
verbose++; |
|
80 |
break; |
|
81 |
case 'r': |
|
82 |
init_cmd = "restrict"; |
|
83 |
break; |
|
84 |
default: |
|
85 |
usage(); |
|
86 |
} |
|
87 |
|
|
88 |
/* If some arguments are not options, we take it as commands */ |
|
89 |
if (optind < argc) |
|
90 |
{ |
|
91 |
char *tmp; |
|
92 |
int i; |
|
93 |
int len = 0; |
|
94 |
|
|
95 |
if (init_cmd) |
|
96 |
usage(); |
|
97 |
|
|
98 |
for (i = optind; i < argc; i++) |
|
99 |
len += strlen(argv[i]) + 1; |
|
100 |
|
|
101 |
tmp = init_cmd = malloc(len); |
|
102 |
for (i = optind; i < argc; i++) |
|
103 |
{ |
|
104 |
strcpy(tmp, argv[i]); |
|
105 |
tmp += strlen(tmp); |
|
106 |
*tmp++ = ' '; |
|
107 |
} |
|
108 |
tmp[-1] = 0; |
|
109 |
|
|
110 |
once = 1; |
|
111 |
} |
|
112 |
} |
|
113 |
|
|
114 |
static void |
|
115 |
run_init_cmd(void) |
|
116 |
{ |
|
117 |
if (init_cmd) |
|
118 |
{ |
|
119 |
/* First transition - client received hello from BIRD |
|
120 |
and there is waiting initial command */ |
|
121 |
submit_server_command(init_cmd); |
|
122 |
init_cmd = NULL; |
|
123 |
return; |
|
124 |
} |
|
125 |
|
|
126 |
if (!init_cmd && once && (cstate == STATE_PROMPT)) |
|
127 |
{ |
|
128 |
/* Initial command is finished and we want to exit */ |
|
129 |
cleanup(); |
|
130 |
exit(0); |
|
131 |
} |
|
132 |
} |
|
133 |
|
|
134 |
/*** Input ***/ |
|
135 |
|
|
136 |
static void |
|
137 |
got_line(char *cmd_buffer) |
|
138 |
{ |
|
139 |
char *cmd; |
|
140 |
|
|
141 |
if (!cmd_buffer) |
|
142 |
{ |
|
143 |
cleanup(); |
|
144 |
exit(0); |
|
145 |
} |
|
146 |
if (cmd_buffer[0]) |
|
147 |
{ |
|
148 |
cmd = cmd_expand(cmd_buffer); |
|
149 |
if (cmd) |
|
150 |
{ |
|
151 |
if (!handle_internal_command(cmd)) |
|
152 |
submit_server_command(cmd); |
|
153 |
|
|
154 |
free(cmd); |
|
155 |
} |
|
156 |
} |
|
157 |
free(cmd_buffer); |
|
158 |
} |
|
159 |
|
|
160 |
void |
|
161 |
cleanup(void) |
|
162 |
{ |
|
163 |
/* No ncurses -> restore terminal state. */ |
|
164 |
if (interactive) |
|
165 |
if (tcsetattr (0, TCSANOW, &tty_save) != 0) |
|
166 |
{ |
|
167 |
perror("tcsetattr error"); |
|
168 |
exit(EXIT_FAILURE); |
|
169 |
} |
|
170 |
} |
|
171 |
|
|
172 |
static void |
|
173 |
print_prompt(void) |
|
174 |
{ |
|
175 |
/* No ncurses -> no status to reveal/hide, print prompt manually. */ |
|
176 |
printf("bird> "); |
|
177 |
fflush(stdout); |
|
178 |
} |
|
179 |
|
|
180 |
|
|
181 |
static int lastnb(char *str) |
|
182 |
{ |
|
183 |
int i; |
|
184 |
for (i=strlen(str)-1; i>0; i--) |
|
185 |
{ |
|
186 |
if(!isblank(str[i])) |
|
187 |
return i; |
|
188 |
} |
|
189 |
|
|
190 |
return 0; |
|
191 |
} |
|
192 |
|
|
193 |
static void |
|
194 |
term_read(void) |
|
195 |
{ |
|
196 |
char *buf = malloc(INPUT_BUF_LEN); |
|
197 |
|
|
198 |
if (fgets(buf, INPUT_BUF_LEN, stdin) == NULL) { |
|
199 |
free(buf); |
|
200 |
exit(0); |
|
201 |
} |
|
202 |
|
|
203 |
if (buf[strlen(buf)-1] != '\n') |
|
204 |
{ |
|
205 |
printf("Input too long.\n"); |
|
206 |
free(buf); |
|
207 |
return; |
|
208 |
} |
|
209 |
|
|
210 |
if (strlen(buf) <= 0) |
|
211 |
{ |
|
212 |
free(buf); |
|
213 |
return; |
|
214 |
} |
|
215 |
|
|
216 |
buf[strlen(buf)-1] = '\0'; |
|
217 |
|
|
218 |
if (!interactive) |
|
219 |
{ |
|
220 |
print_prompt(); |
|
221 |
printf("%s\n",buf); |
|
222 |
} |
|
223 |
|
|
224 |
if (buf[lastnb(buf)] == '?') |
|
225 |
{ |
|
226 |
printf("\n"); |
|
227 |
cmd_help(buf, strlen(buf)); |
|
228 |
free(buf); |
|
229 |
return; |
|
230 |
} |
|
231 |
|
|
232 |
if (strlen(buf) > 0) |
|
233 |
{ |
|
234 |
got_line(buf); /* buf is free()-ed inside */ |
|
235 |
} |
|
236 |
else |
|
237 |
{ |
|
238 |
free(buf); /* no command, only newline -> no-op */ |
|
239 |
return; |
|
240 |
} |
|
241 |
|
|
242 |
} |
|
243 |
|
|
244 |
/*** Communication with server ***/ |
|
245 |
|
|
246 |
void |
|
247 |
more(void) |
|
248 |
{ |
|
249 |
struct termios tty; |
|
250 |
|
|
251 |
printf("--More--\015"); |
|
252 |
fflush(stdout); |
|
253 |
|
|
254 |
if (tcgetattr(0, &tty) != 0) |
|
255 |
{ |
|
256 |
perror("tcgetattr error"); |
|
257 |
exit(EXIT_FAILURE); |
|
258 |
} |
|
259 |
tty.c_lflag &= (~ECHO); |
|
260 |
tty.c_lflag &= (~ICANON); |
|
261 |
if (tcsetattr (0, TCSANOW, &tty) != 0) |
|
262 |
{ |
|
263 |
perror("tcsetattr error"); |
|
264 |
exit(EXIT_FAILURE); |
|
265 |
} |
|
266 |
|
|
267 |
redo: |
|
268 |
switch (getchar()) |
|
269 |
{ |
|
270 |
case 32: |
|
271 |
num_lines = 2; |
|
272 |
break; |
|
273 |
case 13: |
|
274 |
num_lines--; |
|
275 |
break; |
|
276 |
case '\n': |
|
277 |
num_lines--; |
|
278 |
break; |
|
279 |
case 'q': |
|
280 |
skip_input = 1; |
|
281 |
break; |
|
282 |
default: |
|
283 |
goto redo; |
|
284 |
} |
|
285 |
|
|
286 |
tty.c_lflag |= ECHO; |
|
287 |
tty.c_lflag |= ICANON; |
|
288 |
if (tcsetattr (0, TCSANOW, &tty) != 0) |
|
289 |
{ |
|
290 |
perror("tcsetattr error"); |
|
291 |
exit(EXIT_FAILURE); |
|
292 |
} |
|
293 |
|
|
294 |
printf(" \015"); |
|
295 |
fflush(stdout); |
|
296 |
} |
|
297 |
|
|
298 |
static void |
|
299 |
get_term_size(void) |
|
300 |
{ |
|
301 |
struct winsize tws; |
|
302 |
if (ioctl(0, TIOCGWINSZ, &tws) == 0) |
|
303 |
{ |
|
304 |
term_lns = tws.ws_row; |
|
305 |
term_cls = tws.ws_col; |
|
306 |
} |
|
307 |
else |
|
308 |
{ |
|
309 |
term_lns = 25; |
|
310 |
term_cls = 80; |
|
311 |
} |
|
312 |
} |
|
313 |
|
|
314 |
#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) |
|
315 |
|
|
316 |
void |
|
317 |
server_got_reply(char *x) |
|
318 |
{ |
|
319 |
int code; |
|
320 |
int len = 0; |
|
321 |
|
|
322 |
if (*x == '+') /* Async reply */ |
|
323 |
PRINTF(len, ">>> %s\n", x+1); |
|
324 |
else if (x[0] == ' ') /* Continuation */ |
|
325 |
PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); |
|
326 |
else if (strlen(x) > 4 && |
|
327 |
sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && |
|
328 |
(x[4] == ' ' || x[4] == '-')) |
|
329 |
{ |
|
330 |
if (code) |
|
331 |
PRINTF(len, "%s\n", verbose ? x : x+5); |
|
332 |
|
|
333 |
if (x[4] == ' ') |
|
334 |
{ |
|
335 |
cstate = STATE_PROMPT; |
|
336 |
skip_input = 0; |
|
337 |
return; |
|
338 |
} |
|
339 |
} |
|
340 |
else |
|
341 |
PRINTF(len, "??? <%s>\n", x); |
|
342 |
|
|
343 |
if (skip_input) |
|
344 |
return; |
|
345 |
|
|
346 |
if (interactive && (len > 0)) |
|
347 |
{ |
|
348 |
num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ |
|
349 |
if (num_lines >= term_lns) |
|
350 |
more(); |
|
351 |
} |
|
352 |
} |
|
353 |
|
|
354 |
static fd_set select_fds; |
|
355 |
|
|
356 |
static void |
|
357 |
select_loop(void) |
|
358 |
{ |
|
359 |
int rv; |
|
360 |
|
|
361 |
while (1) |
|
362 |
{ |
|
363 |
FD_ZERO(&select_fds); |
|
364 |
|
|
365 |
if (cstate != STATE_CMD_USER) |
|
366 |
FD_SET(server_fd, &select_fds); |
|
367 |
|
|
368 |
if (cstate != STATE_CMD_SERVER) |
|
369 |
{ |
|
370 |
FD_SET(0, &select_fds); |
|
371 |
if (interactive) |
|
372 |
print_prompt(); |
|
373 |
} |
|
374 |
|
|
375 |
rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); |
|
376 |
if (rv < 0) |
|
377 |
{ |
|
378 |
if (errno == EINTR) |
|
379 |
continue; |
|
380 |
else |
|
381 |
die("select: %m"); |
|
382 |
} |
|
383 |
|
|
384 |
if (FD_ISSET(server_fd, &select_fds)) |
|
385 |
{ |
|
386 |
server_read(); |
|
387 |
run_init_cmd(); |
|
388 |
} |
|
389 |
|
|
390 |
if (FD_ISSET(0, &select_fds)) |
|
391 |
term_read(); |
|
392 |
} |
|
393 |
} |
|
394 |
|
|
395 |
static void |
|
396 |
sig_handler(int signal) |
|
397 |
{ |
|
398 |
cleanup(); |
|
399 |
exit(0); |
|
400 |
} |
|
401 |
|
|
402 |
int |
|
403 |
main(int argc, char **argv) |
|
404 |
{ |
|
405 |
interactive = isatty(fileno(stdin)); |
|
406 |
if (interactive) |
|
407 |
{ |
|
408 |
if (signal(SIGINT, sig_handler) == SIG_IGN) |
|
409 |
signal(SIGINT, SIG_IGN); |
|
410 |
if (signal(SIGHUP, sig_handler) == SIG_IGN) |
|
411 |
signal(SIGHUP, SIG_IGN); |
|
412 |
if (signal(SIGTERM, sig_handler) == SIG_IGN) |
|
413 |
signal(SIGTERM, SIG_IGN); |
|
414 |
|
|
415 |
get_term_size(); |
|
416 |
|
|
417 |
if (tcgetattr(0, &tty_save) != 0) |
|
418 |
{ |
|
419 |
perror("tcgetattr error"); |
|
420 |
return(EXIT_FAILURE); |
|
421 |
} |
|
422 |
} |
|
423 |
|
|
424 |
parse_args(argc, argv); |
|
425 |
cmd_build_tree(); |
|
426 |
server_connect(); |
|
427 |
select_loop(); |
|
428 |
return 0; |
|
429 |
} |
client/client.h | ||
---|---|---|
20 | 20 |
int cmd_complete(char *cmd, int len, char *buf, int again); |
21 | 21 |
char *cmd_expand(char *cmd); |
22 | 22 |
|
23 |
/* client_common.c */
|
|
23 |
/* common.c */ |
|
24 | 24 |
|
25 | 25 |
#define STATE_PROMPT 0 |
26 | 26 |
#define STATE_CMD_SERVER 1 |
client/client_common.c | ||
---|---|---|
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 |
#include <stdio.h> |
|
11 |
#include <stdlib.h> |
|
12 |
#include <fcntl.h> |
|
13 |
#include <unistd.h> |
|
14 |
#include <errno.h> |
|
15 |
#include <sys/socket.h> |
|
16 |
#include <sys/un.h> |
|
17 |
#include <sys/types.h> |
|
18 |
|
|
19 |
#include "nest/bird.h" |
|
20 |
#include "lib/resource.h" |
|
21 |
#include "lib/string.h" |
|
22 |
#include "client/client.h" |
|
23 |
#include "sysdep/unix/unix.h" |
|
24 |
|
|
25 |
char *server_path = PATH_CONTROL_SOCKET; |
|
26 |
int server_fd; |
|
27 |
byte server_read_buf[SERVER_READ_BUF_LEN]; |
|
28 |
byte *server_read_pos = server_read_buf; |
|
29 |
|
|
30 |
int input_initialized; |
|
31 |
int input_hidden_end; |
|
32 |
int cstate = STATE_CMD_SERVER; |
|
33 |
int nstate = STATE_CMD_SERVER; |
|
34 |
|
|
35 |
int num_lines, skip_input, interactive; |
|
36 |
|
|
37 |
|
|
38 |
/*** Input ***/ |
|
39 |
|
|
40 |
int |
|
41 |
handle_internal_command(char *cmd) |
|
42 |
{ |
|
43 |
if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) |
|
44 |
{ |
|
45 |
cleanup(); |
|
46 |
exit(0); |
|
47 |
} |
|
48 |
if (!strncmp(cmd, "help", 4)) |
|
49 |
{ |
|
50 |
puts("Press `?' for context sensitive help."); |
|
51 |
return 1; |
|
52 |
} |
|
53 |
return 0; |
|
54 |
} |
|
55 |
|
|
56 |
void |
|
57 |
submit_server_command(char *cmd) |
|
58 |
{ |
|
59 |
server_send(cmd); |
|
60 |
nstate = STATE_CMD_SERVER; |
|
61 |
num_lines = 2; |
|
62 |
} |
|
63 |
|
|
64 |
/*** Communication with server ***/ |
|
65 |
|
|
66 |
void |
|
67 |
server_connect(void) |
|
68 |
{ |
|
69 |
struct sockaddr_un sa; |
|
70 |
|
|
71 |
server_fd = socket(AF_UNIX, SOCK_STREAM, 0); |
|
72 |
if (server_fd < 0) |
|
73 |
die("Cannot create socket: %m"); |
|
74 |
|
|
75 |
if (strlen(server_path) >= sizeof(sa.sun_path)) |
|
76 |
die("server_connect: path too long"); |
|
77 |
|
|
78 |
bzero(&sa, sizeof(sa)); |
|
79 |
sa.sun_family = AF_UNIX; |
|
80 |
strcpy(sa.sun_path, server_path); |
|
81 |
if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) |
|
82 |
die("Unable to connect to server control socket (%s): %m", server_path); |
|
83 |
if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) |
|
84 |
die("fcntl: %m"); |
|
85 |
} |
|
86 |
|
|
87 |
void |
|
88 |
server_read(void) |
|
89 |
{ |
|
90 |
int c; |
|
91 |
byte *start, *p; |
|
92 |
|
|
93 |
redo: |
|
94 |
c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); |
|
95 |
if (!c) |
|
96 |
die("Connection closed by server."); |
|
97 |
if (c < 0) |
|
98 |
{ |
|
99 |
if (errno == EINTR) |
|
100 |
goto redo; |
|
101 |
else |
|
102 |
die("Server read error: %m"); |
|
103 |
} |
|
104 |
|
|
105 |
start = server_read_buf; |
|
106 |
p = server_read_pos; |
|
107 |
server_read_pos += c; |
|
108 |
while (p < server_read_pos) |
|
109 |
if (*p++ == '\n') |
|
110 |
{ |
|
111 |
p[-1] = 0; |
|
112 |
server_got_reply(start); |
|
113 |
start = p; |
|
114 |
} |
|
115 |
if (start != server_read_buf) |
|
116 |
{ |
|
117 |
int l = server_read_pos - start; |
|
118 |
memmove(server_read_buf, start, l); |
|
119 |
server_read_pos = server_read_buf + l; |
|
120 |
} |
|
121 |
else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) |
|
122 |
{ |
|
123 |
strcpy(server_read_buf, "?<too-long>"); |
|
124 |
server_read_pos = server_read_buf + 11; |
|
125 |
} |
|
126 |
} |
|
127 |
|
|
128 |
void |
|
129 |
wait_for_write(int fd) |
|
130 |
{ |
|
131 |
while (1) |
|
132 |
{ |
|
133 |
int rv; |
|
134 |
fd_set set; |
|
135 |
FD_ZERO(&set); |
|
136 |
FD_SET(fd, &set); |
|
137 |
|
|
138 |
rv = select(fd+1, NULL, &set, NULL, NULL); |
|
139 |
if (rv < 0) |
|
140 |
{ |
|
141 |
if (errno == EINTR) |
|
142 |
continue; |
|
143 |
else |
|
144 |
die("select: %m"); |
|
145 |
} |
|
146 |
|
|
147 |
if (FD_ISSET(server_fd, &set)) |
|
148 |
return; |
|
149 |
} |
|
150 |
} |
|
151 |
|
|
152 |
void |
|
153 |
server_send(char *cmd) |
|
154 |
{ |
|
155 |
int l = strlen(cmd); |
|
156 |
byte *z = alloca(l + 1); |
|
157 |
|
|
158 |
memcpy(z, cmd, l); |
|
159 |
z[l++] = '\n'; |
|
160 |
while (l) |
|
161 |
{ |
|
162 |
int cnt = write(server_fd, z, l); |
|
163 |
|
|
164 |
if (cnt < 0) |
|
165 |
{ |
|
166 |
if (errno == EAGAIN) |
|
167 |
wait_for_write(server_fd); |
|
168 |
else if (errno == EINTR) |
|
169 |
continue; |
|
170 |
else |
|
171 |
die("Server write error: %m"); |
|
172 |
} |
|
173 |
else |
|
174 |
{ |
|
175 |
l -= cnt; |
|
176 |
z += cnt; |
|
177 |
} |
|
178 |
} |
|
179 |
} |
tools/Makefile.in | ||
---|---|---|
3 | 3 |
|
4 | 4 |
include Rules |
5 | 5 |
|
6 |
.PHONY: all daemon birdci birdcl subdir depend clean distclean tags docs userdocs progdocs
|
|
6 |
.PHONY: all daemon birdc birdcl subdir depend clean distclean tags docs userdocs progdocs |
|
7 | 7 |
|
8 | 8 |
all: sysdep/paths.h .dep-stamp subdir daemon birdcl @CLIENT@ |
9 | 9 |
|
... | ... | |
17 | 17 |
|
18 | 18 |
$(bird-dep): sysdep/paths.h .dep-stamp subdir |
19 | 19 |
|
20 |
birdc-dep := client/birdc/all.o client/all.o lib/birdlib.a
|
|
20 |
birdc-dep := client/birdc.o client/all.o lib/birdlib.a |
|
21 | 21 |
|
22 | 22 |
$(birdc-dep): sysdep/paths.h .dep-stamp subdir |
23 | 23 |
|
24 |
birdcl-dep := client/birdcl/all.o client/all.o lib/birdlib.a
|
|
24 |
birdcl-dep := client/birdcl.o client/all.o lib/birdlib.a |
|
25 | 25 |
|
26 | 26 |
$(birdcl-dep): sysdep/paths.h .dep-stamp subdir |
27 | 27 |
|
28 | 28 |
|
29 |
export client := @CLIENT@ |
|
30 |
|
|
29 | 31 |
depend: sysdep/paths.h .dir-stamp |
30 | 32 |
set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done |
31 |
set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done
|
|
33 |
set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done
|
|
32 | 34 |
|
33 | 35 |
subdir: sysdep/paths.h .dir-stamp .dep-stamp |
34 | 36 |
set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done |
35 |
set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done
|
|
37 |
set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done
|
|
36 | 38 |
|
37 | 39 |
$(exedir)/bird: $(bird-dep) |
38 | 40 |
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) |
... | ... | |
44 | 46 |
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) |
45 | 47 |
|
46 | 48 |
.dir-stamp: sysdep/paths.h |
47 |
mkdir -p $(static-dirs) $(birdcl-dirs) $(birdc-dirs) $(doc-dirs)
|
|
49 |
mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs)
|
|
48 | 50 |
touch .dir-stamp |
49 | 51 |
|
50 | 52 |
.dep-stamp: |
tools/Rules.in | ||
---|---|---|
11 | 11 |
static-dir-paths := $(addprefix $(srcdir)/,$(static-dirs)) |
12 | 12 |
dynamic-dirs := lib conf |
13 | 13 |
dynamic-dir-paths := $(dynamic-dirs) |
14 |
birdc-dirs := client client/@CLIENT@ |
|
15 |
birdc-dir-paths := $(birdc-dirs) |
|
16 |
birdcl-dirs := client client/birdcl |
|
17 |
birdcl-dir-paths := $(birdcl-dirs) |
|
14 |
client-dirs := client |
|
15 |
client-dir-paths := $(client-dirs) |
|
18 | 16 |
doc-dirs := doc |
19 | 17 |
doc-dir-paths := $(doc-dirs) |
20 | 18 |
|
21 |
all-dirs:=$(static-dirs) $(dynamic-dirs) $(birdc-dirs) $(doc-dirs)
|
|
19 |
all-dirs:=$(static-dirs) $(dynamic-dirs) $(client-dirs) $(doc-dirs)
|
|
22 | 20 |
clean-dirs:=$(all-dirs) proto sysdep |
23 | 21 |
|
24 | 22 |
CPPFLAGS=-I$(root-rel) -I$(srcdir) @CPPFLAGS@ |
... | ... | |
77 | 75 |
%.o: $(src-path)%.c |
78 | 76 |
$(CC) $(CFLAGS) -o $@ -c $< |
79 | 77 |
|
78 |
ifndef source-dep |
|
79 |
source-dep := $(source) |
|
80 |
endif |
|
81 |
|
|
80 | 82 |
depend: |
81 |
$(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source)) >depend |
|
83 |
$(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source-dep)) >depend
|
|
82 | 84 |
|
83 | 85 |
ifneq ($(wildcard depend),) |
84 | 86 |
include depend |
Also available in: Unified diff