Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / client / client.c @ 6b3f1a54

History | View | Annotate | Download (8.01 KB)

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/select.h>
29
#include <sys/socket.h>
30
#include <sys/types.h>
31
#include <sys/un.h>
32

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

    
39
#define SERVER_READ_BUF_LEN 4096
40

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

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

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

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

    
57

    
58
/*** Parsing of arguments ***/
59

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

    
67
static void
68
parse_args(int argc, char **argv)
69
{
70
  int server_changed = 0;
71
  int c;
72

    
73
  while ((c = getopt(argc, argv, opt_list)) >= 0)
74
    switch (c)
75
      {
76
      case 's':
77
        server_path = optarg;
78
        server_changed = 1;
79
        break;
80
      case 'v':
81
        verbose++;
82
        break;
83
      case 'r':
84
        restricted = 1;
85
        break;
86
      case 'l':
87
        if (!server_changed)
88
          server_path = xbasename(server_path);
89
        break;
90
      default:
91
        usage(argv[0]);
92
      }
93

    
94
  /* If some arguments are not options, we take it as commands */
95
  if (optind < argc)
96
    {
97
      char *tmp;
98
      int i;
99
      int len = 0;
100

    
101
      for (i = optind; i < argc; i++)
102
        len += strlen(argv[i]) + 1;
103

    
104
      tmp = init_cmd = malloc(len);
105
      for (i = optind; i < argc; i++)
106
        {
107
          strcpy(tmp, argv[i]);
108
          tmp += strlen(tmp);
109
          *tmp++ = ' ';
110
        }
111
      tmp[-1] = 0;
112

    
113
      once = 1;
114
      interactive = 0;
115
    }
116
}
117

    
118

    
119
/*** Input ***/
120

    
121
static void server_send(char *cmd);
122

    
123
static int
124
handle_internal_command(char *cmd)
125
{
126
  if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4))
127
    {
128
      cleanup();
129
      exit(0);
130
    }
131
  if (!strncmp(cmd, "help", 4))
132
    {
133
      puts("Press `?' for context sensitive help.");
134
      return 1;
135
    }
136
  return 0;
137
}
138

    
139
static void
140
submit_server_command(char *cmd)
141
{
142
  busy = 1;
143
  num_lines = 2;
144
  server_send(cmd);
145
}
146

    
147
static inline void
148
submit_init_command(char *cmd_raw)
149
{
150
  char *cmd = cmd_expand(cmd_raw);
151

    
152
  if (!cmd)
153
  {
154
    cleanup();
155
    exit(0);
156
  }
157

    
158
  submit_server_command(cmd);
159
  free(cmd);
160
}
161

    
162
void
163
submit_command(char *cmd_raw)
164
{
165
  char *cmd = cmd_expand(cmd_raw);
166

    
167
  if (!cmd)
168
    return;
169

    
170
  if (!handle_internal_command(cmd))
171
    submit_server_command(cmd);
172

    
173
  free(cmd);
174
}
175

    
176
static void
177
init_commands(void)
178
{
179
  if (restricted)
180
    {
181
       submit_server_command("restrict");
182
       restricted = 0;
183
       return;
184
    }
185

    
186
  if (init_cmd)
187
    {
188
      /* First transition - client received hello from BIRD
189
         and there is waiting initial command */
190
      submit_init_command(init_cmd);
191
      init_cmd = NULL;
192
      return;
193
    }
194

    
195
  if (once)
196
    {
197
      /* Initial command is finished and we want to exit */
198
      cleanup();
199
      exit(0);
200
    }
201

    
202
  input_init();
203

    
204
  term_lns = (term_lns > 0) ? term_lns : 25;
205
  term_cls = (term_cls > 0) ? term_cls : 80;
206

    
207
  init = 0;
208
}
209

    
210

    
211
/*** Output ***/
212

    
213
void
214
more(void)
215
{
216
  more_begin();
217
  printf("--More--\015");
218
  fflush(stdout);
219

    
220
 redo:
221
  switch (getchar())
222
    {
223
    case ' ':
224
      num_lines = 2;
225
      break;
226
    case '\n':
227
    case '\r':
228
      num_lines--;
229
      break;
230
    case 'q':
231
      skip_input = 1;
232
      break;
233
    default:
234
      goto redo;
235
    }
236

    
237
  printf("        \015");
238
  fflush(stdout);
239
  more_end();
240
}
241

    
242

    
243
/*** Communication with server ***/
244

    
245
static void
246
server_connect(void)
247
{
248
  struct sockaddr_un sa;
249

    
250
  server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
251
  if (server_fd < 0)
252
    DIE("Cannot create socket");
253

    
254
  if (strlen(server_path) >= sizeof(sa.sun_path))
255
    die("server_connect: path too long");
256

    
257
  bzero(&sa, sizeof(sa));
258
  sa.sun_family = AF_UNIX;
259
  strcpy(sa.sun_path, server_path);
260
  if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
261
    DIE("Unable to connect to server control socket (%s)", server_path);
262
  if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
263
    DIE("fcntl");
264
}
265

    
266

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

    
269
static void
270
server_got_reply(char *x)
271
{
272
  int code;
273
  int len = 0;
274

    
275
  if (*x == '+')                        /* Async reply */
276
    PRINTF(len, ">>> %s\n", x+1);
277
  else if (x[0] == ' ')                 /* Continuation */
278
    PRINTF(len, "%s%s\n", verbose ? "     " : "", x+1);
279
  else if (strlen(x) > 4 &&
280
           sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
281
           (x[4] == ' ' || x[4] == '-'))
282
    {
283
      if (code)
284
        PRINTF(len, "%s\n", verbose ? x : x+5);
285

    
286
      if (x[4] == ' ')
287
      {
288
        busy = 0;
289
        skip_input = 0;
290
        return;
291
      }
292
    }
293
  else
294
    PRINTF(len, "??? <%s>\n", x);
295

    
296
  if (interactive && busy && !skip_input && !init && (len > 0))
297
    {
298
      num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */
299
      if (num_lines >= term_lns)
300
        more();
301
    }
302
}
303

    
304
static void
305
server_read(void)
306
{
307
  int c;
308
  byte *start, *p;
309

    
310
 redo:
311
  c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos);
312
  if (!c)
313
    die("Connection closed by server");
314
  if (c < 0)
315
    {
316
      if (errno == EINTR)
317
        goto redo;
318
      else
319
        DIE("Server read error");
320
    }
321

    
322
  start = server_read_buf;
323
  p = server_read_pos;
324
  server_read_pos += c;
325
  while (p < server_read_pos)
326
    if (*p++ == '\n')
327
      {
328
        p[-1] = 0;
329
        server_got_reply(start);
330
        start = p;
331
      }
332
  if (start != server_read_buf)
333
    {
334
      int l = server_read_pos - start;
335
      memmove(server_read_buf, start, l);
336
      server_read_pos = server_read_buf + l;
337
    }
338
  else if (server_read_pos == server_read_buf + sizeof(server_read_buf))
339
    {
340
      strcpy(server_read_buf, "?<too-long>");
341
      server_read_pos = server_read_buf + 11;
342
    }
343
}
344

    
345
static void
346
select_loop(void)
347
{
348
  int rv;
349
  while (1)
350
    {
351
      if (init && !busy)
352
        init_commands();
353

    
354
      if (!init)
355
        input_notify(!busy);
356

    
357
      fd_set select_fds;
358
      FD_ZERO(&select_fds);
359

    
360
      FD_SET(server_fd, &select_fds);
361
      if (!busy)
362
        FD_SET(0, &select_fds);
363

    
364
      rv = select(server_fd+1, &select_fds, NULL, NULL, NULL);
365
      if (rv < 0)
366
        {
367
          if (errno == EINTR)
368
            continue;
369
          else
370
            DIE("select");
371
        }
372

    
373
      if (FD_ISSET(0, &select_fds))
374
        {
375
          input_read();
376
          continue;
377
        }
378

    
379
      if (FD_ISSET(server_fd, &select_fds))
380
        {
381
          server_read();
382
          continue;
383
        }
384
    }
385
}
386

    
387
static void
388
wait_for_write(int fd)
389
{
390
  while (1)
391
    {
392
      int rv;
393
      fd_set set;
394
      FD_ZERO(&set);
395
      FD_SET(fd, &set);
396

    
397
      rv = select(fd+1, NULL, &set, NULL, NULL);
398
      if (rv < 0)
399
        {
400
          if (errno == EINTR)
401
            continue;
402
          else
403
            DIE("select");
404
        }
405

    
406
      if (FD_ISSET(server_fd, &set))
407
        return;
408
    }
409
}
410

    
411
static void
412
server_send(char *cmd)
413
{
414
  int l = strlen(cmd);
415
  byte *z = alloca(l + 1);
416

    
417
  memcpy(z, cmd, l);
418
  z[l++] = '\n';
419
  while (l)
420
    {
421
      int cnt = write(server_fd, z, l);
422

    
423
      if (cnt < 0)
424
        {
425
          if (errno == EAGAIN)
426
            wait_for_write(server_fd);
427
          else if (errno == EINTR)
428
            continue;
429
          else
430
            DIE("Server write error");
431
        }
432
      else
433
        {
434
          l -= cnt;
435
          z += cnt;
436
        }
437
    }
438
}
439

    
440
int
441
main(int argc, char **argv)
442
{
443
  interactive = isatty(0);
444
  parse_args(argc, argv);
445
  cmd_build_tree();
446
  server_connect();
447
  select_loop();
448
  return 0;
449
}