mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
CLI: Improved auto-completion behavior
Auto-complete keywords (for, where, filter, ...) and symbol names (names of protocols, tables, ...). Client can request daemon for list of all symbols using new cli command `refresh symbols`. Next changes: - Behavior is configured by *.Y files using flags CLI_SF_* - The file doc/reply_codes was moved to header file client/reply_codes.h. - Share birdcl input_read() function code for birdc non-interactive mode. - BIRD daemon notifies the client about new symbol set. - BIRD pushes notification to the client about new symbol set and then the client should request a package with all symbols (`refresh symbols`). - File-based history of previous commands(). In interactive mode in birdc is stored history of all commands in ~/.birdc_history file. - BIRD daemon sends notification to clients about interface updates - Maintains a list of all connected cli clients to daemon. Daemon sends to all cli clients notification about interfaces states up and down.
This commit is contained in:
parent
5de0e848de
commit
b7f24e23fd
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
@ -20,6 +21,8 @@
|
||||
#include "lib/string.h"
|
||||
#include "client/client.h"
|
||||
|
||||
#define BIRDC_HISTORY_FILENAME ".birdc_history"
|
||||
|
||||
static int input_hidden_end;
|
||||
static int prompt_active;
|
||||
|
||||
@ -78,7 +81,7 @@ static int
|
||||
input_complete(int arg UNUSED, int key UNUSED)
|
||||
{
|
||||
static int complete_flag;
|
||||
char buf[256];
|
||||
char buf[256] = {};
|
||||
|
||||
if (rl_last_func != input_complete)
|
||||
complete_flag = 0;
|
||||
@ -137,20 +140,48 @@ input_help(int arg, int key UNUSED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_rl_history_filepath - return the string with path to readline history file
|
||||
* @buf: buffer for writing
|
||||
* @size: size of @buf including a |NULL| terminator at the end
|
||||
*
|
||||
* This function returns the string with path to readline history file ($HOME/.birdc_history).
|
||||
* A function is copied from readline repository histfile.c from function history_filename()
|
||||
*/
|
||||
static char *
|
||||
get_rl_history_filepath(char *buf, size_t size)
|
||||
{
|
||||
const char *home = getenv("HOME");
|
||||
if (!home)
|
||||
return NULL;
|
||||
|
||||
if (snprintf(buf, size, "%s/%s", home, BIRDC_HISTORY_FILENAME) >= size)
|
||||
return NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
input_init(void)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
prompt_active = 0;
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
prompt_active = 1;
|
||||
|
||||
rl_readline_name = "birdc";
|
||||
rl_add_defun("bird-complete", input_complete, '\t');
|
||||
rl_add_defun("bird-help", input_help, '?');
|
||||
read_history(get_rl_history_filepath(path, sizeof(path)));
|
||||
rl_callback_handler_install("bird> ", input_got_line);
|
||||
}
|
||||
|
||||
// rl_get_screen_size();
|
||||
term_lns = LINES;
|
||||
term_cls = COLS;
|
||||
|
||||
prompt_active = 1;
|
||||
|
||||
// readline library does strange things when stdin is nonblocking.
|
||||
// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
|
||||
// DIE("fcntl");
|
||||
@ -197,7 +228,10 @@ input_notify(int prompt)
|
||||
void
|
||||
input_read(void)
|
||||
{
|
||||
if (interactive)
|
||||
rl_callback_read_char();
|
||||
else
|
||||
simple_input_read();
|
||||
}
|
||||
|
||||
void
|
||||
@ -213,9 +247,14 @@ more_end(void)
|
||||
void
|
||||
cleanup(void)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (init)
|
||||
return;
|
||||
|
||||
if (interactive)
|
||||
write_history(get_rl_history_filepath(path, sizeof(path)));
|
||||
|
||||
input_hide();
|
||||
rl_callback_handler_remove();
|
||||
}
|
||||
|
@ -22,8 +22,6 @@
|
||||
#include "client/client.h"
|
||||
#include "sysdep/unix/unix.h"
|
||||
|
||||
#define INPUT_BUF_LEN 2048
|
||||
|
||||
struct termios tty_save;
|
||||
|
||||
void
|
||||
@ -42,59 +40,17 @@ void
|
||||
input_notify(int prompt)
|
||||
{
|
||||
/* No ncurses -> no status to reveal/hide, print prompt manually. */
|
||||
if (!prompt)
|
||||
if (!prompt || !interactive)
|
||||
return;
|
||||
|
||||
printf("bird> ");
|
||||
printf("\rbird> ");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
lastnb(char *str, int i)
|
||||
{
|
||||
while (i--)
|
||||
if ((str[i] != ' ') && (str[i] != '\t'))
|
||||
return str[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
input_read(void)
|
||||
{
|
||||
char buf[INPUT_BUF_LEN];
|
||||
|
||||
if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0))
|
||||
{
|
||||
putchar('\n');
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int l = strlen(buf);
|
||||
if ((l+1) == INPUT_BUF_LEN)
|
||||
{
|
||||
printf("Input too long.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf[l-1] == '\n')
|
||||
buf[--l] = '\0';
|
||||
|
||||
if (!interactive)
|
||||
printf("%s\n", buf);
|
||||
|
||||
if (l == 0)
|
||||
return;
|
||||
|
||||
if (lastnb(buf, l) == '?')
|
||||
{
|
||||
cmd_help(buf, strlen(buf));
|
||||
return;
|
||||
}
|
||||
|
||||
submit_command(buf);
|
||||
simple_input_read();
|
||||
}
|
||||
|
||||
static struct termios stored_tty;
|
||||
|
168
client/client.c
168
client/client.c
@ -34,8 +34,11 @@
|
||||
#include "lib/string.h"
|
||||
#include "client/client.h"
|
||||
#include "sysdep/unix/unix.h"
|
||||
#include "client/reply_codes.h"
|
||||
|
||||
#define SERVER_READ_BUF_LEN 4096
|
||||
#define INPUT_BUF_LEN 2048
|
||||
#define REFRESH_SYMBOLS_CMD "refresh symbols" /* Name of cli command for retrieve new symbols from daemon */
|
||||
|
||||
static char *opt_list = "s:vrl";
|
||||
static int verbose, restricted, once;
|
||||
@ -47,12 +50,16 @@ static byte server_read_buf[SERVER_READ_BUF_LEN];
|
||||
static byte *server_read_pos = server_read_buf;
|
||||
|
||||
int init = 1; /* During intial sequence */
|
||||
int busy = 1; /* Executing BIRD command */
|
||||
int busy = 0; /* Executing BIRD command */
|
||||
int interactive; /* Whether stdin is terminal */
|
||||
int welcomed = 0; /* Was welcome message with BIRD version printed out? */
|
||||
|
||||
static int num_lines, skip_input;
|
||||
int term_lns, term_cls;
|
||||
|
||||
static list symbols;
|
||||
static uint longest_symbol_len;
|
||||
|
||||
|
||||
/*** Parsing of arguments ***/
|
||||
|
||||
@ -107,7 +114,7 @@ parse_args(int argc, char **argv)
|
||||
tmp += strlen(tmp);
|
||||
*tmp++ = ' ';
|
||||
}
|
||||
tmp[-1] = 0;
|
||||
tmp[-1] = 0; /* Put ending null-terminator */
|
||||
|
||||
once = 1;
|
||||
interactive = 0;
|
||||
@ -132,6 +139,12 @@ handle_internal_command(char *cmd)
|
||||
puts("Press `?' for context sensitive help.");
|
||||
return 1;
|
||||
}
|
||||
if (!strncmp(cmd, REFRESH_SYMBOLS_CMD, sizeof(REFRESH_SYMBOLS_CMD)-1))
|
||||
{
|
||||
retrieve_symbols();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -172,6 +185,41 @@ submit_command(char *cmd_raw)
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void
|
||||
add_to_symbols(int flag, const char *name)
|
||||
{
|
||||
struct cli_symbol *sym = malloc(sizeof(struct cli_symbol));
|
||||
sym->flags = flag;
|
||||
|
||||
sym->len = strlen(name);
|
||||
char *name_ = malloc(sym->len + 1);
|
||||
memcpy(name_, name, sym->len + 1);
|
||||
sym->name = name_;
|
||||
add_tail(&symbols, &sym->n);
|
||||
|
||||
if (longest_symbol_len < sym->len)
|
||||
longest_symbol_len = sym->len;
|
||||
}
|
||||
|
||||
void
|
||||
retrieve_symbols(void)
|
||||
{
|
||||
/* Purge old symbols */
|
||||
list *syms = cli_get_symbol_list();
|
||||
struct cli_symbol *sym, *next;
|
||||
WALK_LIST_DELSAFE(sym, next, *syms)
|
||||
{
|
||||
rem_node(&sym->n);
|
||||
free((char *) sym->name);
|
||||
free(sym);
|
||||
}
|
||||
|
||||
add_to_symbols(CLI_SF_KW_ALL, "all");
|
||||
add_to_symbols(CLI_SF_KW_OFF, "off");
|
||||
|
||||
submit_server_command(REFRESH_SYMBOLS_CMD);
|
||||
}
|
||||
|
||||
static void
|
||||
init_commands(void)
|
||||
{
|
||||
@ -198,6 +246,13 @@ init_commands(void)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
init_list(&symbols);
|
||||
longest_symbol_len = 1; /* Be careful, it's used as denominator! */
|
||||
|
||||
/* In symbol list is a BIRD version for welcome message too */
|
||||
if (interactive)
|
||||
retrieve_symbols();
|
||||
|
||||
input_init();
|
||||
|
||||
term_lns = (term_lns > 0) ? term_lns : 25;
|
||||
@ -262,25 +317,83 @@ server_connect(void)
|
||||
DIE("fcntl");
|
||||
}
|
||||
|
||||
list *
|
||||
cli_get_symbol_list(void)
|
||||
{
|
||||
return &symbols;
|
||||
}
|
||||
|
||||
uint
|
||||
cli_get_symbol_maxlen(void)
|
||||
{
|
||||
return longest_symbol_len;
|
||||
}
|
||||
|
||||
static void
|
||||
process_internal_message(int reply_code, const char *name)
|
||||
{
|
||||
u32 flag = 0;
|
||||
|
||||
switch (reply_code)
|
||||
{
|
||||
case RC_BIRD_VERSION_NUM:
|
||||
if (interactive && !welcomed)
|
||||
{
|
||||
welcomed = 1;
|
||||
printf("BIRD %s ready.\n", name);
|
||||
}
|
||||
return;
|
||||
|
||||
case RC_NOTIFY:
|
||||
if (interactive)
|
||||
retrieve_symbols();
|
||||
return;
|
||||
|
||||
/* Symbols */
|
||||
case RC_CONSTANT_NAME: flag = CLI_SF_CONSTANT; break;
|
||||
case RC_VARIABLE_NAME: flag = CLI_SF_VARIABLE; break;
|
||||
case RC_FILTER_NAME: flag = CLI_SF_FILTER; break;
|
||||
case RC_FUNCTION_NAME: flag = CLI_SF_FUNCTION; break;
|
||||
case RC_PROTOCOL_NAME: flag = CLI_SF_PROTOCOL; break;
|
||||
case RC_TABLE_NAME: flag = CLI_SF_TABLE; break;
|
||||
case RC_TEMPLATE_NAME: flag = CLI_SF_TEMPLATE; break;
|
||||
case RC_INTERFACE_NAME: flag = CLI_SF_INTERFACE; break;
|
||||
default:
|
||||
printf("Undefined %d: %s", reply_code, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flag && name && *name)
|
||||
add_to_symbols(flag, name);
|
||||
}
|
||||
|
||||
#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
|
||||
|
||||
static void
|
||||
server_got_reply(char *x)
|
||||
{
|
||||
int code;
|
||||
int code = 0;
|
||||
int len = 0;
|
||||
|
||||
if (*x == '+') /* Async reply */
|
||||
if (*x == '+') { /* Async reply */
|
||||
busy = 1;
|
||||
input_notify(0);
|
||||
PRINTF(len, ">>> %s\n", x+1);
|
||||
}
|
||||
else if (x[0] == ' ') /* Continuation */
|
||||
PRINTF(len, "%s%s\n", verbose ? " " : "", x+1);
|
||||
else if (strlen(x) > 4 &&
|
||||
sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
|
||||
(x[4] == ' ' || x[4] == '-'))
|
||||
{
|
||||
if (code)
|
||||
if (code >= 3000 && code < 4000)
|
||||
{
|
||||
process_internal_message(code, x+5);
|
||||
}
|
||||
else if (code)
|
||||
{
|
||||
PRINTF(len, "%s\n", verbose ? x : x+5);
|
||||
}
|
||||
|
||||
if (x[4] == ' ')
|
||||
{
|
||||
@ -341,6 +454,51 @@ server_read(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
lastnb(char *str, int i)
|
||||
{
|
||||
while (i--)
|
||||
if ((str[i] != ' ') && (str[i] != '\t'))
|
||||
return str[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
simple_input_read(void)
|
||||
{
|
||||
char buf[INPUT_BUF_LEN];
|
||||
|
||||
if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0))
|
||||
{
|
||||
if (interactive)
|
||||
putchar('\n');
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int l = strlen(buf);
|
||||
if ((l+1) == INPUT_BUF_LEN)
|
||||
{
|
||||
printf("Input too long.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf[l-1] == '\n')
|
||||
buf[--l] = '\0';
|
||||
|
||||
if (l == 0)
|
||||
return;
|
||||
|
||||
if (lastnb(buf, l) == '?')
|
||||
{
|
||||
cmd_help(buf, strlen(buf));
|
||||
return;
|
||||
}
|
||||
|
||||
submit_command(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
select_loop(void)
|
||||
{
|
||||
|
@ -6,6 +6,8 @@
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_CLIENT_H_
|
||||
#define _BIRD_CLIENT_H_
|
||||
|
||||
extern int init, busy, interactive;
|
||||
extern int term_lns, term_cls;
|
||||
@ -33,7 +35,41 @@ char *cmd_expand(char *cmd);
|
||||
|
||||
/* client.c */
|
||||
|
||||
/* Client Symbol Flags: Types */
|
||||
#define CLI_SF_CONSTANT (1 << 0)
|
||||
#define CLI_SF_VARIABLE (1 << 1)
|
||||
#define CLI_SF_FILTER (1 << 2)
|
||||
#define CLI_SF_FUNCTION (1 << 3)
|
||||
#define CLI_SF_PROTOCOL (1 << 4)
|
||||
#define CLI_SF_TABLE (1 << 5)
|
||||
#define CLI_SF_TEMPLATE (1 << 6)
|
||||
#define CLI_SF_INTERFACE (1 << 7)
|
||||
|
||||
#define CLI_SF_OPTIONAL (1 << 8) /* This node is optional not mandatory */
|
||||
#define CLI_SF_PARAMETER (1 << 9) /* A parameter/word will follow after this node */
|
||||
|
||||
/* Client Symbol Flags: Keywords */
|
||||
#define CLI_SF_KW_ALL (1 << 10)
|
||||
#define CLI_SF_KW_OFF (1 << 11)
|
||||
|
||||
|
||||
struct cli_symbol
|
||||
{
|
||||
node n;
|
||||
const char *name;
|
||||
uint len;
|
||||
u32 flags; /* CLI_SF_* */
|
||||
};
|
||||
|
||||
void submit_command(char *cmd_raw);
|
||||
|
||||
/* die() with system error messages */
|
||||
#define DIE(x, y...) die(x ": %s", ##y, strerror(errno))
|
||||
|
||||
void retrieve_symbols(void);
|
||||
void add_keywords_to_symbols(void);
|
||||
list *cli_get_symbol_list(void);
|
||||
uint cli_get_symbol_maxlen(void);
|
||||
void simple_input_read(void);
|
||||
|
||||
#endif
|
||||
|
@ -16,10 +16,14 @@
|
||||
#include "client/client.h"
|
||||
|
||||
struct cmd_info {
|
||||
/* use for build cmd tree and cli commands */
|
||||
char *command;
|
||||
char *args;
|
||||
char *help;
|
||||
|
||||
/* only for build tree */
|
||||
int is_real_cmd;
|
||||
u32 flags; /* Mask of (CLI_SF_*) */
|
||||
};
|
||||
|
||||
static struct cmd_info command_table[] = {
|
||||
@ -27,11 +31,15 @@ static struct cmd_info command_table[] = {
|
||||
};
|
||||
|
||||
struct cmd_node {
|
||||
struct cmd_node *sibling, *son, **plastson;
|
||||
struct cmd_info *cmd, *help;
|
||||
int len;
|
||||
signed char prio;
|
||||
char token[1];
|
||||
struct cmd_node *sibling;
|
||||
struct cmd_node *son;
|
||||
struct cmd_node **plastson; /* Helping pointer to son */
|
||||
struct cmd_info *cmd; /* Short info */
|
||||
struct cmd_info *help; /* Detailed info */
|
||||
signed char prio; /* Priority */
|
||||
u32 flags; /* Mask of (CLI_SF_*) */
|
||||
int len; /* Length of string in token */
|
||||
char token[1]; /* Name of command */
|
||||
};
|
||||
|
||||
static struct cmd_node cmd_root;
|
||||
@ -78,6 +86,7 @@ cmd_build_tree(void)
|
||||
old->cmd = cmd;
|
||||
else
|
||||
old->help = cmd;
|
||||
old->flags |= cmd->flags;
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,6 +143,14 @@ cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len)
|
||||
for(m=root->son; m; m=m->sibling)
|
||||
if (m->len > len && !memcmp(m->token, cmd, len))
|
||||
cmd_display_help(m->help, m->cmd);
|
||||
|
||||
struct cli_symbol *sym;
|
||||
list *syms = cli_get_symbol_list();
|
||||
WALK_LIST(sym, *syms)
|
||||
{
|
||||
if ((sym->flags & root->flags) && sym->len > len && memcmp(sym->name, cmd, len) == 0)
|
||||
printf("%s\n", sym->name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -170,14 +187,43 @@ cmd_help(char *cmd, int len)
|
||||
cmd_display_help(m->help, m->cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return length of common prefix of all matches,
|
||||
* Write common prefix string into buf
|
||||
*/
|
||||
static int
|
||||
cmd_merge_match_with_others(int max_common_len, const char *token_name, int token_len, char *buf, int from)
|
||||
{
|
||||
if (max_common_len < 0)
|
||||
{
|
||||
/* For a case that we'll have exactly one match */
|
||||
strcpy(buf, token_name + from);
|
||||
max_common_len = token_len - from;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
while (i < max_common_len && i < token_len - from && buf[i] == token_name[from+i])
|
||||
i++;
|
||||
max_common_len = i;
|
||||
}
|
||||
return max_common_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return length of common prefix of all matches,
|
||||
* Write count of all matches into pcount,
|
||||
* Write common prefix string into buf
|
||||
*/
|
||||
static int
|
||||
cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf)
|
||||
{
|
||||
struct cmd_node *m;
|
||||
int best, best_prio, i;
|
||||
int max_common_len;
|
||||
int best_prio;
|
||||
|
||||
*pcount = 0;
|
||||
best = -1;
|
||||
max_common_len = -1;
|
||||
best_prio = -1;
|
||||
for(m=root->son; m; m=m->sibling)
|
||||
{
|
||||
@ -190,25 +236,31 @@ cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, ch
|
||||
if (best_prio < m->prio)
|
||||
{
|
||||
*pcount = 0;
|
||||
best = -1;
|
||||
max_common_len = -1;
|
||||
}
|
||||
|
||||
(*pcount)++;
|
||||
if (best < 0)
|
||||
{
|
||||
strcpy(buf, m->token + len);
|
||||
best = m->len - len;
|
||||
if (max_common_len < 0)
|
||||
best_prio = m->prio;
|
||||
|
||||
(*pcount)++;
|
||||
max_common_len = cmd_merge_match_with_others(max_common_len, m->token, m->len, buf, len);
|
||||
}
|
||||
else
|
||||
|
||||
list *syms = cli_get_symbol_list();
|
||||
struct cli_symbol *sym;
|
||||
WALK_LIST(sym, *syms)
|
||||
{
|
||||
i = 0;
|
||||
while (i < best && i < m->len - len && buf[i] == m->token[len+i])
|
||||
i++;
|
||||
best = i;
|
||||
if (!(sym->flags & root->flags))
|
||||
continue;
|
||||
|
||||
if (sym->len < len || memcmp(sym->name, cmd, len))
|
||||
continue;
|
||||
|
||||
(*pcount)++;
|
||||
max_common_len = cmd_merge_match_with_others(max_common_len, sym->name, sym->len, buf, len);
|
||||
}
|
||||
}
|
||||
return best;
|
||||
|
||||
return max_common_len;
|
||||
}
|
||||
|
||||
int
|
||||
@ -217,7 +269,7 @@ cmd_complete(char *cmd, int len, char *buf, int again)
|
||||
char *start = cmd;
|
||||
char *end = cmd + len;
|
||||
char *fin;
|
||||
struct cmd_node *n, *m;
|
||||
struct cmd_node *n, *m = NULL;
|
||||
char *z;
|
||||
int ambig, cnt = 0, common;
|
||||
|
||||
@ -227,7 +279,7 @@ cmd_complete(char *cmd, int len, char *buf, int again)
|
||||
|
||||
/* Find the context */
|
||||
n = &cmd_root;
|
||||
while (cmd < fin && n->son)
|
||||
while (cmd < fin)
|
||||
{
|
||||
if (isspace(*cmd))
|
||||
{
|
||||
@ -249,12 +301,39 @@ cmd_complete(char *cmd, int len, char *buf, int again)
|
||||
}
|
||||
if (!m)
|
||||
return -1;
|
||||
|
||||
/* Try skip a parameter/word */
|
||||
if (m->flags & CLI_SF_PARAMETER)
|
||||
{
|
||||
z = cmd;
|
||||
|
||||
/* Skip spaces before parameter */
|
||||
while (cmd < fin && isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
/* Skip one parameter/word */
|
||||
while (cmd < fin && !isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
/* Check ending of parameter */
|
||||
if (isspace(*cmd))
|
||||
{
|
||||
if (m->flags & CLI_SF_OPTIONAL)
|
||||
m = n;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
cmd = z;
|
||||
}
|
||||
|
||||
/* Do not enter to optional command nodes */
|
||||
if (!(m->flags & CLI_SF_OPTIONAL))
|
||||
n = m;
|
||||
}
|
||||
|
||||
/* Completion of parameters is not yet supported */
|
||||
if (!n->son)
|
||||
return -1;
|
||||
/* Enter to the last command node */
|
||||
if (m && (m->flags & CLI_SF_PARAMETER))
|
||||
n = m;
|
||||
|
||||
/* We know the context, let's try to complete */
|
||||
common = cmd_find_common_match(n, fin, end-fin, &cnt, buf);
|
||||
@ -282,8 +361,8 @@ cmd_complete(char *cmd, int len, char *buf, int again)
|
||||
char *
|
||||
cmd_expand(char *cmd)
|
||||
{
|
||||
struct cmd_node *n, *m;
|
||||
char *c, *b, *args;
|
||||
struct cmd_node *n, *m, *last_real_cmd = NULL;
|
||||
char *c, *b, *args, *lrc_args = NULL;
|
||||
int ambig;
|
||||
|
||||
args = c = cmd;
|
||||
@ -307,14 +386,28 @@ cmd_expand(char *cmd)
|
||||
cmd_list_ambiguous(n, b, c-b);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
args = c;
|
||||
n = m;
|
||||
|
||||
if (m->cmd)
|
||||
{
|
||||
last_real_cmd = m;
|
||||
lrc_args = c;
|
||||
}
|
||||
if (!n->cmd)
|
||||
}
|
||||
|
||||
if (!n->cmd && !last_real_cmd)
|
||||
{
|
||||
puts("No such command. Press `?' for help.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last_real_cmd && last_real_cmd != n)
|
||||
{
|
||||
n = last_real_cmd;
|
||||
args = lrc_args;
|
||||
}
|
||||
b = malloc(strlen(n->cmd->command) + strlen(args) + 1);
|
||||
sprintf(b, "%s%s", n->cmd->command, args);
|
||||
return b;
|
||||
|
103
client/reply_codes.h
Normal file
103
client/reply_codes.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* BIRD Client -- Reply codes for communication between client and daemon
|
||||
*
|
||||
* (c) 2016 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_REPLY_CODES_H_
|
||||
#define _BIRD_REPLY_CODES_H_
|
||||
|
||||
/*
|
||||
Reply codes of BIRD command-line interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
0xxx Action suceessfully completed
|
||||
1xxx Table entry
|
||||
2xxx Table heading
|
||||
3xxx Internal messages
|
||||
8xxx Run-time error
|
||||
9xxx Parse-time error
|
||||
<space> Continuation
|
||||
+ Spontaneous printout
|
||||
*/
|
||||
|
||||
enum reply_code {
|
||||
RC_OK = 0,
|
||||
RC_WELCOME = 1,
|
||||
RC_READING_CONFIGURATION = 2,
|
||||
RC_RECONFIGURED = 3,
|
||||
RC_RECONFIGURATION_IN_PROGRESS = 4,
|
||||
RC_RECONFIGURATION_ALREADY_IN_PROGRESS, QUEUEING = 5,
|
||||
RC_RECONFIGURATION_IGNORED_SHUTTING_DOWN = 6,
|
||||
RC_SHUTDOWN_ORDERED = 7,
|
||||
RC_ALREADY_DISABLED = 8,
|
||||
RC_DISABLED = 9,
|
||||
RC_ALREADY_ENABLED = 10,
|
||||
RC_ENABLED = 11,
|
||||
RC_RESTARTED = 12,
|
||||
RC_STATUS_REPORT = 13,
|
||||
RC_ROUTE_COUNT = 14,
|
||||
RC_RELOADING = 15,
|
||||
RC_ACCESS_RESTRICTED = 16,
|
||||
RC_RECONFIGURATION_ALREADY_IN_PROGRESS_REMOVING_QUEUED_CONFIG = 17,
|
||||
RC_RECONFIGURATION_CONFIRMED = 18,
|
||||
RC_NOTHING_TO_DO_CONFIGURE_UNDO_CONFIRM = 19,
|
||||
RC_CONFIGURATION_OK = 20,
|
||||
RC_UNDO_REQUESTED = 21,
|
||||
RC_UNDO_SCHEDULED = 22,
|
||||
RC_EVALUATION_OF_EXPRESSION = 23,
|
||||
RC_GRACEFUL_RESTART_STATUS_REPORT = 24,
|
||||
|
||||
RC_BIRD_VERSION = 1000,
|
||||
RC_INTERFACE_LIST = 1001,
|
||||
RC_PROTOCOL_LIST = 1002,
|
||||
RC_INTERFACE_ADDRESS = 1003,
|
||||
RC_INTERFACE_FLAGS = 1004,
|
||||
RC_INTERFACE_SUMMARY = 1005,
|
||||
RC_PROTOCOL_DETAILS = 1006,
|
||||
RC_ROUTE_LIST = 1007,
|
||||
RC_ROUTE_DETAILS = 1008,
|
||||
RC_STATIC_ROUTE_LIST = 1009,
|
||||
RC_SYMBOL_LIST = 1010,
|
||||
RC_UPTIME = 1011,
|
||||
RC_ROUTE_EXTENDED_ATTRIBUTE_LIST = 1012,
|
||||
RC_SHOW_OSPF_NEIGHBORS = 1013,
|
||||
RC_SHOW_OSPF = 1014,
|
||||
RC_SHOW_OSPF_INTERFACE = 1015,
|
||||
RC_SHOW_OSPF_STATE_TOPOLOGY = 1016,
|
||||
RC_SHOW_OSPF_LSADB = 1017,
|
||||
RC_SHOW_MEMORY = 1018,
|
||||
RC_SHOW_ROA_LIST = 1019,
|
||||
RC_SHOW_BFD_SESSIONS = 1020,
|
||||
RC_SHOW_RIP_INTERFACE = 1021,
|
||||
RC_SHOW_RIP_NEIGHBORS = 1022,
|
||||
|
||||
RC_BIRD_VERSION_NUM = 3000,
|
||||
RC_TABLE_NAME = 3001,
|
||||
RC_PROTOCOL_NAME = 3002,
|
||||
RC_FILTER_NAME = 3003,
|
||||
RC_FUNCTION_NAME = 3004,
|
||||
RC_CONSTANT_NAME = 3005,
|
||||
RC_VARIABLE_NAME = 3006,
|
||||
RC_TEMPLATE_NAME = 3007,
|
||||
RC_INTERFACE_NAME = 3008,
|
||||
RC_NOTIFY = 3009, /* Daemon notifies the client about new symbol set */
|
||||
|
||||
RC_REPLY_TOO_LONG = 8000,
|
||||
RC_ROUTE_NOT_FOUND = 8001,
|
||||
RC_CONFIGURATION_FILE_ERROR = 8002,
|
||||
RC_NO_PROTOCOLS_MATCH = 8003,
|
||||
RC_STOPPED_DUE_TO_RECONFIGURATION = 8004,
|
||||
RC_PROTOCOL_IS_DOWN_CANNOT_DUMP = 8005,
|
||||
RC_RELOAD_FAILED = 8006,
|
||||
RC_ACCESS_DENIED = 8007,
|
||||
RC_EVALUATION_RUNTIME_ERROR = 8008,
|
||||
|
||||
RC_COMMAND_TOO_LONG = 9000,
|
||||
RC_PARSE_ERROR = 9001,
|
||||
RC_INVALID_SYMBOL_TYPE = 9002,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -448,6 +448,7 @@ config_undo(void)
|
||||
}
|
||||
|
||||
extern void cmd_reconfig_undo_notify(void);
|
||||
extern void cmd_reconfig_msg(int r);
|
||||
|
||||
static void
|
||||
config_timeout(struct timer *t)
|
||||
@ -458,6 +459,8 @@ config_timeout(struct timer *t)
|
||||
int r = config_undo();
|
||||
if (r < 0)
|
||||
log(L_ERR "Undo request failed");
|
||||
|
||||
cmd_reconfig_msg(r);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -158,6 +158,9 @@ char *cf_symbol_class_name(struct symbol *sym);
|
||||
static inline int cf_symbol_is_constant(struct symbol *sym)
|
||||
{ return (sym->class & 0xff00) == SYM_CONSTANT; }
|
||||
|
||||
static inline int cf_symbol_is_variable(struct symbol *sym)
|
||||
{ return (sym->class & 0xff00) == SYM_VARIABLE; }
|
||||
|
||||
|
||||
/* Parser */
|
||||
|
||||
|
@ -7,13 +7,13 @@ m4_divert(-1)m4_dnl
|
||||
# Can be freely distributed and used under the terms of the GNU GPL.
|
||||
#
|
||||
|
||||
m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 },
|
||||
m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1, $5 },
|
||||
m4_divert(-1)')
|
||||
|
||||
m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 },
|
||||
m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1, 0 },
|
||||
m4_divert(-1)')
|
||||
|
||||
m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 },
|
||||
m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0, $4 },
|
||||
m4_divert(-1)')
|
||||
|
||||
# As we are processing C source, we must access all M4 primitives via
|
||||
|
40
nest/cli.c
40
nest/cli.c
@ -27,7 +27,8 @@
|
||||
* white space character.
|
||||
*
|
||||
* Reply codes starting with 0 stand for `action successfully completed' messages,
|
||||
* 1 means `table entry', 8 `runtime error' and 9 `syntax error'.
|
||||
* 1 means `table entry', 3 means `internal message`, 8 `runtime error' and 9
|
||||
* `syntax error'.
|
||||
*
|
||||
* Each CLI session is internally represented by a &cli structure and a
|
||||
* resource pool containing all resources associated with the connection,
|
||||
@ -67,8 +68,10 @@
|
||||
#include "nest/cli.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/string.h"
|
||||
#include "client/reply_codes.h"
|
||||
|
||||
pool *cli_pool;
|
||||
static list cli_client_list;
|
||||
|
||||
static byte *
|
||||
cli_alloc_out(cli *c, int size)
|
||||
@ -196,13 +199,6 @@ cli_copy_message(cli *c)
|
||||
q[-1] = '\n';
|
||||
}
|
||||
|
||||
static void
|
||||
cli_hello(cli *c)
|
||||
{
|
||||
cli_printf(c, 1, "BIRD " BIRD_VERSION " ready.");
|
||||
c->cont = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cli_free_out(cli *c)
|
||||
{
|
||||
@ -228,7 +224,7 @@ cli_written(cli *c)
|
||||
ev_schedule(c->event);
|
||||
}
|
||||
|
||||
|
||||
/* cli read hooks variables */
|
||||
static byte *cli_rh_pos;
|
||||
static uint cli_rh_len;
|
||||
static int cli_rh_trick_flag;
|
||||
@ -304,18 +300,18 @@ cli *
|
||||
cli_new(void *priv)
|
||||
{
|
||||
pool *p = rp_new(cli_pool, "CLI");
|
||||
cli *c = mb_alloc(p, sizeof(cli));
|
||||
cli *c = mb_allocz(p, sizeof(cli));
|
||||
|
||||
bzero(c, sizeof(cli));
|
||||
c->pool = p;
|
||||
c->priv = priv;
|
||||
c->event = ev_new(p);
|
||||
c->event->hook = cli_event;
|
||||
c->event->data = c;
|
||||
c->cont = cli_hello;
|
||||
c->cont = NULL;
|
||||
c->parser_pool = lp_new(c->pool, 4096);
|
||||
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
|
||||
ev_schedule(c->event);
|
||||
add_tail(&cli_client_list, &c->cli_client_node);
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -407,6 +403,7 @@ cli_free(cli *c)
|
||||
c->cleanup(c);
|
||||
if (c == cmd_reconfig_stored_cli)
|
||||
cmd_reconfig_stored_cli = NULL;
|
||||
rem_node(&c->cli_client_node);
|
||||
rfree(c->pool);
|
||||
}
|
||||
|
||||
@ -422,4 +419,23 @@ cli_init(void)
|
||||
cli_pool = rp_new(&root_pool, "CLI");
|
||||
init_list(&cli_log_hooks);
|
||||
cli_log_inited = 1;
|
||||
init_list(&cli_client_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* cli_notify_all_clients - send push notification to all cli clients
|
||||
*
|
||||
* Send a notification to all command line clients about some news.
|
||||
* Client could then send a request for pulling symbols.
|
||||
*/
|
||||
void
|
||||
cli_notify_all_clients(void)
|
||||
{
|
||||
struct cli *cli;
|
||||
node *n;
|
||||
WALK_LIST2(cli, n, cli_client_list, cli_client_node)
|
||||
{
|
||||
cli_printf(cli, RC_NOTIFY, "");
|
||||
cli_write_trigger(cli);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ struct cli_out {
|
||||
|
||||
typedef struct cli {
|
||||
node n; /* Node in list of all log hooks */
|
||||
node cli_client_node; /* Node in list of all cli clients */
|
||||
pool *pool;
|
||||
void *priv; /* Private to sysdep layer */
|
||||
byte *rx_buf, *rx_pos, *rx_aux; /* sysdep */
|
||||
@ -65,6 +66,7 @@ void cli_free(cli *);
|
||||
void cli_kick(cli *);
|
||||
void cli_written(cli *);
|
||||
void cli_echo(uint class, byte *msg);
|
||||
void cli_notify_all_clients(void);
|
||||
|
||||
static inline int cli_access_restricted(void)
|
||||
{
|
||||
|
52
nest/cmds.c
52
nest/cmds.c
@ -15,6 +15,8 @@
|
||||
#include "lib/string.h"
|
||||
#include "lib/resource.h"
|
||||
#include "filter/filter.h"
|
||||
#include "client/reply_codes.h"
|
||||
#include "nest/iface.h"
|
||||
|
||||
extern int shutting_down;
|
||||
extern int configuring;
|
||||
@ -64,6 +66,56 @@ cmd_show_symbols(struct sym_show_data *sd)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
get_cli_code_for_sym(struct symbol *sym)
|
||||
{
|
||||
if (cf_symbol_is_constant(sym))
|
||||
return RC_CONSTANT_NAME;
|
||||
|
||||
if (cf_symbol_is_variable(sym))
|
||||
return RC_VARIABLE_NAME;
|
||||
|
||||
switch (sym->class & 0xff)
|
||||
{
|
||||
case SYM_PROTO: return RC_PROTOCOL_NAME;
|
||||
case SYM_TEMPLATE: return RC_TEMPLATE_NAME;
|
||||
case SYM_FUNCTION: return RC_FUNCTION_NAME;
|
||||
case SYM_FILTER: return RC_FILTER_NAME;
|
||||
case SYM_TABLE: return RC_TABLE_NAME;
|
||||
default:
|
||||
log(L_ERR "Undefined class %d of %s", sym->class, sym->name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cmd_send_symbols - send all symbols for auto-completion interactive CLI
|
||||
*
|
||||
* This function sends all known symbols for auto-completion interactive BIRD's
|
||||
* CLI. The first symbol is version of BIRD.
|
||||
*/
|
||||
void
|
||||
cmd_send_symbols(void)
|
||||
{
|
||||
int code, pos = 0;
|
||||
struct symbol *sym = NULL;
|
||||
|
||||
cli_msg(RC_BIRD_VERSION_NUM, "%s", BIRD_VERSION);
|
||||
|
||||
while (sym = cf_walk_symbols(config, sym, &pos))
|
||||
{
|
||||
code = get_cli_code_for_sym(sym);
|
||||
cli_msg(code, "%s", sym->name);
|
||||
}
|
||||
|
||||
struct iface *i;
|
||||
WALK_LIST(i, iface_list)
|
||||
if (!(i->flags & IF_SHUTDOWN))
|
||||
cli_msg(RC_INTERFACE_NAME, "\"%s\"", i->name);
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
static void
|
||||
print_size(char *dsc, size_t val)
|
||||
{
|
||||
|
@ -15,5 +15,6 @@ struct f_inst;
|
||||
|
||||
void cmd_show_status(void);
|
||||
void cmd_show_symbols(struct sym_show_data *sym);
|
||||
void cmd_send_symbols(void);
|
||||
void cmd_show_memory(void);
|
||||
void cmd_eval(struct f_inst *expr);
|
||||
|
@ -72,6 +72,7 @@ CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
|
||||
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
|
||||
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
|
||||
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
|
||||
CF_KEYWORDS(REFRESH)
|
||||
|
||||
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
||||
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
|
||||
@ -467,10 +468,10 @@ CF_CLI(SHOW STATUS,,, [[Show router status]])
|
||||
CF_CLI(SHOW MEMORY,,, [[Show memory usage]])
|
||||
{ cmd_show_memory(); } ;
|
||||
|
||||
CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]])
|
||||
CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]], CLI_SF_PROTOCOL)
|
||||
{ proto_apply_cmd($3, proto_cmd_show, 0, 0); } ;
|
||||
|
||||
CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]])
|
||||
CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]], CLI_SF_PROTOCOL)
|
||||
{ proto_apply_cmd($4, proto_cmd_show, 0, 1); } ;
|
||||
|
||||
optsym:
|
||||
@ -488,6 +489,20 @@ CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
|
||||
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
|
||||
{ rt_show($3); } ;
|
||||
|
||||
CF_CLI_HELP(SHOW ROUTE FOR, <prefix> | <ip> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
|
||||
CF_CLI_HELP(SHOW ROUTE TABLE, <table> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_TABLE)
|
||||
CF_CLI_HELP(SHOW ROUTE FILTER, <filter> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_FILTER)
|
||||
CF_CLI_HELP(SHOW ROUTE WHERE, <condition> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
|
||||
CF_CLI_HELP(SHOW ROUTE ALL, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW ROUTE PRIMARY, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW ROUTE FILTRED, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW ROUTE EXPORT, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
|
||||
CF_CLI_HELP(SHOW ROUTE PREEXPORT, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
|
||||
CF_CLI_HELP(SHOW ROUTE NOEXPORT, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
|
||||
CF_CLI_HELP(SHOW ROUTE PROTOCOL, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
|
||||
CF_CLI_HELP(SHOW ROUTE STATS, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW ROUTE COUNT, ...,, CLI_SF_OPTIONAL)
|
||||
|
||||
r_args:
|
||||
/* empty */ {
|
||||
$$ = cfg_allocz(sizeof(struct rt_show_data));
|
||||
@ -567,9 +582,13 @@ export_mode:
|
||||
|
||||
|
||||
CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]])
|
||||
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]])
|
||||
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]], ~CLI_SF_OPTIONAL)
|
||||
{ cmd_show_symbols($3); } ;
|
||||
|
||||
CF_CLI_HELP(REFRESH, symbols, [[Check out new symbols from daemon for autocomplete in BIRD Client]])
|
||||
CF_CLI(REFRESH SYMBOLS,,, [[Check out new symbols from daemon for autocomplete in BIRD Client]])
|
||||
{ cmd_send_symbols(); } ;
|
||||
|
||||
sym_args:
|
||||
/* empty */ {
|
||||
$$ = cfg_allocz(sizeof(struct sym_show_data));
|
||||
@ -601,11 +620,11 @@ CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
|
||||
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
|
||||
{ protos_dump_all(); cli_msg(0, ""); } ;
|
||||
|
||||
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
|
||||
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]], CLI_SF_CONSTANT | CLI_SF_VARIABLE)
|
||||
{ cmd_eval($2); } ;
|
||||
|
||||
CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
|
||||
CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug | trace | info | remote | warning | error | auth }) [<buffer-size>], [[Control echoing of log messages]]) {
|
||||
CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug | trace | info | remote | warning | error | auth }) [<buffer-size>], [[Control echoing of log messages]], CLI_SF_KW_ALL | CLI_SF_KW_OFF) {
|
||||
cli_set_log_echo(this_cli, $2, $3);
|
||||
cli_msg(0, "");
|
||||
} ;
|
||||
@ -624,25 +643,25 @@ echo_size:
|
||||
}
|
||||
;
|
||||
|
||||
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]])
|
||||
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
|
||||
{ proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ;
|
||||
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]])
|
||||
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
|
||||
{ proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ;
|
||||
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
|
||||
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
|
||||
{ proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ;
|
||||
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
|
||||
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
|
||||
{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
|
||||
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
|
||||
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
|
||||
{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_IN); } ;
|
||||
CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]])
|
||||
CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
|
||||
{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
|
||||
|
||||
CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]])
|
||||
CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | interfaces | events | packets }), [[Control protocol debugging via BIRD logs]])
|
||||
CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | interfaces | events | packets }), [[Control protocol debugging via BIRD logs]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL | CLI_SF_KW_OFF | CLI_SF_PARAMETER)
|
||||
{ proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
|
||||
|
||||
CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
|
||||
CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]])
|
||||
CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL | CLI_SF_KW_OFF | CLI_SF_PARAMETER)
|
||||
{ proto_apply_cmd($2, proto_cmd_mrtdump, 1, $3); } ;
|
||||
|
||||
CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
|
||||
|
@ -206,27 +206,32 @@ if_notify_change(unsigned c, struct iface *i)
|
||||
#endif
|
||||
|
||||
if (c & IF_CHANGE_DOWN)
|
||||
{
|
||||
neigh_if_down(i);
|
||||
|
||||
if (c & IF_CHANGE_DOWN)
|
||||
WALK_LIST(a, i->addrs)
|
||||
{
|
||||
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
||||
ifa_notify_change_(IF_CHANGE_DOWN, a);
|
||||
}
|
||||
|
||||
cli_notify_all_clients();
|
||||
}
|
||||
|
||||
WALK_LIST(p, proto_list)
|
||||
if_send_notify(p, c, i);
|
||||
|
||||
if (c & IF_CHANGE_UP)
|
||||
{
|
||||
WALK_LIST(a, i->addrs)
|
||||
{
|
||||
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
||||
ifa_notify_change_(IF_CHANGE_UP, a);
|
||||
}
|
||||
|
||||
if (c & IF_CHANGE_UP)
|
||||
neigh_if_up(i);
|
||||
cli_notify_all_clients();
|
||||
}
|
||||
|
||||
if ((c & (IF_CHANGE_UP | IF_CHANGE_DOWN | IF_CHANGE_LINK)) == IF_CHANGE_LINK)
|
||||
neigh_if_link(i);
|
||||
|
@ -417,13 +417,13 @@ CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEM
|
||||
CF_ADDTO(dynamic_attr, OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID | EAF_TEMP, T_QUAD, EA_OSPF_ROUTER_ID); })
|
||||
|
||||
CF_CLI_HELP(SHOW OSPF, ..., [[Show information about OSPF protocol]]);
|
||||
CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol XXX]])
|
||||
CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol XXX]], CLI_SF_PROTOCOL)
|
||||
{ ospf_sh(proto_get_named($3, &proto_ospf)); };
|
||||
|
||||
CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]])
|
||||
CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]], CLI_SF_PROTOCOL | CLI_SF_INTERFACE | CLI_SF_PARAMETER)
|
||||
{ ospf_sh_neigh(proto_get_named($4, &proto_ospf), $5); };
|
||||
|
||||
CF_CLI(SHOW OSPF INTERFACE, optsym opttext, [<name>] [\"<interface>\"], [[Show information about interface]])
|
||||
CF_CLI(SHOW OSPF INTERFACE, optsym opttext, [<name>] [\"<interface>\"], [[Show information about interface]], CLI_SF_PROTOCOL | CLI_SF_INTERFACE | CLI_SF_PARAMETER)
|
||||
{ ospf_sh_iface(proto_get_named($4, &proto_ospf), $5); };
|
||||
|
||||
CF_CLI_HELP(SHOW OSPF TOPOLOGY, [all] [<name>], [[Show information about OSPF network topology]])
|
||||
@ -431,20 +431,27 @@ CF_CLI_HELP(SHOW OSPF TOPOLOGY, [all] [<name>], [[Show information about OSPF ne
|
||||
CF_CLI(SHOW OSPF TOPOLOGY, optsym opttext, [<name>], [[Show information about reachable OSPF network topology]])
|
||||
{ ospf_sh_state(proto_get_named($4, &proto_ospf), 0, 1); };
|
||||
|
||||
CF_CLI(SHOW OSPF TOPOLOGY ALL, optsym opttext, [<name>], [[Show information about all OSPF network topology]])
|
||||
CF_CLI(SHOW OSPF TOPOLOGY ALL, optsym opttext, [<name>], [[Show information about all OSPF network topology]], CLI_SF_PROTOCOL)
|
||||
{ ospf_sh_state(proto_get_named($5, &proto_ospf), 0, 0); };
|
||||
|
||||
CF_CLI_HELP(SHOW OSPF STATE, [all] [<name>], [[Show information about OSPF network state]])
|
||||
|
||||
CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about reachable OSPF network state]])
|
||||
CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about reachable OSPF network state]], CLI_SF_PROTOCOL)
|
||||
{ ospf_sh_state(proto_get_named($4, &proto_ospf), 1, 1); };
|
||||
|
||||
CF_CLI(SHOW OSPF STATE ALL, optsym opttext, [<name>], [[Show information about all OSPF network state]])
|
||||
CF_CLI(SHOW OSPF STATE ALL, optsym opttext, [<name>], [[Show information about all OSPF network state]], CLI_SF_PROTOCOL)
|
||||
{ ospf_sh_state(proto_get_named($5, &proto_ospf), 1, 0); };
|
||||
|
||||
CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]]);
|
||||
CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [lsid <id>] [self | router <id>] [<proto>], [[Show content of OSPF LSA database]])
|
||||
CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]])
|
||||
CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [lsid <id>] [self | router <id>] [<proto>], [[Show content of OSPF LSA database]], CLI_SF_PROTOCOL)
|
||||
{ ospf_sh_lsadb($4); };
|
||||
CF_CLI_HELP(SHOW OSPF LSADB GLOBAL, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW OSPF LSADB AREA, <id> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
|
||||
CF_CLI_HELP(SHOW OSPF LSADB LINK, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW OSPF LSADB TYPE, <num> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
|
||||
CF_CLI_HELP(SHOW OSPF LSADB LSID, <num> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
|
||||
CF_CLI_HELP(SHOW OSPF LSADB SELF, ...,, CLI_SF_OPTIONAL)
|
||||
CF_CLI_HELP(SHOW OSPF LSADB ROUTER, <num> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
|
||||
|
||||
lsadb_args:
|
||||
/* empty */ {
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "nest/locks.h"
|
||||
#include "conf/conf.h"
|
||||
#include "filter/filter.h"
|
||||
#include "client/reply_codes.h"
|
||||
|
||||
#include "unix.h"
|
||||
#include "krt.h"
|
||||
@ -269,12 +270,12 @@ cmd_check_config(char *name)
|
||||
config_free(conf);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
cmd_reconfig_msg(int r)
|
||||
{
|
||||
switch (r)
|
||||
{
|
||||
case CONF_DONE: cli_msg( 3, "Reconfigured"); break;
|
||||
case CONF_DONE: cli_msg( 3, "Reconfigured"); cli_msg(RC_NOTIFY, ""); cli_msg(19, "Nothing to do"); break;
|
||||
case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break;
|
||||
case CONF_QUEUED: cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break;
|
||||
case CONF_UNQUEUED: cli_msg(17, "Reconfiguration already in progress, removing queued config"); break;
|
||||
|
Loading…
Reference in New Issue
Block a user