Annotation of embedaddon/bird2/conf/conf.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  *     BIRD Internet Routing Daemon -- Configuration File Handling
        !             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: /**
        !            10:  * DOC: Configuration manager
        !            11:  *
        !            12:  * Configuration of BIRD is complex, yet straightforward. There are three
        !            13:  * modules taking care of the configuration: config manager (which takes care
        !            14:  * of storage of the config information and controls switching between configs),
        !            15:  * lexical analyzer and parser.
        !            16:  *
        !            17:  * The configuration manager stores each config as a &config structure
        !            18:  * accompanied by a linear pool from which all information associated
        !            19:  * with the config and pointed to by the &config structure is allocated.
        !            20:  *
        !            21:  * There can exist up to four different configurations at one time: an active
        !            22:  * one (pointed to by @config), configuration we are just switching from
        !            23:  * (@old_config), one queued for the next reconfiguration (@future_config; if
        !            24:  * there is one and the user wants to reconfigure once again, we just free the
        !            25:  * previous queued config and replace it with the new one) and finally a config
        !            26:  * being parsed (@new_config). The stored @old_config is also used for undo
        !            27:  * reconfiguration, which works in a similar way. Reconfiguration could also
        !            28:  * have timeout (using @config_timer) and undo is automatically called if the
        !            29:  * new configuration is not confirmed later. The new config (@new_config) and
        !            30:  * associated linear pool (@cfg_mem) is non-NULL only during parsing.
        !            31:  *
        !            32:  * Loading of new configuration is very simple: just call config_alloc() to get
        !            33:  * a new &config structure, then use config_parse() to parse a configuration
        !            34:  * file and fill all fields of the structure and finally ask the config manager
        !            35:  * to switch to the new config by calling config_commit().
        !            36:  *
        !            37:  * CLI commands are parsed in a very similar way -- there is also a stripped-down
        !            38:  * &config structure associated with them and they are lex-ed and parsed by the
        !            39:  * same functions, only a special fake token is prepended before the command
        !            40:  * text to make the parser recognize only the rules corresponding to CLI commands.
        !            41:  */
        !            42: 
        !            43: #include <setjmp.h>
        !            44: #include <stdarg.h>
        !            45: 
        !            46: #undef LOCAL_DEBUG
        !            47: 
        !            48: #include "nest/bird.h"
        !            49: #include "nest/route.h"
        !            50: #include "nest/protocol.h"
        !            51: #include "nest/iface.h"
        !            52: #include "lib/resource.h"
        !            53: #include "lib/string.h"
        !            54: #include "lib/event.h"
        !            55: #include "lib/timer.h"
        !            56: #include "conf/conf.h"
        !            57: #include "filter/filter.h"
        !            58: 
        !            59: 
        !            60: static jmp_buf conf_jmpbuf;
        !            61: 
        !            62: struct config *config, *new_config;
        !            63: 
        !            64: static struct config *old_config;      /* Old configuration */
        !            65: static struct config *future_config;   /* New config held here if recon requested during recon */
        !            66: static int old_cftype;                 /* Type of transition old_config -> config (RECONFIG_SOFT/HARD) */
        !            67: static int future_cftype;              /* Type of scheduled transition, may also be RECONFIG_UNDO */
        !            68: /* Note that when future_cftype is RECONFIG_UNDO, then future_config is NULL,
        !            69:    therefore proper check for future scheduled config checks future_cftype */
        !            70: 
        !            71: static event *config_event;            /* Event for finalizing reconfiguration */
        !            72: static timer *config_timer;            /* Timer for scheduled configuration rollback */
        !            73: 
        !            74: /* These are public just for cmd_show_status(), should not be accessed elsewhere */
        !            75: int shutting_down;                     /* Shutdown requested, do not accept new config changes */
        !            76: int configuring;                       /* Reconfiguration is running */
        !            77: int undo_available;                    /* Undo was not requested from last reconfiguration */
        !            78: /* Note that both shutting_down and undo_available are related to requests, not processing */
        !            79: 
        !            80: /**
        !            81:  * config_alloc - allocate a new configuration
        !            82:  * @name: name of the config
        !            83:  *
        !            84:  * This function creates new &config structure, attaches a resource
        !            85:  * pool and a linear memory pool to it and makes it available for
        !            86:  * further use. Returns a pointer to the structure.
        !            87:  */
        !            88: struct config *
        !            89: config_alloc(const char *name)
        !            90: {
        !            91:   pool *p = rp_new(&root_pool, "Config");
        !            92:   linpool *l = lp_new_default(p);
        !            93:   struct config *c = lp_allocz(l, sizeof(struct config));
        !            94: 
        !            95:   /* Duplication of name string in local linear pool */
        !            96:   uint nlen = strlen(name) + 1;
        !            97:   char *ndup = lp_allocu(l, nlen);
        !            98:   memcpy(ndup, name, nlen);
        !            99: 
        !           100:   init_list(&c->tests);
        !           101:   init_list(&c->symbols);
        !           102:   c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
        !           103:   c->pool = p;
        !           104:   c->mem = l;
        !           105:   c->file_name = ndup;
        !           106:   c->load_time = current_time();
        !           107:   c->tf_route = c->tf_proto = TM_ISO_SHORT_MS;
        !           108:   c->tf_base = c->tf_log = TM_ISO_LONG_MS;
        !           109:   c->gr_wait = DEFAULT_GR_WAIT;
        !           110: 
        !           111:   return c;
        !           112: }
        !           113: 
        !           114: /**
        !           115:  * config_parse - parse a configuration
        !           116:  * @c: configuration
        !           117:  *
        !           118:  * config_parse() reads input by calling a hook function pointed to
        !           119:  * by @cf_read_hook and parses it according to the configuration
        !           120:  * grammar. It also calls all the preconfig and postconfig hooks
        !           121:  * before, resp. after parsing.
        !           122:  *
        !           123:  * Result: 1 if the config has been parsed successfully, 0 if any
        !           124:  * error has occurred (such as anybody calling cf_error()) and
        !           125:  * the @err_msg field has been set to the error message.
        !           126:  */
        !           127: int
        !           128: config_parse(struct config *c)
        !           129: {
        !           130:   int done = 0;
        !           131:   DBG("Parsing configuration file `%s'\n", c->file_name);
        !           132:   new_config = c;
        !           133:   cfg_mem = c->mem;
        !           134:   if (setjmp(conf_jmpbuf))
        !           135:     goto cleanup;
        !           136: 
        !           137:   cf_lex_init(0, c);
        !           138:   sysdep_preconfig(c);
        !           139:   protos_preconfig(c);
        !           140:   rt_preconfig(c);
        !           141:   cf_parse();
        !           142: 
        !           143:   if (EMPTY_LIST(c->protos))
        !           144:     cf_error("No protocol is specified in the config file");
        !           145: 
        !           146:   /*
        !           147:   if (!c->router_id)
        !           148:     cf_error("Router ID must be configured manually");
        !           149:   */
        !           150: 
        !           151:   done = 1;
        !           152: 
        !           153: cleanup:
        !           154:   new_config = NULL;
        !           155:   cfg_mem = NULL;
        !           156:   return done;
        !           157: }
        !           158: 
        !           159: /**
        !           160:  * cli_parse - parse a CLI command
        !           161:  * @c: temporary config structure
        !           162:  *
        !           163:  * cli_parse() is similar to config_parse(), but instead of a configuration,
        !           164:  * it parses a CLI command. See the CLI module for more information.
        !           165:  */
        !           166: int
        !           167: cli_parse(struct config *c)
        !           168: {
        !           169:   int done = 0;
        !           170:   c->fallback = config;
        !           171:   new_config = c;
        !           172:   cfg_mem = c->mem;
        !           173:   if (setjmp(conf_jmpbuf))
        !           174:     goto cleanup;
        !           175: 
        !           176:   cf_lex_init(1, c);
        !           177:   cf_parse();
        !           178:   done = 1;
        !           179: 
        !           180: cleanup:
        !           181:   c->fallback = NULL;
        !           182:   new_config = NULL;
        !           183:   cfg_mem = NULL;
        !           184:   return done;
        !           185: }
        !           186: 
        !           187: /**
        !           188:  * config_free - free a configuration
        !           189:  * @c: configuration to be freed
        !           190:  *
        !           191:  * This function takes a &config structure and frees all resources
        !           192:  * associated with it.
        !           193:  */
        !           194: void
        !           195: config_free(struct config *c)
        !           196: {
        !           197:   if (c)
        !           198:     rfree(c->pool);
        !           199: }
        !           200: 
        !           201: void
        !           202: config_add_obstacle(struct config *c)
        !           203: {
        !           204:   DBG("+++ adding obstacle %d\n", c->obstacle_count);
        !           205:   c->obstacle_count++;
        !           206: }
        !           207: 
        !           208: void
        !           209: config_del_obstacle(struct config *c)
        !           210: {
        !           211:   DBG("+++ deleting obstacle %d\n", c->obstacle_count);
        !           212:   c->obstacle_count--;
        !           213:   if (!c->obstacle_count && (c != config))
        !           214:     ev_schedule(config_event);
        !           215: }
        !           216: 
        !           217: static int
        !           218: global_commit(struct config *new, struct config *old)
        !           219: {
        !           220:   if (!old)
        !           221:     return 0;
        !           222: 
        !           223:   if (!new->router_id)
        !           224:     {
        !           225:       new->router_id = old->router_id;
        !           226: 
        !           227:       if (new->router_id_from)
        !           228:        {
        !           229:          u32 id = if_choose_router_id(new->router_id_from, old->router_id);
        !           230:          if (!id)
        !           231:            log(L_WARN "Cannot determine router ID, using old one");
        !           232:          else
        !           233:            new->router_id = id;
        !           234:        }
        !           235:     }
        !           236: 
        !           237:   return 0;
        !           238: }
        !           239: 
        !           240: static int
        !           241: config_do_commit(struct config *c, int type)
        !           242: {
        !           243:   if (type == RECONFIG_UNDO)
        !           244:     {
        !           245:       c = old_config;
        !           246:       type = old_cftype;
        !           247:     }
        !           248:   else
        !           249:     config_free(old_config);
        !           250: 
        !           251:   old_config = config;
        !           252:   old_cftype = type;
        !           253:   config = c;
        !           254: 
        !           255:   configuring = 1;
        !           256:   if (old_config && !config->shutdown)
        !           257:     log(L_INFO "Reconfiguring");
        !           258: 
        !           259:   if (old_config)
        !           260:     old_config->obstacle_count++;
        !           261: 
        !           262:   DBG("filter_commit\n");
        !           263:   filter_commit(c, old_config);
        !           264:   DBG("sysdep_commit\n");
        !           265:   int force_restart = sysdep_commit(c, old_config);
        !           266:   DBG("global_commit\n");
        !           267:   force_restart |= global_commit(c, old_config);
        !           268:   DBG("rt_commit\n");
        !           269:   rt_commit(c, old_config);
        !           270:   DBG("protos_commit\n");
        !           271:   protos_commit(c, old_config, force_restart, type);
        !           272: 
        !           273:   int obs = 0;
        !           274:   if (old_config)
        !           275:     obs = --old_config->obstacle_count;
        !           276: 
        !           277:   DBG("do_commit finished with %d obstacles remaining\n", obs);
        !           278:   return !obs;
        !           279: }
        !           280: 
        !           281: static void
        !           282: config_done(void *unused UNUSED)
        !           283: {
        !           284:   if (config->shutdown)
        !           285:     sysdep_shutdown_done();
        !           286: 
        !           287:   configuring = 0;
        !           288:   if (old_config)
        !           289:     log(L_INFO "Reconfigured");
        !           290: 
        !           291:   if (future_cftype)
        !           292:     {
        !           293:       int type = future_cftype;
        !           294:       struct config *conf = future_config;
        !           295:       future_cftype = RECONFIG_NONE;
        !           296:       future_config = NULL;
        !           297: 
        !           298:       log(L_INFO "Reconfiguring to queued configuration");
        !           299:       if (config_do_commit(conf, type))
        !           300:        config_done(NULL);
        !           301:     }
        !           302: }
        !           303: 
        !           304: /**
        !           305:  * config_commit - commit a configuration
        !           306:  * @c: new configuration
        !           307:  * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
        !           308:  * @timeout: timeout for undo (in seconds; or 0 for no timeout)
        !           309:  *
        !           310:  * When a configuration is parsed and prepared for use, the
        !           311:  * config_commit() function starts the process of reconfiguration.
        !           312:  * It checks whether there is already a reconfiguration in progress
        !           313:  * in which case it just queues the new config for later processing.
        !           314:  * Else it notifies all modules about the new configuration by calling
        !           315:  * their commit() functions which can either accept it immediately
        !           316:  * or call config_add_obstacle() to report that they need some time
        !           317:  * to complete the reconfiguration. After all such obstacles are removed
        !           318:  * using config_del_obstacle(), the old configuration is freed and
        !           319:  * everything runs according to the new one.
        !           320:  *
        !           321:  * When @timeout is nonzero, the undo timer is activated with given
        !           322:  * timeout. The timer is deactivated when config_commit(),
        !           323:  * config_confirm() or config_undo() is called.
        !           324:  *
        !           325:  * Result: %CONF_DONE if the configuration has been accepted immediately,
        !           326:  * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED
        !           327:  * if it's been queued due to another reconfiguration being in progress now
        !           328:  * or %CONF_SHUTDOWN if BIRD is in shutdown mode and no new configurations
        !           329:  * are accepted.
        !           330:  */
        !           331: int
        !           332: config_commit(struct config *c, int type, uint timeout)
        !           333: {
        !           334:   if (shutting_down)
        !           335:     {
        !           336:       config_free(c);
        !           337:       return CONF_SHUTDOWN;
        !           338:     }
        !           339: 
        !           340:   undo_available = 1;
        !           341:   if (timeout)
        !           342:     tm_start(config_timer, timeout S);
        !           343:   else
        !           344:     tm_stop(config_timer);
        !           345: 
        !           346:   if (configuring)
        !           347:     {
        !           348:       if (future_cftype)
        !           349:        {
        !           350:          log(L_INFO "Queueing new configuration, ignoring the one already queued");
        !           351:          config_free(future_config);
        !           352:        }
        !           353:       else
        !           354:        log(L_INFO "Queueing new configuration");
        !           355: 
        !           356:       future_cftype = type;
        !           357:       future_config = c;
        !           358:       return CONF_QUEUED;
        !           359:     }
        !           360: 
        !           361:   if (config_do_commit(c, type))
        !           362:     {
        !           363:       config_done(NULL);
        !           364:       return CONF_DONE;
        !           365:     }
        !           366:   return CONF_PROGRESS;
        !           367: }
        !           368: 
        !           369: /**
        !           370:  * config_confirm - confirm a commited configuration
        !           371:  *
        !           372:  * When the undo timer is activated by config_commit() with nonzero timeout,
        !           373:  * this function can be used to deactivate it and therefore confirm
        !           374:  * the current configuration.
        !           375:  *
        !           376:  * Result: %CONF_CONFIRM when the current configuration is confirmed,
        !           377:  * %CONF_NONE when there is nothing to confirm (i.e. undo timer is not active).
        !           378:  */
        !           379: int
        !           380: config_confirm(void)
        !           381: {
        !           382:   if (config_timer->expires == 0)
        !           383:     return CONF_NOTHING;
        !           384: 
        !           385:   tm_stop(config_timer);
        !           386: 
        !           387:   return CONF_CONFIRM;
        !           388: }
        !           389: 
        !           390: /**
        !           391:  * config_undo - undo a configuration
        !           392:  *
        !           393:  * Function config_undo() can be used to change the current
        !           394:  * configuration back to stored %old_config. If no reconfiguration is
        !           395:  * running, this stored configuration is commited in the same way as a
        !           396:  * new configuration in config_commit(). If there is already a
        !           397:  * reconfiguration in progress and no next reconfiguration is
        !           398:  * scheduled, then the undo is scheduled for later processing as
        !           399:  * usual, but if another reconfiguration is already scheduled, then
        !           400:  * such reconfiguration is removed instead (i.e. undo is applied on
        !           401:  * the last commit that scheduled it).
        !           402:  *
        !           403:  * Result: %CONF_DONE if the configuration has been accepted immediately,
        !           404:  * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED
        !           405:  * if it's been queued due to another reconfiguration being in progress now,
        !           406:  * %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING
        !           407:  * if there is no relevant configuration to undo (the previous config request
        !           408:  * was config_undo() too)  or %CONF_SHUTDOWN if BIRD is in shutdown mode and
        !           409:  * no new configuration changes  are accepted.
        !           410:  */
        !           411: int
        !           412: config_undo(void)
        !           413: {
        !           414:   if (shutting_down)
        !           415:     return CONF_SHUTDOWN;
        !           416: 
        !           417:   if (!undo_available || !old_config)
        !           418:     return CONF_NOTHING;
        !           419: 
        !           420:   undo_available = 0;
        !           421:   tm_stop(config_timer);
        !           422: 
        !           423:   if (configuring)
        !           424:     {
        !           425:       if (future_cftype)
        !           426:        {
        !           427:          config_free(future_config);
        !           428:          future_config = NULL;
        !           429: 
        !           430:          log(L_INFO "Removing queued configuration");
        !           431:          future_cftype = RECONFIG_NONE;
        !           432:          return CONF_UNQUEUED;
        !           433:        }
        !           434:       else
        !           435:        {
        !           436:          log(L_INFO "Queueing undo configuration");
        !           437:          future_cftype = RECONFIG_UNDO;
        !           438:          return CONF_QUEUED;
        !           439:        }
        !           440:     }
        !           441: 
        !           442:   if (config_do_commit(NULL, RECONFIG_UNDO))
        !           443:     {
        !           444:       config_done(NULL);
        !           445:       return CONF_DONE;
        !           446:     }
        !           447:   return CONF_PROGRESS;
        !           448: }
        !           449: 
        !           450: int
        !           451: config_status(void)
        !           452: {
        !           453:   if (shutting_down)
        !           454:     return CONF_SHUTDOWN;
        !           455: 
        !           456:   if (configuring)
        !           457:     return future_cftype ? CONF_QUEUED : CONF_PROGRESS;
        !           458: 
        !           459:   return CONF_DONE;
        !           460: }
        !           461: 
        !           462: btime
        !           463: config_timer_status(void)
        !           464: {
        !           465:   return tm_active(config_timer) ? tm_remains(config_timer) : -1;
        !           466: }
        !           467: 
        !           468: extern void cmd_reconfig_undo_notify(void);
        !           469: 
        !           470: static void
        !           471: config_timeout(timer *t UNUSED)
        !           472: {
        !           473:   log(L_INFO "Config timeout expired, starting undo");
        !           474:   cmd_reconfig_undo_notify();
        !           475: 
        !           476:   int r = config_undo();
        !           477:   if (r < 0)
        !           478:     log(L_ERR "Undo request failed");
        !           479: }
        !           480: 
        !           481: void
        !           482: config_init(void)
        !           483: {
        !           484:   config_event = ev_new(&root_pool);
        !           485:   config_event->hook = config_done;
        !           486: 
        !           487:   config_timer = tm_new(&root_pool);
        !           488:   config_timer->hook = config_timeout;
        !           489: }
        !           490: 
        !           491: /**
        !           492:  * order_shutdown - order BIRD shutdown
        !           493:  *
        !           494:  * This function initiates shutdown of BIRD. It's accomplished by asking
        !           495:  * for switching to an empty configuration.
        !           496:  */
        !           497: void
        !           498: order_shutdown(int gr)
        !           499: {
        !           500:   struct config *c;
        !           501: 
        !           502:   if (shutting_down)
        !           503:     return;
        !           504: 
        !           505:   if (!gr)
        !           506:     log(L_INFO "Shutting down");
        !           507:   else
        !           508:     log(L_INFO "Shutting down for graceful restart");
        !           509: 
        !           510:   c = lp_alloc(config->mem, sizeof(struct config));
        !           511:   memcpy(c, config, sizeof(struct config));
        !           512:   init_list(&c->protos);
        !           513:   init_list(&c->tables);
        !           514:   c->shutdown = 1;
        !           515:   c->gr_down = gr;
        !           516: 
        !           517:   config_commit(c, RECONFIG_HARD, 0);
        !           518:   shutting_down = 1;
        !           519: }
        !           520: 
        !           521: /**
        !           522:  * cf_error - report a configuration error
        !           523:  * @msg: printf-like format string
        !           524:  *
        !           525:  * cf_error() can be called during execution of config_parse(), that is
        !           526:  * from the parser, a preconfig hook or a postconfig hook, to report an
        !           527:  * error in the configuration.
        !           528:  */
        !           529: void
        !           530: cf_error(const char *msg, ...)
        !           531: {
        !           532:   char buf[1024];
        !           533:   va_list args;
        !           534: 
        !           535:   va_start(args, msg);
        !           536:   if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
        !           537:     strcpy(buf, "<bug: error message too long>");
        !           538:   va_end(args);
        !           539:   new_config->err_msg = cfg_strdup(buf);
        !           540:   new_config->err_lino = ifs->lino;
        !           541:   new_config->err_chno = ifs->chno - ifs->toklen + 1;
        !           542:   new_config->err_file_name = ifs->file_name;
        !           543:   cf_lex_unwind();
        !           544:   longjmp(conf_jmpbuf, 1);
        !           545: }
        !           546: 
        !           547: /**
        !           548:  * cfg_strdup - copy a string to config memory
        !           549:  * @c: string to copy
        !           550:  *
        !           551:  * cfg_strdup() creates a new copy of the string in the memory
        !           552:  * pool associated with the configuration being currently parsed.
        !           553:  * It's often used when a string literal occurs in the configuration
        !           554:  * and we want to preserve it for further use.
        !           555:  */
        !           556: char *
        !           557: cfg_strdup(const char *c)
        !           558: {
        !           559:   int l = strlen(c) + 1;
        !           560:   char *z = cfg_allocu(l);
        !           561:   memcpy(z, c, l);
        !           562:   return z;
        !           563: }
        !           564: 
        !           565: 
        !           566: void
        !           567: cfg_copy_list(list *dest, list *src, unsigned node_size)
        !           568: {
        !           569:   node *dn, *sn;
        !           570: 
        !           571:   init_list(dest);
        !           572:   WALK_LIST(sn, *src)
        !           573:   {
        !           574:     dn = cfg_alloc(node_size);
        !           575:     memcpy(dn, sn, node_size);
        !           576:     add_tail(dest, dn);
        !           577:   }
        !           578: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>