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>