]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Add lightweight client - birdcl
authorTomas Hlavacek <tmshlvck@gmail.com>
Sun, 24 Feb 2013 22:47:22 +0000 (23:47 +0100)
committerTomas Hlavacek <tmshlvck@gmail.com>
Tue, 19 Mar 2013 17:03:49 +0000 (18:03 +0100)
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
client/birdc/Makefile [new file with mode: 0644]
client/birdc/client.c [moved from client/client_full.c with 100% similarity]
client/birdcl/Makefile [new file with mode: 0644]
client/birdcl/client.c [new file with mode: 0644]
client/client.h
configure.in
tools/Makefile.in
tools/Rules.in

index fdefe6ab189ddb618e83b0fbdbad7f3fa8bf0145..3568833ed070176e1ebcbf887f051947fb4a62d9 100644 (file)
@@ -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 (file)
index 0000000..154a8d2
--- /dev/null
@@ -0,0 +1,5 @@
+source=client.c
+root-rel=../../
+dir-name=client/birdc
+
+include ../../Rules
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 (file)
index 0000000..fcdc5eb
--- /dev/null
@@ -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 (file)
index 0000000..0a7e380
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ *     BIRD Client
+ *
+ *     (c) 1999--2004 Martin Mares <mj@ucw.cz>
+ *      (c) 2013 Tomas Hlavacek <tomas.hlavacek@nic.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#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 <control-socket>] [-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;
+}
index 373b1c6f66e743eeefc150a54ff2c0fc19226f41..2d215059fc3eb8c9475d592b0377e055197fb2a0 100644 (file)
@@ -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 */
 
index 54993dfce457e020a1949b2974a702711340b86f..534baf217e349ac22e53eca635ac374c6ef2031d 100644 (file)
@@ -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,
index 728e5797f2004166e655264199961cab8336fc9d..adf35b64262a8cfd25fe45101d526062a076bf21 100644 (file)
@@ -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
index fc06aeb1d04dcd87826841c6594543e7f970ad45..fd10f5de1bcb3e60046ca7709c916473a748650a 100644 (file)
@@ -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@