Statistics
| Branch: | Revision:

iof-bird-daemon / client / client.c @ e81b440f

History | View | Annotate | Download (10 KB)

1
/*
2
 *        BIRD Client
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 <fcntl.h>
12
#include <unistd.h>
13
#include <termios.h>
14
#include <errno.h>
15
#include <sys/socket.h>
16
#include <sys/un.h>
17
#include <sys/types.h>
18
#include <readline/readline.h>
19
#include <readline/history.h>
20
#include <curses.h>
21

    
22
#include "nest/bird.h"
23
#include "lib/resource.h"
24
#include "lib/string.h"
25
#include "client/client.h"
26
#include "sysdep/unix/unix.h"
27

    
28
static char *opt_list = "s:vr";
29
static int verbose;
30
static char *init_cmd;
31
static int once;
32

    
33
static char *server_path = PATH_CONTROL_SOCKET;
34
static int server_fd;
35
static byte server_read_buf[4096];
36
static byte *server_read_pos = server_read_buf;
37

    
38
#define STATE_PROMPT                0
39
#define STATE_CMD_SERVER        1
40
#define STATE_CMD_USER                2
41

    
42
static int input_initialized;
43
static int input_hidden_end;
44
static int cstate = STATE_CMD_SERVER;
45
static int nstate = STATE_CMD_SERVER;
46

    
47
static int num_lines, skip_input, interactive;
48

    
49
/*** Parsing of arguments ***/
50

    
51
static void
52
usage(void)
53
{
54
  fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v] [-r]\n");
55
  exit(1);
56
}
57

    
58
static void
59
parse_args(int argc, char **argv)
60
{
61
  int c;
62

    
63
  while ((c = getopt(argc, argv, opt_list)) >= 0)
64
    switch (c)
65
      {
66
      case 's':
67
        server_path = optarg;
68
        break;
69
      case 'v':
70
        verbose++;
71
        break;
72
      case 'r':
73
        init_cmd = "restrict";
74
        break;
75
      default:
76
        usage();
77
      }
78

    
79
  /* If some arguments are not options, we take it as commands */
80
  if (optind < argc)
81
    {
82
      char *tmp;
83
      int i;
84
      int len = 0;
85

    
86
      if (init_cmd)
87
        usage();
88

    
89
      for (i = optind; i < argc; i++)
90
        len += strlen(argv[i]) + 1;
91

    
92
      tmp = init_cmd = malloc(len);
93
      for (i = optind; i < argc; i++)
94
        {
95
          strcpy(tmp, argv[i]);
96
          tmp += strlen(tmp);
97
          *tmp++ = ' ';
98
        }
99

    
100
      once = 1;
101
    }
102
}
103

    
104
/*** Input ***/
105

    
106
static void server_send(char *);
107

    
108
/* HACK: libreadline internals we need to access */
109
extern int _rl_vis_botlin;
110
extern void _rl_move_vert(int);
111
extern Function *rl_last_func;
112

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

    
129
void
130
submit_server_command(char *cmd)
131
{
132
  server_send(cmd);
133
  nstate = STATE_CMD_SERVER;
134
  num_lines = 2;
135
}
136

    
137

    
138
static void
139
got_line(char *cmd_buffer)
140
{
141
  char *cmd;
142

    
143
  if (!cmd_buffer)
144
    {
145
      cleanup();
146
      exit(0);
147
    }
148
  if (cmd_buffer[0])
149
    {
150
      cmd = cmd_expand(cmd_buffer);
151
      if (cmd)
152
        {
153
          add_history(cmd);
154

    
155
          if (!handle_internal_command(cmd))
156
            submit_server_command(cmd);
157

    
158
          free(cmd);
159
        }
160
      else
161
        add_history(cmd_buffer);
162
    }
163
  free(cmd_buffer);
164
}
165

    
166
void
167
input_start_list(void)                        /* Leave the currently edited line and make space for listing */
168
{
169
  _rl_move_vert(_rl_vis_botlin);
170
#ifdef HAVE_RL_CRLF
171
  rl_crlf();
172
#endif
173
}
174

    
175
void
176
input_stop_list(void)                        /* Reprint the currently edited line after listing */
177
{
178
  rl_on_new_line();
179
  rl_redisplay();
180
}
181

    
182
static int
183
input_complete(int arg UNUSED, int key UNUSED)
184
{
185
  static int complete_flag;
186
  char buf[256];
187

    
188
  if (rl_last_func != input_complete)
189
    complete_flag = 0;
190
  switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag))
191
    {
192
    case 0:
193
      complete_flag = 1;
194
      break;
195
    case 1:
196
      rl_insert_text(buf);
197
      break;
198
    default:
199
      complete_flag = 1;
200
#ifdef HAVE_RL_DING
201
      rl_ding();
202
#endif
203
    }
204
  return 0;
205
}
206

    
207
static int
208
input_help(int arg, int key UNUSED)
209
{
210
  int i, in_string, in_bracket;
211

    
212
  if (arg != 1)
213
    return rl_insert(arg, '?');
214

    
215
  in_string = in_bracket = 0;
216
  for (i = 0; i < rl_point; i++)
217
    {
218
   
219
      if (rl_line_buffer[i] == '"')
220
        in_string = ! in_string;
221
      else if (! in_string)
222
        {
223
          if (rl_line_buffer[i] == '[')
224
            in_bracket++;
225
          else if (rl_line_buffer[i] == ']')
226
            in_bracket--;
227
        }
228
    }
229

    
230
  /* `?' inside string or path -> insert */
231
  if (in_string || in_bracket)
232
    return rl_insert(1, '?');
233

    
234
  rl_begin_undo_group();                /* HACK: We want to display `?' at point position */
235
  rl_insert_text("?");
236
  rl_redisplay();
237
  rl_end_undo_group();
238
  input_start_list();
239
  cmd_help(rl_line_buffer, rl_point);
240
  rl_undo_command(1, 0);
241
  input_stop_list();
242
  return 0;
243
}
244

    
245
static void
246
input_init(void)
247
{
248
  rl_readline_name = "birdc";
249
  rl_add_defun("bird-complete", input_complete, '\t');
250
  rl_add_defun("bird-help", input_help, '?');
251
  rl_callback_handler_install("bird> ", got_line);
252
  input_initialized = 1;
253
//  readline library does strange things when stdin is nonblocking.
254
//  if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
255
//    die("fcntl: %m");
256
}
257

    
258
static void
259
input_hide(void)
260
{
261
  input_hidden_end = rl_end;
262
  rl_end = 0;
263
  rl_expand_prompt("");
264
  rl_redisplay();
265
}
266

    
267
static void
268
input_reveal(void)
269
{
270
  /* need this, otherwise some lib seems to eat pending output when
271
     the prompt is displayed */
272
  fflush(stdout);
273
  tcdrain(fileno(stdout));
274

    
275
  rl_end = input_hidden_end;
276
  rl_expand_prompt("bird> ");
277
  rl_forced_update_display();
278
}
279

    
280
void
281
cleanup(void)
282
{
283
  if (input_initialized)
284
    {
285
      input_initialized = 0;
286
      input_hide();
287
      rl_callback_handler_remove();
288
    }
289
}
290

    
291
void
292
update_state(void)
293
{
294
  if (nstate == cstate)
295
    return;
296

    
297
  if (init_cmd)
298
    {
299
      /* First transition - client received hello from BIRD
300
         and there is waiting initial command */
301
      submit_server_command(init_cmd);
302
      init_cmd = NULL;
303
      return;
304
    }
305

    
306
  if (!init_cmd && once)
307
    {
308
      /* Initial command is finished and we want to exit */
309
      cleanup();
310
      exit(0);
311
    }
312

    
313
  if (nstate == STATE_PROMPT)
314
    {
315
      if (input_initialized)
316
        input_reveal();
317
      else
318
        input_init();
319
    }
320

    
321
  if (nstate != STATE_PROMPT)
322
    input_hide();
323

    
324
  cstate = nstate;
325
}
326

    
327
void
328
more(void)
329
{
330
  printf("--More--\015");
331
  fflush(stdout);
332

    
333
 redo:
334
  switch (getchar())
335
    {
336
    case 32:
337
      num_lines = 2;
338
      break;
339
    case 13:
340
      num_lines--;
341
      break;
342
    case 'q':
343
      skip_input = 1;
344
      break;
345
    default:
346
      goto redo;
347
    }
348

    
349
  printf("        \015");
350
  fflush(stdout);
351
}
352

    
353

    
354
/*** Communication with server ***/
355

    
356
static void
357
server_connect(void)
358
{
359
  struct sockaddr_un sa;
360

    
361
  server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
362
  if (server_fd < 0)
363
    die("Cannot create socket: %m");
364

    
365
  if (strlen(server_path) >= sizeof(sa.sun_path))
366
    die("server_connect: path too long");
367

    
368
  bzero(&sa, sizeof(sa));
369
  sa.sun_family = AF_UNIX;
370
  strcpy(sa.sun_path, server_path);
371
  if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
372
    die("Unable to connect to server control socket (%s): %m", server_path);
373
  if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
374
    die("fcntl: %m");
375
}
376

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

    
379
static void
380
server_got_reply(char *x)
381
{
382
  int code;
383
  int len = 0;
384

    
385
  if (*x == '+')                        /* Async reply */
386
    PRINTF(len, ">>> %s\n", x+1);
387
  else if (x[0] == ' ')                        /* Continuation */
388
    PRINTF(len, "%s%s\n", verbose ? "     " : "", x+1);
389
  else if (strlen(x) > 4 &&
390
           sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
391
           (x[4] == ' ' || x[4] == '-'))
392
    {
393
      if (code)
394
        PRINTF(len, "%s\n", verbose ? x : x+5);
395
      if (x[4] == ' ')
396
      {
397
        nstate = STATE_PROMPT;
398
        skip_input = 0;
399
        return;
400
      }
401
    }
402
  else
403
    PRINTF(len, "??? <%s>\n", x);
404

    
405
  if (skip_input)
406
    return;
407

    
408
  if (interactive && input_initialized && (len > 0))
409
    {
410
      int lns = LINES ? LINES : 25;
411
      int cls = COLS ? COLS : 80;
412
      num_lines += (len + cls - 1) / cls; /* Divide and round up */
413
      if ((num_lines >= lns)  && (cstate == STATE_CMD_SERVER))
414
        more();
415
    }
416
}
417

    
418
static void
419
server_read(void)
420
{
421
  int c;
422
  byte *start, *p;
423

    
424
 redo:
425
  c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos);
426
  if (!c)
427
    die("Connection closed by server.");
428
  if (c < 0)
429
    {
430
      if (errno == EINTR)
431
        goto redo;
432
      else
433
        die("Server read error: %m");
434
    }
435

    
436
  start = server_read_buf;
437
  p = server_read_pos;
438
  server_read_pos += c;
439
  while (p < server_read_pos)
440
    if (*p++ == '\n')
441
      {
442
        p[-1] = 0;
443
        server_got_reply(start);
444
        start = p;
445
      }
446
  if (start != server_read_buf)
447
    {
448
      int l = server_read_pos - start;
449
      memmove(server_read_buf, start, l);
450
      server_read_pos = server_read_buf + l;
451
    }
452
  else if (server_read_pos == server_read_buf + sizeof(server_read_buf))
453
    {
454
      strcpy(server_read_buf, "?<too-long>");
455
      server_read_pos = server_read_buf + 11;
456
    }
457
}
458

    
459
static fd_set select_fds;
460

    
461
static void
462
select_loop(void)
463
{
464
  int rv;
465
  while (1)
466
    {
467
      FD_ZERO(&select_fds);
468

    
469
      if (cstate != STATE_CMD_USER)
470
        FD_SET(server_fd, &select_fds);
471
      if (cstate != STATE_CMD_SERVER)
472
        FD_SET(0, &select_fds);
473

    
474
      rv = select(server_fd+1, &select_fds, NULL, NULL, NULL);
475
      if (rv < 0)
476
        {
477
          if (errno == EINTR)
478
            continue;
479
          else
480
            die("select: %m");
481
        }
482

    
483
      if (FD_ISSET(server_fd, &select_fds))
484
        {
485
          server_read();
486
          update_state();
487
        }
488

    
489
      if (FD_ISSET(0, &select_fds))
490
        {
491
          rl_callback_read_char();
492
          update_state();
493
        }
494
    }
495
}
496

    
497
static void
498
wait_for_write(int fd)
499
{
500
  while (1)
501
    {
502
      int rv;
503
      fd_set set;
504
      FD_ZERO(&set);
505
      FD_SET(fd, &set);
506

    
507
      rv = select(fd+1, NULL, &set, NULL, NULL);
508
      if (rv < 0)
509
        {
510
          if (errno == EINTR)
511
            continue;
512
          else
513
            die("select: %m");
514
        }
515

    
516
      if (FD_ISSET(server_fd, &set))
517
        return;
518
    }
519
}
520

    
521
static void
522
server_send(char *cmd)
523
{
524
  int l = strlen(cmd);
525
  byte *z = alloca(l + 1);
526

    
527
  memcpy(z, cmd, l);
528
  z[l++] = '\n';
529
  while (l)
530
    {
531
      int cnt = write(server_fd, z, l);
532

    
533
      if (cnt < 0)
534
        {
535
          if (errno == EAGAIN)
536
            wait_for_write(server_fd);
537
          else if (errno == EINTR)
538
            continue;
539
          else
540
            die("Server write error: %m");
541
        }
542
      else
543
        {
544
          l -= cnt;
545
          z += cnt;
546
        }
547
    }
548
}
549

    
550
int
551
main(int argc, char **argv)
552
{
553
#ifdef HAVE_LIBDMALLOC
554
  if (!getenv("DMALLOC_OPTIONS"))
555
    dmalloc_debug(0x2f03d00);
556
#endif
557

    
558
  interactive = isatty(0);
559
  parse_args(argc, argv);
560
  cmd_build_tree();
561
  server_connect();
562
  select_loop();
563
  return 0;
564
}