Annotation of embedaddon/libpdel/config/app_config.c, revision 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>