]> git.ipfire.org Git - thirdparty/bird.git/blob - nest/proto.c
Adds support for soft reconfiguration.
[thirdparty/bird.git] / nest / proto.c
1 /*
2 * BIRD -- Protocols
3 *
4 * (c) 1998--2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9 #undef LOCAL_DEBUG
10
11 #include "nest/bird.h"
12 #include "nest/protocol.h"
13 #include "lib/resource.h"
14 #include "lib/lists.h"
15 #include "lib/event.h"
16 #include "lib/string.h"
17 #include "conf/conf.h"
18 #include "nest/route.h"
19 #include "nest/iface.h"
20 #include "nest/cli.h"
21 #include "filter/filter.h"
22
23 static pool *proto_pool;
24
25 static list protocol_list;
26 static list proto_list;
27
28 #define WALK_PROTO_LIST(p) do { \
29 node *nn; \
30 WALK_LIST(nn, proto_list) { \
31 struct proto *p = SKIP_BACK(struct proto, glob_node, nn);
32 #define WALK_PROTO_LIST_END } } while(0)
33
34 #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
35
36 list active_proto_list;
37 static list inactive_proto_list;
38 static list initial_proto_list;
39 static list flush_proto_list;
40
41 static event *proto_flush_event;
42
43 static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
44 static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
45
46 static void proto_flush_all(void *);
47 static void proto_rethink_goal(struct proto *p);
48 static char *proto_state_name(struct proto *p);
49
50 static void
51 proto_enqueue(list *l, struct proto *p)
52 {
53 add_tail(l, &p->n);
54 p->last_state_change = now;
55 }
56
57 static void
58 proto_relink(struct proto *p)
59 {
60 list *l;
61
62 if (p->debug & D_STATES)
63 {
64 char *name = proto_state_name(p);
65 if (name != p->last_state_name_announced)
66 {
67 p->last_state_name_announced = name;
68 PD(p, "State changed to %s", proto_state_name(p));
69 }
70 }
71 else
72 p->last_state_name_announced = NULL;
73 rem_node(&p->n);
74 switch (p->core_state)
75 {
76 case FS_HAPPY:
77 l = &active_proto_list;
78 break;
79 case FS_FLUSHING:
80 l = &flush_proto_list;
81 break;
82 default:
83 l = &inactive_proto_list;
84 }
85 proto_enqueue(l, p);
86 }
87
88 /**
89 * proto_new - create a new protocol instance
90 * @c: protocol configuration
91 * @size: size of protocol data structure (each protocol instance is represented by
92 * a structure starting with generic part [struct &proto] and continued
93 * with data specific to the protocol)
94 *
95 * When a new configuration has been read in, the core code starts
96 * initializing all the protocol instances configured by calling their
97 * init() hooks with the corresponding instance configuration. The initialization
98 * code of the protocol is expected to create a new instance according to the
99 * configuration by calling this function and then modifying the default settings
100 * to values wanted by the protocol.
101 */
102 void *
103 proto_new(struct proto_config *c, unsigned size)
104 {
105 struct protocol *pr = c->protocol;
106 struct proto *p = mb_allocz(proto_pool, size);
107
108 p->cf = c;
109 p->debug = c->debug;
110 p->name = c->name;
111 p->preference = c->preference;
112 p->disabled = c->disabled;
113 p->proto = pr;
114 p->table = c->table->table;
115 p->in_filter = c->in_filter;
116 p->out_filter = c->out_filter;
117 p->min_scope = SCOPE_SITE;
118 p->hash_key = random_u32();
119 c->proto = p;
120 return p;
121 }
122
123 static void
124 proto_init_instance(struct proto *p)
125 {
126 /* Here we cannot use p->cf->name since it won't survive reconfiguration */
127 p->pool = rp_new(proto_pool, p->proto->name);
128 p->attn = ev_new(p->pool);
129 p->attn->data = p;
130 rt_lock_table(p->table);
131 }
132
133 /**
134 * proto_add_announce_hook - connect protocol to a routing table
135 * @p: protocol instance
136 * @t: routing table to connect to
137 *
138 * This function creates a connection between the protocol instance @p
139 * and the routing table @t, making the protocol hear all changes in
140 * the table.
141 *
142 * Unless you want to listen to multiple routing tables (as the Pipe
143 * protocol does), you needn't to worry about this function since the
144 * connection to the protocol's primary routing table is initialized
145 * automatically by the core code.
146 */
147 struct announce_hook *
148 proto_add_announce_hook(struct proto *p, struct rtable *t)
149 {
150 struct announce_hook *h;
151
152 if (!p->rt_notify)
153 return NULL;
154 DBG("Connecting protocol %s to table %s\n", p->name, t->name);
155 PD(p, "Connected to table %s", t->name);
156 h = mb_alloc(p->pool, sizeof(struct announce_hook));
157 h->table = t;
158 h->proto = p;
159 h->next = p->ahooks;
160 p->ahooks = h;
161 add_tail(&t->hooks, &h->n);
162 return h;
163 }
164
165 static void
166 proto_flush_hooks(struct proto *p)
167 {
168 struct announce_hook *h;
169
170 for(h=p->ahooks; h; h=h->next)
171 rem_node(&h->n);
172 p->ahooks = NULL;
173 }
174
175 /**
176 * proto_config_new - create a new protocol configuration
177 * @pr: protocol the configuration will belong to
178 * @size: size of the structure including generic data
179 *
180 * Whenever the configuration file says that a new instance
181 * of a routing protocol should be created, the parser calls
182 * proto_config_new() to create a configuration entry for this
183 * instance (a structure staring with the &proto_config header
184 * containing all the generic items followed by protocol-specific
185 * ones). Also, the configuration entry gets added to the list
186 * of protocol instances kept in the configuration.
187 */
188 void *
189 proto_config_new(struct protocol *pr, unsigned size)
190 {
191 struct proto_config *c = cfg_allocz(size);
192
193 add_tail(&new_config->protos, &c->n);
194 c->global = new_config;
195 c->protocol = pr;
196 c->name = pr->name;
197 c->out_filter = FILTER_REJECT;
198 c->table = c->global->master_rtc;
199 c->debug = new_config->proto_default_debug;
200 return c;
201 }
202
203 /**
204 * protos_preconfig - pre-configuration processing
205 * @c: new configuration
206 *
207 * This function calls the preconfig() hooks of all routing
208 * protocols available to prepare them for reading of the new
209 * configuration.
210 */
211 void
212 protos_preconfig(struct config *c)
213 {
214 struct protocol *p;
215
216 init_list(&c->protos);
217 DBG("Protocol preconfig:");
218 WALK_LIST(p, protocol_list)
219 {
220 DBG(" %s", p->name);
221 p->name_counter = 0;
222 if (p->preconfig)
223 p->preconfig(p, c);
224 }
225 DBG("\n");
226 }
227
228 /**
229 * protos_postconfig - post-configuration processing
230 * @c: new configuration
231 *
232 * This function calls the postconfig() hooks of all protocol
233 * instances specified in configuration @c.
234 */
235 void
236 protos_postconfig(struct config *c)
237 {
238 struct proto_config *x;
239 struct protocol *p;
240
241 DBG("Protocol postconfig:");
242 WALK_LIST(x, c->protos)
243 {
244 DBG(" %s", x->name);
245 p = x->protocol;
246 if (p->postconfig)
247 p->postconfig(x);
248 }
249 DBG("\n");
250 }
251
252 static struct proto *
253 proto_init(struct proto_config *c)
254 {
255 struct protocol *p = c->protocol;
256 struct proto *q = p->init(c);
257
258 q->proto_state = PS_DOWN;
259 q->core_state = FS_HUNGRY;
260 proto_enqueue(&initial_proto_list, q);
261 add_tail(&proto_list, &q->glob_node);
262 PD(q, "Initializing%s", q->disabled ? " [disabled]" : "");
263 return q;
264 }
265
266 /**
267 * protos_commit - commit new protocol configuration
268 * @new: new configuration
269 * @old: old configuration or %NULL if it's boot time config
270 * @force_reconfig: force restart of all protocols (used for example
271 * when the router ID changes)
272 * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
273 *
274 * Scan differences between @old and @new configuration and adjust all
275 * protocol instances to conform to the new configuration.
276 *
277 * When a protocol exists in the new configuration, but it doesn't in the
278 * original one, it's immediately started. When a collision with the other
279 * running protocol would arise, the new protocol will be temporarily stopped
280 * by the locking mechanism.
281 *
282 * When a protocol exists in the old configuration, but it doesn't in the
283 * new one, it's shut down and deleted after the shutdown completes.
284 *
285 * When a protocol exists in both configurations, the core decides
286 * whether it's possible to reconfigure it dynamically - it checks all
287 * the core properties of the protocol (changes in filters are ignored
288 * if type is RECONFIG_SOFT) and if they match, it asks the
289 * reconfigure() hook of the protocol to see if the protocol is able
290 * to switch to the new configuration. If it isn't possible, the
291 * protocol is shut down and a new instance is started with the new
292 * configuration after the shutdown is completed.
293 */
294 void
295 protos_commit(struct config *new, struct config *old, int force_reconfig, int type)
296 {
297 struct proto_config *oc, *nc;
298 struct proto *p, *n;
299
300 DBG("protos_commit:\n");
301 if (old)
302 {
303 WALK_LIST(oc, old->protos)
304 {
305 struct proto *p = oc->proto;
306 struct symbol *sym = cf_find_symbol(oc->name);
307 if (sym && sym->class == SYM_PROTO && !new->shutdown)
308 {
309 /* Found match, let's check if we can smoothly switch to new configuration */
310 nc = sym->def;
311 if (!force_reconfig
312 && nc->protocol == oc->protocol
313 && nc->preference == oc->preference
314 && nc->disabled == oc->disabled
315 && nc->table->table == oc->table->table
316 && ((type == RECONFIG_SOFT) || filter_same(nc->in_filter, oc->in_filter))
317 && ((type == RECONFIG_SOFT) || filter_same(nc->out_filter, oc->out_filter))
318 && p->proto_state != PS_DOWN)
319 {
320 /* Generic attributes match, try converting them and then ask the protocol */
321 p->debug = nc->debug;
322 if (p->proto->reconfigure && p->proto->reconfigure(p, nc))
323 {
324 DBG("\t%s: same\n", oc->name);
325 PD(p, "Reconfigured");
326 p->cf = nc;
327 p->name = nc->name;
328 p->in_filter = nc->in_filter;
329 p->out_filter = nc->out_filter;
330 nc->proto = p;
331 continue;
332 }
333 }
334 /* Unsuccessful, force reconfig */
335 DBG("\t%s: power cycling\n", oc->name);
336 PD(p, "Reconfiguration failed, restarting");
337 p->cf_new = nc;
338 nc->proto = p;
339 }
340 else
341 {
342 DBG("\t%s: deleting\n", oc->name);
343 PD(p, "Unconfigured");
344 p->cf_new = NULL;
345 }
346 p->reconfiguring = 1;
347 config_add_obstacle(old);
348 proto_rethink_goal(p);
349 }
350 }
351
352 WALK_LIST(nc, new->protos)
353 if (!nc->proto)
354 {
355 DBG("\t%s: adding\n", nc->name);
356 proto_init(nc);
357 }
358 DBG("\tdone\n");
359
360 DBG("Protocol start\n");
361 WALK_LIST_DELSAFE(p, n, initial_proto_list)
362 proto_rethink_goal(p);
363 }
364
365 static void
366 proto_rethink_goal(struct proto *p)
367 {
368 struct protocol *q;
369
370 if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
371 {
372 struct proto_config *nc = p->cf_new;
373 DBG("%s has shut down for reconfiguration\n", p->name);
374 config_del_obstacle(p->cf->global);
375 rem_node(&p->n);
376 rem_node(&p->glob_node);
377 mb_free(p);
378 if (!nc)
379 return;
380 p = proto_init(nc);
381 }
382
383 /* Determine what state we want to reach */
384 if (p->disabled || p->reconfiguring)
385 {
386 p->core_goal = FS_HUNGRY;
387 if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
388 return;
389 }
390 else
391 {
392 p->core_goal = FS_HAPPY;
393 if (p->core_state == FS_HAPPY && p->proto_state == PS_UP)
394 return;
395 }
396
397 q = p->proto;
398 if (p->core_goal == FS_HAPPY) /* Going up */
399 {
400 if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
401 {
402 DBG("Kicking %s up\n", p->name);
403 PD(p, "Starting");
404 proto_init_instance(p);
405 proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
406 }
407 }
408 else /* Going down */
409 {
410 if (p->proto_state == PS_START || p->proto_state == PS_UP)
411 {
412 DBG("Kicking %s down\n", p->name);
413 PD(p, "Shutting down");
414 proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
415 }
416 }
417 }
418
419 /**
420 * protos_dump_all - dump status of all protocols
421 *
422 * This function dumps status of all existing protocol instances to the
423 * debug output. It involves printing of general status information
424 * such as protocol states, its position on the protocol lists
425 * and also calling of a dump() hook of the protocol to print
426 * the internals.
427 */
428 void
429 protos_dump_all(void)
430 {
431 struct proto *p;
432
433 debug("Protocols:\n");
434
435 WALK_LIST(p, active_proto_list)
436 {
437 debug(" protocol %s state %s/%s\n", p->name,
438 p_states[p->proto_state], c_states[p->core_state]);
439 if (p->in_filter)
440 debug("\tInput filter: %s\n", filter_name(p->in_filter));
441 if (p->out_filter != FILTER_REJECT)
442 debug("\tOutput filter: %s\n", filter_name(p->out_filter));
443 if (p->disabled)
444 debug("\tDISABLED\n");
445 else if (p->proto->dump)
446 p->proto->dump(p);
447 }
448 WALK_LIST(p, inactive_proto_list)
449 debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
450 WALK_LIST(p, initial_proto_list)
451 debug(" initial %s\n", p->name);
452 WALK_LIST(p, flush_proto_list)
453 debug(" flushing %s\n", p->name);
454 }
455
456 /**
457 * proto_build - make a single protocol available
458 * @p: the protocol
459 *
460 * After the platform specific initialization code uses protos_build()
461 * to add all the standard protocols, it should call proto_build() for
462 * all platform specific protocols to inform the core that they exist.
463 */
464 void
465 proto_build(struct protocol *p)
466 {
467 add_tail(&protocol_list, &p->n);
468 if (p->attr_class)
469 {
470 ASSERT(!attr_class_to_protocol[p->attr_class]);
471 attr_class_to_protocol[p->attr_class] = p;
472 }
473 }
474
475 /**
476 * protos_build - build a protocol list
477 *
478 * This function is called during BIRD startup to insert
479 * all standard protocols to the global protocol list. Insertion
480 * of platform specific protocols (such as the kernel syncer)
481 * is in the domain of competence of the platform dependent
482 * startup code.
483 */
484 void
485 protos_build(void)
486 {
487 init_list(&protocol_list);
488 init_list(&proto_list);
489 init_list(&active_proto_list);
490 init_list(&inactive_proto_list);
491 init_list(&initial_proto_list);
492 init_list(&flush_proto_list);
493 proto_build(&proto_device);
494 #ifdef CONFIG_RIP
495 proto_build(&proto_rip);
496 #endif
497 #ifdef CONFIG_STATIC
498 proto_build(&proto_static);
499 #endif
500 #ifdef CONFIG_OSPF
501 proto_build(&proto_ospf);
502 #endif
503 #ifdef CONFIG_PIPE
504 proto_build(&proto_pipe);
505 #endif
506 #ifdef CONFIG_BGP
507 proto_build(&proto_bgp);
508 #endif
509 proto_pool = rp_new(&root_pool, "Protocols");
510 proto_flush_event = ev_new(proto_pool);
511 proto_flush_event->hook = proto_flush_all;
512 }
513
514 static void
515 proto_fell_down(struct proto *p)
516 {
517 DBG("Protocol %s down\n", p->name);
518 ASSERT(p->stats.imp_routes == 0);
519
520 bzero(&p->stats, sizeof(struct proto_stats));
521 rt_unlock_table(p->table);
522 proto_rethink_goal(p);
523 }
524
525 static void
526 proto_feed_more(void *P)
527 {
528 struct proto *p = P;
529
530 if (p->core_state != FS_FEEDING)
531 return;
532
533 DBG("Feeding protocol %s continued\n", p->name);
534 if (rt_feed_baby(p))
535 {
536 p->core_state = FS_HAPPY;
537 proto_relink(p);
538 DBG("Protocol %s up and running\n", p->name);
539 }
540 else
541 {
542 p->attn->hook = proto_feed_more;
543 ev_schedule(p->attn); /* Will continue later... */
544 }
545 }
546
547 static void
548 proto_feed(void *P)
549 {
550 struct proto *p = P;
551
552 if (p->core_state != FS_FEEDING)
553 return;
554
555 DBG("Feeding protocol %s\n", p->name);
556 proto_add_announce_hook(p, p->table);
557 if_feed_baby(p);
558 proto_feed_more(P);
559 }
560
561 static void
562 proto_schedule_flush(struct proto *p)
563 {
564 /* Need to abort feeding */
565 if (p->core_state == FS_FEEDING)
566 rt_feed_baby_abort(p);
567
568 DBG("%s: Scheduling flush\n", p->name);
569 p->core_state = FS_FLUSHING;
570 proto_relink(p);
571 proto_flush_hooks(p);
572 ev_schedule(proto_flush_event);
573 }
574
575 static void
576 proto_schedule_feed(struct proto *p)
577 {
578 DBG("%s: Scheduling meal\n", p->name);
579 p->core_state = FS_FEEDING;
580 proto_relink(p);
581 p->attn->hook = proto_feed;
582 ev_schedule(p->attn);
583 }
584
585 /**
586 * proto_notify_state - notify core about protocol state change
587 * @p: protocol the state of which has changed
588 * @ps: the new status
589 *
590 * Whenever a state of a protocol changes due to some event internal
591 * to the protocol (i.e., not inside a start() or shutdown() hook),
592 * it should immediately notify the core about the change by calling
593 * proto_notify_state() which will write the new state to the &proto
594 * structure and take all the actions necessary to adapt to the new
595 * state. State change to PS_DOWN immediately frees resources of protocol
596 * and might execute start callback of protocol; therefore,
597 * it should be used at tail positions of protocol callbacks.
598 */
599 void
600 proto_notify_state(struct proto *p, unsigned ps)
601 {
602 unsigned ops = p->proto_state;
603 unsigned cs = p->core_state;
604
605 DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
606 if (ops == ps)
607 return;
608
609 p->proto_state = ps;
610
611 switch (ps)
612 {
613 case PS_DOWN:
614 if ((cs = FS_FEEDING) || (cs == FS_HAPPY))
615 proto_schedule_flush(p);
616
617 neigh_prune(); // FIXME convert neighbors to resource?
618 rfree(p->pool);
619 p->pool = NULL;
620
621 if (cs == FS_HUNGRY) /* Shutdown finished */
622 {
623 proto_fell_down(p);
624 return; /* The protocol might have ceased to exist */
625 }
626 break;
627 case PS_START:
628 ASSERT(ops == PS_DOWN);
629 ASSERT(cs == FS_HUNGRY);
630 break;
631 case PS_UP:
632 ASSERT(ops == PS_DOWN || ops == PS_START);
633 ASSERT(cs == FS_HUNGRY);
634 proto_schedule_feed(p);
635 break;
636 case PS_STOP:
637 if ((cs = FS_FEEDING) || (cs == FS_HAPPY))
638 proto_schedule_flush(p);
639 break;
640 default:
641 bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]);
642 }
643 }
644
645 static void
646 proto_flush_all(void *unused UNUSED)
647 {
648 struct proto *p;
649
650 rt_prune_all();
651 while ((p = HEAD(flush_proto_list))->n.next)
652 {
653 DBG("Flushing protocol %s\n", p->name);
654 p->core_state = FS_HUNGRY;
655 proto_relink(p);
656 if (p->proto_state == PS_DOWN)
657 proto_fell_down(p);
658 }
659 }
660
661 /*
662 * CLI Commands
663 */
664
665 static char *
666 proto_state_name(struct proto *p)
667 {
668 #define P(x,y) ((x << 4) | y)
669 switch (P(p->proto_state, p->core_state))
670 {
671 case P(PS_DOWN, FS_HUNGRY): return "down";
672 case P(PS_START, FS_HUNGRY): return "start";
673 case P(PS_UP, FS_HUNGRY):
674 case P(PS_UP, FS_FEEDING): return "feed";
675 case P(PS_STOP, FS_HUNGRY): return "stop";
676 case P(PS_UP, FS_HAPPY): return "up";
677 case P(PS_STOP, FS_FLUSHING):
678 case P(PS_DOWN, FS_FLUSHING): return "flush";
679 default: return "???";
680 }
681 #undef P
682 }
683
684 static void
685 proto_do_show(struct proto *p, int verbose)
686 {
687 byte buf[256], reltime[TM_RELTIME_BUFFER_SIZE];
688
689 buf[0] = 0;
690 if (p->proto->get_status)
691 p->proto->get_status(p, buf);
692 tm_format_reltime(reltime, p->last_state_change);
693 cli_msg(-1002, "%-8s %-8s %-8s %-5s %-5s %s",
694 p->name,
695 p->proto->name,
696 p->table->name,
697 proto_state_name(p),
698 reltime,
699 buf);
700 if (verbose)
701 {
702 cli_msg(-1006, " Preference: %d", p->preference);
703 cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter));
704 cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter));
705
706 if (p->proto_state != PS_DOWN)
707 {
708 cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
709 p->stats.imp_routes, p->stats.exp_routes, p->stats.pref_routes);
710 cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
711 cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
712 p->stats.imp_updates_received, p->stats.imp_updates_invalid,
713 p->stats.imp_updates_filtered, p->stats.imp_updates_ignored,
714 p->stats.imp_updates_accepted);
715 cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
716 p->stats.imp_withdraws_received, p->stats.imp_withdraws_invalid,
717 p->stats.imp_withdraws_ignored, p->stats.imp_withdraws_accepted);
718 cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
719 p->stats.exp_updates_received, p->stats.exp_updates_rejected,
720 p->stats.exp_updates_filtered, p->stats.exp_updates_accepted);
721 cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
722 p->stats.exp_withdraws_received, p->stats.exp_withdraws_accepted);
723 }
724
725 cli_msg(-1006, "");
726 }
727 }
728
729 void
730 proto_show(struct symbol *s, int verbose)
731 {
732 if (s && s->class != SYM_PROTO)
733 {
734 cli_msg(9002, "%s is not a protocol", s->name);
735 return;
736 }
737 cli_msg(-2002, "name proto table state since info");
738 if (s)
739 proto_do_show(((struct proto_config *)s->def)->proto, verbose);
740 else
741 {
742 WALK_PROTO_LIST(p)
743 proto_do_show(p, verbose);
744 WALK_PROTO_LIST_END;
745 }
746 cli_msg(0, "");
747 }
748
749 struct proto *
750 proto_get_named(struct symbol *sym, struct protocol *pr)
751 {
752 struct proto *p, *q;
753
754 if (sym)
755 {
756 if (sym->class != SYM_PROTO)
757 cf_error("%s: Not a protocol", sym->name);
758 p = ((struct proto_config *)sym->def)->proto;
759 if (!p || p->proto != pr)
760 cf_error("%s: Not a %s protocol", sym->name, pr->name);
761 }
762 else
763 {
764 p = NULL;
765 WALK_LIST(q, active_proto_list)
766 if (q->proto == pr)
767 {
768 if (p)
769 cf_error("There are multiple %s protocols running", pr->name);
770 p = q;
771 }
772 if (!p)
773 cf_error("There is no %s protocol running", pr->name);
774 }
775 return p;
776 }
777
778 void
779 proto_xxable(char *pattern, int xx)
780 {
781 int cnt = 0;
782 WALK_PROTO_LIST(p)
783 if (patmatch(pattern, p->name))
784 {
785 cnt++;
786 switch (xx)
787 {
788 case 0:
789 if (p->disabled)
790 cli_msg(-8, "%s: already disabled", p->name);
791 else
792 {
793 cli_msg(-9, "%s: disabled", p->name);
794 p->disabled = 1;
795 }
796 break;
797 case 1:
798 if (!p->disabled)
799 cli_msg(-10, "%s: already enabled", p->name);
800 else
801 {
802 cli_msg(-11, "%s: enabled", p->name);
803 p->disabled = 0;
804 }
805 break;
806 case 2:
807 if (p->disabled)
808 cli_msg(-8, "%s: already disabled", p->name);
809 else
810 {
811 p->disabled = 1;
812 proto_rethink_goal(p);
813 p->disabled = 0;
814 cli_msg(-12, "%s: restarted", p->name);
815 }
816 break;
817 default:
818 ASSERT(0);
819 }
820 proto_rethink_goal(p);
821 }
822 WALK_PROTO_LIST_END;
823 if (!cnt)
824 cli_msg(8003, "No protocols match");
825 else
826 cli_msg(0, "");
827 }
828
829 void
830 proto_debug(char *pattern, unsigned int mask)
831 {
832 int cnt = 0;
833 WALK_PROTO_LIST(p)
834 if (patmatch(pattern, p->name))
835 {
836 cnt++;
837 p->debug = mask;
838 }
839 WALK_PROTO_LIST_END;
840 if (!cnt)
841 cli_msg(8003, "No protocols match");
842 else
843 cli_msg(0, "");
844 }