Annotation of embedaddon/libpdel/config/app_config.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (c) 2001-2002 Packet Design, LLC.
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Subject to the following obligations and disclaimer of warranty,
                      7:  * use and redistribution of this software, in source or object code
                      8:  * forms, with or without modifications are expressly permitted by
                      9:  * Packet Design; provided, however, that:
                     10:  * 
                     11:  *    (i)  Any and all reproductions of the source or object code
                     12:  *         must include the copyright notice above and the following
                     13:  *         disclaimer of warranties; and
                     14:  *    (ii) No rights are granted, in any manner or form, to use
                     15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
                     16:  *         on advertising, endorsements, or otherwise except as such
                     17:  *         appears in the above copyright notice or in the software.
                     18:  * 
                     19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
                     20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
                     21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
                     22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
                     23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
                     24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
                     25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
                     26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
                     27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
                     28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
                     29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
                     30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
                     31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
                     32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
                     33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
                     35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
                     36:  * THE POSSIBILITY OF SUCH DAMAGE.
                     37:  *
                     38:  * Author: Archie Cobbs <archie@freebsd.org>
                     39:  */
                     40: 
                     41: #include <sys/types.h>
                     42: #include <sys/param.h>
                     43: #include <sys/stat.h>
                     44: 
                     45: #include <netinet/in.h>
                     46: 
                     47: #include <stdlib.h>
                     48: #include <stdio.h>
                     49: #include <signal.h>
                     50: #include <assert.h>
                     51: #include <string.h>
                     52: #include <errno.h>
                     53: #include <syslog.h>
                     54: #include <limits.h>
                     55: #include <unistd.h>
                     56: 
                     57: #include <pthread.h>
                     58: 
                     59: #include "structs/structs.h"
                     60: #include "structs/type/array.h"
                     61: #include "structs/xml.h"
                     62: #include "sys/alog.h"
                     63: #include "util/pevent.h"
                     64: #include "util/typed_mem.h"
                     65: #include "config/app_config.h"
                     66: 
                     67: /************************************************************************
                     68:                                DEFINITIONS
                     69: ************************************************************************/
                     70: 
                     71: #define MEM_TYPE               "app_config"
                     72: 
                     73: #define ATTR_VERSION           "version"
                     74: 
                     75: /* Info passed to restart thread */
                     76: struct app_restart {
                     77:        u_int           delay;
                     78:        u_char          have_mutex;
                     79:        u_char          writeback;
                     80:        void            *config;
                     81: };
                     82: 
                     83: #define SS_NEED_STOP   0x01
                     84: #define SS_NEED_START  0x02
                     85: #define SS_RUNNING     0x04
                     86: 
                     87: /* Configuration state flags */
                     88: #define CONFIG_PENDING         0x0001          /* c->pending is valid */
                     89: #define CONFIG_APPLYING                0x0002          /* c->applying is valid */
                     90: #define CONFIG_RESTARTING      0x0004          /* a thread is doing restarts */
                     91: 
                     92: /* Application configuration state */
                     93: struct app_config_ctx {
                     94:        struct pevent_ctx       *ctx;           /* event context */
                     95:        struct app_config       info;           /* application info */
                     96:        void                    *cookie;        /* application cookie */
                     97:        int                     flags;          /* state flags */
                     98:        const struct structs_type *type;        /* config structs type */
                     99:        int                     num_ss;         /* length of info.slist */
                    100:        void                    *current;       /* current configuration */
                    101:        void                    *pending;       /* pending new config */
                    102:        void                    *applying;      /* config being applied */
                    103:        u_char                  *ss_flags;      /* subsystem state flags */
                    104:        char                    *xml_path;      /* xml file pathname */
                    105:        int                     xml_writeback;  /* allow xml writeback */
                    106:        void                    *xml_cache;     /* cached xml file contents */
                    107:        pthread_mutex_t         mutex;          /* mutex for config state */
                    108:        struct pevent           *timer;         /* pending restart timer */
                    109: };
                    110: 
                    111: /************************************************************************
                    112:                                FUNCTIONS
                    113: ************************************************************************/
                    114: 
                    115: /*
                    116:  * Internal functions
                    117:  */
                    118: static const   void *app_config_get_last(struct app_config_ctx *c);
                    119: static int     app_config_equal(struct app_config_ctx *c,
                    120:                        const void *config1, const void *config2);
                    121: static void    app_config_restart(struct app_config_ctx *c);
                    122: static void    app_config_store(struct app_config_ctx *c);
                    123: 
                    124: static pevent_handler_t                app_config_apply;
                    125: static structs_xmllog_t                app_config_xml_logger;
                    126: 
                    127: /*
                    128:  * Initialize the application configuration framework.
                    129:  */
                    130: struct app_config_ctx *
                    131: app_config_init(struct pevent_ctx *ctx,
                    132:        const struct app_config *info, void *cookie)
                    133: {
                    134:        struct app_config_ctx *c;
                    135:        int i, j;
                    136: 
                    137:        /* Sanity check */
                    138:        for (i = 0; i <= info->version; i++) {
                    139:                if (info->types[i] == NULL) {
                    140:                        errno = EINVAL;
                    141:                        return (NULL);
                    142:                }
                    143:        }
                    144: 
                    145:        /* Check each subsystem has a unique name */
                    146:        for (i = 0; info->slist[i] != NULL; i++) {
                    147:                for (j = i + 1; info->slist[j] != NULL; j++) {
                    148:                        if (strcmp(info->slist[i]->name,
                    149:                            info->slist[j]->name) == 0) {
                    150:                                errno = EINVAL;
                    151:                                return (NULL);
                    152:                        }
                    153:                }
                    154:        }
                    155: 
                    156:        /* Create new state object */
                    157:        if ((c = MALLOC(MEM_TYPE, sizeof(*c))) == NULL) {
                    158:                alogf(LOG_ERR, "%s: %m", "malloc");
                    159:                return (NULL);
                    160:        }
                    161:        memset(c, 0, sizeof(*c));
                    162:        c->ctx = ctx;
                    163:        c->cookie = cookie;
                    164: 
                    165:        /* Copy application config */
                    166:        c->info = *info;
                    167:        c->type = c->info.types[c->info.version];
                    168:        c->info.slist = NULL;
                    169: 
                    170:        /* Copy subsystem list */
                    171:        for (c->num_ss = 0; info->slist[c->num_ss] != NULL; c->num_ss++);
                    172:        if ((c->info.slist = MALLOC(MEM_TYPE,
                    173:            c->num_ss * sizeof(*c->info.slist))) == NULL) {
                    174:                alogf(LOG_ERR, "%s: %m", "malloc");
                    175:                goto fail;
                    176:        }
                    177:        memcpy(c->info.slist,
                    178:            info->slist, c->num_ss * sizeof(*c->info.slist));
                    179: 
                    180:        /* Allocate subsystem flags list */
                    181:        if ((c->ss_flags = MALLOC(MEM_TYPE,
                    182:            c->num_ss * sizeof(*c->ss_flags))) == NULL) {
                    183:                alogf(LOG_ERR, "%s: %m", "malloc");
                    184:                goto fail;
                    185:        }
                    186:        memset(c->ss_flags, 0, c->num_ss * sizeof(*c->ss_flags));
                    187: 
                    188:        /* Initialize mutex */
                    189:        if ((errno = pthread_mutex_init(&c->mutex, NULL)) != 0) {
                    190:                alogf(LOG_ERR, "%s: %m", "pthread_mutex_init");
                    191:                goto fail;
                    192:        }
                    193: 
                    194:        /* Done */
                    195:        return (c);
                    196: 
                    197: fail:
                    198:        /* Clean up after failure */
                    199:        FREE(MEM_TYPE, c->ss_flags);
                    200:        FREE(MEM_TYPE, c->info.slist);
                    201:        FREE(MEM_TYPE, c);
                    202:        return (NULL);
                    203: }
                    204: 
                    205: /*
                    206:  * Shutdown and free application configuration stuff.
                    207:  */
                    208: int
                    209: app_config_uninit(struct app_config_ctx **cp)
                    210: {
                    211:        struct app_config_ctx *const c = *cp;
                    212:        int r;
                    213: 
                    214:        /* Sanity check */
                    215:        if (c == NULL)
                    216:                return (0);
                    217: 
                    218:        /* Acquire mutex */
                    219:        r = pthread_mutex_lock(&c->mutex);
                    220:        assert(r == 0);
                    221: 
                    222:        /* Check that nothing is happening and everything is shut down */
                    223:        if (c->current != NULL
                    224:            || (c->flags & (CONFIG_PENDING|CONFIG_RESTARTING)) != 0) {
                    225:                r = pthread_mutex_unlock(&c->mutex);
                    226:                assert(r == 0);
                    227:                errno = EBUSY;
                    228:                return (-1);
                    229:        }
                    230: 
                    231:        /* Nuke caller's reference, release mutex */
                    232:        *cp = NULL;
                    233:        r = pthread_mutex_unlock(&c->mutex);
                    234:        assert(r == 0);
                    235: 
                    236:        /* Destroy config state */
                    237:        pevent_unregister(&c->timer);           /* not really necessary */
                    238:        app_config_free(c, &c->xml_cache);
                    239:        FREE(MEM_TYPE, c->xml_path);
                    240:        pthread_mutex_destroy(&c->mutex);
                    241:        FREE(MEM_TYPE, c->ss_flags);
                    242:        FREE(MEM_TYPE, c->info.slist);
                    243:        FREE(MEM_TYPE, c);
                    244: 
                    245:        /* Done */
                    246:        return (0);
                    247: }
                    248: 
                    249: /*
                    250:  * Get the application cookie.
                    251:  */
                    252: void *
                    253: app_config_get_cookie(struct app_config_ctx *c)
                    254: {
                    255:        return (c->cookie);
                    256: }
                    257: 
                    258: /*
                    259:  * Get the configuration object type.
                    260:  */
                    261: const struct structs_type *
                    262: app_config_get_type(struct app_config_ctx *c)
                    263: {
                    264:        return (c->type);
                    265: }
                    266: 
                    267: /*
                    268:  * Load in application configuration from an XML file and apply it.
                    269:  * The named file becomes the implicit file for app_config_reload().
                    270:  *
                    271:  * If the file does not exist, one is automatically created after
                    272:  * calling the applications "getnew" routine.
                    273:  */
                    274: int
                    275: app_config_load(struct app_config_ctx *c, const char *path, int writeback)
                    276: {
                    277:        void *old_config = NULL;
                    278:        char *attrs = NULL;
                    279:        u_int old_version;
                    280:        FILE *fp = NULL;
                    281:        struct stat sb;
                    282:        int old_cstate;
                    283:        char ebuf[128];
                    284:        void *config;
                    285:        int rtn = -1;
                    286:        char *s;
                    287:        int r;
                    288: 
                    289:        /* Block thread from being canceled */
                    290:        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cstate);
                    291: 
                    292:        /* Create a new configuration with the default values */
                    293:        if ((config = app_config_new(c)) == NULL)
                    294:                goto fail;
                    295: 
                    296:        /* If configuration file is there but empty, remove it */
                    297:        if (stat(path, &sb) == 0 && sb.st_size == 0) {
                    298:                char resolved_path[MAXPATHLEN];
                    299: 
                    300:                if (realpath(path, resolved_path) == NULL)
                    301:                        goto fail;
                    302:                (void)unlink(resolved_path);
                    303:        }
                    304: 
                    305:        /* If configuration file doesn't exist, create one */
                    306:        if (stat(path, &sb) == -1) {
                    307: 
                    308:                /* Bail on any wierd errors */
                    309:                if (errno != ENOENT)
                    310:                        goto fail;
                    311: 
                    312:                /* Create new configuration */
                    313:                if (c->info.getnew != NULL
                    314:                    && (*c->info.getnew)(c, config) == -1)
                    315:                        goto fail;
                    316: 
                    317:                /* Normalize it */
                    318:                if (c->info.normalize != NULL)
                    319:                        (*c->info.normalize)(c, config);
                    320:                goto ready;
                    321:        }
                    322: 
                    323:        /* Open XML config file */
                    324:        if ((fp = fopen(path, "r")) == NULL)
                    325:                goto fail;
                    326: 
                    327:        /* Read top level XML attributes only */
                    328:        r = structs_xml_input(NULL, APP_CONFIG_XML_TAG, &attrs,
                    329:            TYPED_MEM_TEMP, fp, NULL, STRUCTS_XML_SCAN, app_config_xml_logger);
                    330:        if (r == -1) {
                    331:                alog(LOG_ERR, "error reading configuration from \"%s\"", path);
                    332:                goto fail;
                    333:        }
                    334: 
                    335:        /* Find the version number attribute and parse version */
                    336:        for (s = attrs, old_version = 0; *s != '\0'; s += strlen(s) + 1) {
                    337:                u_long vers;
                    338:                char *eptr;
                    339:                int match;
                    340: 
                    341:                match = (strcmp(s, ATTR_VERSION) == 0);
                    342:                s += strlen(s) + 1;
                    343:                if (!match)
                    344:                        continue;
                    345:                vers = strtoul(s, &eptr, 10);
                    346:                if (*s == '\0' || *eptr != '\0') {
                    347:                        alog(LOG_ERR, "\"%s\" contains invalid version \"%s\"",
                    348:                            path, s);
                    349:                        FREE(TYPED_MEM_TEMP, attrs);
                    350:                        errno = EPROGMISMATCH;
                    351:                        goto fail;
                    352:                }
                    353:                old_version = vers;
                    354:                break;
                    355:        }
                    356:        FREE(TYPED_MEM_TEMP, attrs);
                    357: 
                    358:        /* Sanity check version and rewind file */
                    359:        if (old_version > c->info.version) {
                    360:                alog(LOG_ERR, "\"%s\" contains newer version %d > %u",
                    361:                    path, old_version, c->info.version);
                    362:                errno = EPROGMISMATCH;
                    363:                goto fail;
                    364:        }
                    365:        if (fseek(fp, 0, SEEK_SET) == -1)
                    366:                goto fail;
                    367: 
                    368:        /* Upgrade older format if necessary */
                    369:        if (old_version < c->info.version) {
                    370:                const struct structs_type *const old_type
                    371:                    = c->info.types[old_version];
                    372: 
                    373:                /* Do we have an upgrade method? */
                    374:                if (c->info.upgrade == NULL) {
                    375:                        alog(LOG_ERR,
                    376:                            "\"%s\" contains obsolete version %d < %u",
                    377:                            path, old_version, c->info.version);
                    378:                        errno = EPROGMISMATCH;
                    379:                        goto fail;
                    380:                }
                    381: 
                    382:                /* Log it */
                    383:                alog(LOG_INFO, "\"%s\" contains old version %d < %u, upgrading",
                    384:                    path, old_version, c->info.version);
                    385: 
                    386:                /* Create an (uninitialized) old config structure */
                    387:                if ((old_config = MALLOC(MEM_TYPE, old_type->size)) == NULL)
                    388:                        goto fail;
                    389: 
                    390:                /* Read in the old version XML (and initialize) */
                    391:                r = structs_xml_input(old_type, APP_CONFIG_XML_TAG, NULL,
                    392:                    TYPED_MEM_TEMP, fp, old_config,
                    393:                    STRUCTS_XML_UNINIT | STRUCTS_XML_LOOSE,
                    394:                    app_config_xml_logger);
                    395:                fclose(fp);
                    396:                fp = NULL;
                    397:                if (r == -1) {
                    398:                        alog(LOG_ERR,
                    399:                            "error reading configuration from \"%s\"", path);
                    400:                        FREE(MEM_TYPE, old_config);
                    401:                        goto fail;
                    402:                }
                    403: 
                    404:                /* Upgrade from the old version to the new version */
                    405:                r = (*c->info.upgrade)(c, old_config, old_version, config);
                    406:                structs_free(old_type, NULL, old_config);
                    407:                FREE(MEM_TYPE, old_config);
                    408:                if (r == -1) {
                    409:                        alog(LOG_ERR,
                    410:                            "error upgrading configuration \"%s\" version"
                    411:                            " from %d -> %u", path,
                    412:                            old_version, c->info.version);
                    413:                        goto fail;
                    414:                }
                    415:        } else {
                    416: 
                    417:                /* Version is correct, read in configuration */
                    418:                r = structs_xml_input(c->type, APP_CONFIG_XML_TAG, NULL,
                    419:                    TYPED_MEM_TEMP, fp, config, STRUCTS_XML_LOOSE,
                    420:                    app_config_xml_logger);
                    421:                fclose(fp);
                    422:                fp = NULL;
                    423:                if (r == -1) {
                    424:                        alog(LOG_ERR,
                    425:                            "error reading configuration from \"%s\"", path);
                    426:                        goto fail;
                    427:                }
                    428:        }
                    429: 
                    430: ready:
                    431:        /* Remember path and writeback settings */
                    432:        r = pthread_mutex_lock(&c->mutex);
                    433:        assert(r == 0);
                    434:        FREE(MEM_TYPE, c->xml_path);
                    435:        if ((c->xml_path = STRDUP(MEM_TYPE, path)) == NULL)
                    436:                alogf(LOG_ERR, "%s: %m", "strdup");
                    437:        app_config_free(c, &c->xml_cache);
                    438:        c->xml_writeback = writeback;
                    439:        r = pthread_mutex_unlock(&c->mutex);
                    440:        assert(r == 0);
                    441: 
                    442:        /* Apply configuration read from file */
                    443:        if ((rtn = app_config_set(c, config, 0, ebuf, sizeof(ebuf))) == -1) {
                    444:                alog(LOG_ERR, "error applying configuration from \"%s\": %s",
                    445:                    path, ebuf);
                    446:        }
                    447: 
                    448: fail:
                    449:        /* Done */
                    450:        app_config_free(c, &config);
                    451:        if (fp != NULL)
                    452:                fclose(fp);
                    453:        pthread_setcancelstate(old_cstate, NULL);
                    454:        return (rtn);
                    455: }
                    456: 
                    457: /*
                    458:  * Re-read the XML file passed to app_config_load() and reconfigure
                    459:  * as appropriate.
                    460:  */
                    461: int
                    462: app_config_reload(struct app_config_ctx *c)
                    463: {
                    464:        int writeback;
                    465:        char *path;
                    466:        int rtn;
                    467:        int r;
                    468: 
                    469:        /* Get the relevant info */
                    470:        r = pthread_mutex_lock(&c->mutex);
                    471:        assert(r == 0);
                    472:        if (c->xml_path == NULL) {
                    473:                r = pthread_mutex_unlock(&c->mutex);
                    474:                assert(r == 0);
                    475:                errno = ENXIO;
                    476:                return (-1);
                    477:        }
                    478:        if ((path = STRDUP(TYPED_MEM_TEMP, c->xml_path)) == NULL) {
                    479:                r = pthread_mutex_unlock(&c->mutex);
                    480:                assert(r == 0);
                    481:                return (-1);
                    482:        }
                    483:        writeback = c->xml_writeback;
                    484:        r = pthread_mutex_unlock(&c->mutex);
                    485:        assert(r == 0);
                    486: 
                    487:        /* Apply it */
                    488:        rtn = app_config_load(c, path, writeback);
                    489: 
                    490:        /* Done */
                    491:        FREE(TYPED_MEM_TEMP, path);
                    492:        return (rtn);
                    493: }
                    494: 
                    495: /*
                    496:  * Function to create a configuration structure. This structure will
                    497:  * have the application's default values.
                    498:  */
                    499: void *
                    500: app_config_new(struct app_config_ctx *c)
                    501: {
                    502:        static int did_check;
                    503:        void *config;
                    504: 
                    505:        /* Create initialized structure */
                    506:        if ((config = MALLOC(MEM_TYPE, c->type->size)) == NULL)
                    507:                return (NULL);
                    508:        if (structs_init(c->type, NULL, config) == -1) {
                    509:                FREE(MEM_TYPE, config);
                    510:                return (NULL);
                    511:        }
                    512: 
                    513:        /* Apply application defaults */
                    514:        if (c->info.init != NULL && (*c->info.init)(c, config) == -1) {
                    515:                structs_free(c->type, NULL, config);
                    516:                FREE(MEM_TYPE, config);
                    517:                return (NULL);
                    518:        }
                    519: 
                    520:        /* Sanity check that default configuration is really valid */
                    521:        if (!did_check) {
                    522:                char buf[512];
                    523: 
                    524:                snprintf(buf, sizeof(buf), "generic problem");
                    525:                if (c->info.checker != NULL
                    526:                    && !(*c->info.checker)(c, config, buf, sizeof(buf))) {
                    527:                        alog(LOG_CRIT,
                    528:                            "default configuration is invalid: %s", buf);
                    529:                }
                    530:                did_check = 1;
                    531:        }
                    532:        return (config);
                    533: }
                    534: 
                    535: /*
                    536:  * Get the application's most recently applied configuration.
                    537:  *
                    538:  * This assumes the mutex is held.
                    539:  */
                    540: static const void *
                    541: app_config_get_last(struct app_config_ctx *c)
                    542: {
                    543:        return (((c->flags & CONFIG_PENDING) != 0) ? c->pending
                    544:            : ((c->flags & CONFIG_APPLYING) != 0) ? c->applying
                    545:            : c->current);
                    546: }
                    547: 
                    548: /*
                    549:  * Compare two configurations, either of which may be NULL.
                    550:  */
                    551: static int
                    552: app_config_equal(struct app_config_ctx *c,
                    553:        const void *config1, const void *config2)
                    554: {
                    555:        if (config1 == NULL && config2 == NULL)
                    556:                return (1);
                    557:        if (config1 == NULL || config2 == NULL)
                    558:                return (0);
                    559:        return (structs_equal(c->type, NULL, config1, config2));
                    560: }
                    561: 
                    562: /*
                    563:  * Get a copy of the application's current or pending configuration.
                    564:  */
                    565: void *
                    566: app_config_get(struct app_config_ctx *c, int pending)
                    567: {
                    568:        void *copy;
                    569:        int r;
                    570: 
                    571:        /* Get copy of config */
                    572:        r = pthread_mutex_lock(&c->mutex);
                    573:        assert(r == 0);
                    574:        copy = app_config_copy(c, pending ?
                    575:            app_config_get_last(c) : c->current);
                    576:        r = pthread_mutex_unlock(&c->mutex);
                    577:        assert(r == 0);
                    578: 
                    579:        /* Done */
                    580:        return (copy);
                    581: }
                    582: 
                    583: /*
                    584:  * Get a copy of a configuration.
                    585:  */
                    586: void *
                    587: app_config_copy(struct app_config_ctx *c, const void *config)
                    588: {
                    589:        void *copy;
                    590: 
                    591:        if (config == NULL)
                    592:                return (NULL);
                    593:        if ((copy = MALLOC(MEM_TYPE, c->type->size)) == NULL) {
                    594:                alogf(LOG_ERR, "%s: %m", "malloc");
                    595:                return (NULL);
                    596:        }
                    597:        if (structs_get(c->type, NULL, config, copy) == -1) {
                    598:                alogf(LOG_ERR, "%s: %m", "structs_get");
                    599:                FREE(MEM_TYPE, copy);
                    600:                return (NULL);
                    601:        }
                    602:        return (copy);
                    603: }
                    604: 
                    605: /*
                    606:  * Free a configuration.
                    607:  */
                    608: void
                    609: app_config_free(struct app_config_ctx *c, void **configp)
                    610: {
                    611:        void *const config = *configp;
                    612: 
                    613:        if (config == NULL)
                    614:                return;
                    615:        *configp = NULL;
                    616:        structs_free(c->type, NULL, config);
                    617:        FREE(MEM_TYPE, config);
                    618: }
                    619: 
                    620: /*
                    621:  * Change the application's current configuration.
                    622:  */
                    623: int
                    624: app_config_set(struct app_config_ctx *c,
                    625:        const void *config0, u_long delay, char *ebuf, int emax)
                    626: {
                    627:        struct pevent_info info;
                    628:        char buf[512];
                    629:        void *config;
                    630:        int rtn = 0;
                    631:        int r;
                    632: 
                    633:        /* Allow NULL error buffer to be passed; initialize buffer */
                    634:        if (ebuf == NULL) {
                    635:                ebuf = buf;
                    636:                emax = sizeof(buf);
                    637:        }
                    638:        snprintf(ebuf, emax, "unknown problem");
                    639: 
                    640:        /* Copy config */
                    641:        if (config0 == NULL)
                    642:                config = NULL;
                    643:        else if ((config = app_config_copy(c, config0)) == NULL)
                    644:                return (-1);
                    645: 
                    646:        /* Normalize it */
                    647:        if (config != NULL && c->info.normalize != NULL)
                    648:                (*c->info.normalize)(c, config);
                    649: 
                    650:        /* Validate the new configuration */
                    651:        if (config != NULL
                    652:            && c->info.checker != NULL
                    653:            && !(*c->info.checker)(c, config, ebuf, emax)) {
                    654:                app_config_free(c, &config);
                    655:                errno = EINVAL;
                    656:                return (-1);
                    657:        }
                    658: 
                    659:        /* Acquire mutex */
                    660:        r = pthread_mutex_lock(&c->mutex);
                    661:        assert(r == 0);
                    662: 
                    663:        /* Ignore new configs while a shutdown is pending */
                    664:        if ((c->timer != NULL && c->pending == NULL)
                    665:            || ((c->flags & CONFIG_RESTARTING) != 0 && c->applying == NULL)) {
                    666:                app_config_free(c, &config);
                    667:                goto done;
                    668:        }
                    669: 
                    670:        /* See if new config is really new */
                    671:        if (app_config_equal(c, config, app_config_get_last(c))) {
                    672:                app_config_free(c, &config);    /* same as what we got */
                    673:                goto done;
                    674:        }
                    675: 
                    676:        /* Update pending configuration */
                    677:        app_config_free(c, &c->pending);
                    678:        c->pending = config;
                    679:        c->flags |= CONFIG_PENDING;
                    680: 
                    681:        /* If a previous restart thread is still running, we're done */
                    682:        if ((c->flags & CONFIG_RESTARTING) != 0)
                    683:                goto done;
                    684: 
                    685:        /* If the restart timer is not running, need to start it */
                    686:        if (c->timer == NULL)
                    687:                goto start_timer;
                    688: 
                    689:        /* Get time remaining on the restart timer */
                    690:        if (pevent_get_info(c->timer, &info) == -1) {
                    691:                alogf(LOG_ERR, "%s: %m", "pevent_get_info");
                    692:                goto start_timer;
                    693:        }
                    694: 
                    695:        /* See if we need a shorter delay */
                    696:        if (delay > INT_MAX || delay >= info.u.millis)
                    697:                goto done;              /* existing timeout is sufficient */
                    698: 
                    699: start_timer:
                    700:        /* Cancel previous timer */
                    701:        pevent_unregister(&c->timer);
                    702: 
                    703:        /* Start restart timer */
                    704:        if (pevent_register(c->ctx, &c->timer, PEVENT_OWN_THREAD,
                    705:            &c->mutex, app_config_apply, c, PEVENT_TIME, delay) == -1) {
                    706:                alogf(LOG_ERR, "%s: %m", "pevent_register");
                    707:                rtn = -1;
                    708:                goto done;
                    709:        }
                    710: 
                    711: done:
                    712:        /* Done */
                    713:        r = pthread_mutex_unlock(&c->mutex);
                    714:        assert(r == 0);
                    715:        return (rtn);
                    716: }
                    717: 
                    718: /*
                    719:  * Write the current configuration back out to XML config file.
                    720:  *
                    721:  * This assumes c->current != NULL and the mutex is locked.
                    722:  */
                    723: static void
                    724: app_config_store(struct app_config_ctx *c)
                    725: {
                    726:        char attrs[sizeof(ATTR_VERSION) + 32];
                    727:        char *tpath = NULL;
                    728:        FILE *fp = NULL;
                    729:        int old_cstate;
                    730: 
                    731:        /* Get configuration and XML pathname */
                    732:        assert(c->xml_path != NULL);
                    733: 
                    734:        /* Block thread from being canceled */
                    735:        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cstate);
                    736: 
                    737:        /* Check if configuration is different from cached file contents */
                    738:        if (c->xml_cache != NULL
                    739:            && structs_equal(c->type, NULL, c->current, c->xml_cache))
                    740:                goto done;
                    741: 
                    742:        /* Set version attribute */
                    743:        snprintf(attrs, sizeof(attrs), "%s%c%u%c",
                    744:            ATTR_VERSION, '\0', c->info.version, '\0');
                    745: 
                    746:        /* Write out temporary new file */
                    747:        ASPRINTF(TYPED_MEM_TEMP, &tpath, "%s.new", c->xml_path);
                    748:        if (tpath == NULL) {
                    749:                alogf(LOG_ERR, "%s: %m", "asprintf");
                    750:                goto done;
                    751:        }
                    752:        if ((fp = fopen(tpath, "w")) == NULL) {
                    753:                alogf(LOG_ERR, "%s: %m", tpath);
                    754:                goto done;
                    755:        }
                    756:        if (structs_xml_output(c->type, APP_CONFIG_XML_TAG,
                    757:            attrs, c->current, fp, NULL, STRUCTS_XML_FULL) == -1) {
                    758:                alogf(LOG_ERR, "%s: %m", "structs_xml_output");
                    759:                (void)unlink(tpath);
                    760:                goto done;
                    761:        }
                    762:        fflush(fp);
                    763:        fsync(fileno(fp));
                    764:        fclose(fp);
                    765:        fp = NULL;
                    766: 
                    767:        /* Atomically overwrite exsting file with new file using rename(2) */
                    768:        if (rename(tpath, c->xml_path) == -1) {
                    769:                alogf(LOG_ERR, "%s(%s, %s): %m", "rename", tpath, c->xml_path);
                    770:                (void)unlink(tpath);
                    771:                goto done;
                    772:        }
                    773: 
                    774:        /* Cache file contents */
                    775:        app_config_free(c, &c->xml_cache);
                    776:        if ((c->xml_cache = app_config_copy(c, c->current)) == NULL)
                    777:                alogf(LOG_ERR, "%s: %m", "app_config_copy");
                    778: 
                    779: done:
                    780:        /* Clean up */
                    781:        if (fp != NULL)
                    782:                fclose(fp);
                    783:        if (tpath != NULL)
                    784:                FREE(TYPED_MEM_TEMP, tpath);
                    785:        pthread_setcancelstate(old_cstate, NULL);
                    786: }
                    787: 
                    788: /*
                    789:  * Entry point for restarter thread.
                    790:  *
                    791:  * The mutex will be acquired when we enter and released when we return.
                    792:  */
                    793: static void
                    794: app_config_apply(void *arg)
                    795: {
                    796:        struct app_config_ctx *const c = arg;
                    797: 
                    798:        /* Sanity checks */
                    799:        assert((c->flags & CONFIG_APPLYING) == 0);
                    800:        assert((c->flags & CONFIG_RESTARTING) == 0);
                    801:        assert(c->applying == NULL);
                    802: 
                    803:        /* Set flag indicating this thread is applying new configs */
                    804:        c->flags |= CONFIG_RESTARTING;
                    805: 
                    806:        /* Keep applying configurations until there are no more new ones */
                    807:        while ((c->flags & CONFIG_PENDING) != 0) {
                    808:                int r;
                    809: 
                    810:                /* Make sure latest new configuration is different */
                    811:                if (app_config_equal(c, c->pending, c->current)) {
                    812:                        app_config_free(c, &c->pending);
                    813:                        c->flags &= ~CONFIG_PENDING;
                    814:                        goto done;
                    815:                }
                    816: 
                    817:                /* Grab pending config and apply it */
                    818:                c->applying = c->pending;
                    819:                c->pending = NULL;
                    820:                c->flags &= ~CONFIG_PENDING;
                    821:                c->flags |= CONFIG_APPLYING;
                    822: 
                    823:                /* Restart subsystems */
                    824:                r = pthread_mutex_unlock(&c->mutex);
                    825:                assert(r == 0);
                    826:                app_config_restart(c);
                    827:                r = pthread_mutex_lock(&c->mutex);
                    828:                assert(r == 0);
                    829:        }
                    830: 
                    831:        /* Save new configuration to disk */
                    832:        if (c->xml_writeback && c->current != NULL)
                    833:                app_config_store(c);
                    834: 
                    835: done:
                    836:        /* Done */
                    837:        c->flags &= ~CONFIG_RESTARTING;
                    838: }
                    839: 
                    840: /*
                    841:  * Restart subsystems to effect the new configuration 'config'.
                    842:  *
                    843:  * This assumes the mutex is NOT locked.
                    844:  */
                    845: static void
                    846: app_config_restart(struct app_config_ctx *c)
                    847: {
                    848:        int i;
                    849:        int r;
                    850: 
                    851:        /* Initialize actions */
                    852:        for (i = 0; i < c->num_ss; i++)
                    853:                c->ss_flags[i] &= ~(SS_NEED_STOP|SS_NEED_START);
                    854: 
                    855:        /* Determine what needs to be shutdown and started up */
                    856:        for (i = 0; i < c->num_ss; i++) {
                    857:                const struct app_subsystem *const ss = c->info.slist[i];
                    858: 
                    859:                /* Do simple analysis */
                    860:                if (c->current != NULL
                    861:                    && (c->ss_flags[i] & SS_RUNNING) != 0)
                    862:                        c->ss_flags[i] |= SS_NEED_STOP;
                    863:                if (c->applying != NULL
                    864:                    && (ss->willrun == NULL
                    865:                      || (*ss->willrun)(c, ss, c->applying) != 0))
                    866:                        c->ss_flags[i] |= SS_NEED_START;
                    867: 
                    868:                /* Optimize away unnecessary restarts */
                    869:                if ((c->ss_flags[i] & (SS_NEED_STOP|SS_NEED_START))
                    870:                    == (SS_NEED_STOP|SS_NEED_START)) {
                    871: 
                    872:                        /* If either is NULL, the other isn't, so must do it */
                    873:                        if (c->current == NULL || c->applying == NULL)
                    874:                                goto no_optimize;
                    875: 
                    876:                        /* Check dependency list */
                    877:                        if (ss->deplist != NULL) {
                    878:                                int j;
                    879: 
                    880:                                for (j = 0; ss->deplist[j] != NULL; j++) {
                    881:                                        if ((r = structs_equal(c->type,
                    882:                                            ss->deplist[j], c->current,
                    883:                                            c->applying)) != 1) {
                    884:                                                if (r != -1)
                    885:                                                        goto no_optimize;
                    886:                                                alogf(LOG_ERR, "%s(%s): %m",
                    887:                                                    "structs_equal",
                    888:                                                    ss->deplist[j]);
                    889:                                        }
                    890:                                }
                    891:                        }
                    892: 
                    893:                        /* Check using subsystem's method */
                    894:                        if (ss->changed != NULL
                    895:                            && (*ss->changed)(c, ss, c->current, c->applying))
                    896:                                goto no_optimize;
                    897: 
                    898:                        /* Optimize away restart because it's not needed */
                    899:                        c->ss_flags[i] &= ~(SS_NEED_STOP|SS_NEED_START);
                    900: no_optimize:;
                    901:                }
                    902:        }
                    903: 
                    904:        /* Shut stuff down */
                    905:        for (i = c->num_ss - 1; i >= 0; i--) {
                    906:                const struct app_subsystem *const ss = c->info.slist[i];
                    907: 
                    908:                if ((c->ss_flags[i] & SS_NEED_STOP) != 0) {
                    909:                        alog(LOG_DEBUG, "stopping subsystem \"%s\"", ss->name);
                    910:                        if (ss->stop != NULL)
                    911:                                (*ss->stop)(c, ss, c->current);
                    912:                        c->ss_flags[i] &= ~SS_RUNNING;
                    913:                }
                    914:        }
                    915: 
                    916:        /* Update current configuration */
                    917:        r = pthread_mutex_lock(&c->mutex);
                    918:        assert(r == 0);
                    919:        assert((c->flags & CONFIG_APPLYING) != 0);
                    920:        assert((c->flags & CONFIG_RESTARTING) != 0);
                    921:        app_config_free(c, &c->current);
                    922:        c->current = c->applying;
                    923:        c->applying = NULL;
                    924:        c->flags &= ~CONFIG_APPLYING;
                    925:        r = pthread_mutex_unlock(&c->mutex);
                    926:        assert(r == 0);
                    927: 
                    928:        /* If new config is NULL, we've just successfully shut down */
                    929:        if (c->current == NULL)
                    930:                return;
                    931: 
                    932:        /* Start stuff up */
                    933:        for (i = 0; i < c->num_ss; i++) {
                    934:                const struct app_subsystem *const ss = c->info.slist[i];
                    935: 
                    936:                if ((c->ss_flags[i] & SS_NEED_START) != 0) {
                    937:                        alog(LOG_DEBUG, "starting subsystem \"%s\"", ss->name);
                    938:                        if (ss->start != NULL
                    939:                            && (*ss->start)(c, ss, c->current) == -1) {
                    940:                                alog(LOG_ERR, "subsystem \"%s\" startup failed",
                    941:                                    ss->name);
                    942:                                continue;
                    943:                        }
                    944:                        c->ss_flags[i] |= SS_RUNNING;
                    945:                }
                    946:        }
                    947: }
                    948: 
                    949: /*
                    950:  * Logger for XML parsing
                    951:  */
                    952: static void
                    953: app_config_xml_logger(int sev, const char *fmt, ...)
                    954: {
                    955:        char buf[512];
                    956:        va_list args;
                    957: 
                    958:        snprintf(buf, sizeof(buf), "XML error: ");
                    959:        va_start(args, fmt);
                    960:        vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, args);
                    961:        va_end(args);
                    962:        alog(sev, "%s", buf);
                    963: }
                    964: 

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