0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-03-11 17:08:46 +00:00

Merge commit 'a5e9f3d26f887deb451a3ea086e52266c117aa0a' into integrated

This commit is contained in:
Ondrej Zajicek 2013-05-23 00:16:39 +02:00
commit 34b735e63c
13 changed files with 870 additions and 1077 deletions

View File

@ -1,5 +1,11 @@
source=commands.c util.c client_common.c
source=commands.c util.c client.c
root-rel=../
dir-name=client
clients := $(client) birdcl
source-dep := $(source) $(addsuffix .c,$(clients))
subdir: $(addsuffix .o,$(clients))
include ../Rules

223
client/birdc.c Normal file
View File

@ -0,0 +1,223 @@
/*
* BIRD Client - Readline variant I/O
*
* (c) 1999--2004 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <curses.h>
#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();
}

View File

@ -1,5 +0,0 @@
source=client.c
root-rel=../../
dir-name=client/birdc
include ../../Rules

View File

@ -1,430 +0,0 @@
/*
* BIRD Client
*
* (c) 1999--2004 Martin Mares <mj@ucw.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 <readline/readline.h>
#include <readline/history.h>
#include <curses.h>
#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 <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':
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;
}

165
client/birdcl.c Normal file
View File

@ -0,0 +1,165 @@
/*
* BIRD Client - Light variant I/O
*
* (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 <unistd.h>
#include <termios.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
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();
}

View File

@ -1,5 +0,0 @@
source=client.c
root-rel=../../
dir-name=client/birdcl
include ../../Rules

View File

@ -1,429 +0,0 @@
/*
* 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 <ctype.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 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 <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 && (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;
}

436
client/client.c Normal file
View File

@ -0,0 +1,436 @@
/*
* BIRD Client
*
* (c) 1999--2004 Martin Mares <mj@ucw.cz>
* (c) 2013 Tomas Hlavacek <tmshlvck@gmail.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#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 <control-socket>] [-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, "?<too-long>");
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;
}

View File

@ -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);
/* client_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);

View File

@ -1,179 +0,0 @@
/*
* BIRD Client
*
* (c) 1999--2004 Martin Mares <mj@ucw.cz>
* (c) 2013 Tomas Hlavacek <tmshlvck@gmail.com>
*
* 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 <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#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, "?<too-long>");
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;
}
}
}

View File

@ -623,7 +623,13 @@ codes along with the messages. You do not necessarily need to use
-- the format of communication between BIRD and <file/birdc/ is stable
(see the programmer's documentation).
Many commands have the <m/name/ of the protocol instance as an argument.
<p>There is also lightweight variant of BIRD client called
<file/birdcl/, which does not support command line editing and history
and has minimal dependencies. This is useful for running BIRD in
resource constrained environments, where Readline library (required
for regular BIRD client) is not available.
<p>Many commands have the <m/name/ of the protocol instance as an argument.
This argument can be omitted if there exists only a single instance.
<p>Here is a brief list of supported functions:

View File

@ -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:

View File

@ -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