From 5c2c4ea8b1e924fce433094e744c0467da55aaab Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Wed, 23 Jan 2013 15:51:04 +0100 Subject: [PATCH 1/7] Rename client/client.c to client_full.c Rename client/client.c to client-full.c and change the Makefile accordingly. This is a preparation step for introducing a new lightweight client which should reuse as much code as possible from the old one but it should not depend on external libraries. Signed-off-by: Tomas Hlavacek --- client/Makefile | 2 +- client/{client.c => client_full.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename client/{client.c => client_full.c} (100%) diff --git a/client/Makefile b/client/Makefile index 867476cc..74b30b77 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=client.c commands.c util.c +source=client_full.c commands.c util.c root-rel=../ dir-name=client diff --git a/client/client.c b/client/client_full.c similarity index 100% rename from client/client.c rename to client/client_full.c From e454916149d4efe66732fdd0388181813cab6ed0 Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Wed, 23 Jan 2013 17:14:53 +0100 Subject: [PATCH 2/7] Pull out independent routines from client_full.c Pull out routines for interacting with the server and interpreting internal commands which are not dependent on libreadline and ncurses libraries. This is a preparation step for a new lightweight birdc client. --- client/Makefile | 2 +- client/client.h | 14 +++ client/client_common.c | 179 ++++++++++++++++++++++++++++ client/client_full.c | 264 +++++++++-------------------------------- 4 files changed, 252 insertions(+), 207 deletions(-) create mode 100644 client/client_common.c diff --git a/client/Makefile b/client/Makefile index 74b30b77..fdefe6ab 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=client_full.c commands.c util.c +source=client_full.c commands.c util.c client_common.c root-rel=../ dir-name=client diff --git a/client/client.h b/client/client.h index 64de97ec..373b1c6f 100644 --- a/client/client.h +++ b/client/client.h @@ -18,3 +18,17 @@ void cmd_build_tree(void); void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); + +/* client_common.c */ + +#define STATE_PROMPT 0 +#define STATE_CMD_SERVER 1 +#define STATE_CMD_USER 2 + +#define SERVER_READ_BUF_LEN 4096 + +int handle_internal_command(char *cmd); +void submit_server_command(char *cmd); +void server_connect(void); +void server_read(void); +void server_send(char *cmd); diff --git a/client/client_common.c b/client/client_common.c new file mode 100644 index 00000000..5f0a36dd --- /dev/null +++ b/client/client_common.c @@ -0,0 +1,179 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +char *server_path = PATH_CONTROL_SOCKET; +int server_fd; +byte server_read_buf[SERVER_READ_BUF_LEN]; +byte *server_read_pos = server_read_buf; + +int input_initialized; +int input_hidden_end; +int cstate = STATE_CMD_SERVER; +int nstate = STATE_CMD_SERVER; + +int num_lines, skip_input, interactive; + + +/*** Input ***/ + +int +handle_internal_command(char *cmd) +{ + if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) + { + cleanup(); + exit(0); + } + if (!strncmp(cmd, "help", 4)) + { + puts("Press `?' for context sensitive help."); + return 1; + } + return 0; +} + +void +submit_server_command(char *cmd) +{ + server_send(cmd); + nstate = STATE_CMD_SERVER; + num_lines = 2; +} + +/*** Communication with server ***/ + +void +server_connect(void) +{ + struct sockaddr_un sa; + + server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) + die("Cannot create socket: %m"); + + if (strlen(server_path) >= sizeof(sa.sun_path)) + die("server_connect: path too long"); + + bzero(&sa, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, server_path); + if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) + die("Unable to connect to server control socket (%s): %m", server_path); + if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl: %m"); +} + +void +server_read(void) +{ + int c; + byte *start, *p; + + redo: + c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); + if (!c) + die("Connection closed by server."); + if (c < 0) + { + if (errno == EINTR) + goto redo; + else + die("Server read error: %m"); + } + + start = server_read_buf; + p = server_read_pos; + server_read_pos += c; + while (p < server_read_pos) + if (*p++ == '\n') + { + p[-1] = 0; + server_got_reply(start); + start = p; + } + if (start != server_read_buf) + { + int l = server_read_pos - start; + memmove(server_read_buf, start, l); + server_read_pos = server_read_buf + l; + } + else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) + { + strcpy(server_read_buf, "?"); + server_read_pos = server_read_buf + 11; + } +} + +void +wait_for_write(int fd) +{ + while (1) + { + int rv; + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + + rv = select(fd+1, NULL, &set, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &set)) + return; + } +} + +void +server_send(char *cmd) +{ + int l = strlen(cmd); + byte *z = alloca(l + 1); + + memcpy(z, cmd, l); + z[l++] = '\n'; + while (l) + { + int cnt = write(server_fd, z, l); + + if (cnt < 0) + { + if (errno == EAGAIN) + wait_for_write(server_fd); + else if (errno == EINTR) + continue; + else + die("Server write error: %m"); + } + else + { + l -= cnt; + z += cnt; + } + } +} diff --git a/client/client_full.c b/client/client_full.c index d8f0060c..9b4dd2fb 100644 --- a/client/client_full.c +++ b/client/client_full.c @@ -31,21 +31,17 @@ static char *init_cmd; static int once; static int restricted; -static char *server_path = PATH_CONTROL_SOCKET; -static int server_fd; -static byte server_read_buf[4096]; -static byte *server_read_pos = server_read_buf; +extern char *server_path; +extern int server_fd; +extern byte server_read_buf[SERVER_READ_BUF_LEN]; +extern byte *server_read_pos; -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 +extern int input_initialized; +extern int input_hidden_end; +extern int cstate; +extern int nstate; -static int input_initialized; -static int input_hidden_end; -static int cstate = STATE_CMD_SERVER; -static int nstate = STATE_CMD_SERVER; - -static int num_lines, skip_input, interactive; +extern int num_lines, skip_input, interactive; /*** Parsing of arguments ***/ @@ -102,37 +98,11 @@ parse_args(int argc, char **argv) /*** Input ***/ -static void server_send(char *); - /* HACK: libreadline internals we need to access */ extern int _rl_vis_botlin; extern void _rl_move_vert(int); extern Function *rl_last_func; -static int -handle_internal_command(char *cmd) -{ - if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) - { - cleanup(); - exit(0); - } - if (!strncmp(cmd, "help", 4)) - { - puts("Press `?' for context sensitive help."); - return 1; - } - return 0; -} - -void -submit_server_command(char *cmd) -{ - server_send(cmd); - nstate = STATE_CMD_SERVER; - num_lines = 2; -} - static void add_history_dedup(char *cmd) { @@ -142,8 +112,7 @@ add_history_dedup(char *cmd) add_history(cmd); } -static void -got_line(char *cmd_buffer) +void got_line(char *cmd_buffer) { char *cmd; @@ -156,16 +125,16 @@ got_line(char *cmd_buffer) { cmd = cmd_expand(cmd_buffer); if (cmd) - { - add_history_dedup(cmd); + { + add_history_dedup(cmd); - if (!handle_internal_command(cmd)) - submit_server_command(cmd); + if (!handle_internal_command(cmd)) + submit_server_command(cmd); - free(cmd); - } + free(cmd); + } else - add_history_dedup(cmd_buffer); + add_history_dedup(cmd_buffer); } free(cmd_buffer); } @@ -284,17 +253,6 @@ input_reveal(void) rl_forced_update_display(); } -void -cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - void update_state(void) { @@ -364,112 +322,19 @@ more(void) fflush(stdout); } +void cleanup(void) +{ + if (input_initialized) + { + input_initialized = 0; + input_hide(); + rl_callback_handler_remove(); + } +} + /*** Communication with server ***/ -static void -server_connect(void) -{ - struct sockaddr_un sa; - - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server_fd < 0) - die("Cannot create socket: %m"); - - if (strlen(server_path) >= sizeof(sa.sun_path)) - die("server_connect: path too long"); - - bzero(&sa, sizeof(sa)); - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, server_path); - if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - die("Unable to connect to server control socket (%s): %m", server_path); - if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) - die("fcntl: %m"); -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -static void -server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - if (x[4] == ' ') - { - nstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) - { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); - } -} - -static void -server_read(void) -{ - int c; - byte *start, *p; - - redo: - c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); - if (!c) - die("Connection closed by server."); - if (c < 0) - { - if (errno == EINTR) - goto redo; - else - die("Server read error: %m"); - } - - start = server_read_buf; - p = server_read_pos; - server_read_pos += c; - while (p < server_read_pos) - if (*p++ == '\n') - { - p[-1] = 0; - server_got_reply(start); - start = p; - } - if (start != server_read_buf) - { - int l = server_read_pos - start; - memmove(server_read_buf, start, l); - server_read_pos = server_read_buf + l; - } - else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) - { - strcpy(server_read_buf, "?"); - server_read_pos = server_read_buf + 11; - } -} - static fd_set select_fds; static void @@ -508,56 +373,43 @@ select_loop(void) } } -static void -wait_for_write(int fd) +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +void server_got_reply(char *x) { - while (1) + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) { - int rv; - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); - - rv = select(fd+1, NULL, &set, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &set)) - return; + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + if (x[4] == ' ') + { + nstate = STATE_PROMPT; + skip_input = 0; + return; + } } -} + else + PRINTF(len, "??? <%s>\n", x); -static void -server_send(char *cmd) -{ - int l = strlen(cmd); - byte *z = alloca(l + 1); + if (skip_input) + return; - memcpy(z, cmd, l); - z[l++] = '\n'; - while (l) + if (interactive && input_initialized && (len > 0)) { - int cnt = write(server_fd, z, l); - - if (cnt < 0) - { - if (errno == EAGAIN) - wait_for_write(server_fd); - else if (errno == EINTR) - continue; - else - die("Server write error: %m"); - } - else - { - l -= cnt; - z += cnt; - } + int lns = LINES ? LINES : 25; + int cls = COLS ? COLS : 80; + num_lines += (len + cls - 1) / cls; /* Divide and round up */ + if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) + more(); } } From 8322ecde124188a9408b54afead4666bb954e5a5 Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Sun, 24 Feb 2013 23:47:22 +0100 Subject: [PATCH 3/7] Add lightweight client - birdcl Restructure client/ subdir. Add two different flavors of client. The full featured birdc client code is in client/birdc/. The new light client birtcl is in client/birdcl/. Common sources of both clients are directly in client/. Rework on-line auto-completion in client/command.c to conditionally turn off ncurses-specific code. Add lightweight client without libreadline and ncurses dependencies - birdcl. The birdcl lacks support of history, on-line auto-completion and there are different implementations of "more" functionality and help on '?' press. New client operates in canonical terminal mode (apart from "more" display) and therefore all commands have to be executed by a return key including help commands (called by '?' character in the end of the line). Apart from these limitations the interaction style should be the same as for the full client - birdc. Build of birdcl is always on (independent on --enable-client parameter). --- client/Makefile | 2 +- client/birdc/Makefile | 5 + client/{client_full.c => birdc/client.c} | 0 client/birdcl/Makefile | 5 + client/birdcl/client.c | 416 +++++++++++++++++++++++ client/client.h | 3 +- configure.in | 2 +- tools/Makefile.in | 27 +- tools/Rules.in | 8 +- 9 files changed, 454 insertions(+), 14 deletions(-) create mode 100644 client/birdc/Makefile rename client/{client_full.c => birdc/client.c} (100%) create mode 100644 client/birdcl/Makefile create mode 100644 client/birdcl/client.c diff --git a/client/Makefile b/client/Makefile index fdefe6ab..3568833e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=client_full.c commands.c util.c client_common.c +source=commands.c util.c client_common.c root-rel=../ dir-name=client diff --git a/client/birdc/Makefile b/client/birdc/Makefile new file mode 100644 index 00000000..154a8d20 --- /dev/null +++ b/client/birdc/Makefile @@ -0,0 +1,5 @@ +source=client.c +root-rel=../../ +dir-name=client/birdc + +include ../../Rules diff --git a/client/client_full.c b/client/birdc/client.c similarity index 100% rename from client/client_full.c rename to client/birdc/client.c diff --git a/client/birdcl/Makefile b/client/birdcl/Makefile new file mode 100644 index 00000000..fcdc5eb2 --- /dev/null +++ b/client/birdcl/Makefile @@ -0,0 +1,5 @@ +source=client.c +root-rel=../../ +dir-name=client/birdcl + +include ../../Rules diff --git a/client/birdcl/client.c b/client/birdcl/client.c new file mode 100644 index 00000000..0a7e3808 --- /dev/null +++ b/client/birdcl/client.c @@ -0,0 +1,416 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define INPUT_BUF_LEN 2048 + +static char *opt_list = "s:vr"; +static int verbose; +static char *init_cmd; +static int once; + +extern char *server_path; +extern int server_fd; + +extern int nstate; +extern int num_lines, skip_input, interactive; + +static int term_lns=25; +static int term_cls=80; +struct termios tty_save; + +void +input_start_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_stop_list(void) +{ + /* Empty in non-ncurses version. */ +} + +/*** Parsing of arguments ***/ + +static void +usage(void) +{ + fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); + exit(1); +} + +static void +parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, opt_list)) >= 0) + switch (c) + { + case 's': + server_path = optarg; + break; + case 'v': + verbose++; + break; + case 'r': + init_cmd = "restrict"; + break; + default: + usage(); + } + + /* If some arguments are not options, we take it as commands */ + if (optind < argc) + { + char *tmp; + int i; + int len = 0; + + if (init_cmd) + usage(); + + for (i = optind; i < argc; i++) + len += strlen(argv[i]) + 1; + + tmp = init_cmd = malloc(len); + for (i = optind; i < argc; i++) + { + strcpy(tmp, argv[i]); + tmp += strlen(tmp); + *tmp++ = ' '; + } + tmp[-1] = 0; + + once = 1; + } +} + +static void +run_init_cmd(void) +{ + if (init_cmd) + { + /* First transition - client received hello from BIRD + and there is waiting initial command */ + submit_server_command(init_cmd); + init_cmd = NULL; + return; + } + + if (!init_cmd && once && (nstate == STATE_CMD_USER)) + { + /* Initial command is finished and we want to exit */ + cleanup(); + exit(0); + } +} + +/*** Input ***/ + +static void +got_line(char *cmd_buffer) +{ + char *cmd; + + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + if (cmd_buffer[0]) + { + cmd = cmd_expand(cmd_buffer); + if (cmd) + { + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); + } + } + free(cmd_buffer); +} + +void +cleanup(void) +{ + /* No ncurses -> restore terminal state. */ + if (interactive) + if (tcsetattr (0, TCSANOW, &tty_save) != 0) + { + perror("tcsetattr error"); + exit(EXIT_FAILURE); + } +} + +static void +print_prompt(void) +{ + /* No ncurses -> no status to reveal/hide, print prompt manually. */ + printf("bird> "); + fflush(stdout); +} + + +static void +term_read(void) +{ + char *buf = malloc(INPUT_BUF_LEN); + + if (fgets(buf, INPUT_BUF_LEN, stdin) == NULL) { + free(buf); + exit(0); + } + + if (buf[strlen(buf)-1] != '\n') + { + printf("Input too long.\n"); + free(buf); + return; + } + + if (strlen(buf) <= 0) + { + free(buf); + return; + } + + buf[strlen(buf)-1] = '\0'; + + if (!interactive) + { + print_prompt(); + printf("%s\n",buf); + } + + if (strchr(buf, '?')) + { + printf("\n"); + cmd_help(buf, strlen(buf)); + free(buf); + return; + } + + if (strlen(buf) > 0) + { + got_line(buf); /* buf is free()-ed inside */ + } + else + { + free(buf); /* no command, only newline -> no-op */ + return; + } + +} + +/*** Communication with server ***/ + +void +more(void) +{ + struct termios tty; + + printf("--More--\015"); + fflush(stdout); + + if (tcgetattr(0, &tty) != 0) + { + perror("tcgetattr error"); + exit(EXIT_FAILURE); + } + tty.c_lflag &= (~ECHO); + tty.c_lflag &= (~ICANON); + if (tcsetattr (0, TCSANOW, &tty) != 0) + { + perror("tcsetattr error"); + exit(EXIT_FAILURE); + } + + redo: + switch (getchar()) + { + case 32: + num_lines = 2; + break; + case 13: + num_lines--; + break; + case '\n': + num_lines--; + break; + case 'q': + skip_input = 1; + break; + default: + goto redo; + } + + tty.c_lflag |= ECHO; + tty.c_lflag |= ICANON; + if (tcsetattr (0, TCSANOW, &tty) != 0) + { + perror("tcsetattr error"); + exit(EXIT_FAILURE); + } + + printf(" \015"); + fflush(stdout); +} + +static void +get_term_size(void) +{ + struct winsize tws; + if (ioctl(0, TIOCGWINSZ, &tws) == 0) + { + term_lns = tws.ws_row; + term_cls = tws.ws_col; + } + else + { + term_lns = 25; + term_cls = 80; + } +} + +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +void +server_got_reply(char *x) +{ + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + + if (x[4] == ' ') + { + nstate = STATE_CMD_USER; + skip_input = 0; + return; + } + } + else + PRINTF(len, "??? <%s>\n", x); + + if (skip_input) + return; + + if (interactive && (len > 0)) + { + num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ + if (num_lines >= term_lns) + more(); + } +} + +static fd_set select_fds; + +static void +select_loop(void) +{ + int rv; + + while (1) + { + FD_ZERO(&select_fds); + + if (nstate != STATE_CMD_USER) + FD_SET(server_fd, &select_fds); + + if (nstate != STATE_CMD_SERVER) + { + FD_SET(0, &select_fds); + if (interactive) + print_prompt(); + } + + rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &select_fds)) + { + server_read(); + run_init_cmd(); + } + + if (FD_ISSET(0, &select_fds)) + term_read(); + } +} + +static void +sig_handler(int signal) +{ + cleanup(); + exit(0); +} + +int +main(int argc, char **argv) +{ + interactive = isatty(fileno(stdin)); + if (interactive) + { + if (signal(SIGINT, sig_handler) == SIG_IGN) + signal(SIGINT, SIG_IGN); + if (signal(SIGHUP, sig_handler) == SIG_IGN) + signal(SIGHUP, SIG_IGN); + if (signal(SIGTERM, sig_handler) == SIG_IGN) + signal(SIGTERM, SIG_IGN); + + get_term_size(); + + if (tcgetattr(0, &tty_save) != 0) + { + perror("tcgetattr error"); + return(EXIT_FAILURE); + } + } + + parse_args(argc, argv); + cmd_build_tree(); + server_connect(); + select_loop(); + return 0; +} diff --git a/client/client.h b/client/client.h index 373b1c6f..2d215059 100644 --- a/client/client.h +++ b/client/client.h @@ -6,11 +6,12 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* client.c */ +/* client.c callbacks */ void cleanup(void); void input_start_list(void); void input_stop_list(void); +void server_got_reply(char *x); /* commands.c */ diff --git a/configure.in b/configure.in index 54993dfc..534baf21 100644 --- a/configure.in +++ b/configure.in @@ -234,7 +234,7 @@ fi CLIENT= CLIENT_LIBS= if test "$enable_client" = yes ; then - CLIENT=client + CLIENT=birdc AC_CHECK_LIB(history, add_history, CLIENT_LIBS="-lhistory") AC_CHECK_LIB(ncurses, tgetent, USE_TERMCAP_LIB=-lncurses, AC_CHECK_LIB(curses, tgetent, USE_TERMCAP_LIB=-lcurses, diff --git a/tools/Makefile.in b/tools/Makefile.in index 728e5797..adf35b64 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -3,29 +3,36 @@ include Rules -.PHONY: all daemon client subdir depend clean distclean tags docs userdocs progdocs +.PHONY: all daemon birdci birdcl subdir depend clean distclean tags docs userdocs progdocs -all: sysdep/paths.h .dep-stamp subdir daemon @CLIENT@ +all: sysdep/paths.h .dep-stamp subdir daemon birdcl @CLIENT@ daemon: $(exedir)/bird -client: $(exedir)/birdc +birdc: $(exedir)/birdc + +birdcl: $(exedir)/birdcl bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a $(bird-dep): sysdep/paths.h .dep-stamp subdir -birdc-dep := client/all.o lib/birdlib.a +birdc-dep := client/birdc/all.o client/all.o lib/birdlib.a $(birdc-dep): sysdep/paths.h .dep-stamp subdir +birdcl-dep := client/birdcl/all.o client/all.o lib/birdlib.a + +$(birdcl-dep): sysdep/paths.h .dep-stamp subdir + + depend: sysdep/paths.h .dir-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done subdir: sysdep/paths.h .dir-stamp .dep-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done $(exedir)/bird: $(bird-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) @@ -33,8 +40,11 @@ $(exedir)/bird: $(bird-dep) $(exedir)/birdc: $(birdc-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CLIENT_LIBS) +$(exedir)/birdcl: $(birdcl-dep) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + .dir-stamp: sysdep/paths.h - mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs) + mkdir -p $(static-dirs) $(birdcl-dirs) $(birdc-dirs) $(doc-dirs) touch .dir-stamp .dep-stamp: @@ -58,6 +68,7 @@ tags: install: all $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@ $(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ + $(INSTALL_PROGRAM) -s $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ if test -n "@CLIENT@" ; then \ $(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ fi @@ -74,7 +85,7 @@ install-docs: clean: find . -name "*.[oa]" -o -name core -o -name depend -o -name "*.html" | xargs rm -f rm -f conf/cf-lex.c conf/cf-parse.* conf/commands.h conf/keywords.h - rm -f $(exedir)/bird $(exedir)/birdc $(exedir)/bird.ctl $(exedir)/bird6.ctl .dep-stamp + rm -f $(exedir)/bird $(exedir)/birdcl $(exedir)/birdc $(exedir)/bird.ctl $(exedir)/bird6.ctl .dep-stamp distclean: clean rm -f config.* configure sysdep/autoconf.h sysdep/paths.h Makefile Rules diff --git a/tools/Rules.in b/tools/Rules.in index fc06aeb1..fd10f5de 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -11,12 +11,14 @@ static-dirs := nest filter $(addprefix proto/,$(protocols)) static-dir-paths := $(addprefix $(srcdir)/,$(static-dirs)) dynamic-dirs := lib conf dynamic-dir-paths := $(dynamic-dirs) -client-dirs := @CLIENT@ -client-dir-paths := $(client-dirs) +birdc-dirs := client client/@CLIENT@ +birdc-dir-paths := $(birdc-dirs) +birdcl-dirs := client client/birdcl +birdcl-dir-paths := $(birdcl-dirs) doc-dirs := doc doc-dir-paths := $(doc-dirs) -all-dirs:=$(static-dirs) $(dynamic-dirs) $(client-dirs) $(doc-dirs) +all-dirs:=$(static-dirs) $(dynamic-dirs) $(birdc-dirs) $(doc-dirs) clean-dirs:=$(all-dirs) proto sysdep CPPFLAGS=-I$(root-rel) -I$(srcdir) @CPPFLAGS@ From ce1348537455e5482a283f7a4cae734d13dcf34e Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Tue, 19 Mar 2013 18:02:40 +0100 Subject: [PATCH 4/7] Fix birdcl questionmark handling Fix handling of questionmark handling in the bird light client. The questionmark should display help when it is the last non-blank character on command line. Otherwise the questionmark does not have any special meaning and it could be a part of a pattern. --- client/birdcl/client.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/birdcl/client.c b/client/birdcl/client.c index 0a7e3808..21b63270 100644 --- a/client/birdcl/client.c +++ b/client/birdcl/client.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "nest/bird.h" #include "lib/resource.h" @@ -177,6 +178,18 @@ print_prompt(void) } +static int lastnb(char *str) +{ + int i; + for (i=strlen(str)-1; i>0; i--) + { + if(!isblank(str[i])) + return i; + } + + return 0; +} + static void term_read(void) { @@ -208,7 +221,7 @@ term_read(void) printf("%s\n",buf); } - if (strchr(buf, '?')) + if (buf[lastnb(buf)] == '?') { printf("\n"); cmd_help(buf, strlen(buf)); From 568d9c9faeab70951d8e9bfea521e1b38a9a3d1c Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Sat, 6 Apr 2013 22:07:32 +0200 Subject: [PATCH 5/7] Fix birdcl async message handling Fix handling of async messafe in the bird light client. The async message may occure at the any moment so we need the client to liste for the message from server when it waits for user input. --- client/birdcl/client.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/birdcl/client.c b/client/birdcl/client.c index 21b63270..1e920ce8 100644 --- a/client/birdcl/client.c +++ b/client/birdcl/client.c @@ -36,7 +36,7 @@ static int once; extern char *server_path; extern int server_fd; -extern int nstate; +extern int cstate; extern int num_lines, skip_input, interactive; static int term_lns=25; @@ -123,7 +123,7 @@ run_init_cmd(void) return; } - if (!init_cmd && once && (nstate == STATE_CMD_USER)) + if (!init_cmd && once && (cstate == STATE_PROMPT)) { /* Initial command is finished and we want to exit */ cleanup(); @@ -332,7 +332,7 @@ server_got_reply(char *x) if (x[4] == ' ') { - nstate = STATE_CMD_USER; + cstate = STATE_PROMPT; skip_input = 0; return; } @@ -362,10 +362,10 @@ select_loop(void) { FD_ZERO(&select_fds); - if (nstate != STATE_CMD_USER) + if (cstate != STATE_CMD_USER) FD_SET(server_fd, &select_fds); - if (nstate != STATE_CMD_SERVER) + if (cstate != STATE_CMD_SERVER) { FD_SET(0, &select_fds); if (interactive) From d2c392d44839baaefa48f4a38060be648d3415fb Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 19 Apr 2013 13:59:08 +0200 Subject: [PATCH 6/7] Removes unnecessary client subdirectories and updates buildsystem. Renames some files: birdc/client.c -> birdc.c birdcl/client.c -> birdcl.c client_common.c -> common.c --- client/Makefile | 8 +- client/birdc/Makefile | 5 - client/birdc/client.c | 430 ----------------------------------------- client/birdcl/Makefile | 5 - client/birdcl/client.c | 429 ---------------------------------------- client/client.h | 2 +- client/client_common.c | 179 ----------------- tools/Makefile.in | 14 +- tools/Rules.in | 14 +- 9 files changed, 24 insertions(+), 1062 deletions(-) delete mode 100644 client/birdc/Makefile delete mode 100644 client/birdc/client.c delete mode 100644 client/birdcl/Makefile delete mode 100644 client/birdcl/client.c delete mode 100644 client/client_common.c diff --git a/client/Makefile b/client/Makefile index 3568833e..8c2f91e0 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,5 +1,11 @@ -source=commands.c util.c client_common.c +source=commands.c util.c common.c root-rel=../ dir-name=client +clients := $(client) birdcl + +source-dep := $(source) $(addsuffix .c,$(clients)) + +subdir: $(addsuffix .o,$(clients)) + include ../Rules diff --git a/client/birdc/Makefile b/client/birdc/Makefile deleted file mode 100644 index 154a8d20..00000000 --- a/client/birdc/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -source=client.c -root-rel=../../ -dir-name=client/birdc - -include ../../Rules diff --git a/client/birdc/client.c b/client/birdc/client.c deleted file mode 100644 index 9b4dd2fb..00000000 --- a/client/birdc/client.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -static char *opt_list = "s:vr"; -static int verbose; -static char *init_cmd; -static int once; -static int restricted; - -extern char *server_path; -extern int server_fd; -extern byte server_read_buf[SERVER_READ_BUF_LEN]; -extern byte *server_read_pos; - -extern int input_initialized; -extern int input_hidden_end; -extern int cstate; -extern int nstate; - -extern int num_lines, skip_input, interactive; - -/*** Parsing of arguments ***/ - -static void -usage(void) -{ - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); - exit(1); -} - -static void -parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, opt_list)) >= 0) - switch (c) - { - case 's': - server_path = optarg; - break; - case 'v': - verbose++; - break; - case 'r': - restricted = 1; - break; - default: - usage(); - } - - /* If some arguments are not options, we take it as commands */ - if (optind < argc) - { - char *tmp; - int i; - int len = 0; - - for (i = optind; i < argc; i++) - len += strlen(argv[i]) + 1; - - tmp = init_cmd = malloc(len); - for (i = optind; i < argc; i++) - { - strcpy(tmp, argv[i]); - tmp += strlen(tmp); - *tmp++ = ' '; - } - tmp[-1] = 0; - - once = 1; - } -} - -/*** Input ***/ - -/* HACK: libreadline internals we need to access */ -extern int _rl_vis_botlin; -extern void _rl_move_vert(int); -extern Function *rl_last_func; - -static void -add_history_dedup(char *cmd) -{ - /* Add history line if it differs from the last one */ - HIST_ENTRY *he = history_get(history_length); - if (!he || strcmp(he->line, cmd)) - add_history(cmd); -} - -void got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - add_history_dedup(cmd); - - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - else - add_history_dedup(cmd_buffer); - } - free(cmd_buffer); -} - -void -input_start_list(void) /* Leave the currently edited line and make space for listing */ -{ - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif -} - -void -input_stop_list(void) /* Reprint the currently edited line after listing */ -{ - rl_on_new_line(); - rl_redisplay(); -} - -static int -input_complete(int arg UNUSED, int key UNUSED) -{ - static int complete_flag; - char buf[256]; - - if (rl_last_func != input_complete) - complete_flag = 0; - switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) - { - case 0: - complete_flag = 1; - break; - case 1: - rl_insert_text(buf); - break; - default: - complete_flag = 1; -#ifdef HAVE_RL_DING - rl_ding(); -#endif - } - return 0; -} - -static int -input_help(int arg, int key UNUSED) -{ - int i, in_string, in_bracket; - - if (arg != 1) - return rl_insert(arg, '?'); - - in_string = in_bracket = 0; - for (i = 0; i < rl_point; i++) - { - - if (rl_line_buffer[i] == '"') - in_string = ! in_string; - else if (! in_string) - { - if (rl_line_buffer[i] == '[') - in_bracket++; - else if (rl_line_buffer[i] == ']') - in_bracket--; - } - } - - /* `?' inside string or path -> insert */ - if (in_string || in_bracket) - return rl_insert(1, '?'); - - rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ - rl_insert_text("?"); - rl_redisplay(); - rl_end_undo_group(); - input_start_list(); - cmd_help(rl_line_buffer, rl_point); - rl_undo_command(1, 0); - input_stop_list(); - return 0; -} - -static void -input_init(void) -{ - rl_readline_name = "birdc"; - rl_add_defun("bird-complete", input_complete, '\t'); - rl_add_defun("bird-help", input_help, '?'); - rl_callback_handler_install("bird> ", got_line); - input_initialized = 1; -// readline library does strange things when stdin is nonblocking. -// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) -// die("fcntl: %m"); -} - -static void -input_hide(void) -{ - input_hidden_end = rl_end; - rl_end = 0; - rl_expand_prompt(""); - rl_redisplay(); -} - -static void -input_reveal(void) -{ - /* need this, otherwise some lib seems to eat pending output when - the prompt is displayed */ - fflush(stdout); - tcdrain(fileno(stdout)); - - rl_end = input_hidden_end; - rl_expand_prompt("bird> "); - rl_forced_update_display(); -} - -void -update_state(void) -{ - if (nstate == cstate) - return; - - if (restricted) - { - submit_server_command("restrict"); - restricted = 0; - return; - } - - if (init_cmd) - { - /* First transition - client received hello from BIRD - and there is waiting initial command */ - submit_server_command(init_cmd); - init_cmd = NULL; - return; - } - - if (!init_cmd && once) - { - /* Initial command is finished and we want to exit */ - cleanup(); - exit(0); - } - - if (nstate == STATE_PROMPT) - { - if (input_initialized) - input_reveal(); - else - input_init(); - } - - if (nstate != STATE_PROMPT) - input_hide(); - - cstate = nstate; -} - -void -more(void) -{ - printf("--More--\015"); - fflush(stdout); - - redo: - switch (getchar()) - { - case 32: - num_lines = 2; - break; - case 13: - num_lines--; - break; - case 'q': - skip_input = 1; - break; - default: - goto redo; - } - - printf(" \015"); - fflush(stdout); -} - -void cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - - -/*** Communication with server ***/ - -static fd_set select_fds; - -static void -select_loop(void) -{ - int rv; - while (1) - { - FD_ZERO(&select_fds); - - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - if (cstate != STATE_CMD_SERVER) - FD_SET(0, &select_fds); - - rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &select_fds)) - { - server_read(); - update_state(); - } - - if (FD_ISSET(0, &select_fds)) - { - rl_callback_read_char(); - update_state(); - } - } -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -void server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - if (x[4] == ' ') - { - nstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) - { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); - } -} - -int -main(int argc, char **argv) -{ -#ifdef HAVE_LIBDMALLOC - if (!getenv("DMALLOC_OPTIONS")) - dmalloc_debug(0x2f03d00); -#endif - - interactive = isatty(0); - parse_args(argc, argv); - cmd_build_tree(); - server_connect(); - select_loop(); - return 0; -} diff --git a/client/birdcl/Makefile b/client/birdcl/Makefile deleted file mode 100644 index fcdc5eb2..00000000 --- a/client/birdcl/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -source=client.c -root-rel=../../ -dir-name=client/birdcl - -include ../../Rules diff --git a/client/birdcl/client.c b/client/birdcl/client.c deleted file mode 100644 index 1e920ce8..00000000 --- a/client/birdcl/client.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * (c) 2013 Tomas Hlavacek - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -#define INPUT_BUF_LEN 2048 - -static char *opt_list = "s:vr"; -static int verbose; -static char *init_cmd; -static int once; - -extern char *server_path; -extern int server_fd; - -extern int cstate; -extern int num_lines, skip_input, interactive; - -static int term_lns=25; -static int term_cls=80; -struct termios tty_save; - -void -input_start_list(void) -{ - /* Empty in non-ncurses version. */ -} - -void -input_stop_list(void) -{ - /* Empty in non-ncurses version. */ -} - -/*** Parsing of arguments ***/ - -static void -usage(void) -{ - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); - exit(1); -} - -static void -parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, opt_list)) >= 0) - switch (c) - { - case 's': - server_path = optarg; - break; - case 'v': - verbose++; - break; - case 'r': - init_cmd = "restrict"; - break; - default: - usage(); - } - - /* If some arguments are not options, we take it as commands */ - if (optind < argc) - { - char *tmp; - int i; - int len = 0; - - if (init_cmd) - usage(); - - for (i = optind; i < argc; i++) - len += strlen(argv[i]) + 1; - - tmp = init_cmd = malloc(len); - for (i = optind; i < argc; i++) - { - strcpy(tmp, argv[i]); - tmp += strlen(tmp); - *tmp++ = ' '; - } - tmp[-1] = 0; - - once = 1; - } -} - -static void -run_init_cmd(void) -{ - if (init_cmd) - { - /* First transition - client received hello from BIRD - and there is waiting initial command */ - submit_server_command(init_cmd); - init_cmd = NULL; - return; - } - - if (!init_cmd && once && (cstate == STATE_PROMPT)) - { - /* Initial command is finished and we want to exit */ - cleanup(); - exit(0); - } -} - -/*** Input ***/ - -static void -got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - } - free(cmd_buffer); -} - -void -cleanup(void) -{ - /* No ncurses -> restore terminal state. */ - if (interactive) - if (tcsetattr (0, TCSANOW, &tty_save) != 0) - { - perror("tcsetattr error"); - exit(EXIT_FAILURE); - } -} - -static void -print_prompt(void) -{ - /* No ncurses -> no status to reveal/hide, print prompt manually. */ - printf("bird> "); - fflush(stdout); -} - - -static int lastnb(char *str) -{ - int i; - for (i=strlen(str)-1; i>0; i--) - { - if(!isblank(str[i])) - return i; - } - - return 0; -} - -static void -term_read(void) -{ - char *buf = malloc(INPUT_BUF_LEN); - - if (fgets(buf, INPUT_BUF_LEN, stdin) == NULL) { - free(buf); - exit(0); - } - - if (buf[strlen(buf)-1] != '\n') - { - printf("Input too long.\n"); - free(buf); - return; - } - - if (strlen(buf) <= 0) - { - free(buf); - return; - } - - buf[strlen(buf)-1] = '\0'; - - if (!interactive) - { - print_prompt(); - printf("%s\n",buf); - } - - if (buf[lastnb(buf)] == '?') - { - printf("\n"); - cmd_help(buf, strlen(buf)); - free(buf); - return; - } - - if (strlen(buf) > 0) - { - got_line(buf); /* buf is free()-ed inside */ - } - else - { - free(buf); /* no command, only newline -> no-op */ - return; - } - -} - -/*** Communication with server ***/ - -void -more(void) -{ - struct termios tty; - - printf("--More--\015"); - fflush(stdout); - - if (tcgetattr(0, &tty) != 0) - { - perror("tcgetattr error"); - exit(EXIT_FAILURE); - } - tty.c_lflag &= (~ECHO); - tty.c_lflag &= (~ICANON); - if (tcsetattr (0, TCSANOW, &tty) != 0) - { - perror("tcsetattr error"); - exit(EXIT_FAILURE); - } - - redo: - switch (getchar()) - { - case 32: - num_lines = 2; - break; - case 13: - num_lines--; - break; - case '\n': - num_lines--; - break; - case 'q': - skip_input = 1; - break; - default: - goto redo; - } - - tty.c_lflag |= ECHO; - tty.c_lflag |= ICANON; - if (tcsetattr (0, TCSANOW, &tty) != 0) - { - perror("tcsetattr error"); - exit(EXIT_FAILURE); - } - - printf(" \015"); - fflush(stdout); -} - -static void -get_term_size(void) -{ - struct winsize tws; - if (ioctl(0, TIOCGWINSZ, &tws) == 0) - { - term_lns = tws.ws_row; - term_cls = tws.ws_col; - } - else - { - term_lns = 25; - term_cls = 80; - } -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -void -server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - - if (x[4] == ' ') - { - cstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && (len > 0)) - { - num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ - if (num_lines >= term_lns) - more(); - } -} - -static fd_set select_fds; - -static void -select_loop(void) -{ - int rv; - - while (1) - { - FD_ZERO(&select_fds); - - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - - if (cstate != STATE_CMD_SERVER) - { - FD_SET(0, &select_fds); - if (interactive) - print_prompt(); - } - - rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &select_fds)) - { - server_read(); - run_init_cmd(); - } - - if (FD_ISSET(0, &select_fds)) - term_read(); - } -} - -static void -sig_handler(int signal) -{ - cleanup(); - exit(0); -} - -int -main(int argc, char **argv) -{ - interactive = isatty(fileno(stdin)); - if (interactive) - { - if (signal(SIGINT, sig_handler) == SIG_IGN) - signal(SIGINT, SIG_IGN); - if (signal(SIGHUP, sig_handler) == SIG_IGN) - signal(SIGHUP, SIG_IGN); - if (signal(SIGTERM, sig_handler) == SIG_IGN) - signal(SIGTERM, SIG_IGN); - - get_term_size(); - - if (tcgetattr(0, &tty_save) != 0) - { - perror("tcgetattr error"); - return(EXIT_FAILURE); - } - } - - parse_args(argc, argv); - cmd_build_tree(); - server_connect(); - select_loop(); - return 0; -} diff --git a/client/client.h b/client/client.h index 2d215059..2e4e2ea3 100644 --- a/client/client.h +++ b/client/client.h @@ -20,7 +20,7 @@ void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); -/* client_common.c */ +/* common.c */ #define STATE_PROMPT 0 #define STATE_CMD_SERVER 1 diff --git a/client/client_common.c b/client/client_common.c deleted file mode 100644 index 5f0a36dd..00000000 --- a/client/client_common.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * (c) 2013 Tomas Hlavacek - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -char *server_path = PATH_CONTROL_SOCKET; -int server_fd; -byte server_read_buf[SERVER_READ_BUF_LEN]; -byte *server_read_pos = server_read_buf; - -int input_initialized; -int input_hidden_end; -int cstate = STATE_CMD_SERVER; -int nstate = STATE_CMD_SERVER; - -int num_lines, skip_input, interactive; - - -/*** Input ***/ - -int -handle_internal_command(char *cmd) -{ - if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) - { - cleanup(); - exit(0); - } - if (!strncmp(cmd, "help", 4)) - { - puts("Press `?' for context sensitive help."); - return 1; - } - return 0; -} - -void -submit_server_command(char *cmd) -{ - server_send(cmd); - nstate = STATE_CMD_SERVER; - num_lines = 2; -} - -/*** Communication with server ***/ - -void -server_connect(void) -{ - struct sockaddr_un sa; - - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server_fd < 0) - die("Cannot create socket: %m"); - - if (strlen(server_path) >= sizeof(sa.sun_path)) - die("server_connect: path too long"); - - bzero(&sa, sizeof(sa)); - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, server_path); - if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - die("Unable to connect to server control socket (%s): %m", server_path); - if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) - die("fcntl: %m"); -} - -void -server_read(void) -{ - int c; - byte *start, *p; - - redo: - c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); - if (!c) - die("Connection closed by server."); - if (c < 0) - { - if (errno == EINTR) - goto redo; - else - die("Server read error: %m"); - } - - start = server_read_buf; - p = server_read_pos; - server_read_pos += c; - while (p < server_read_pos) - if (*p++ == '\n') - { - p[-1] = 0; - server_got_reply(start); - start = p; - } - if (start != server_read_buf) - { - int l = server_read_pos - start; - memmove(server_read_buf, start, l); - server_read_pos = server_read_buf + l; - } - else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) - { - strcpy(server_read_buf, "?"); - server_read_pos = server_read_buf + 11; - } -} - -void -wait_for_write(int fd) -{ - while (1) - { - int rv; - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); - - rv = select(fd+1, NULL, &set, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &set)) - return; - } -} - -void -server_send(char *cmd) -{ - int l = strlen(cmd); - byte *z = alloca(l + 1); - - memcpy(z, cmd, l); - z[l++] = '\n'; - while (l) - { - int cnt = write(server_fd, z, l); - - if (cnt < 0) - { - if (errno == EAGAIN) - wait_for_write(server_fd); - else if (errno == EINTR) - continue; - else - die("Server write error: %m"); - } - else - { - l -= cnt; - z += cnt; - } - } -} diff --git a/tools/Makefile.in b/tools/Makefile.in index adf35b64..feb83b9f 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -3,7 +3,7 @@ include Rules -.PHONY: all daemon birdci birdcl subdir depend clean distclean tags docs userdocs progdocs +.PHONY: all daemon birdc birdcl subdir depend clean distclean tags docs userdocs progdocs all: sysdep/paths.h .dep-stamp subdir daemon birdcl @CLIENT@ @@ -17,22 +17,24 @@ bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a $(bird-dep): sysdep/paths.h .dep-stamp subdir -birdc-dep := client/birdc/all.o client/all.o lib/birdlib.a +birdc-dep := client/birdc.o client/all.o lib/birdlib.a $(birdc-dep): sysdep/paths.h .dep-stamp subdir -birdcl-dep := client/birdcl/all.o client/all.o lib/birdlib.a +birdcl-dep := client/birdcl.o client/all.o lib/birdlib.a $(birdcl-dep): sysdep/paths.h .dep-stamp subdir +export client := @CLIENT@ + depend: sysdep/paths.h .dir-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done subdir: sysdep/paths.h .dir-stamp .dep-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done $(exedir)/bird: $(bird-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) @@ -44,7 +46,7 @@ $(exedir)/birdcl: $(birdcl-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) .dir-stamp: sysdep/paths.h - mkdir -p $(static-dirs) $(birdcl-dirs) $(birdc-dirs) $(doc-dirs) + mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs) touch .dir-stamp .dep-stamp: diff --git a/tools/Rules.in b/tools/Rules.in index fd10f5de..ca930ec8 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -11,14 +11,12 @@ static-dirs := nest filter $(addprefix proto/,$(protocols)) static-dir-paths := $(addprefix $(srcdir)/,$(static-dirs)) dynamic-dirs := lib conf dynamic-dir-paths := $(dynamic-dirs) -birdc-dirs := client client/@CLIENT@ -birdc-dir-paths := $(birdc-dirs) -birdcl-dirs := client client/birdcl -birdcl-dir-paths := $(birdcl-dirs) +client-dirs := client +client-dir-paths := $(client-dirs) doc-dirs := doc doc-dir-paths := $(doc-dirs) -all-dirs:=$(static-dirs) $(dynamic-dirs) $(birdc-dirs) $(doc-dirs) +all-dirs:=$(static-dirs) $(dynamic-dirs) $(client-dirs) $(doc-dirs) clean-dirs:=$(all-dirs) proto sysdep CPPFLAGS=-I$(root-rel) -I$(srcdir) @CPPFLAGS@ @@ -77,8 +75,12 @@ endif %.o: $(src-path)%.c $(CC) $(CFLAGS) -o $@ -c $< +ifndef source-dep +source-dep := $(source) +endif + depend: - $(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source)) >depend + $(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source-dep)) >depend ifneq ($(wildcard depend),) include depend From a5e9f3d26f887deb451a3ea086e52266c117aa0a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 23 Apr 2013 02:42:35 +0200 Subject: [PATCH 7/7] Restructures birdc and birdcl to merge duplicated code. The BIRD client code is restructured that most of the code (including main function) is shared in client.c, while birdc.c and birdcl.c contain just I/O-specific callbacks. This removes all duplicated code from variant-specific files. --- client/Makefile | 2 +- client/birdc.c | 223 +++++++++++++++++++++++++ client/birdcl.c | 165 ++++++++++++++++++ client/client.c | 436 ++++++++++++++++++++++++++++++++++++++++++++++++ client/client.h | 31 ++-- doc/bird.sgml | 8 +- 6 files changed, 848 insertions(+), 17 deletions(-) create mode 100644 client/birdc.c create mode 100644 client/birdcl.c create mode 100644 client/client.c diff --git a/client/Makefile b/client/Makefile index 8c2f91e0..a1578766 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=commands.c util.c common.c +source=commands.c util.c client.c root-rel=../ dir-name=client diff --git a/client/birdc.c b/client/birdc.c new file mode 100644 index 00000000..9dd6d9b9 --- /dev/null +++ b/client/birdc.c @@ -0,0 +1,223 @@ +/* + * BIRD Client - Readline variant I/O + * + * (c) 1999--2004 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +static int input_hidden_end; +static int prompt_active; + +/*** Input ***/ + +/* HACK: libreadline internals we need to access */ +extern int _rl_vis_botlin; +extern void _rl_move_vert(int); +extern Function *rl_last_func; + +static void +add_history_dedup(char *cmd) +{ + /* Add history line if it differs from the last one */ + HIST_ENTRY *he = history_get(history_length); + if (!he || strcmp(he->line, cmd)) + add_history(cmd); +} + +static void +input_got_line(char *cmd_buffer) +{ + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + + if (cmd_buffer[0]) + { + add_history_dedup(cmd_buffer); + submit_command(cmd_buffer); + } + + free(cmd_buffer); +} + +void +input_start_list(void) +{ + /* Leave the currently edited line and make space for listing */ + _rl_move_vert(_rl_vis_botlin); +#ifdef HAVE_RL_CRLF + rl_crlf(); +#endif +} + +void +input_stop_list(void) +{ + /* Reprint the currently edited line after listing */ + rl_on_new_line(); + rl_redisplay(); +} + +static int +input_complete(int arg UNUSED, int key UNUSED) +{ + static int complete_flag; + char buf[256]; + + if (rl_last_func != input_complete) + complete_flag = 0; + switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) + { + case 0: + complete_flag = 1; + break; + case 1: + rl_insert_text(buf); + break; + default: + complete_flag = 1; +#ifdef HAVE_RL_DING + rl_ding(); +#endif + } + return 0; +} + +static int +input_help(int arg, int key UNUSED) +{ + int i, in_string, in_bracket; + + if (arg != 1) + return rl_insert(arg, '?'); + + in_string = in_bracket = 0; + for (i = 0; i < rl_point; i++) + { + + if (rl_line_buffer[i] == '"') + in_string = ! in_string; + else if (! in_string) + { + if (rl_line_buffer[i] == '[') + in_bracket++; + else if (rl_line_buffer[i] == ']') + in_bracket--; + } + } + + /* `?' inside string or path -> insert */ + if (in_string || in_bracket) + return rl_insert(1, '?'); + + rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ + rl_insert_text("?"); + rl_redisplay(); + rl_end_undo_group(); + input_start_list(); + cmd_help(rl_line_buffer, rl_point); + rl_undo_command(1, 0); + input_stop_list(); + return 0; +} + +void +input_init(void) +{ + rl_readline_name = "birdc"; + rl_add_defun("bird-complete", input_complete, '\t'); + rl_add_defun("bird-help", input_help, '?'); + rl_callback_handler_install("bird> ", input_got_line); + + // rl_get_screen_size(); + term_lns = LINES ? LINES : 25; + term_cls = COLS ? COLS : 80; + + prompt_active = 1; + + // readline library does strange things when stdin is nonblocking. + // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) + // die("fcntl: %m"); +} + +static void +input_reveal(void) +{ + /* need this, otherwise some lib seems to eat pending output when + the prompt is displayed */ + fflush(stdout); + tcdrain(STDOUT_FILENO); + + rl_end = input_hidden_end; + rl_expand_prompt("bird> "); + rl_forced_update_display(); + + prompt_active = 1; +} + +static void +input_hide(void) +{ + input_hidden_end = rl_end; + rl_end = 0; + rl_expand_prompt(""); + rl_redisplay(); + + prompt_active = 0; +} + +void +input_notify(int prompt) +{ + if (prompt == prompt_active) + return; + + if (prompt) + input_reveal(); + else + input_hide(); +} + +void +input_read(void) +{ + rl_callback_read_char(); +} + +void +more_begin(void) +{ +} + +void +more_end(void) +{ +} + +void +cleanup(void) +{ + if (init) + return; + + input_hide(); + rl_callback_handler_remove(); +} diff --git a/client/birdcl.c b/client/birdcl.c new file mode 100644 index 00000000..c41b046c --- /dev/null +++ b/client/birdcl.c @@ -0,0 +1,165 @@ +/* + * BIRD Client - Light variant I/O + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define INPUT_BUF_LEN 2048 + +struct termios tty_save; + +void +input_start_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_stop_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_notify(int prompt) +{ + /* No ncurses -> no status to reveal/hide, print prompt manually. */ + if (!prompt) + return; + + printf("bird> "); + fflush(stdout); +} + + +static int +lastnb(char *str, int i) +{ + while (i--) + if ((str[i] != ' ') && (str[i] != '\t')) + return str[i]; + + return 0; +} + +void +input_read(void) +{ + char buf[INPUT_BUF_LEN]; + + if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0)) + { + putchar('\n'); + cleanup(); + exit(0); + } + + int l = strlen(buf); + if ((l+1) == INPUT_BUF_LEN) + { + printf("Input too long.\n"); + return; + } + + if (buf[l-1] == '\n') + buf[--l] = '\0'; + + if (!interactive) + printf("%s\n", buf); + + if (l == 0) + return; + + if (lastnb(buf, l) == '?') + { + cmd_help(buf, strlen(buf)); + return; + } + + submit_command(buf); +} + +static struct termios stored_tty; +static int more_active = 0; + +void +more_begin(void) +{ + static struct termios tty; + + tty = stored_tty; + tty.c_lflag &= (~ECHO); + tty.c_lflag &= (~ICANON); + + if (tcsetattr (0, TCSANOW, &tty) < 0) + die("tcsetattr: %m"); + + more_active = 1; +} + +void +more_end(void) +{ + more_active = 0; + + if (tcsetattr (0, TCSANOW, &stored_tty) < 0) + die("tcsetattr: %m"); +} + +static void +sig_handler(int signal) +{ + cleanup(); + exit(0); +} + +void +input_init(void) +{ + if (!interactive) + return; + + if (tcgetattr(0, &stored_tty) < 0) + die("tcgetattr: %m"); + + if (signal(SIGINT, sig_handler) == SIG_IGN) + signal(SIGINT, SIG_IGN); + if (signal(SIGTERM, sig_handler) == SIG_IGN) + signal(SIGTERM, SIG_IGN); + + struct winsize tws; + if (ioctl(0, TIOCGWINSZ, &tws) == 0) + { + term_lns = tws.ws_row; + term_cls = tws.ws_col; + } + else + { + term_lns = 25; + term_cls = 80; + } +} + +void +cleanup(void) +{ + if (more_active) + more_end(); +} diff --git a/client/client.c b/client/client.c new file mode 100644 index 00000000..61caf38b --- /dev/null +++ b/client/client.c @@ -0,0 +1,436 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/** + * DOC: BIRD client + * + * There are two variants of BIRD client: regular and light. regular + * variant depends on readline and ncurses libraries, while light + * variant uses just libc. Most of the code and the main() is common + * for both variants (in client.c file) and just a few functions are + * different (in birdc.c for regular and birdcl.c for light). Two + * binaries are generated by linking common object files like client.o + * (which is compiled from client.c just once) with either birdc.o or + * birdcl.o for each variant. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define SERVER_READ_BUF_LEN 4096 + +static char *opt_list = "s:vr"; +static int verbose, restricted, once; +static char *init_cmd; + +static char *server_path = PATH_CONTROL_SOCKET; +static int server_fd; +static byte server_read_buf[SERVER_READ_BUF_LEN]; +static byte *server_read_pos = server_read_buf; + +int init = 1; /* During intial sequence */ +int busy = 1; /* Executing BIRD command */ +int interactive; /* Whether stdin is terminal */ + +static int num_lines, skip_input; +int term_lns, term_cls; + + +/*** Parsing of arguments ***/ + +static void +usage(char *name) +{ + fprintf(stderr, "Usage: %s [-s ] [-v] [-r]\n", name); + exit(1); +} + +static void +parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, opt_list)) >= 0) + switch (c) + { + case 's': + server_path = optarg; + break; + case 'v': + verbose++; + break; + case 'r': + restricted = 1; + break; + default: + usage(argv[0]); + } + + /* If some arguments are not options, we take it as commands */ + if (optind < argc) + { + char *tmp; + int i; + int len = 0; + + for (i = optind; i < argc; i++) + len += strlen(argv[i]) + 1; + + tmp = init_cmd = malloc(len); + for (i = optind; i < argc; i++) + { + strcpy(tmp, argv[i]); + tmp += strlen(tmp); + *tmp++ = ' '; + } + tmp[-1] = 0; + + once = 1; + interactive = 0; + } +} + + +/*** Input ***/ + +static void server_send(char *cmd); + +static int +handle_internal_command(char *cmd) +{ + if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) + { + cleanup(); + exit(0); + } + if (!strncmp(cmd, "help", 4)) + { + puts("Press `?' for context sensitive help."); + return 1; + } + return 0; +} + +static void +submit_server_command(char *cmd) +{ + busy = 1; + num_lines = 2; + server_send(cmd); +} + +void +submit_command(char *cmd_raw) +{ + char *cmd = cmd_expand(cmd_raw); + + if (!cmd) + return; + + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); +} + +static void +init_commands(void) +{ + if (restricted) + { + submit_server_command("restrict"); + restricted = 0; + return; + } + + if (init_cmd) + { + /* First transition - client received hello from BIRD + and there is waiting initial command */ + submit_server_command(init_cmd); + init_cmd = NULL; + return; + } + + if (once) + { + /* Initial command is finished and we want to exit */ + cleanup(); + exit(0); + } + + input_init(); + init = 0; +} + + +/*** Output ***/ + +void +more(void) +{ + more_begin(); + printf("--More--\015"); + fflush(stdout); + + redo: + switch (getchar()) + { + case ' ': + num_lines = 2; + break; + case '\n': + case '\r': + num_lines--; + break; + case 'q': + skip_input = 1; + break; + default: + goto redo; + } + + printf(" \015"); + fflush(stdout); + more_end(); +} + + +/*** Communication with server ***/ + +static void +server_connect(void) +{ + struct sockaddr_un sa; + + server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) + die("Cannot create socket: %m"); + + if (strlen(server_path) >= sizeof(sa.sun_path)) + die("server_connect: path too long"); + + bzero(&sa, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, server_path); + if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) + die("Unable to connect to server control socket (%s): %m", server_path); + if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl: %m"); +} + + +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +static void +server_got_reply(char *x) +{ + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + + if (x[4] == ' ') + { + busy = 0; + skip_input = 0; + return; + } + } + else + PRINTF(len, "??? <%s>\n", x); + + if (interactive && busy && !skip_input && !init && (len > 0)) + { + num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ + if (num_lines >= term_lns) + more(); + } +} + +static void +server_read(void) +{ + int c; + byte *start, *p; + + redo: + c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); + if (!c) + die("Connection closed by server."); + if (c < 0) + { + if (errno == EINTR) + goto redo; + else + die("Server read error: %m"); + } + + start = server_read_buf; + p = server_read_pos; + server_read_pos += c; + while (p < server_read_pos) + if (*p++ == '\n') + { + p[-1] = 0; + server_got_reply(start); + start = p; + } + if (start != server_read_buf) + { + int l = server_read_pos - start; + memmove(server_read_buf, start, l); + server_read_pos = server_read_buf + l; + } + else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) + { + strcpy(server_read_buf, "?"); + server_read_pos = server_read_buf + 11; + } +} + +static void +select_loop(void) +{ + int rv; + while (1) + { + if (init && !busy) + init_commands(); + + if (!init) + input_notify(!busy); + + fd_set select_fds; + FD_ZERO(&select_fds); + + FD_SET(server_fd, &select_fds); + if (!busy) + FD_SET(0, &select_fds); + + rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(0, &select_fds)) + { + input_read(); + continue; + } + + if (FD_ISSET(server_fd, &select_fds)) + { + server_read(); + continue; + } + } +} + +static void +wait_for_write(int fd) +{ + while (1) + { + int rv; + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + + rv = select(fd+1, NULL, &set, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &set)) + return; + } +} + +static void +server_send(char *cmd) +{ + int l = strlen(cmd); + byte *z = alloca(l + 1); + + memcpy(z, cmd, l); + z[l++] = '\n'; + while (l) + { + int cnt = write(server_fd, z, l); + + if (cnt < 0) + { + if (errno == EAGAIN) + wait_for_write(server_fd); + else if (errno == EINTR) + continue; + else + die("Server write error: %m"); + } + else + { + l -= cnt; + z += cnt; + } + } +} + + +/* XXXX + + get_term_size(); + + if (tcgetattr(0, &tty_save) != 0) + { + perror("tcgetattr error"); + return(EXIT_FAILURE); + } + } + + */ +int +main(int argc, char **argv) +{ + interactive = isatty(0); + parse_args(argc, argv); + cmd_build_tree(); + server_connect(); + select_loop(); + return 0; +} diff --git a/client/client.h b/client/client.h index 2e4e2ea3..b194a772 100644 --- a/client/client.h +++ b/client/client.h @@ -6,12 +6,23 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* client.c callbacks */ -void cleanup(void); +extern int init, busy, interactive; +extern int term_lns, term_cls; + +/* birdc.c / birdcl.c */ + void input_start_list(void); void input_stop_list(void); -void server_got_reply(char *x); + +void input_init(void); +void input_notify(int prompt); +void input_read(void); + +void more_begin(void); +void more_end(void); + +void cleanup(void); /* commands.c */ @@ -20,16 +31,6 @@ void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); -/* common.c */ +/* client.c */ -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 - -#define SERVER_READ_BUF_LEN 4096 - -int handle_internal_command(char *cmd); -void submit_server_command(char *cmd); -void server_connect(void); -void server_read(void); -void server_send(char *cmd); +void submit_command(char *cmd_raw); diff --git a/doc/bird.sgml b/doc/bird.sgml index e83cf0e1..88d35e49 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -623,7 +623,13 @@ codes along with the messages. You do not necessarily need to use -- the format of communication between BIRD and There is also lightweight variant of BIRD client called +Many commands have the Here is a brief list of supported functions: