0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-10-18 09:58:43 +00:00

BIRD Client: Improve autocomplete bahavior

Autocomplete 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`.

Autocomplete behavior is controlled by *.Y files using flags CLI_SF_*

Transform doc/reply_codes to c header file client/reply_codes.h
This commit is contained in:
Pavel Tvrdik 2016-04-15 11:41:08 +02:00
parent 4bdf1881dc
commit 822e2f1cc7
15 changed files with 452 additions and 133 deletions

View File

@ -78,7 +78,7 @@ static int
input_complete(int arg UNUSED, int key UNUSED) input_complete(int arg UNUSED, int key UNUSED)
{ {
static int complete_flag; static int complete_flag;
char buf[256]; char buf[256] = {};
if (rl_last_func != input_complete) if (rl_last_func != input_complete)
complete_flag = 0; complete_flag = 0;
@ -140,6 +140,9 @@ input_help(int arg, int key UNUSED)
void void
input_init(void) input_init(void)
{ {
retrieve_symbols();
printf("BIRD Client " BIRD_VERSION " ready.\n");
rl_readline_name = "birdc"; rl_readline_name = "birdc";
rl_add_defun("bird-complete", input_complete, '\t'); rl_add_defun("bird-complete", input_complete, '\t');
rl_add_defun("bird-help", input_help, '?'); rl_add_defun("bird-help", input_help, '?');

View File

@ -136,6 +136,8 @@ input_init(void)
if (!interactive) if (!interactive)
return; return;
printf("BIRD Client Light " BIRD_VERSION " ready.\n");
if (tcgetattr(0, &stored_tty) < 0) if (tcgetattr(0, &stored_tty) < 0)
die("tcgetattr: %m"); die("tcgetattr: %m");

View File

@ -34,6 +34,7 @@
#include "lib/string.h" #include "lib/string.h"
#include "client/client.h" #include "client/client.h"
#include "sysdep/unix/unix.h" #include "sysdep/unix/unix.h"
#include "client/reply_codes.h"
#define SERVER_READ_BUF_LEN 4096 #define SERVER_READ_BUF_LEN 4096
@ -47,12 +48,15 @@ static byte server_read_buf[SERVER_READ_BUF_LEN];
static byte *server_read_pos = server_read_buf; static byte *server_read_pos = server_read_buf;
int init = 1; /* During intial sequence */ 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 interactive; /* Whether stdin is terminal */
static int num_lines, skip_input; static int num_lines, skip_input;
int term_lns, term_cls; int term_lns, term_cls;
static list symbols;
static uint longest_symbol_len;
/*** Parsing of arguments ***/ /*** Parsing of arguments ***/
@ -94,14 +98,14 @@ parse_args(int argc, char **argv)
for (i = optind; i < argc; i++) for (i = optind; i < argc; i++)
len += strlen(argv[i]) + 1; len += strlen(argv[i]) + 1;
tmp = init_cmd = malloc(len); tmp = init_cmd = xmalloc(len);
for (i = optind; i < argc; i++) for (i = optind; i < argc; i++)
{ {
strcpy(tmp, argv[i]); strcpy(tmp, argv[i]);
tmp += strlen(tmp); tmp += strlen(tmp);
*tmp++ = ' '; *tmp++ = ' ';
} }
tmp[-1] = 0; tmp[-1] = 0; /* Put ending null-terminator */
once = 1; once = 1;
interactive = 0; interactive = 0;
@ -126,6 +130,12 @@ handle_internal_command(char *cmd)
puts("Press `?' for context sensitive help."); puts("Press `?' for context sensitive help.");
return 1; return 1;
} }
if (!strncmp(cmd, REFRESH_SYMBOLS_CMD, sizeof(REFRESH_SYMBOLS_CMD)-1))
{
retrieve_symbols();
return 1;
}
return 0; return 0;
} }
@ -166,6 +176,41 @@ submit_command(char *cmd_raw)
free(cmd); free(cmd);
} }
static void
add_to_symbols(int flag, const char *name)
{
struct cli_symbol *sym = xmalloc(sizeof(struct cli_symbol));
sym->flags = flag;
sym->len = strlen(name);
char *name_ = xmalloc(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)
{
rem2_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 static void
init_commands(void) init_commands(void)
{ {
@ -192,6 +237,8 @@ init_commands(void)
exit(0); exit(0);
} }
init_list(&symbols);
longest_symbol_len = 1; /* Be careful, it's used as denominator! */
input_init(); input_init();
term_lns = (term_lns > 0) ? term_lns : 25; term_lns = (term_lns > 0) ? term_lns : 25;
@ -256,13 +303,47 @@ server_connect(void)
die("fcntl: %m"); die("fcntl: %m");
} }
list *
cli_get_symbol_list(void)
{
return &symbols;
}
uint
cli_get_symbol_maxlen(void)
{
return longest_symbol_len;
}
static void
server_got_symbol(int reply_code, const char *name)
{
u32 flag = 0;
switch (reply_code)
{
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;
}
add_to_symbols(flag, name);
}
#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
static void static void
server_got_reply(char *x) server_got_reply(char *x)
{ {
int code; int code = 0;
int len = 0; int len = 0;
if (*x == '+') /* Async reply */ if (*x == '+') /* Async reply */
@ -273,14 +354,20 @@ server_got_reply(char *x)
sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
(x[4] == ' ' || x[4] == '-')) (x[4] == ' ' || x[4] == '-'))
{ {
if (code) if (code >= 3000 && code < 4000)
PRINTF(len, "%s\n", verbose ? x : x+5); {
server_got_symbol(code, x+5);
}
else if (code)
{
PRINTF(len, "%s\n", verbose ? x : x+5);
}
if (x[4] == ' ') if (x[4] == ' ')
{ {
busy = 0; busy = 0;
skip_input = 0; skip_input = 0;
return; return;
} }
} }
else else

View File

@ -6,6 +6,10 @@
* Can be freely distributed and used under the terms of the GNU GPL. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
#ifndef _BIRD_CLIENT_H_
#define _BIRD_CLIENT_H_
#define REFRESH_SYMBOLS_CMD "refresh symbols"
extern int init, busy, interactive; extern int init, busy, interactive;
extern int term_lns, term_cls; extern int term_lns, term_cls;
@ -33,4 +37,36 @@ char *cmd_expand(char *cmd);
/* client.c */ /* 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); void submit_command(char *cmd_raw);
void retrieve_symbols(void);
void add_keywords_to_symbols(void);
list *cli_get_symbol_list(void);
uint cli_get_symbol_maxlen(void);
#endif

View File

@ -15,10 +15,14 @@
#include "client/client.h" #include "client/client.h"
struct cmd_info { struct cmd_info {
/* use for build tree and command cli */
char *command; char *command;
char *args; char *args;
char *help; char *help;
/* only for build tree */
int is_real_cmd; int is_real_cmd;
u32 flags; /* Mask of (CLI_SF_*) */
}; };
static struct cmd_info command_table[] = { static struct cmd_info command_table[] = {
@ -26,11 +30,15 @@ static struct cmd_info command_table[] = {
}; };
struct cmd_node { struct cmd_node {
struct cmd_node *sibling, *son, **plastson; struct cmd_node *sibling;
struct cmd_info *cmd, *help; struct cmd_node *son;
int len; struct cmd_node **plastson; /* Helping pointer to son */
signed char prio; struct cmd_info *cmd; /* Short info */
char token[1]; struct cmd_info *help; /* Detailed info */
signed char prio; /* Priority */
int len; /* Length of string in token */
u32 flags; /* Mask of (CLI_SF_*) */
char token[1]; /* Name of command */
}; };
static struct cmd_node cmd_root; static struct cmd_node cmd_root;
@ -77,6 +85,7 @@ cmd_build_tree(void)
old->cmd = cmd; old->cmd = cmd;
else else
old->help = cmd; old->help = cmd;
old->flags |= cmd->flags;
} }
} }
@ -102,6 +111,8 @@ static struct cmd_node *
cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous)
{ {
struct cmd_node *m, *best = NULL, *best2 = NULL; struct cmd_node *m, *best = NULL, *best2 = NULL;
list *l_syms = cli_get_symbol_list();
struct cli_symbol *sym;
*pambiguous = 0; *pambiguous = 0;
for(m=root->son; m; m=m->sibling) for(m=root->son; m; m=m->sibling)
@ -133,6 +144,14 @@ cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len)
for(m=root->son; m; m=m->sibling) for(m=root->son; m; m=m->sibling)
if (m->len > len && !memcmp(m->token, cmd, len)) if (m->len > len && !memcmp(m->token, cmd, len))
cmd_display_help(m->help, m->cmd); 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 void
@ -169,11 +188,17 @@ cmd_help(char *cmd, int len)
cmd_display_help(m->help, m->cmd); cmd_display_help(m->help, m->cmd);
} }
/*
* Return length of common prefix of all matches,
* Write count of all matches into pcount,
* Write common prefix string into buf
*/
static int static int
cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf) cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf)
{ {
struct cmd_node *m; struct cmd_node *m;
int best, best_prio, i; int best, /* len of common prefix */
best_prio, i;
*pcount = 0; *pcount = 0;
best = -1; best = -1;
@ -195,6 +220,7 @@ cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, ch
(*pcount)++; (*pcount)++;
if (best < 0) if (best < 0)
{ {
/* For a case that we'll have exactly one match */
strcpy(buf, m->token + len); strcpy(buf, m->token + len);
best = m->len - len; best = m->len - len;
best_prio = m->prio; best_prio = m->prio;
@ -207,6 +233,33 @@ cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, ch
best = i; best = i;
} }
} }
list *syms = cli_get_symbol_list();
struct cli_symbol *sym;
WALK_LIST(sym, *syms)
{
if (!(sym->flags & root->flags))
continue;
if (sym->len < len || memcmp(sym->name, cmd, len))
continue;
(*pcount)++;
if (best < 0)
{
strcpy(buf, sym->name + len);
best = sym->len - len; /* for a case that we'll have only one match */
}
else
{
i = 0;
while (i < best && i < sym->len - len && buf[i] == sym->name[len+i])
i++;
best = i;
}
}
return best; return best;
} }
@ -216,7 +269,7 @@ cmd_complete(char *cmd, int len, char *buf, int again)
char *start = cmd; char *start = cmd;
char *end = cmd + len; char *end = cmd + len;
char *fin; char *fin;
struct cmd_node *n, *m; struct cmd_node *n, *m = NULL;
char *z; char *z;
int ambig, cnt = 0, common; int ambig, cnt = 0, common;
@ -226,7 +279,7 @@ cmd_complete(char *cmd, int len, char *buf, int again)
/* Find the context */ /* Find the context */
n = &cmd_root; n = &cmd_root;
while (cmd < fin && n->son) while (cmd < fin)
{ {
if (isspace(*cmd)) if (isspace(*cmd))
{ {
@ -248,12 +301,39 @@ cmd_complete(char *cmd, int len, char *buf, int again)
} }
if (!m) if (!m)
return -1; return -1;
n = m;
/* 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 */ /* Enter to the last command node */
if (!n->son) if (m && (m->flags & CLI_SF_PARAMETER))
return -1; n = m;
/* We know the context, let's try to complete */ /* We know the context, let's try to complete */
common = cmd_find_common_match(n, fin, end-fin, &cnt, buf); common = cmd_find_common_match(n, fin, end-fin, &cnt, buf);
@ -281,8 +361,8 @@ cmd_complete(char *cmd, int len, char *buf, int again)
char * char *
cmd_expand(char *cmd) cmd_expand(char *cmd)
{ {
struct cmd_node *n, *m; struct cmd_node *n, *m, *last_real_cmd = NULL;
char *c, *b, *args; char *c, *b, *args, *lrc_args = NULL;
int ambig; int ambig;
args = c = cmd; args = c = cmd;
@ -306,14 +386,27 @@ cmd_expand(char *cmd)
cmd_list_ambiguous(n, b, c-b); cmd_list_ambiguous(n, b, c-b);
return NULL; return NULL;
} }
args = c; args = c;
n = m; 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."); puts("No such command. Press `?' for help.");
return NULL; return NULL;
} }
if (last_real_cmd && last_real_cmd != n)
{
n = last_real_cmd;
args = lrc_args;
}
b = xmalloc(strlen(n->cmd->command) + strlen(args) + 1); b = xmalloc(strlen(n->cmd->command) + strlen(args) + 1);
sprintf(b, "%s%s", n->cmd->command, args); sprintf(b, "%s%s", n->cmd->command, args);
return b; return b;

101
client/reply_codes.h Normal file
View File

@ -0,0 +1,101 @@
/*
* 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_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_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

View File

@ -158,6 +158,9 @@ char *cf_symbol_class_name(struct symbol *sym);
static inline int cf_symbol_is_constant(struct symbol *sym) static inline int cf_symbol_is_constant(struct symbol *sym)
{ return (sym->class & 0xff00) == SYM_CONSTANT; } { return (sym->class & 0xff00) == SYM_CONSTANT; }
static inline int cf_symbol_is_variable(struct symbol *sym)
{ return (sym->class & 0xff00) == SYM_VARIABLE; }
/* Parser */ /* Parser */

View File

@ -7,13 +7,13 @@ m4_divert(-1)m4_dnl
# Can be freely distributed and used under the terms of the GNU GPL. # 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_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_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)') m4_divert(-1)')
# As we are processing C source, we must access all M4 primitives via # As we are processing C source, we must access all M4 primitives via

View File

@ -1,73 +0,0 @@
Reply codes of BIRD command-line interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0xxx Action suceessfully completed
1xxx Table entry
2xxx Table heading
8xxx Run-time error
9xxx Parse-time error
<space> Continuation
+ Spontaneous printout
0000 OK
0001 Welcome
0002 Reading configuration
0003 Reconfigured
0004 Reconfiguration in progress
0005 Reconfiguration already in progress, queueing
0006 Reconfiguration ignored, shutting down
0007 Shutdown ordered
0008 Already disabled
0009 Disabled
0010 Already enabled
0011 Enabled
0012 Restarted
0013 Status report
0014 Route count
0015 Reloading
0016 Access restricted
0017 Reconfiguration already in progress, removing queued config
0018 Reconfiguration confirmed
0019 Nothing to do (configure undo/confirm)
0020 Configuration OK
0021 Undo requested
0022 Undo scheduled
0023 Evaluation of expression
0024 Graceful restart status report
1000 BIRD version
1001 Interface list
1002 Protocol list
1003 Interface address
1004 Interface flags
1005 Interface summary
1006 Protocol details
1007 Route list
1008 Route details
1009 Static route list
1010 Symbol list
1011 Uptime
1012 Route extended attribute list
1013 Show ospf neighbors
1014 Show ospf
1015 Show ospf interface
1016 Show ospf state/topology
1017 Show ospf lsadb
1018 Show memory
1019 Show ROA list
1020 Show BFD sessions
1021 Show RIP interface
1022 Show RIP neighbors
8000 Reply too long
8001 Route not found
8002 Configuration file error
8003 No protocols match
8004 Stopped due to reconfiguration
8005 Protocol is down => cannot dump
8006 Reload failed
8007 Access denied
8008 Evaluation runtime error
9000 Command too long
9001 Parse error
9002 Invalid symbol type

View File

@ -27,7 +27,8 @@
* white space character. * white space character.
* *
* Reply codes starting with 0 stand for `action successfully completed' messages, * 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 * Each CLI session is internally represented by a &cli structure and a
* resource pool containing all resources associated with the connection, * resource pool containing all resources associated with the connection,
@ -196,13 +197,6 @@ cli_copy_message(cli *c)
q[-1] = '\n'; q[-1] = '\n';
} }
static void
cli_hello(cli *c)
{
cli_printf(c, 1, "BIRD " BIRD_VERSION " ready.");
c->cont = NULL;
}
static void static void
cli_free_out(cli *c) cli_free_out(cli *c)
{ {
@ -228,7 +222,7 @@ cli_written(cli *c)
ev_schedule(c->event); ev_schedule(c->event);
} }
/* cli read hooks variables */
static byte *cli_rh_pos; static byte *cli_rh_pos;
static uint cli_rh_len; static uint cli_rh_len;
static int cli_rh_trick_flag; static int cli_rh_trick_flag;
@ -312,7 +306,7 @@ cli_new(void *priv)
c->event = ev_new(p); c->event = ev_new(p);
c->event->hook = cli_event; c->event->hook = cli_event;
c->event->data = c; c->event->data = c;
c->cont = cli_hello; c->cont = NULL;
c->parser_pool = lp_new(c->pool, 4096); c->parser_pool = lp_new(c->pool, 4096);
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE); c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
ev_schedule(c->event); ev_schedule(c->event);

View File

@ -15,6 +15,8 @@
#include "lib/string.h" #include "lib/string.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "filter/filter.h" #include "filter/filter.h"
#include "client/reply_codes.h"
#include "nest/iface.h"
extern int shutting_down; extern int shutting_down;
extern int configuring; extern int configuring;
@ -64,6 +66,50 @@ 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;
}
/*
* Send all symbols for autocomplete interactive Bird command line
*/
void
cmd_send_symbols(void)
{
int code, pos = 0;
struct symbol *sym = NULL;
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)
cli_msg(RC_INTERFACE_NAME, "\"%s\"", i->name);
cli_msg(0, "");
}
static void static void
print_size(char *dsc, size_t val) print_size(char *dsc, size_t val)
{ {

View File

@ -15,5 +15,6 @@ struct f_inst;
void cmd_show_status(void); void cmd_show_status(void);
void cmd_show_symbols(struct sym_show_data *sym); void cmd_show_symbols(struct sym_show_data *sym);
void cmd_send_symbols(void);
void cmd_show_memory(void); void cmd_show_memory(void);
void cmd_eval(struct f_inst *expr); void cmd_eval(struct f_inst *expr);

View File

@ -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(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_KEYWORDS(REFRESH)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@ -468,10 +469,10 @@ CF_CLI(SHOW STATUS,,, [[Show router status]])
CF_CLI(SHOW MEMORY,,, [[Show memory usage]]) CF_CLI(SHOW MEMORY,,, [[Show memory usage]])
{ cmd_show_memory(); } ; { 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); } ; { 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); } ; { proto_apply_cmd($4, proto_cmd_show, 0, 1); } ;
optsym: optsym:
@ -489,6 +490,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]]) 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); } ; { 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: r_args:
/* empty */ { /* empty */ {
$$ = cfg_allocz(sizeof(struct rt_show_data)); $$ = cfg_allocz(sizeof(struct rt_show_data));
@ -568,9 +583,13 @@ export_mode:
CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]]) 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); } ; { 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: sym_args:
/* empty */ { /* empty */ {
$$ = cfg_allocz(sizeof(struct sym_show_data)); $$ = cfg_allocz(sizeof(struct sym_show_data));
@ -602,11 +621,11 @@ CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]]) CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ; { 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); } ; { cmd_eval($2); } ;
CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]]) 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_set_log_echo(this_cli, $2, $3);
cli_msg(0, ""); cli_msg(0, "");
} ; } ;
@ -625,25 +644,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); } ; { 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); } ; { 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); } ; { 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); } ; { 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); } ; { 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); } ; { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]]) 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); } ; { proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]]) 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); } ; { proto_apply_cmd($2, proto_cmd_mrtdump, 1, $3); } ;
CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]]) CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])

View File

@ -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_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_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)); }; { 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); }; { 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); }; { ospf_sh_iface(proto_get_named($4, &proto_ospf), $5); };
CF_CLI_HELP(SHOW OSPF TOPOLOGY, [all] [<name>], [[Show information about OSPF network topology]]) 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]]) 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); }; { 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); }; { 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_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); }; { 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); }; { 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_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(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); }; { 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: lsadb_args:
/* empty */ { /* empty */ {

View File

@ -1564,7 +1564,7 @@ sk_sendmsg(sock *s)
{ {
struct iovec iov = {s->tbuf, s->tpos - s->tbuf}; struct iovec iov = {s->tbuf, s->tpos - s->tbuf};
byte cmsg_buf[CMSG_TX_SPACE]; byte cmsg_buf[CMSG_TX_SPACE];
bzero(cmsg_buf, sizeof(cmsg_buf)); memset(cmsg_buf, 0, sizeof(cmsg_buf));
sockaddr dst = {}; sockaddr dst = {};
sockaddr_fill(&dst, fam_to_af[s->fam], s->daddr, s->iface, s->dport); sockaddr_fill(&dst, fam_to_af[s->fam], s->daddr, s->iface, s->dport);