Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (7.78 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/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
}