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@