Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (7.48 KB)

1 ed608150 Martin Mares
/*
2
 *        BIRD Client
3
 *
4 7deffd84 Martin Mares
 *        (c) 1999--2004 Martin Mares <mj@ucw.cz>
5 ed608150 Martin Mares
 *
6
 *        Can be freely distributed and used under the terms of the GNU GPL.
7
 */
8
9 9fac310d Martin Mares
#include <stdio.h>
10
#include <stdlib.h>
11 c51f132d Martin Mares
#include <fcntl.h>
12 9fac310d Martin Mares
#include <unistd.h>
13 7deffd84 Martin Mares
#include <termios.h>
14 c51f132d Martin Mares
#include <errno.h>
15
#include <sys/socket.h>
16
#include <sys/un.h>
17
#include <sys/types.h>
18 7211be1c Martin Mares
#include <readline/readline.h>
19
#include <readline/history.h>
20 9fac310d Martin Mares
21 ed608150 Martin Mares
#include "nest/bird.h"
22 9fac310d Martin Mares
#include "lib/resource.h"
23 221135d6 Martin Mares
#include "lib/string.h"
24 ed608150 Martin Mares
#include "client/client.h"
25 4daf03e5 Martin Mares
#include "sysdep/unix/unix.h"
26 ed608150 Martin Mares
27 c51f132d Martin Mares
static char *opt_list = "s:v";
28
static int verbose;
29
30 d69e5ff2 Martin Mares
static char *server_path = PATH_CONTROL_SOCKET;
31 c51f132d Martin Mares
static int server_fd;
32
static int server_reply;
33
static byte server_read_buf[4096];
34
static byte *server_read_pos = server_read_buf;
35
36
static int input_initialized;
37
static int input_hidden;
38
static int input_hidden_end;
39
40
/*** Parsing of arguments ***/
41 f50b9e48 Martin Mares
42 9fac310d Martin Mares
static void
43
usage(void)
44
{
45 c51f132d Martin Mares
  fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v]\n");
46 9fac310d Martin Mares
  exit(1);
47
}
48
49
static void
50
parse_args(int argc, char **argv)
51
{
52
  int c;
53
54
  while ((c = getopt(argc, argv, opt_list)) >= 0)
55
    switch (c)
56
      {
57 c51f132d Martin Mares
      case 's':
58
        server_path = optarg;
59
        break;
60
      case 'v':
61
        verbose++;
62
        break;
63 9fac310d Martin Mares
      default:
64
        usage();
65
      }
66
  if (optind < argc)
67
    usage();
68
}
69 f50b9e48 Martin Mares
70 c51f132d Martin Mares
/*** Input ***/
71
72
static void server_send(char *);
73 4daf03e5 Martin Mares
static void select_loop(int);
74 7211be1c Martin Mares
75 fae0396e Martin Mares
/* HACK: libreadline internals we need to access */
76
extern int _rl_vis_botlin;
77
extern void _rl_move_vert(int);
78
extern Function *rl_last_func;
79
80 2983460b Martin Mares
static int
81
handle_internal_command(char *cmd)
82
{
83
  if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4))
84
    {
85
      cleanup();
86
      exit(0);
87
    }
88
  if (!strncmp(cmd, "help", 4))
89
    {
90
      puts("Press `?' for context sensitive help.");
91
      return 1;
92
    }
93
  return 0;
94
}
95
96 c51f132d Martin Mares
static void
97
got_line(char *cmd_buffer)
98
{
99 e69e4ed9 Martin Mares
  char *cmd;
100
101 7211be1c Martin Mares
  if (!cmd_buffer)
102 c51f132d Martin Mares
    {
103
      cleanup();
104
      exit(0);
105
    }
106 7211be1c Martin Mares
  if (cmd_buffer[0])
107 c51f132d Martin Mares
    {
108 e69e4ed9 Martin Mares
      cmd = cmd_expand(cmd_buffer);
109
      if (cmd)
110
        {
111
          add_history(cmd);
112 2983460b Martin Mares
          if (!handle_internal_command(cmd))
113 e69e4ed9 Martin Mares
            {
114 2983460b Martin Mares
              server_send(cmd);
115
              input_hidden = -1;
116 4daf03e5 Martin Mares
              select_loop(0);
117 2983460b Martin Mares
              input_hidden = 0;
118 e69e4ed9 Martin Mares
            }
119
          free(cmd);
120
        }
121 971b2310 Martin Mares
      else
122
        add_history(cmd_buffer);
123 c51f132d Martin Mares
    }
124
  free(cmd_buffer);
125
}
126
127 fae0396e Martin Mares
void
128
input_start_list(void)                        /* Leave the currently edited line and make space for listing */
129
{
130
  _rl_move_vert(_rl_vis_botlin);
131 bd62eeca Ondrej Filip
#ifdef HAVE_RL_CRLF
132 59b96d7b Martin Mares
  rl_crlf();
133 bd62eeca Ondrej Filip
#endif
134 fae0396e Martin Mares
}
135
136
void
137
input_stop_list(void)                        /* Reprint the currently edited line after listing */
138
{
139
  rl_on_new_line();
140
  rl_redisplay();
141
}
142
143 0223d4ff Martin Mares
static int
144 d7390312 Martin Mares
input_complete(int arg UNUSED, int key UNUSED)
145 0223d4ff Martin Mares
{
146 fae0396e Martin Mares
  static int complete_flag;
147
  char buf[256];
148
149
  if (rl_last_func != input_complete)
150
    complete_flag = 0;
151
  switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag))
152
    {
153
    case 0:
154
      complete_flag = 1;
155
      break;
156
    case 1:
157
      rl_insert_text(buf);
158
      break;
159
    default:
160
      complete_flag = 1;
161 bd62eeca Ondrej Filip
#ifdef HAVE_RL_DING
162 59b96d7b Martin Mares
      rl_ding();
163 bd62eeca Ondrej Filip
#endif
164 fae0396e Martin Mares
    }
165 0223d4ff Martin Mares
  return 0;
166
}
167
168
static int
169 d7390312 Martin Mares
input_help(int arg, int key UNUSED)
170 0223d4ff Martin Mares
{
171
  int i = 0;
172
173 fae0396e Martin Mares
  if (arg != 1)
174 0223d4ff Martin Mares
    return rl_insert(arg, '?');
175 fae0396e Martin Mares
  while (i < rl_point)
176 0223d4ff Martin Mares
    {
177
      if (rl_line_buffer[i++] == '"')
178
        do
179
          {
180 fae0396e Martin Mares
            if (i >= rl_point)                /* `?' inside quoted string -> insert */
181 0223d4ff Martin Mares
              return rl_insert(1, '?');
182
          }
183
        while (rl_line_buffer[i++] != '"');
184
    }
185 fae0396e Martin Mares
  rl_begin_undo_group();                /* HACK: We want to display `?' at point position */
186
  rl_insert_text("?");
187 0223d4ff Martin Mares
  rl_redisplay();
188 fae0396e Martin Mares
  rl_end_undo_group();
189
  input_start_list();
190
  cmd_help(rl_line_buffer, rl_point);
191
  rl_undo_command(1, 0);
192
  input_stop_list();
193 0223d4ff Martin Mares
  return 0;
194
}
195
196 c51f132d Martin Mares
static void
197
input_init(void)
198
{
199
  rl_readline_name = "birdc";
200 0223d4ff Martin Mares
  rl_add_defun("bird-complete", input_complete, '\t');
201
  rl_add_defun("bird-help", input_help, '?');
202 c51f132d Martin Mares
  rl_callback_handler_install("bird> ", got_line);
203
  input_initialized = 1;
204
  if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
205
    die("fcntl: %m");
206
}
207
208
static void
209
input_hide(void)
210
{
211
  if (input_hidden)
212
    return;
213
  if (rl_line_buffer)
214
    {
215
      input_hidden_end = rl_end;
216
      rl_end = 0;
217
      rl_expand_prompt("");
218
      rl_redisplay();
219
      input_hidden = 1;
220
    }
221
}
222
223
static void
224
input_reveal(void)
225
{
226
  if (input_hidden <= 0)
227
    return;
228
  rl_end = input_hidden_end;
229
  rl_expand_prompt("bird> ");
230
  rl_forced_update_display();
231
  input_hidden = 0;
232
}
233
234
void
235
cleanup(void)
236
{
237
  if (input_initialized)
238
    {
239
      input_initialized = 0;
240
      input_hide();
241
      rl_callback_handler_remove();
242
    }
243
}
244
245
/*** Communication with server ***/
246
247
static void
248
server_connect(void)
249
{
250
  struct sockaddr_un sa;
251
252
  server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
253
  if (server_fd < 0)
254
    die("Cannot create socket: %m");
255
  bzero(&sa, sizeof(sa));
256
  sa.sun_family = AF_UNIX;
257
  strcpy(sa.sun_path, server_path);
258 0b3bf4b1 Martin Mares
  if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
259 c51f132d Martin Mares
    die("Unable to connect to server control socket (%s): %m", server_path);
260
  if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0)
261
    die("fcntl: %m");
262
}
263
264
static void
265
server_got_reply(char *x)
266
{
267
  int code;
268
269
  input_hide();
270
  if (*x == '+')                        /* Async reply */
271
    printf(">>> %s\n", x+1);
272
  else if (x[0] == ' ')                        /* Continuation */
273 54fb7701 Martin Mares
    printf("%s%s\n", verbose ? "     " : "", x+1);
274 c51f132d Martin Mares
  else if (strlen(x) > 4 &&
275
           sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
276
           (x[4] == ' ' || x[4] == '-'))
277
    {
278
      if (code)
279
        printf("%s\n", verbose ? x : x+5);
280
      if (x[4] == ' ')
281
        server_reply = code;
282
    }
283
  else
284
    printf("??? <%s>\n", x);
285 5f2a6a9f Martin Mares
  /* need this, otherwise some lib seems to eat pending output when
286
     the prompt is displayed */
287
  fflush(stdout);
288
  tcdrain(fileno(stdout));
289 c51f132d Martin Mares
}
290
291
static void
292
server_read(void)
293
{
294
  int c;
295
  byte *start, *p;
296
297
  c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos);
298
  if (!c)
299
    die("Connection closed by server.");
300
  if (c < 0)
301
    die("Server read error: %m");
302
  start = server_read_buf;
303
  p = server_read_pos;
304
  server_read_pos += c;
305
  while (p < server_read_pos)
306
    if (*p++ == '\n')
307
      {
308
        p[-1] = 0;
309
        server_got_reply(start);
310
        start = p;
311
      }
312
  if (start != server_read_buf)
313
    {
314
      int l = server_read_pos - start;
315
      memmove(server_read_buf, start, l);
316
      server_read_pos = server_read_buf + l;
317
    }
318
  else if (server_read_pos == server_read_buf + sizeof(server_read_buf))
319
    {
320
      strcpy(server_read_buf, "?<too-long>");
321
      server_read_pos = server_read_buf + 11;
322
    }
323
}
324
325
static fd_set select_fds;
326
327
static void
328 4daf03e5 Martin Mares
select_loop(int mode)
329 c51f132d Martin Mares
{
330
  server_reply = -1;
331
  while (mode || server_reply < 0)
332
    {
333 9e85a5e6 Martin Mares
      FD_ZERO(&select_fds);
334 c51f132d Martin Mares
      FD_SET(server_fd, &select_fds);
335
      if (mode)
336
        FD_SET(0, &select_fds);
337
      select(server_fd+1, &select_fds, NULL, NULL, NULL);
338
      if (FD_ISSET(server_fd, &select_fds))
339
        {
340
          server_read();
341
          if (mode)
342
            input_reveal();
343
        }
344
      if (FD_ISSET(0, &select_fds))
345
        rl_callback_read_char();
346
    }
347
  input_reveal();
348
}
349
350
static void
351
server_send(char *cmd)
352
{
353
  int l = strlen(cmd);
354
  byte *z = alloca(l + 1);
355
356
  memcpy(z, cmd, l);
357
  z[l++] = '\n';
358
  while (l)
359
    {
360
      int cnt = write(server_fd, z, l);
361
      if (cnt < 0)
362
        {
363
          if (errno == -EAGAIN)
364
            {
365
              fd_set set;
366
              FD_ZERO(&set);
367
              do
368
                {
369
                  FD_SET(server_fd, &set);
370
                  select(server_fd+1, NULL, &set, NULL, NULL);
371
                }
372
              while (!FD_ISSET(server_fd, &set));
373
            }
374
          else
375
            die("Server write error: %m");
376
        }
377
      else
378
        {
379
          l -= cnt;
380
          z += cnt;
381
        }
382
    }
383 7211be1c Martin Mares
}
384
385 ed608150 Martin Mares
int
386
main(int argc, char **argv)
387
{
388 9fac310d Martin Mares
#ifdef HAVE_LIBDMALLOC
389
  if (!getenv("DMALLOC_OPTIONS"))
390
    dmalloc_debug(0x2f03d00);
391
#endif
392
393
  parse_args(argc, argv);
394 0223d4ff Martin Mares
  cmd_build_tree();
395 c51f132d Martin Mares
  server_connect();
396 4daf03e5 Martin Mares
  select_loop(0);
397 9fac310d Martin Mares
398 c51f132d Martin Mares
  input_init();
399
400 4daf03e5 Martin Mares
  select_loop(1);
401 c51f132d Martin Mares
  return 0;
402 ed608150 Martin Mares
}