<tag><label id="cli-debug">debug <m/protocol/|<m/pattern/|all all|off|{ states|routes|filters|events|packets [, <m/.../] }</tag>
Control protocol debugging.
- <tag><label id="cli-dump">dump resources|sockets|interfaces|neighbors|attributes|routes|protocols</tag>
- Dump contents of internal data structures to the debugging output.
+ <tag><label id="cli-dump">dump resources|sockets|interfaces|neighbors|attributes|routes|protocols "<m/file/"</tag>
+ Truncates the given file and dumps contents of internal data structures
+ there. By sending SIGUSR1, you get all of these concatenated to
+ <cf/bird.dump/ in the current directory. The file is only readable for
+ the user running the daemon. The format of dump files is internal and
+ could change in the future.
<tag><label id="cli-echo">echo all|off|{ <m/list of log classes/ } [ <m/buffer-size/ ]</tag>
Control echoing of log messages to the command-line output.
8006 Reload failed
8007 Access denied
8008 Evaluation runtime error
+8009 Failed to open file
9000 Command too long
9001 Parse error
]])
m4_ifelse($4,,,[[
FID_DUMP_BODY()m4_dnl
-debug("%s" $4 "\n", INDENT, $5);
+RDUMP("%s" $4 "\n", INDENT, $5);
]])
FID_INTERPRET_EXEC()m4_dnl
const $1 $2 = whati->$2
pos = linearize(dest, whati->fvar, pos);
item->varcount = whati->varcount;
FID_DUMP_BODY()m4_dnl
- debug("%snumber of varargs %u\n", INDENT, item->varcount);
+ RDUMP("%snumber of varargs %u\n", INDENT, item->varcount);
FID_SAME_BODY()m4_dnl
if (f1->varcount != f2->varcount) return 0;
FID_INTERPRET_BODY()
args = NULL; /* The rest is the line itself */
FID_METHOD_CALL() , arg$1
FID_DUMP_BODY()m4_dnl
-f_dump_line(item->fl$1, indent + 1);
+f_dump_line(dreq, item->fl$1, indent + 1);
FID_LINEARIZE_BODY()m4_dnl
item->fl$1 = f_linearize(whati->f$1, $2);
FID_SAME_BODY()m4_dnl
]])m4_dnl
-FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug)
-case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break;
+FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping
+case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(dreq, item, indent + 1); break;
FID_DUMP()m4_dnl The dumper itself
m4_ifdef([[FID_DUMP_BODY_EXISTS]],
-[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]],
-[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]])
+[[static inline void f_dump_line_item_]]INST_NAME()[[(struct dump_request *dreq, const struct f_line_item *item_, const int indent)]],
+[[static inline void f_dump_line_item_]]INST_NAME()[[(struct dump_request *dreq UNUSED, const struct f_line_item *item UNUSED, const int indent UNUSED)]])
m4_undefine([[FID_DUMP_BODY_EXISTS]])
{
#define item (&(item_->i_]]INST_NAME()[[))
FID_WR_PUT(6)
-void f_dump_line(const struct f_line *dest, uint indent)
+void f_dump_line(struct dump_request *dreq, const struct f_line *dest, uint indent)
{
if (!dest) {
- debug("%sNo filter line (NULL)\n", INDENT);
+ RDUMP("%sNo filter line (NULL)\n", INDENT);
return;
}
- debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len);
+ RDUMP("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len);
for (uint i=0; i<dest->len; i++) {
const struct f_line_item *item = &dest->items[i];
- debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name_(item->fi_code), item->lineno);
+ RDUMP("%sInstruction %s at line %u\n", INDENT, f_instruction_name_(item->fi_code), item->lineno);
switch (item->fi_code) {
FID_WR_PUT(7)
default: bug("Unknown instruction %x in f_dump_line", item->fi_code);
}
}
- debug("%sFilter line %p dump done\n", INDENT, dest);
+ RDUMP("%sFilter line %p dump done\n", INDENT, dest);
}
/* Linearize */
out->len = linearize(out, inst[i], out->len);
out->results = results;
-
-#ifdef LOCAL_DEBUG
- f_dump_line(out, 0);
-#endif
return out;
}
* m4_dnl This structure is returned from the linearizer (105).
* m4_dnl For writing directly to this structure, use FID_LINE_IN.
*
- * m4_dnl f_dump_line_item_FI_EXAMPLE(const struct f_line_item *item, const int indent)
+ * m4_dnl f_dump_line_item_FI_EXAMPLE(struct dump_request *dreq, const struct f_line_item *item, const int indent)
* m4_dnl {
* m4_dnl (104) [[ put it here ]]
* m4_dnl }
- * m4_dnl This code dumps the instruction on debug. Note that the argument
+ * m4_dnl This code dumps the instruction via RDUMP. Note that the argument
* m4_dnl is the linearized instruction; if the instruction has arguments,
* m4_dnl their code has already been linearized and their value is taken
* m4_dnl from the value stack.
static inline struct f_line *f_linearize(const struct f_inst *root, uint results)
{ return f_linearize_concat(&root, 1, results); }
-void f_dump_line(const struct f_line *, uint indent);
+void f_dump_line(struct dump_request *, const struct f_line *, uint indent);
/* Recursive iteration over filter instructions */
}
static void
-ca_dump(resource *r)
+ca_dump(struct dump_request *dreq, resource *r)
{
struct custom_attribute *ca = (void *) r;
- debug("name \"%s\" id 0x%04x ea_type 0x%02x f_type 0x%02x\n",
+ RDUMP("name \"%s\" id 0x%04x ea_type 0x%02x f_type 0x%02x\n",
ca->name, ca->fda->ea_code, ca->fda->type, ca->fda->f_type);
}
#define curline fstk->estk[fstk->ecnt-1]
#define prevline fstk->estk[fstk->ecnt-2]
-#ifdef LOCAL_DEBUG
- debug("Interpreting line.");
- f_dump_line(line, 1);
-#endif
-
while (fstk->ecnt > 0) {
while (curline.pos < curline.line->len) {
const struct f_line_item *what = &(curline.line->items[curline.pos++]);
}
}
-void filters_dump_all(void)
+void filters_dump_all(struct dump_request *dreq)
{
struct symbol *sym;
WALK_LIST(sym, config->symbols) {
switch (sym->class) {
case SYM_FILTER:
- debug("Named filter %s:\n", sym->name);
- f_dump_line(sym->filter->root, 1);
+ RDUMP("Named filter %s:\n", sym->name);
+ f_dump_line(dreq, sym->filter->root, 1);
break;
case SYM_FUNCTION:
- debug("Function %s:\n", sym->name);
- f_dump_line(sym->function, 1);
+ RDUMP("Function %s:\n", sym->name);
+ f_dump_line(dreq, sym->function, 1);
break;
case SYM_PROTO:
{
- debug("Protocol %s:\n", sym->name);
+ RDUMP("Protocol %s:\n", sym->name);
struct channel *c;
WALK_LIST(c, sym->proto->proto->channels) {
- debug(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]);
+ RDUMP(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]);
if (c->in_filter == FILTER_ACCEPT)
- debug(" ALL\n");
+ RDUMP(" ALL\n");
else if (c->in_filter == FILTER_REJECT)
- debug(" NONE\n");
+ RDUMP(" NONE\n");
else if (c->in_filter == FILTER_UNDEF)
- debug(" UNDEF\n");
+ RDUMP(" UNDEF\n");
else if (c->in_filter->sym) {
ASSERT(c->in_filter->sym->filter == c->in_filter);
- debug(" named filter %s\n", c->in_filter->sym->name);
+ RDUMP(" named filter %s\n", c->in_filter->sym->name);
} else {
- debug("\n");
- f_dump_line(c->in_filter->root, 2);
+ RDUMP("\n");
+ f_dump_line(dreq, c->in_filter->root, 2);
}
}
}
void filter_commit(struct config *new, struct config *old);
-void filters_dump_all(void);
+void filters_dump_all(struct dump_request *);
#define FILTER_ACCEPT NULL
#define FILTER_REJECT ((struct filter *) 1)
static inline u32 u64_hash(u64 v)
{ return hash_value(u64_hash0(v, HASH_PARAM, 0)); }
+/* Dumping */
+struct dump_request {
+ u64 size;
+ btime begin;
+ uint indent, offset;
+ void (*write)(struct dump_request *, const char *fmt, ...);
+ void (*report)(struct dump_request *, int state, const char *fmt, ...);
+};
+
+#define RDUMP(...) dreq->write(dreq, __VA_ARGS__)
+
#endif
}
static void
-ev_dump(resource *r)
+ev_dump(struct dump_request *dreq, resource *r)
{
event *e = (event *) r;
- debug("(code %p, data %p, %s)\n",
+ RDUMP("(code %p, data %p, %s)\n",
e->hook,
e->data,
e->n.next ? "scheduled" : "inactive");
_Thread_local linpool *tmp_linpool;
static void lp_free(resource *);
-static void lp_dump(resource *);
+static void lp_dump(struct dump_request *, resource *);
static resource *lp_lookup(resource *, unsigned long);
static struct resmem lp_memsize(resource *r);
}
static void
-lp_dump(resource *r)
+lp_dump(struct dump_request *dreq, resource *r)
{
linpool *m = (linpool *) r;
struct lp_chunk *c;
;
for(cntl=0, c=m->first_large; c; c=c->next, cntl++)
;
- debug("(count=%d+%d total=%d+%d)\n",
+ RDUMP("(count=%d+%d total=%d+%d)\n",
cnt,
cntl,
m->total,
const char *name;
};
-static void pool_dump(resource *);
+static void pool_dump(struct dump_request *, resource *);
static void pool_free(resource *);
static resource *pool_lookup(resource *, unsigned long);
static struct resmem pool_memsize(resource *P);
pool root_pool;
-static int indent;
-
/**
* rp_new - create a resource pool
* @p: parent pool
}
static void
-pool_dump(resource *P)
+pool_dump(struct dump_request *dreq, resource *P)
{
pool *p = (pool *) P;
resource *r;
- debug("%s\n", p->name);
- indent += 3;
+ RDUMP("%s\n", p->name);
+ dreq->indent += 3;
WALK_LIST(r, p->inside)
- rdump(r);
- indent -= 3;
+ rdump(dreq, r);
+ dreq->indent -= 3;
}
static struct resmem
* It works by calling a class-specific dump function.
*/
void
-rdump(void *res)
+rdump(struct dump_request *dreq, void *res)
{
char x[16];
resource *r = res;
- bsprintf(x, "%%%ds%%p ", indent);
- debug(x, "", r);
+ bsprintf(x, "%%%ds%%p ", dreq->indent);
+ RDUMP(x, "", r);
if (r)
{
- debug("%s ", r->class->name);
- r->class->dump(r);
+ RDUMP("%s ", r->class->name);
+ r->class->dump(dreq, r);
}
else
- debug("NULL\n");
+ RDUMP("NULL\n");
+}
+
+void page_dump(struct dump_request *req);
+
+void resource_dump(struct dump_request *req)
+{
+ rdump(req, &root_pool);
+ page_dump(req);
}
struct resmem
return r;
}
+#if 0
/**
* rlookup - look up a memory location
* @a: memory address
else
debug("Not found.\n");
}
+#endif
/**
* resource_init - initialize the resource manager
{
}
-static void mbl_debug(resource *r)
+static void mbl_debug(struct dump_request *dreq, resource *r)
{
struct mblock *m = (struct mblock *) r;
- debug("(size=%d)\n", m->size);
+ RDUMP("(size=%d)\n", m->size);
}
static resource *
char *name; /* Resource class name */
unsigned size; /* Standard size of single resource */
void (*free)(resource *); /* Freeing function */
- void (*dump)(resource *); /* Dump to debug output */
+ void (*dump)(struct dump_request *, resource *); /* Dump to debug output */
resource *(*lookup)(resource *, unsigned long); /* Look up address (only for debugging) */
struct resmem (*memsize)(resource *); /* Return size of memory used by the resource, may be NULL */
};
pool *rp_new(pool *, const char *); /* Create new pool */
pool *rp_newf(pool *, const char *, ...); /* Create a new pool with a formatted string as its name */
void rfree(void *); /* Free single resource */
-void rdump(void *); /* Dump to debug output */
+
+struct dump_request;
+void rdump(struct dump_request *, void *); /* Dump to debug output */
+void resource_dump(struct dump_request *); /* Dump the root pool */
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
void rlookup(unsigned long); /* Look up address (only for debugging) */
void rmove(void *, pool *); /* Move to a different pool */
#endif
static void slab_free(resource *r);
-static void slab_dump(resource *r);
+static void slab_dump(struct dump_request *dreq, resource *r);
static resource *slab_lookup(resource *r, unsigned long addr);
static struct resmem slab_memsize(resource *r);
}
static void
-slab_dump(resource *r)
+slab_dump(struct dump_request *dreq, resource *r)
{
slab *s = (slab *) r;
int ec=0, pc=0, fc=0;
pc++;
WALK_TLIST(sl_head, h, &s->full_heads)
fc++;
- debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
+ RDUMP("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
}
static struct resmem
void sk_set_rbsize(sock *s, uint val); /* Resize RX buffer */
void sk_set_tbsize(sock *s, uint val); /* Resize TX buffer, keeping content */
void sk_set_tbuf(sock *s, void *tbuf); /* Switch TX buffer, NULL-> return to internal */
-void sk_dump_all(void);
+void sk_dump_all(struct dump_request *);
int sk_is_ipv4(sock *s); /* True if socket is IPv4 */
int sk_is_ipv6(sock *s); /* True if socket is IPv6 */
}
static void
-tm_dump(resource *r)
+tm_dump(struct dump_request *dreq, resource *r)
{
timer *t = (void *) r;
- debug("(code %p, data %p, ", t->hook, t->data);
+ RDUMP("(code %p, data %p, ", t->hook, t->data);
if (t->randomize)
- debug("rand %d, ", t->randomize);
+ RDUMP("rand %d, ", t->randomize);
if (t->recurrent)
- debug("recur %ld, ", t->recurrent);
+ RDUMP("recur %ld, ", t->recurrent);
if (t->expires)
- debug("expires in %ld ms)\n", (t->expires - current_time()) TO_MS);
+ RDUMP("expires in %ld ms)\n", (t->expires - current_time()) TO_MS);
else
- debug("inactive)\n");
+ RDUMP("inactive)\n");
}
| sym_args CF_SYM_KNOWN { $$ = $1; $$->sym = $2; }
;
-
-CF_CLI_HELP(DUMP, ..., [[Dump debugging information]])
-CF_CLI(DUMP RESOURCES,,, [[Dump all allocated resource]])
-{ rdump(&root_pool); cli_msg(0, ""); } ;
-CF_CLI(DUMP SOCKETS,,, [[Dump open sockets]])
-{ sk_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP EVENTS,,, [[Dump event log]])
-{ io_log_dump(); cli_msg(0, ""); } ;
-CF_CLI(DUMP INTERFACES,,, [[Dump interface information]])
-{ if_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP NEIGHBORS,,, [[Dump neighbor cache]])
-{ neigh_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP ATTRIBUTES,,, [[Dump attribute cache]])
-{ rta_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
-{ rt_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
-{ protos_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
-{ filters_dump_all(); cli_msg(0, ""); } ;
+CF_CLI_HELP(DUMP, ..., [[Dump debugging information to the given file]])
+CF_CLI(DUMP RESOURCES, text,, [[Dump all allocated resource]])
+{ cmd_dump_file(this_cli, $3, "resources", resource_dump); } ;
+CF_CLI(DUMP SOCKETS, text,, [[Dump open sockets]])
+{ cmd_dump_file(this_cli, $3, "sockets", sk_dump_all); } ;
+CF_CLI(DUMP EVENTS, text,, [[Dump event log]])
+{ cmd_dump_file(this_cli, $3, "event log", io_log_dump); } ;
+CF_CLI(DUMP INTERFACES, text,, [[Dump interface information]])
+{ cmd_dump_file(this_cli, $3, "interfaces", if_dump_all); } ;
+CF_CLI(DUMP NEIGHBORS, text,, [[Dump neighbor cache]])
+{ cmd_dump_file(this_cli, $3, "neighbor cache", neigh_dump_all); } ;
+CF_CLI(DUMP ATTRIBUTES, text,, [[Dump attribute cache]])
+{ cmd_dump_file(this_cli, $3, "attribute cache", rta_dump_all); } ;
+CF_CLI(DUMP ROUTES, text,, [[Dump routing table]])
+{ cmd_dump_file(this_cli, $3, "routing tables", rt_dump_all); } ;
+CF_CLI(DUMP PROTOCOLS, text,, [[Dump protocol information]])
+{ cmd_dump_file(this_cli, $3, "protocols", protos_dump_all); } ;
+CF_CLI(DUMP FILTER ALL, text,, [[Dump all filters in linearized form]])
+{ cmd_dump_file(this_cli, $4, "filter bytecode", filters_dump_all); } ;
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
{ cmd_eval(f_linearize($2, 1)); } ;
* This function dumps contents of an &ifa to the debug output.
*/
void
-ifa_dump(struct ifa *a)
+ifa_dump(struct dump_request *dreq, struct ifa *a)
{
- debug("\t%I, net %N bc %I -> %I%s%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite,
+ RDUMP("\t%I, net %N bc %I -> %I%s%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite,
(a->flags & IA_PRIMARY) ? " PRIMARY" : "",
(a->flags & IA_SECONDARY) ? " SEC" : "",
(a->flags & IA_HOST) ? " HOST" : "",
* network interface to the debug output.
*/
void
-if_dump(struct iface *i)
+if_dump(struct dump_request *dreq, struct iface *i)
{
struct ifa *a;
- debug("IF%d: %s", i->index, i->name);
+ RDUMP("IF%d: %s", i->index, i->name);
if (i->flags & IF_SHUTDOWN)
- debug(" SHUTDOWN");
+ RDUMP(" SHUTDOWN");
if (i->flags & IF_UP)
- debug(" UP");
+ RDUMP(" UP");
else
- debug(" DOWN");
+ RDUMP(" DOWN");
if (i->flags & IF_ADMIN_UP)
- debug(" LINK-UP");
+ RDUMP(" LINK-UP");
if (i->flags & IF_MULTIACCESS)
- debug(" MA");
+ RDUMP(" MA");
if (i->flags & IF_BROADCAST)
- debug(" BC");
+ RDUMP(" BC");
if (i->flags & IF_MULTICAST)
- debug(" MC");
+ RDUMP(" MC");
if (i->flags & IF_LOOPBACK)
- debug(" LOOP");
+ RDUMP(" LOOP");
if (i->flags & IF_IGNORE)
- debug(" IGN");
+ RDUMP(" IGN");
if (i->flags & IF_TMP_DOWN)
- debug(" TDOWN");
- debug(" MTU=%d\n", i->mtu);
+ RDUMP(" TDOWN");
+ RDUMP(" MTU=%d\n", i->mtu);
WALK_LIST(a, i->addrs)
{
- ifa_dump(a);
+ ifa_dump(dreq, a);
ASSERT(!!(a->flags & IA_PRIMARY) ==
((a == i->addr4) || (a == i->addr6) || (a == i->llv6)));
}
* interfaces to the debug output.
*/
void
-if_dump_all(void)
+if_dump_all(struct dump_request *dreq)
{
struct iface *i;
- debug("Known network interfaces:\n");
+ RDUMP("Known network interfaces:\n");
WALK_LIST(i, iface_list)
- if_dump(i);
- debug("Router ID: %08x\n", config->router_id);
+ if_dump(dreq, i);
+ RDUMP("Router ID: %08x\n", config->router_id);
}
static inline unsigned
#define IF_CHANGE_PREFERRED (IF_CHANGE_ADDR4 | IF_CHANGE_ADDR6 | IF_CHANGE_LLV6)
void if_init(void);
-void if_dump(struct iface *);
-void if_dump_all(void);
-void ifa_dump(struct ifa *);
+void if_dump(struct dump_request *dreq, struct iface *);
+void if_dump_all(struct dump_request *);
+void ifa_dump(struct dump_request *dreq, struct ifa *);
void if_show(void);
void if_show_summary(void);
struct iface *if_update(struct iface *);
neighbor *neigh_find(struct proto *p, ip_addr a, struct iface *ifa, uint flags);
-void neigh_dump(neighbor *);
-void neigh_dump_all(void);
+void neigh_dump(struct dump_request *, neighbor *);
+void neigh_dump_all(struct dump_request *);
void neigh_prune(void);
void neigh_if_up(struct iface *);
void neigh_if_down(struct iface *);
}
static void
-olock_dump(resource *r)
+olock_dump(struct dump_request *dreq, resource *r)
{
struct object_lock *l = (struct object_lock *) r;
static char *olock_states[] = { "free", "locked", "waiting", "event" };
- debug("(%d:%s:%I:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->addr_local, l->port, l->inst, olock_states[l->state]);
+ RDUMP("(%d:%s:%I:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->addr_local, l->port, l->inst, olock_states[l->state]);
if (!EMPTY_LIST(l->waiters))
- debug(" [wanted]\n");
+ RDUMP(" [wanted]\n");
}
static struct resclass olock_class = {
* This functions dumps the contents of a given neighbor entry to debug output.
*/
void
-neigh_dump(neighbor *n)
+neigh_dump(struct dump_request *dreq, neighbor *n)
{
- debug("%p %I %s %s ", n, n->addr,
+ RDUMP("%p %I %s %s ", n, n->addr,
n->iface ? n->iface->name : "[]",
n->ifreq ? n->ifreq->name : "[]");
- debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
+ RDUMP("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
if (n->flags & NEF_STICKY)
- debug(" STICKY");
+ RDUMP(" STICKY");
if (n->flags & NEF_ONLINK)
- debug(" ONLINK");
- debug("\n");
+ RDUMP(" ONLINK");
+ RDUMP("\n");
}
/**
* This function dumps the contents of the neighbor cache to debug output.
*/
void
-neigh_dump_all(void)
+neigh_dump_all(struct dump_request *dreq)
{
neighbor *n;
int i;
- debug("Known neighbors:\n");
+ RDUMP("Known neighbors:\n");
for(i=0; i<NEIGH_HASH_SIZE; i++)
WALK_LIST(n, neigh_hash_table[i])
- neigh_dump(n);
- debug("\n");
+ neigh_dump(dreq, n);
+ RDUMP("\n");
}
static inline void
* the internals.
*/
void
-protos_dump_all(void)
+protos_dump_all(struct dump_request *dreq)
{
- debug("Protocols:\n");
+ RDUMP("Protocols:\n");
struct proto *p;
WALK_LIST(p, proto_list)
{
- debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]);
+ RDUMP(" protocol %s state %s\n", p->name, p_states[p->proto_state]);
struct channel *c;
WALK_LIST(c, p->channels)
{
- debug("\tTABLE %s\n", c->table->name);
+ RDUMP("\tTABLE %s\n", c->table->name);
if (c->in_filter)
- debug("\tInput filter: %s\n", filter_name(c->in_filter));
+ RDUMP("\tInput filter: %s\n", filter_name(c->in_filter));
if (c->out_filter)
- debug("\tOutput filter: %s\n", filter_name(c->out_filter));
+ RDUMP("\tOutput filter: %s\n", filter_name(c->out_filter));
}
if (p->proto->dump && (p->proto_state != PS_DOWN))
- p->proto->dump(p);
+ p->proto->dump(p, dreq);
}
}
void (*postconfig)(struct proto_config *); /* After configuring each instance */
struct proto * (*init)(struct proto_config *); /* Create new instance */
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
- void (*dump)(struct proto *); /* Debugging dump */
+ void (*dump)(struct proto *, struct dump_request *); /* Debugging dump */
int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop the instance */
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
void protos_preconfig(struct config *);
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
struct proto * proto_spawn(struct proto_config *cf, uint disabled);
-void protos_dump_all(void);
+void protos_dump_all(struct dump_request *);
#define GA_UNKNOWN 0 /* Attribute not recognized */
#define GA_NAME 1 /* Result = name */
void rt_refresh_end(rtable *t, struct channel *c);
void rt_modify_stale(rtable *t, struct channel *c);
void rt_schedule_prune(rtable *t);
-void rte_dump(rte *);
+void rte_dump(struct dump_request *, rte *);
void rte_free(rte *);
rte *rte_do_cow(rte *);
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
rte *rte_cow_rta(rte *r, linpool *lp);
-void rt_dump(rtable *);
-void rt_dump_all(void);
+void rt_dump(struct dump_request *, rtable *);
+void rt_dump_all(struct dump_request *);
int rt_feed_channel(struct channel *c);
void rt_feed_channel_abort(struct channel *c);
int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
uintptr_t ea_get_int(ea_list *, unsigned ea, uintptr_t def);
-void ea_dump(ea_list *);
+void ea_dump(struct dump_request *, ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */
unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */
void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffer */
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }
rta *rta_do_cow(rta *o, linpool *lp);
static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; }
-void rta_dump(rta *);
-void rta_dump_all(void);
+void rta_dump(struct dump_request *, rta *);
+void rta_dump_all(struct dump_request *);
void rta_show(struct cli *, rta *);
u32 rt_get_igp_metric(rte *rt);
* the debug output.
*/
void
-ea_dump(ea_list *e)
+ea_dump(struct dump_request *dreq, ea_list *e)
{
int i;
if (!e)
{
- debug("NONE");
+ RDUMP("NONE");
return;
}
while (e)
{
- debug("[%c%c%c]",
+ RDUMP("[%c%c%c]",
(e->flags & EALF_SORTED) ? 'S' : 's',
(e->flags & EALF_BISECT) ? 'B' : 'b',
(e->flags & EALF_CACHED) ? 'C' : 'c');
for(i=0; i<e->count; i++)
{
eattr *a = &e->attrs[i];
- debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
- debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
+ RDUMP(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
+ RDUMP("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
if (a->originated)
- debug("o");
+ RDUMP("o");
if (a->type & EAF_EMBEDDED)
- debug(":%08x", a->u.data);
+ RDUMP(":%08x", a->u.data);
else
{
int j, len = a->u.ptr->length;
- debug("[%d]:", len);
+ RDUMP("[%d]:", len);
for(j=0; j<len; j++)
- debug("%02x", a->u.ptr->data[j]);
+ RDUMP("%02x", a->u.ptr->data[j]);
}
}
if (e = e->next)
- debug(" | ");
+ RDUMP(" | ");
}
}
* This function takes a &rta and dumps its contents to the debug output.
*/
void
-rta_dump(rta *a)
+rta_dump(struct dump_request *dreq, rta *a)
{
static char *rts[] = { "", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_RPKI", "RTS_PERF", "RTS_AGGREGATED", };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
- debug("pref=%d uc=%d %s %s%s h=%04x",
+ RDUMP("pref=%d uc=%d %s %s%s h=%04x",
a->pref, a->uc, rts[a->source], ip_scope_text(a->scope),
rtd[a->dest], a->hash_key);
if (!a->cached)
- debug(" !CACHED");
- debug(" <-%I", a->from);
+ RDUMP(" !CACHED");
+ RDUMP(" <-%I", a->from);
if (a->dest == RTD_UNICAST)
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
{
- if (ipa_nonzero(nh->gw)) debug(" ->%I", nh->gw);
- if (nh->labels) debug(" L %d", nh->label[0]);
+ if (ipa_nonzero(nh->gw)) RDUMP(" ->%I", nh->gw);
+ if (nh->labels) RDUMP(" L %d", nh->label[0]);
for (int i=1; i<nh->labels; i++)
- debug("/%d", nh->label[i]);
- debug(" [%s]", nh->iface ? nh->iface->name : "???");
+ RDUMP("/%d", nh->label[i]);
+ RDUMP(" [%s]", nh->iface ? nh->iface->name : "???");
}
if (a->eattrs)
{
- debug(" EA: ");
- ea_dump(a->eattrs);
+ RDUMP(" EA: ");
+ ea_dump(dreq, a->eattrs);
}
}
* to the debug output.
*/
void
-rta_dump_all(void)
+rta_dump_all(struct dump_request *dreq)
{
rta *a;
uint h;
- debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit);
+ RDUMP("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit);
for(h=0; h<rta_cache_size; h++)
for(a=rta_hash_table[h]; a; a=a->next)
{
- debug("%p ", a);
- rta_dump(a);
- debug("\n");
+ RDUMP("%p ", a);
+ rta_dump(dreq, a);
+ RDUMP("\n");
}
- debug("\n");
+ RDUMP("\n");
}
void
* This functions dumps contents of a &rte to debug output.
*/
void
-rte_dump(rte *e)
+rte_dump(struct dump_request *dreq, rte *e)
{
net *n = e->net;
- debug("%-1N ", n->n.addr);
- debug("PF=%02x ", e->pflags);
- rta_dump(e->attrs);
- debug("\n");
+ RDUMP("%-1N ", n->n.addr);
+ RDUMP("PF=%02x ", e->pflags);
+ rta_dump(dreq, e->attrs);
+ RDUMP("\n");
}
/**
* This function dumps contents of a given routing table to debug output.
*/
void
-rt_dump(rtable *t)
+rt_dump(struct dump_request *dreq, rtable *t)
{
- debug("Dump of routing table <%s>\n", t->name);
+ RDUMP("Dump of routing table <%s>\n", t->name);
#ifdef DEBUGGING
fib_check(&t->fib);
#endif
{
rte *e;
for(e=n->routes; e; e=e->next)
- rte_dump(e);
+ rte_dump(dreq, e);
}
FIB_WALK_END;
- debug("\n");
+ RDUMP("\n");
}
/**
* This function dumps contents of all routing tables to debug output.
*/
void
-rt_dump_all(void)
+rt_dump_all(struct dump_request *dreq)
{
rtable *t;
node *n;
WALK_LIST2(t, n, routing_tables, n)
- rt_dump(t);
+ rt_dump(dreq, t);
}
static inline void
}
static void
-rt_res_dump(resource *_r)
+rt_res_dump(struct dump_request *dreq, resource *_r)
{
rtable *r = (rtable *) _r;
- debug("name \"%s\", addr_type=%s, rt_count=%u, use_count=%d\n",
+ RDUMP("name \"%s\", addr_type=%s, rt_count=%u, use_count=%d\n",
r->name, net_label[r->addr_type], r->rt_count, r->use_count);
}
*/
static void
-babel_dump_source(struct babel_source *s)
+babel_dump_source(struct dump_request *dreq, struct babel_source *s)
{
- debug("Source router_id %lR seqno %d metric %d expires %t\n",
+ RDUMP("Source router_id %lR seqno %d metric %d expires %t\n",
s->router_id, s->seqno, s->metric,
s->expires ? s->expires - current_time() : 0);
}
static void
-babel_dump_route(struct babel_route *r)
+babel_dump_route(struct dump_request *dreq, struct babel_route *r)
{
- debug("Route neigh %I if %s seqno %d metric %d/%d router_id %lR expires %t\n",
+ RDUMP("Route neigh %I if %s seqno %d metric %d/%d router_id %lR expires %t\n",
r->neigh->addr, r->neigh->ifa->ifname, r->seqno, r->advert_metric, r->metric,
r->router_id, r->expires ? r->expires - current_time() : 0);
}
static void
-babel_dump_entry(struct babel_entry *e)
+babel_dump_entry(struct dump_request *dreq, struct babel_entry *e)
{
struct babel_source *s;
struct babel_route *r;
- debug("Babel: Entry %N:\n", e->n.addr);
+ RDUMP("Babel: Entry %N:\n", e->n.addr);
WALK_LIST(s,e->sources)
- { debug(" "); babel_dump_source(s); }
+ { RDUMP(" "); babel_dump_source(dreq, s); }
WALK_LIST(r,e->routes)
{
- debug(" ");
- if (r == e->selected) debug("*");
- babel_dump_route(r);
+ RDUMP(" ");
+ if (r == e->selected) RDUMP("*");
+ babel_dump_route(dreq, r);
}
}
static void
-babel_dump_neighbor(struct babel_neighbor *n)
+babel_dump_neighbor(struct dump_request *dreq, struct babel_neighbor *n)
{
- debug("Neighbor %I txcost %d hello_map %x next seqno %d expires %t/%t\n",
+ RDUMP("Neighbor %I txcost %d hello_map %x next seqno %d expires %t/%t\n",
n->addr, n->txcost, n->hello_map, n->next_hello_seqno,
n->hello_expiry ? n->hello_expiry - current_time() : 0,
n->ihu_expiry ? n->ihu_expiry - current_time() : 0);
}
static void
-babel_dump_iface(struct babel_iface *ifa)
+babel_dump_iface(struct dump_request *dreq, struct babel_iface *ifa)
{
struct babel_neighbor *n;
- debug("Babel: Interface %s addr %I rxcost %d type %d hello seqno %d intervals %t %t",
+ RDUMP("Babel: Interface %s addr %I rxcost %d type %d hello seqno %d intervals %t %t",
ifa->ifname, ifa->addr, ifa->cf->rxcost, ifa->cf->type, ifa->hello_seqno,
ifa->cf->hello_interval, ifa->cf->update_interval);
- debug(" next hop v4 %I next hop v6 %I\n", ifa->next_hop_ip4, ifa->next_hop_ip6);
+ RDUMP(" next hop v4 %I next hop v6 %I\n", ifa->next_hop_ip4, ifa->next_hop_ip6);
WALK_LIST(n, ifa->neigh_list)
- { debug(" "); babel_dump_neighbor(n); }
+ { RDUMP(" "); babel_dump_neighbor(dreq, n); }
}
static void
-babel_dump(struct proto *P)
+babel_dump(struct proto *P, struct dump_request *dreq)
{
struct babel_proto *p = (struct babel_proto *) P;
struct babel_iface *ifa;
- debug("Babel: router id %lR update seqno %d\n", p->router_id, p->update_seqno);
+ RDUMP("Babel: router id %lR update seqno %d\n", p->router_id, p->update_seqno);
WALK_LIST(ifa, p->interfaces)
- babel_dump_iface(ifa);
+ babel_dump_iface(dreq, ifa);
FIB_WALK(&p->ip4_rtable, struct babel_entry, e)
{
- babel_dump_entry(e);
+ babel_dump_entry(dreq, e);
}
FIB_WALK_END;
FIB_WALK(&p->ip6_rtable, struct babel_entry, e)
{
- babel_dump_entry(e);
+ babel_dump_entry(dreq, e);
}
FIB_WALK_END;
}
}
static void
-bfd_request_dump(resource *r)
+bfd_request_dump(struct dump_request *dreq, resource *r)
{
struct bfd_request *req = (struct bfd_request *) r;
- debug("(code %p, data %p)\n", req->hook, req->data);
+ RDUMP("(code %p, data %p)\n", req->hook, req->data);
}
static struct resclass bfd_request_class = {
bmp_tx_resource_free(resource *r UNUSED) {}
static void
-bmp_tx_resource_dump(resource *r UNUSED) {}
+bmp_tx_resource_dump(struct dump_request *dreq UNUSED, resource *r UNUSED) {}
static struct resmem
bmp_tx_resource_memsize(resource *r)
}
static void
-ospf_dump(struct proto *P)
+ospf_dump(struct proto *P, struct dump_request *dreq)
{
struct ospf_proto *p = (struct ospf_proto *) P;
struct ospf_iface *ifa;
struct ospf_neighbor *n;
- OSPF_TRACE(D_EVENTS, "Area number: %d", p->areano);
+ RDUMP("Area number: %d\n", p->areano);
WALK_LIST(ifa, p->iface_list)
{
- OSPF_TRACE(D_EVENTS, "Interface: %s", ifa->ifname);
- OSPF_TRACE(D_EVENTS, "state: %u", ifa->state);
- OSPF_TRACE(D_EVENTS, "DR: %R", ifa->drid);
- OSPF_TRACE(D_EVENTS, "BDR: %R", ifa->bdrid);
+ RDUMP("Interface: %s\n", ifa->ifname);
+ RDUMP("state: %u\n", ifa->state);
+ RDUMP("DR: %R\n", ifa->drid);
+ RDUMP("BDR: %R\n", ifa->bdrid);
WALK_LIST(n, ifa->neigh_list)
{
- OSPF_TRACE(D_EVENTS, " neighbor %R in state %u", n->rid, n->state);
+ RDUMP(" neighbor %R in state %u\n", n->rid, n->state);
}
}
/*
- OSPF_TRACE(D_EVENTS, "LSA graph dump start:");
+ RDUMP("LSA graph dump start:");
ospf_top_dump(p->gr, p);
- OSPF_TRACE(D_EVENTS, "LSA graph dump finished");
+ RDUMP("LSA graph dump finished");
*/
- neigh_dump_all();
}
static struct proto *
}
static void
-rip_dump(struct proto *P)
+rip_dump(struct proto *P, struct dump_request *dreq)
{
struct rip_proto *p = (struct rip_proto *) P;
struct rip_iface *ifa;
i = 0;
FIB_WALK(&p->rtable, struct rip_entry, en)
{
- debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %t\n",
+ RDUMP("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %t\n",
i++, en->n.addr, en->next_hop, en->iface ? en->iface->name : "(null)",
en->valid, en->metric, current_time() - en->changed);
for (struct rip_rte *e = en->routes; e; e = e->next)
- debug("RIP: via %I metric %d expires %t\n",
+ RDUMP("RIP: via %I metric %d expires %t\n",
e->next_hop, e->metric, e->expires - current_time());
}
FIB_WALK_END;
i = 0;
WALK_LIST(ifa, p->iface_list)
{
- debug("RIP: interface #%d: %s, %I, up = %d, busy = %d\n",
+ RDUMP("RIP: interface #%d: %s, %I, up = %d, busy = %d\n",
i++, ifa->iface->name, ifa->sk ? ifa->sk->daddr : IPA_NONE,
ifa->up, ifa->tx_active);
}
}
static void
-static_dump_rte(struct static_route *r)
+static_dump_rte(struct dump_request *dreq, struct static_route *r)
{
- debug("%-1N (%u): ", r->net, r->index);
+ RDUMP("%-1N (%u): ", r->net, r->index);
if (r->dest == RTD_UNICAST)
if (r->iface && ipa_zero(r->via))
- debug("dev %s\n", r->iface->name);
+ RDUMP("dev %s\n", r->iface->name);
else
- debug("via %I%J\n", r->via, r->iface);
+ RDUMP("via %I%J\n", r->via, r->iface);
else
- debug("rtd %d\n", r->dest);
+ RDUMP("rtd %d\n", r->dest);
}
static void
-static_dump(struct proto *P)
+static_dump(struct proto *P, struct dump_request *dreq)
{
struct static_config *c = (void *) P->cf;
struct static_route *r;
- debug("Static routes:\n");
+ RDUMP("Static routes:\n");
WALK_LIST(r, c->routes)
- static_dump_rte(r);
+ static_dump_rte(dreq, r);
}
#define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL )
}
#endif
+void
+page_dump(struct dump_request *dreq)
+{
+#ifdef HAVE_MMAP
+ RDUMP("Hot pages:\n");
+ node *n;
+ WALK_LIST(n, global_free_pages.pages)
+ RDUMP(" %p\n", n);
+
+ RDUMP("Cold pages:\n");
+ WALK_LIST(n, global_free_pages.empty)
+ {
+ struct empty_pages *ep = SKIP_BACK(struct empty_pages, n, n);
+ RDUMP(" %p (index)\n", ep);
+ for (uint i=0; i<ep->pos; i++)
+ RDUMP(" %p\n", ep->pages[i]);
+ }
+#endif
+}
+
void
resource_sys_init(void)
{
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <poll.h>
#include "lib/timer.h"
#include "lib/string.h"
#include "nest/iface.h"
+#include "nest/cli.h"
#include "conf/conf.h"
#include "sysdep/unix/unix.h"
}
static void
-rf_dump(resource *r)
+rf_dump(struct dump_request *dreq, resource *r)
{
struct rfile *a = (struct rfile *) r;
- debug("(FILE *%p)\n", a->f);
+ RDUMP("(FILE *%p)\n", a->f);
}
static struct resclass rf_class = {
return fileno(f->f);
}
+/*
+ * Dumping to files
+ */
+
+struct dump_request_file {
+ struct dump_request dr;
+ uint pos, max; int fd;
+ uint last_progress_info;
+ char data[0];
+};
+
+static void
+dump_to_file_flush(struct dump_request_file *req)
+{
+ if (req->fd < 0)
+ return;
+
+ for (uint sent = 0; sent < req->pos; )
+ {
+ int e = write(req->fd, &req->data[sent], req->pos - sent);
+ if (e <= 0)
+ {
+ req->dr.report(&req->dr, 8009, "Failed to write data: %m");
+ close(req->fd);
+ req->fd = -1;
+ return;
+ }
+ sent += e;
+ }
+
+ req->dr.size += req->pos;
+ req->pos = 0;
+
+ for (uint reported = 0; req->dr.size >> req->last_progress_info; req->last_progress_info++)
+ if (!reported++)
+ req->dr.report(&req->dr, -13, "... dumped %lu bytes in %t s",
+ req->dr.size, current_time_now() - req->dr.begin);
+}
+
+static void
+dump_to_file_write(struct dump_request *dr, const char *fmt, ...)
+{
+ struct dump_request_file *req = SKIP_BACK(struct dump_request_file, dr, dr);
+
+ for (uint phase = 0; (req->fd >= 0) && (phase < 2); phase++)
+ {
+ va_list args;
+ va_start(args, fmt);
+ int i = bvsnprintf(&req->data[req->pos], req->max - req->pos, fmt, args);
+ va_end(args);
+
+ if (i >= 0)
+ {
+ req->pos += i;
+ return;
+ }
+ else
+ dump_to_file_flush(req);
+ }
+
+ bug("Too long dump call");
+}
+
+struct dump_request *
+dump_to_file_init(off_t offset)
+{
+ ASSERT_DIE(offset + sizeof(struct dump_request_file) + 1024 < (unsigned long) page_size);
+
+ struct dump_request_file *req = alloc_page() + offset;
+ *req = (struct dump_request_file) {
+ .dr = {
+ .write = dump_to_file_write,
+ .begin = current_time_now(),
+ .offset = offset,
+ },
+ .max = page_size - offset - OFFSETOF(struct dump_request_file, data[0]),
+ .fd = -1,
+ };
+
+ return &req->dr;
+}
+
+void
+dump_to_file_run(struct dump_request *dr, const char *file, const char *what, void (*dump)(struct dump_request *))
+{
+ struct dump_request_file *req = SKIP_BACK(struct dump_request_file, dr, dr);
+ req->fd = open(file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR);
+
+ if (req->fd < 0)
+ {
+ dr->report(dr, 8009, "Failed to open file %s: %m", file);
+ goto cleanup;
+ }
+
+ dr->report(dr, -13, "Dumping %s to %s", what, file);
+
+ dump(dr);
+
+ if (req->fd >= 0)
+ {
+ dump_to_file_flush(req);
+ close(req->fd);
+ }
+
+ btime end = current_time_now();
+ dr->report(dr, 13, "Dumped %lu bytes in %t s", dr->size, end - dr->begin);
+
+cleanup:
+ free_page(((void *) req) - dr->offset);
+}
+
+struct dump_request_cli {
+ cli *cli;
+ struct dump_request dr;
+};
+
+static void
+cmd_dump_report(struct dump_request *dr, int state, const char *fmt, ...)
+{
+ struct dump_request_cli *req = SKIP_BACK(struct dump_request_cli, dr, dr);
+ va_list args;
+ va_start(args, fmt);
+ cli_vprintf(req->cli, state, fmt, args);
+ va_end(args);
+}
+
+void
+cmd_dump_file(struct cli *cli, const char *file, const char *what, void (*dump)(struct dump_request *))
+{
+ if (cli->restricted)
+ return cli_printf(cli, 8007, "Access denied");
+
+ struct dump_request_cli *req = SKIP_BACK(struct dump_request_cli, dr,
+ dump_to_file_init(OFFSETOF(struct dump_request_cli, dr)));
+
+ req->cli = cli;
+ req->dr.report = cmd_dump_report;
+
+ dump_to_file_run(&req->dr, file, what, dump);
+}
+
/*
* Time clock
}
static void
-sk_dump(resource *r)
+sk_dump(struct dump_request *dreq, resource *r)
{
sock *s = (sock *) r;
static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX", "SSH>", "SSH", "DEL!" };
- debug("(%s, ud=%p, sa=%I, sp=%d, da=%I, dp=%d, tos=%d, ttl=%d, if=%s)\n",
+ RDUMP("(%s, ud=%p, sa=%I, sp=%d, da=%I, dp=%d, tos=%d, ttl=%d, if=%s)\n",
sk_type_names[s->type],
s->data,
s->saddr,
}
void
-sk_dump_all(void)
+sk_dump_all(struct dump_request *dreq)
{
node *n;
sock *s;
- debug("Open sockets:\n");
+ RDUMP("Open sockets:\n");
WALK_LIST(n, sock_list)
{
s = SKIP_BACK(sock, n, n);
- debug("%p ", s);
- sk_dump(&s->r);
+ RDUMP("%p ", s);
+ sk_dump(dreq, &s->r);
}
- debug("\n");
+ RDUMP("\n");
}
}
void
-io_log_dump(void)
+io_log_dump(struct dump_request *dreq)
{
int i;
- log(L_DEBUG "Event log:");
+ RDUMP("Event log:");
for (i = 0; i < EVENT_LOG_LENGTH; i++)
{
struct event_log_entry *en = event_log + (event_log_pos + i) % EVENT_LOG_LENGTH;
if (en->hook)
- log(L_DEBUG " Event 0x%p 0x%p at %8d for %d ms", en->hook, en->data,
+ RDUMP(" Event 0x%p 0x%p at %8d for %d ms", en->hook, en->data,
(int) ((last_time - en->timestamp) TO_MS), (int) (en->duration TO_MS));
}
}
}
static void
-krt_dump(struct proto *P)
+krt_dump(struct proto *P, struct dump_request *dreq)
{
struct krt_proto *p = (struct krt_proto *) P;
if (!KRT_CF->learn)
return;
- debug("KRT: Table of inheritable routes\n");
- rt_dump(p->krt_table);
+ RDUMP("KRT: Table of inheritable routes\n");
+ rt_dump(dreq, p->krt_table);
}
#endif
* Debugging
*/
-void
-async_dump(void)
+static void
+async_dump_report(struct dump_request *dr UNUSED, int state, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vlog(((state > 1000) ? L_ERR : L_INFO)[0], fmt, args);
+ va_end(args);
+}
+
+static void
+async_dump_run(struct dump_request *dreq)
{
- debug("INTERNAL STATE DUMP\n\n");
+ RDUMP("ASYNC STATE DUMP\n");
- rdump(&root_pool);
- sk_dump_all();
+ rdump(dreq, &root_pool);
+ sk_dump_all(dreq);
// XXXX tm_dump_all();
- if_dump_all();
- neigh_dump_all();
- rta_dump_all();
- rt_dump_all();
- protos_dump_all();
+ if_dump_all(dreq);
+ neigh_dump_all(dreq);
+ rta_dump_all(dreq);
+ rt_dump_all(dreq);
+ protos_dump_all(dreq);
debug("\n");
}
+void
+async_dump(void)
+{
+ struct dump_request *dr = dump_to_file_init(0);
+ dr->report = async_dump_report;
+ dump_to_file_run(dr, "bird.dump", "async dump", async_dump_run);
+}
+
/*
* Dropping privileges
*/
struct iface;
struct birdsock;
struct rfile;
+struct cli;
/* main.c */
void io_init(void);
void io_loop(void);
-void io_log_dump(void);
+void io_log_dump(struct dump_request *);
int sk_open_unix(struct birdsock *s, const char *name);
struct rfile *rf_open(struct pool *, const char *name, const char *mode);
struct rfile *rf_fdopen(pool *p, int fd, const char *mode);
void test_old_bird(const char *path);
ip_addr resolve_hostname(const char *host, int type, const char **err_msg);
+struct dump_request *dump_to_file_init(off_t offset);
+void dump_to_file_run(struct dump_request *dr, const char *file, const char *what, void (*dump)(struct dump_request *));
+
+void cmd_dump_file(struct cli *cli, const char *file, const char *what, void (*dump)(struct dump_request *));
+
/* krt.c bits */
void krt_io_init(void);