Revision a5e9f3d2

View differences:

client/Makefile
1
source=commands.c util.c common.c
1
source=commands.c util.c client.c
2 2
root-rel=../
3 3
dir-name=client
4 4

  
client/birdc.c
1
/*
2
 *	BIRD Client - Readline variant I/O
3
 *
4
 *	(c) 1999--2004 Martin Mares <mj@ucw.cz>
5
 *
6
 *	Can be freely distributed and used under the terms of the GNU GPL.
7
 */
8

  
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <unistd.h>
12
#include <termios.h>
13

  
14
#include <readline/readline.h>
15
#include <readline/history.h>
16
#include <curses.h>
17

  
18
#include "nest/bird.h"
19
#include "lib/resource.h"
20
#include "lib/string.h"
21
#include "client/client.h"
22
#include "sysdep/unix/unix.h"
23

  
24
static int input_hidden_end;
25
static int prompt_active;
26

  
27
/*** Input ***/
28

  
29
/* HACK: libreadline internals we need to access */
30
extern int _rl_vis_botlin;
31
extern void _rl_move_vert(int);
32
extern Function *rl_last_func;
33

  
34
static void
35
add_history_dedup(char *cmd)
36
{
37
  /* Add history line if it differs from the last one */
38
  HIST_ENTRY *he = history_get(history_length);
39
  if (!he || strcmp(he->line, cmd))
40
    add_history(cmd);
41
}
42

  
43
static void
44
input_got_line(char *cmd_buffer)
45
{
46
  if (!cmd_buffer)
47
    {
48
      cleanup();
49
      exit(0);
50
    }
51

  
52
  if (cmd_buffer[0])
53
    {
54
      add_history_dedup(cmd_buffer);
55
      submit_command(cmd_buffer);
56
    }
57

  
58
  free(cmd_buffer);
59
}
60

  
61
void
62
input_start_list(void)
63
{
64
  /* Leave the currently edited line and make space for listing */
65
  _rl_move_vert(_rl_vis_botlin);
66
#ifdef HAVE_RL_CRLF
67
  rl_crlf();
68
#endif
69
}
70

  
71
void
72
input_stop_list(void)
73
{
74
  /* Reprint the currently edited line after listing */
75
  rl_on_new_line();
76
  rl_redisplay();
77
}
78

  
79
static int
80
input_complete(int arg UNUSED, int key UNUSED)
81
{
82
  static int complete_flag;
83
  char buf[256];
84

  
85
  if (rl_last_func != input_complete)
86
    complete_flag = 0;
87
  switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag))
88
    {
89
    case 0:
90
      complete_flag = 1;
91
      break;
92
    case 1:
93
      rl_insert_text(buf);
94
      break;
95
    default:
96
      complete_flag = 1;
97
#ifdef HAVE_RL_DING
98
      rl_ding();
99
#endif
100
    }
101
  return 0;
102
}
103

  
104
static int
105
input_help(int arg, int key UNUSED)
106
{
107
  int i, in_string, in_bracket;
108

  
109
  if (arg != 1)
110
    return rl_insert(arg, '?');
111

  
112
  in_string = in_bracket = 0;
113
  for (i = 0; i < rl_point; i++)
114
    {
115
   
116
      if (rl_line_buffer[i] == '"')
117
	in_string = ! in_string;
118
      else if (! in_string)
119
        {
120
	  if (rl_line_buffer[i] == '[')
121
	    in_bracket++;
122
	  else if (rl_line_buffer[i] == ']')
123
	    in_bracket--;
124
        }
125
    }
126

  
127
  /* `?' inside string or path -> insert */
128
  if (in_string || in_bracket)
129
    return rl_insert(1, '?');
130

  
131
  rl_begin_undo_group();		/* HACK: We want to display `?' at point position */
132
  rl_insert_text("?");
133
  rl_redisplay();
134
  rl_end_undo_group();
135
  input_start_list();
136
  cmd_help(rl_line_buffer, rl_point);
137
  rl_undo_command(1, 0);
138
  input_stop_list();
139
  return 0;
140
}
141

  
142
void
143
input_init(void)
144
{
145
  rl_readline_name = "birdc";
146
  rl_add_defun("bird-complete", input_complete, '\t');
147
  rl_add_defun("bird-help", input_help, '?');
148
  rl_callback_handler_install("bird> ", input_got_line);
149

  
150
  // rl_get_screen_size();
151
  term_lns = LINES ? LINES : 25;
152
  term_cls = COLS ? COLS : 80;
153

  
154
  prompt_active = 1;
155

  
156
  // readline library does strange things when stdin is nonblocking.
157
  // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
158
  //   die("fcntl: %m");
159
}
160

  
161
static void
162
input_reveal(void)
163
{
164
  /* need this, otherwise some lib seems to eat pending output when
165
     the prompt is displayed */
166
  fflush(stdout);
167
  tcdrain(STDOUT_FILENO);
168

  
169
  rl_end = input_hidden_end;
170
  rl_expand_prompt("bird> ");
171
  rl_forced_update_display();
172

  
173
  prompt_active = 1;
174
}
175

  
176
static void
177
input_hide(void)
178
{
179
  input_hidden_end = rl_end;
180
  rl_end = 0;
181
  rl_expand_prompt("");
182
  rl_redisplay();
183

  
184
  prompt_active = 0;
185
}
186

  
187
void
188
input_notify(int prompt)
189
{
190
  if (prompt == prompt_active)
191
    return;
192

  
193
  if (prompt)
194
    input_reveal();
195
  else
196
    input_hide();
197
}
198

  
199
void
200
input_read(void)
201
{
202
  rl_callback_read_char();
203
}
204

  
205
void
206
more_begin(void)
207
{
208
}
209

  
210
void
211
more_end(void)
212
{
213
}
214

  
215
void
216
cleanup(void)
217
{
218
  if (init)
219
    return;
220

  
221
  input_hide();
222
  rl_callback_handler_remove();
223
}
client/birdcl.c
1
/*
2
 *	BIRD Client - Light variant I/O
3
 *
4
 *	(c) 1999--2004 Martin Mares <mj@ucw.cz>
5
 *      (c) 2013 Tomas Hlavacek <tomas.hlavacek@nic.cz>
6
 *
7
 *	Can be freely distributed and used under the terms of the GNU GPL.
8
 */
9

  
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <unistd.h>
13
#include <termios.h>
14

  
15
#include <sys/ioctl.h>
16
#include <signal.h>
17

  
18
#include "nest/bird.h"
19
#include "lib/resource.h"
20
#include "lib/string.h"
21
#include "client/client.h"
22
#include "sysdep/unix/unix.h"
23

  
24
#define INPUT_BUF_LEN 2048
25

  
26
struct termios tty_save;
27

  
28
void
29
input_start_list(void)
30
{
31
  /* Empty in non-ncurses version. */
32
}
33

  
34
void
35
input_stop_list(void)
36
{
37
  /* Empty in non-ncurses version. */
38
}
39

  
40
void
41
input_notify(int prompt)
42
{
43
  /* No ncurses -> no status to reveal/hide, print prompt manually. */
44
  if (!prompt)
45
    return;
46

  
47
  printf("bird> ");
48
  fflush(stdout);
49
}
50

  
51

  
52
static int
53
lastnb(char *str, int i)
54
{
55
  while (i--)
56
    if ((str[i] != ' ') && (str[i] != '\t'))
57
      return str[i];
58

  
59
  return 0;
60
}
61

  
62
void
63
input_read(void)
64
{
65
  char buf[INPUT_BUF_LEN];
66

  
67
  if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0))
68
  {
69
    putchar('\n');
70
    cleanup();
71
    exit(0);
72
  }
73

  
74
  int l = strlen(buf);
75
  if ((l+1) == INPUT_BUF_LEN)
76
    {
77
      printf("Input too long.\n");
78
      return;
79
    }
80

  
81
  if (buf[l-1] == '\n')
82
    buf[--l] = '\0';
83

  
84
  if (!interactive)
85
    printf("%s\n", buf);
86

  
87
  if (l == 0)
88
    return;
89

  
90
  if (lastnb(buf, l) == '?')
91
    {
92
      cmd_help(buf, strlen(buf));
93
      return;
94
    }
95

  
96
  submit_command(buf);
97
}
98

  
99
static struct termios stored_tty;
100
static int more_active = 0;
101

  
102
void
103
more_begin(void)
104
{
105
  static struct termios tty;
106

  
107
  tty = stored_tty;
108
  tty.c_lflag &= (~ECHO);
109
  tty.c_lflag &= (~ICANON);
110

  
111
  if (tcsetattr (0, TCSANOW, &tty) < 0)
112
    die("tcsetattr: %m");
113

  
114
  more_active = 1;
115
}
116

  
117
void
118
more_end(void)
119
{
120
  more_active = 0;
121

  
122
  if (tcsetattr (0, TCSANOW, &stored_tty) < 0)
123
    die("tcsetattr: %m");
124
}
125

  
126
static void
127
sig_handler(int signal)
128
{
129
  cleanup();
130
  exit(0);
131
}
132

  
133
void
134
input_init(void)
135
{
136
  if (!interactive)
137
    return;
138

  
139
  if (tcgetattr(0, &stored_tty) < 0)
140
    die("tcgetattr: %m");
141

  
142
  if (signal(SIGINT, sig_handler) == SIG_IGN)
143
    signal(SIGINT, SIG_IGN);
144
  if (signal(SIGTERM, sig_handler) == SIG_IGN)
145
    signal(SIGTERM, SIG_IGN);
146

  
147
  struct winsize tws;
148
  if (ioctl(0, TIOCGWINSZ, &tws) == 0)
149
    {
150
      term_lns = tws.ws_row;
151
      term_cls = tws.ws_col;
152
    }
153
  else
154
    {
155
       term_lns = 25;
156
       term_cls = 80;
157
    }
158
}
159

  
160
void
161
cleanup(void)
162
{
163
  if (more_active)
164
    more_end();
165
}
client/client.c
1
/*
2
 *	BIRD Client
3
 *
4
 *	(c) 1999--2004 Martin Mares <mj@ucw.cz>
5
 *	(c) 2013 Tomas Hlavacek <tmshlvck@gmail.com>
6
 *
7
 *	Can be freely distributed and used under the terms of the GNU GPL.
8
 */
9

  
10
/**
11
 * DOC: BIRD client
12
 *
13
 * There are two variants of BIRD client: regular and light. regular
14
 * variant depends on readline and ncurses libraries, while light
15
 * variant uses just libc. Most of the code and the main() is common
16
 * for both variants (in client.c file) and just a few functions are
17
 * different (in birdc.c for regular and birdcl.c for light). Two
18
 * binaries are generated by linking common object files like client.o
19
 * (which is compiled from client.c just once) with either birdc.o or
20
 * birdcl.o for each variant.
21
 */
22

  
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <fcntl.h>
26
#include <unistd.h>
27
#include <errno.h>
28
#include <sys/socket.h>
29
#include <sys/un.h>
30
#include <sys/types.h>
31

  
32
#include "nest/bird.h"
33
#include "lib/resource.h"
34
#include "lib/string.h"
35
#include "client/client.h"
36
#include "sysdep/unix/unix.h"
37

  
38
#define SERVER_READ_BUF_LEN 4096
39

  
40
static char *opt_list = "s:vr";
41
static int verbose, restricted, once;
42
static char *init_cmd;
43

  
44
static char *server_path = PATH_CONTROL_SOCKET;
45
static int server_fd;
46
static byte server_read_buf[SERVER_READ_BUF_LEN];
47
static byte *server_read_pos = server_read_buf;
48

  
49
int init = 1;		/* During intial sequence */
50
int busy = 1;		/* Executing BIRD command */
51
int interactive;	/* Whether stdin is terminal */
52

  
53
static int num_lines, skip_input;
54
int term_lns, term_cls;
55

  
56

  
57
/*** Parsing of arguments ***/
58

  
59
static void
60
usage(char *name)
61
{
62
  fprintf(stderr, "Usage: %s [-s <control-socket>] [-v] [-r]\n", name);
63
  exit(1);
64
}
65

  
66
static void
67
parse_args(int argc, char **argv)
68
{
69
  int c;
70

  
71
  while ((c = getopt(argc, argv, opt_list)) >= 0)
72
    switch (c)
73
      {
74
      case 's':
75
	server_path = optarg;
76
	break;
77
      case 'v':
78
	verbose++;
79
	break;
80
      case 'r':
81
	restricted = 1;
82
	break;
83
      default:
84
	usage(argv[0]);
85
      }
86

  
87
  /* If some arguments are not options, we take it as commands */
88
  if (optind < argc)
89
    {
90
      char *tmp;
91
      int i;
92
      int len = 0;
93

  
94
      for (i = optind; i < argc; i++)
95
	len += strlen(argv[i]) + 1;
96

  
97
      tmp = init_cmd = malloc(len);
98
      for (i = optind; i < argc; i++)
99
	{
100
	  strcpy(tmp, argv[i]);
101
	  tmp += strlen(tmp);
102
	  *tmp++ = ' ';
103
	}
104
      tmp[-1] = 0;
105

  
106
      once = 1;
107
      interactive = 0;
108
    }
109
}
110

  
111

  
112
/*** Input ***/
113

  
114
static void server_send(char *cmd);
115

  
116
static int
117
handle_internal_command(char *cmd)
118
{
119
  if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4))
120
    {
121
      cleanup();
122
      exit(0);
123
    }
124
  if (!strncmp(cmd, "help", 4))
125
    {
126
      puts("Press `?' for context sensitive help.");
127
      return 1;
128
    }
129
  return 0;
130
}
131

  
132
static void
133
submit_server_command(char *cmd)
134
{
135
  busy = 1;
136
  num_lines = 2;
137
  server_send(cmd);
138
}
139

  
140
void
141
submit_command(char *cmd_raw)
142
{
143
  char *cmd = cmd_expand(cmd_raw);
144

  
145
  if (!cmd)
146
    return;
147

  
148
  if (!handle_internal_command(cmd))
149
    submit_server_command(cmd);
150

  
151
  free(cmd);
152
}
153

  
154
static void
155
init_commands(void)
156
{
157
  if (restricted)
158
    {
159
       submit_server_command("restrict");
160
       restricted = 0;
161
       return;
162
    }
163

  
164
  if (init_cmd)
165
    {
166
      /* First transition - client received hello from BIRD
167
	 and there is waiting initial command */
168
      submit_server_command(init_cmd);
169
      init_cmd = NULL;
170
      return;
171
    }
172

  
173
  if (once)
174
    {
175
      /* Initial command is finished and we want to exit */
176
      cleanup();
177
      exit(0);
178
    }
179

  
180
  input_init();
181
  init = 0;
182
}
183

  
184

  
185
/*** Output ***/
186

  
187
void
188
more(void)
189
{
190
  more_begin();
191
  printf("--More--\015");
192
  fflush(stdout);
193

  
194
 redo:
195
  switch (getchar())
196
    {
197
    case ' ':
198
      num_lines = 2;
199
      break;
200
    case '\n':
201
    case '\r':
202
      num_lines--;
203
      break;
204
    case 'q':
205
      skip_input = 1;
206
      break;
207
    default:
208
      goto redo;
209
    }
210

  
211
  printf("        \015");
212
  fflush(stdout);
213
  more_end();
214
}
215

  
216

  
217
/*** Communication with server ***/
218

  
219
static void
220
server_connect(void)
221
{
222
  struct sockaddr_un sa;
223

  
224
  server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
225
  if (server_fd < 0)
226
    die("Cannot create socket: %m");
227

  
228
  if (strlen(server_path) >= sizeof(sa.sun_path))
229
    die("server_connect: path too long");
230

  
231
  bzero(&sa, sizeof(sa));
232
  sa.sun_family = AF_UNIX;
233
  strcpy(sa.sun_path, server_path);
234
  if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
235
    die("Unable to connect to server control socket (%s): %m", server_path);
236
  if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
237
    die("fcntl: %m");
238
}
239

  
240

  
241
#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
242

  
243
static void
244
server_got_reply(char *x)
245
{
246
  int code;
247
  int len = 0;
248

  
249
  if (*x == '+')                        /* Async reply */
250
    PRINTF(len, ">>> %s\n", x+1);
251
  else if (x[0] == ' ')                 /* Continuation */
252
    PRINTF(len, "%s%s\n", verbose ? "     " : "", x+1);
253
  else if (strlen(x) > 4 &&
254
           sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
255
           (x[4] == ' ' || x[4] == '-'))
256
    {
257
      if (code)
258
        PRINTF(len, "%s\n", verbose ? x : x+5);
259

  
260
      if (x[4] == ' ')
261
      {
262
        busy = 0;
263
        skip_input = 0;
264
        return;
265
      }
266
    }
267
  else
268
    PRINTF(len, "??? <%s>\n", x);
269

  
270
  if (interactive && busy && !skip_input && !init && (len > 0))
271
    {
272
      num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */
273
      if (num_lines >= term_lns)
274
        more();
275
    }
276
}
277

  
278
static void
279
server_read(void)
280
{
281
  int c;
282
  byte *start, *p;
283

  
284
 redo:
285
  c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos);
286
  if (!c)
287
    die("Connection closed by server.");
288
  if (c < 0)
289
    {
290
      if (errno == EINTR)
291
	goto redo;
292
      else
293
	die("Server read error: %m");
294
    }
295

  
296
  start = server_read_buf;
297
  p = server_read_pos;
298
  server_read_pos += c;
299
  while (p < server_read_pos)
300
    if (*p++ == '\n')
301
      {
302
	p[-1] = 0;
303
	server_got_reply(start);
304
	start = p;
305
      }
306
  if (start != server_read_buf)
307
    {
308
      int l = server_read_pos - start;
309
      memmove(server_read_buf, start, l);
310
      server_read_pos = server_read_buf + l;
311
    }
312
  else if (server_read_pos == server_read_buf + sizeof(server_read_buf))
313
    {
314
      strcpy(server_read_buf, "?<too-long>");
315
      server_read_pos = server_read_buf + 11;
316
    }
317
}
318

  
319
static void
320
select_loop(void)
321
{
322
  int rv;
323
  while (1)
324
    {
325
      if (init && !busy)
326
	init_commands();
327

  
328
      if (!init)
329
	input_notify(!busy);
330

  
331
      fd_set select_fds;
332
      FD_ZERO(&select_fds);
333

  
334
      FD_SET(server_fd, &select_fds);
335
      if (!busy)
336
	FD_SET(0, &select_fds);
337

  
338
      rv = select(server_fd+1, &select_fds, NULL, NULL, NULL);
339
      if (rv < 0)
340
	{
341
	  if (errno == EINTR)
342
	    continue;
343
	  else
344
	    die("select: %m");
345
	}
346

  
347
      if (FD_ISSET(0, &select_fds))
348
	{
349
	  input_read();
350
	  continue;
351
	}
352

  
353
      if (FD_ISSET(server_fd, &select_fds))
354
	{
355
	  server_read();
356
	  continue;
357
	}
358
    }
359
}
360

  
361
static void
362
wait_for_write(int fd)
363
{
364
  while (1)
365
    {
366
      int rv;
367
      fd_set set;
368
      FD_ZERO(&set);
369
      FD_SET(fd, &set);
370

  
371
      rv = select(fd+1, NULL, &set, NULL, NULL);
372
      if (rv < 0)
373
	{
374
	  if (errno == EINTR)
375
	    continue;
376
	  else
377
	    die("select: %m");
378
	}
379

  
380
      if (FD_ISSET(server_fd, &set))
381
	return;
382
    }
383
}
384

  
385
static void
386
server_send(char *cmd)
387
{
388
  int l = strlen(cmd);
389
  byte *z = alloca(l + 1);
390

  
391
  memcpy(z, cmd, l);
392
  z[l++] = '\n';
393
  while (l)
394
    {
395
      int cnt = write(server_fd, z, l);
396

  
397
      if (cnt < 0)
398
	{
399
	  if (errno == EAGAIN)
400
	    wait_for_write(server_fd);
401
	  else if (errno == EINTR)
402
	    continue;
403
	  else
404
	    die("Server write error: %m");
405
	}
406
      else
407
	{
408
	  l -= cnt;
409
	  z += cnt;
410
	}
411
    }
412
}
413

  
414

  
415
/* XXXX
416

  
417
      get_term_size();
418

  
419
      if (tcgetattr(0, &tty_save) != 0)
420
        {
421
          perror("tcgetattr error");
422
          return(EXIT_FAILURE);
423
        }
424
    }
425

  
426
 */
427
int
428
main(int argc, char **argv)
429
{
430
  interactive = isatty(0);
431
  parse_args(argc, argv);
432
  cmd_build_tree();
433
  server_connect();
434
  select_loop();
435
  return 0;
436
}
client/client.h
6 6
 *	Can be freely distributed and used under the terms of the GNU GPL.
7 7
 */
8 8

  
9
/* client.c callbacks */
10 9

  
11
void cleanup(void);
10
extern int init, busy, interactive;
11
extern int term_lns, term_cls;
12

  
13
/* birdc.c / birdcl.c */
14

  
12 15
void input_start_list(void);
13 16
void input_stop_list(void);
14
void server_got_reply(char *x);
17

  
18
void input_init(void);
19
void input_notify(int prompt);
20
void input_read(void);
21

  
22
void more_begin(void);
23
void more_end(void);
24

  
25
void cleanup(void);
15 26

  
16 27
/* commands.c */
17 28

  
......
20 31
int cmd_complete(char *cmd, int len, char *buf, int again);
21 32
char *cmd_expand(char *cmd);
22 33

  
23
/* common.c */
24

  
25
#define STATE_PROMPT           0
26
#define STATE_CMD_SERVER       1
27
#define STATE_CMD_USER         2
28

  
29
#define SERVER_READ_BUF_LEN 4096
34
/* client.c */
30 35

  
31
int handle_internal_command(char *cmd);
32
void submit_server_command(char *cmd);
33
void server_connect(void);
34
void server_read(void);
35
void server_send(char *cmd);
36
void submit_command(char *cmd_raw);
doc/bird.sgml
623 623
-- the format of communication between BIRD and <file/birdc/ is stable
624 624
(see the programmer's documentation).
625 625

  
626
Many commands have the <m/name/ of the protocol instance as an argument.
626
<p>There is also lightweight variant of BIRD client called
627
<file/birdcl/, which does not support command line editing and history
628
and has minimal dependencies. This is useful for running BIRD in
629
resource constrained environments, where Readline library (required
630
for regular BIRD client) is not available.
631

  
632
<p>Many commands have the <m/name/ of the protocol instance as an argument.
627 633
This argument can be omitted if there exists only a single instance.
628 634

  
629 635
<p>Here is a brief list of supported functions:

Also available in: Unified diff