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>