Annotation of embedaddon/bird2/nest/cli.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD Internet Routing Daemon -- Command-Line Interface
! 3: *
! 4: * (c) 1999--2000 Martin Mares <mj@ucw.cz>
! 5: *
! 6: * Can be freely distributed and used under the terms of the GNU GPL.
! 7: */
! 8:
! 9: /**
! 10: * DOC: Command line interface
! 11: *
! 12: * This module takes care of the BIRD's command-line interface (CLI).
! 13: * The CLI exists to provide a way to control BIRD remotely and to inspect
! 14: * its status. It uses a very simple textual protocol over a stream
! 15: * connection provided by the platform dependent code (on UNIX systems,
! 16: * it's a UNIX domain socket).
! 17: *
! 18: * Each session of the CLI consists of a sequence of request and replies,
! 19: * slightly resembling the FTP and SMTP protocols.
! 20: * Requests are commands encoded as a single line of text, replies are
! 21: * sequences of lines starting with a four-digit code followed by either
! 22: * a space (if it's the last line of the reply) or a minus sign (when the
! 23: * reply is going to continue with the next line), the rest of the line
! 24: * contains a textual message semantics of which depends on the numeric
! 25: * code. If a reply line has the same code as the previous one and it's
! 26: * a continuation line, the whole prefix can be replaced by a single
! 27: * white space character.
! 28: *
! 29: * Reply codes starting with 0 stand for `action successfully completed' messages,
! 30: * 1 means `table entry', 8 `runtime error' and 9 `syntax error'.
! 31: *
! 32: * Each CLI session is internally represented by a &cli structure and a
! 33: * resource pool containing all resources associated with the connection,
! 34: * so that it can be easily freed whenever the connection gets closed, not depending
! 35: * on the current state of command processing.
! 36: *
! 37: * The CLI commands are declared as a part of the configuration grammar
! 38: * by using the |CF_CLI| macro. When a command is received, it is processed
! 39: * by the same lexical analyzer and parser as used for the configuration, but
! 40: * it's switched to a special mode by prepending a fake token to the text,
! 41: * so that it uses only the CLI command rules. Then the parser invokes
! 42: * an execution routine corresponding to the command, which either constructs
! 43: * the whole reply and returns it back or (in case it expects the reply will be long)
! 44: * it prints a partial reply and asks the CLI module (using the @cont hook)
! 45: * to call it again when the output is transferred to the user.
! 46: *
! 47: * The @this_cli variable points to a &cli structure of the session being
! 48: * currently parsed, but it's of course available only in command handlers
! 49: * not entered using the @cont hook.
! 50: *
! 51: * TX buffer management works as follows: At cli.tx_buf there is a
! 52: * list of TX buffers (struct cli_out), cli.tx_write is the buffer
! 53: * currently used by the producer (cli_printf(), cli_alloc_out()) and
! 54: * cli.tx_pos is the buffer currently used by the consumer
! 55: * (cli_write(), in system dependent code). The producer uses
! 56: * cli_out.wpos ptr as the current write position and the consumer
! 57: * uses cli_out.outpos ptr as the current read position. When the
! 58: * producer produces something, it calls cli_write_trigger(). If there
! 59: * is not enough space in the current buffer, the producer allocates
! 60: * the new one. When the consumer processes everything in the buffer
! 61: * queue, it calls cli_written(), tha frees all buffers (except the
! 62: * first one) and schedules cli.event .
! 63: *
! 64: */
! 65:
! 66: #include "nest/bird.h"
! 67: #include "nest/cli.h"
! 68: #include "conf/conf.h"
! 69: #include "lib/string.h"
! 70:
! 71: pool *cli_pool;
! 72:
! 73: static byte *
! 74: cli_alloc_out(cli *c, int size)
! 75: {
! 76: struct cli_out *o;
! 77:
! 78: if (!(o = c->tx_write) || o->wpos + size > o->end)
! 79: {
! 80: if (!o && c->tx_buf)
! 81: o = c->tx_buf;
! 82: else
! 83: {
! 84: o = mb_alloc(c->pool, sizeof(struct cli_out) + CLI_TX_BUF_SIZE);
! 85: if (c->tx_write)
! 86: c->tx_write->next = o;
! 87: else
! 88: c->tx_buf = o;
! 89: o->wpos = o->outpos = o->buf;
! 90: o->end = o->buf + CLI_TX_BUF_SIZE;
! 91: }
! 92: c->tx_write = o;
! 93: if (!c->tx_pos)
! 94: c->tx_pos = o;
! 95: o->next = NULL;
! 96: }
! 97: o->wpos += size;
! 98: return o->wpos - size;
! 99: }
! 100:
! 101: /**
! 102: * cli_printf - send reply to a CLI connection
! 103: * @c: CLI connection
! 104: * @code: numeric code of the reply, negative for continuation lines
! 105: * @msg: a printf()-like formatting string.
! 106: *
! 107: * This function send a single line of reply to a given CLI connection.
! 108: * In works in all aspects like bsprintf() except that it automatically
! 109: * prepends the reply line prefix.
! 110: *
! 111: * Please note that if the connection can be already busy sending some
! 112: * data in which case cli_printf() stores the output to a temporary buffer,
! 113: * so please avoid sending a large batch of replies without waiting
! 114: * for the buffers to be flushed.
! 115: *
! 116: * If you want to write to the current CLI output, you can use the cli_msg()
! 117: * macro instead.
! 118: */
! 119: void
! 120: cli_printf(cli *c, int code, char *msg, ...)
! 121: {
! 122: va_list args;
! 123: byte buf[CLI_LINE_SIZE];
! 124: int cd = code;
! 125: int errcode;
! 126: int size, cnt;
! 127:
! 128: if (cd < 0)
! 129: {
! 130: cd = -cd;
! 131: if (cd == c->last_reply)
! 132: size = bsprintf(buf, " ");
! 133: else
! 134: size = bsprintf(buf, "%04d-", cd);
! 135: errcode = -8000;
! 136: }
! 137: else if (cd == CLI_ASYNC_CODE)
! 138: {
! 139: size = 1; buf[0] = '+';
! 140: errcode = cd;
! 141: }
! 142: else
! 143: {
! 144: size = bsprintf(buf, "%04d ", cd);
! 145: errcode = 8000;
! 146: }
! 147:
! 148: c->last_reply = cd;
! 149: va_start(args, msg);
! 150: cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args);
! 151: va_end(args);
! 152: if (cnt < 0)
! 153: {
! 154: cli_printf(c, errcode, "<line overflow>");
! 155: return;
! 156: }
! 157: size += cnt;
! 158: buf[size++] = '\n';
! 159: memcpy(cli_alloc_out(c, size), buf, size);
! 160: }
! 161:
! 162: static void
! 163: cli_copy_message(cli *c)
! 164: {
! 165: byte *p, *q;
! 166: uint cnt = 2;
! 167:
! 168: if (c->ring_overflow)
! 169: {
! 170: byte buf[64];
! 171: int n = bsprintf(buf, "<%d messages lost>\n", c->ring_overflow);
! 172: c->ring_overflow = 0;
! 173: memcpy(cli_alloc_out(c, n), buf, n);
! 174: }
! 175: p = c->ring_read;
! 176: while (*p)
! 177: {
! 178: cnt++;
! 179: p++;
! 180: if (p == c->ring_end)
! 181: p = c->ring_buf;
! 182: ASSERT(p != c->ring_write);
! 183: }
! 184: c->async_msg_size += cnt;
! 185: q = cli_alloc_out(c, cnt);
! 186: *q++ = '+';
! 187: p = c->ring_read;
! 188: do
! 189: {
! 190: *q = *p++;
! 191: if (p == c->ring_end)
! 192: p = c->ring_buf;
! 193: }
! 194: while (*q++);
! 195: c->ring_read = p;
! 196: q[-1] = '\n';
! 197: }
! 198:
! 199: static void
! 200: cli_hello(cli *c)
! 201: {
! 202: cli_printf(c, 1, "BIRD " BIRD_VERSION " ready.");
! 203: c->cont = NULL;
! 204: }
! 205:
! 206: static void
! 207: cli_free_out(cli *c)
! 208: {
! 209: struct cli_out *o, *p;
! 210:
! 211: if (o = c->tx_buf)
! 212: {
! 213: o->wpos = o->outpos = o->buf;
! 214: while (p = o->next)
! 215: {
! 216: o->next = p->next;
! 217: mb_free(p);
! 218: }
! 219: }
! 220: c->tx_write = c->tx_pos = NULL;
! 221: c->async_msg_size = 0;
! 222: }
! 223:
! 224: void
! 225: cli_written(cli *c)
! 226: {
! 227: cli_free_out(c);
! 228: ev_schedule(c->event);
! 229: }
! 230:
! 231:
! 232: static byte *cli_rh_pos;
! 233: static uint cli_rh_len;
! 234: static int cli_rh_trick_flag;
! 235: struct cli *this_cli;
! 236:
! 237: static int
! 238: cli_cmd_read_hook(byte *buf, uint max, UNUSED int fd)
! 239: {
! 240: if (!cli_rh_trick_flag)
! 241: {
! 242: cli_rh_trick_flag = 1;
! 243: buf[0] = '!';
! 244: return 1;
! 245: }
! 246: if (max > cli_rh_len)
! 247: max = cli_rh_len;
! 248: memcpy(buf, cli_rh_pos, max);
! 249: cli_rh_pos += max;
! 250: cli_rh_len -= max;
! 251: return max;
! 252: }
! 253:
! 254: static void
! 255: cli_command(struct cli *c)
! 256: {
! 257: struct config f;
! 258: int res;
! 259:
! 260: if (config->cli_debug > 1)
! 261: log(L_TRACE "CLI: %s", c->rx_buf);
! 262: bzero(&f, sizeof(f));
! 263: f.mem = c->parser_pool;
! 264: f.pool = rp_new(c->pool, "Config");
! 265: init_list(&f.symbols);
! 266: cf_read_hook = cli_cmd_read_hook;
! 267: cli_rh_pos = c->rx_buf;
! 268: cli_rh_len = strlen(c->rx_buf);
! 269: cli_rh_trick_flag = 0;
! 270: this_cli = c;
! 271: lp_flush(c->parser_pool);
! 272: res = cli_parse(&f);
! 273: if (!res)
! 274: cli_printf(c, 9001, f.err_msg);
! 275:
! 276: config_free(&f);
! 277: }
! 278:
! 279: static void
! 280: cli_event(void *data)
! 281: {
! 282: cli *c = data;
! 283: int err;
! 284:
! 285: while (c->ring_read != c->ring_write &&
! 286: c->async_msg_size < CLI_MAX_ASYNC_QUEUE)
! 287: cli_copy_message(c);
! 288:
! 289: if (c->tx_pos)
! 290: ;
! 291: else if (c->cont)
! 292: c->cont(c);
! 293: else
! 294: {
! 295: err = cli_get_command(c);
! 296: if (!err)
! 297: return;
! 298: if (err < 0)
! 299: cli_printf(c, 9000, "Command too long");
! 300: else
! 301: cli_command(c);
! 302: }
! 303:
! 304: cli_write_trigger(c);
! 305: }
! 306:
! 307: cli *
! 308: cli_new(void *priv)
! 309: {
! 310: pool *p = rp_new(cli_pool, "CLI");
! 311: cli *c = mb_alloc(p, sizeof(cli));
! 312:
! 313: bzero(c, sizeof(cli));
! 314: c->pool = p;
! 315: c->priv = priv;
! 316: c->event = ev_new(p);
! 317: c->event->hook = cli_event;
! 318: c->event->data = c;
! 319: c->cont = cli_hello;
! 320: c->parser_pool = lp_new_default(c->pool);
! 321: c->show_pool = lp_new_default(c->pool);
! 322: c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
! 323: ev_schedule(c->event);
! 324: return c;
! 325: }
! 326:
! 327: void
! 328: cli_kick(cli *c)
! 329: {
! 330: if (!c->cont && !c->tx_pos)
! 331: ev_schedule(c->event);
! 332: }
! 333:
! 334: static list cli_log_hooks;
! 335: static int cli_log_inited;
! 336:
! 337: void
! 338: cli_set_log_echo(cli *c, uint mask, uint size)
! 339: {
! 340: if (c->ring_buf)
! 341: {
! 342: mb_free(c->ring_buf);
! 343: c->ring_buf = c->ring_end = c->ring_read = c->ring_write = NULL;
! 344: rem_node(&c->n);
! 345: }
! 346: c->log_mask = mask;
! 347: if (mask && size)
! 348: {
! 349: c->ring_buf = mb_alloc(c->pool, size);
! 350: c->ring_end = c->ring_buf + size;
! 351: c->ring_read = c->ring_write = c->ring_buf;
! 352: add_tail(&cli_log_hooks, &c->n);
! 353: c->log_threshold = size / 8;
! 354: }
! 355: c->ring_overflow = 0;
! 356: }
! 357:
! 358: void
! 359: cli_echo(uint class, byte *msg)
! 360: {
! 361: unsigned len, free, i, l;
! 362: cli *c;
! 363: byte *m;
! 364:
! 365: if (!cli_log_inited || EMPTY_LIST(cli_log_hooks))
! 366: return;
! 367: len = strlen(msg) + 1;
! 368: WALK_LIST(c, cli_log_hooks)
! 369: {
! 370: if (!(c->log_mask & (1 << class)))
! 371: continue;
! 372: if (c->ring_read <= c->ring_write)
! 373: free = (c->ring_end - c->ring_buf) - (c->ring_write - c->ring_read + 1);
! 374: else
! 375: free = c->ring_read - c->ring_write - 1;
! 376: if ((len > free) ||
! 377: (free < c->log_threshold && class < (unsigned) L_INFO[0]))
! 378: {
! 379: c->ring_overflow++;
! 380: continue;
! 381: }
! 382: if (c->ring_read == c->ring_write)
! 383: ev_schedule(c->event);
! 384: m = msg;
! 385: l = len;
! 386: while (l)
! 387: {
! 388: if (c->ring_read <= c->ring_write)
! 389: i = c->ring_end - c->ring_write;
! 390: else
! 391: i = c->ring_read - c->ring_write;
! 392: if (i > l)
! 393: i = l;
! 394: memcpy(c->ring_write, m, i);
! 395: m += i;
! 396: l -= i;
! 397: c->ring_write += i;
! 398: if (c->ring_write == c->ring_end)
! 399: c->ring_write = c->ring_buf;
! 400: }
! 401: }
! 402: }
! 403:
! 404: /* Hack for scheduled undo notification */
! 405: extern cli *cmd_reconfig_stored_cli;
! 406:
! 407: void
! 408: cli_free(cli *c)
! 409: {
! 410: cli_set_log_echo(c, 0, 0);
! 411: if (c->cleanup)
! 412: c->cleanup(c);
! 413: if (c == cmd_reconfig_stored_cli)
! 414: cmd_reconfig_stored_cli = NULL;
! 415: rfree(c->pool);
! 416: }
! 417:
! 418: /**
! 419: * cli_init - initialize the CLI module
! 420: *
! 421: * This function is called during BIRD startup to initialize
! 422: * the internal data structures of the CLI module.
! 423: */
! 424: void
! 425: cli_init(void)
! 426: {
! 427: cli_pool = rp_new(&root_pool, "CLI");
! 428: init_list(&cli_log_hooks);
! 429: cli_log_inited = 1;
! 430: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>