Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (9.98 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
    if (input_initialized)
315
      input_reveal();
316
    else
317
      input_init();
318

    
319
  if (nstate != STATE_PROMPT)
320
    input_hide();
321

    
322
  cstate = nstate;
323
}
324

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

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

    
347
  printf("        \015");
348
  fflush(stdout);
349
}
350

    
351

    
352
/*** Communication with server ***/
353

    
354
static void
355
server_connect(void)
356
{
357
  struct sockaddr_un sa;
358

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

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

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

    
375
static void
376
server_got_reply(char *x)
377
{
378
  int code;
379
  int len = 0;
380

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

    
401
  if (skip_input)
402
    return;
403

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

    
414
static void
415
server_read(void)
416
{
417
  int c;
418
  byte *start, *p;
419

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

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

    
455
static fd_set select_fds;
456

    
457
static void
458
select_loop(void)
459
{
460
  int rv;
461
  while (1)
462
    {
463
      FD_ZERO(&select_fds);
464

    
465
      if (cstate != STATE_CMD_USER)
466
        FD_SET(server_fd, &select_fds);
467
      if (cstate != STATE_CMD_SERVER)
468
        FD_SET(0, &select_fds);
469

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

    
479
      if (FD_ISSET(server_fd, &select_fds))
480
        {
481
          server_read();
482
          update_state();
483
        }
484

    
485
      if (FD_ISSET(0, &select_fds))
486
        {
487
          rl_callback_read_char();
488
          update_state();
489
        }
490
    }
491
}
492

    
493
static void
494
wait_for_write(int fd)
495
{
496
  while (1)
497
    {
498
      int rv;
499
      fd_set set;
500
      FD_ZERO(&set);
501
      FD_SET(fd, &set);
502

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

    
512
      if (FD_ISSET(server_fd, &set))
513
        return;
514
    }
515
}
516

    
517
static void
518
server_send(char *cmd)
519
{
520
  int l = strlen(cmd);
521
  byte *z = alloca(l + 1);
522

    
523
  memcpy(z, cmd, l);
524
  z[l++] = '\n';
525
  while (l)
526
    {
527
      int cnt = write(server_fd, z, l);
528

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

    
546
int
547
main(int argc, char **argv)
548
{
549
#ifdef HAVE_LIBDMALLOC
550
  if (!getenv("DMALLOC_OPTIONS"))
551
    dmalloc_debug(0x2f03d00);
552
#endif
553

    
554
  interactive = isatty(0);
555
  parse_args(argc, argv);
556
  cmd_build_tree();
557
  server_connect();
558
  select_loop();
559
  return 0;
560
}