/*
* Copyright (c) 2001-2002 Packet Design, LLC.
* All rights reserved.
*
* Subject to the following obligations and disclaimer of warranty,
* use and redistribution of this software, in source or object code
* forms, with or without modifications are expressly permitted by
* Packet Design; provided, however, that:
*
* (i) Any and all reproductions of the source or object code
* must include the copyright notice above and the following
* disclaimer of warranties; and
* (ii) No rights are granted, in any manner or form, to use
* Packet Design trademarks, including the mark "PACKET DESIGN"
* on advertising, endorsements, or otherwise except as such
* appears in the above copyright notice or in the software.
*
* THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
* THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
* OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
* OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
* OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
* RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
* LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
* OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
* DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
* USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Archie Cobbs <archie@freebsd.org>
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <limits.h>
#include <unistd.h>
#include <pthread.h>
#include "structs/structs.h"
#include "structs/type/array.h"
#include "structs/xml.h"
#include "sys/alog.h"
#include "util/pevent.h"
#include "util/typed_mem.h"
#include "config/app_config.h"
/************************************************************************
DEFINITIONS
************************************************************************/
#define MEM_TYPE "app_config"
#define ATTR_VERSION "version"
/* Info passed to restart thread */
struct app_restart {
u_int delay;
u_char have_mutex;
u_char writeback;
void *config;
};
#define SS_NEED_STOP 0x01
#define SS_NEED_START 0x02
#define SS_RUNNING 0x04
/* Configuration state flags */
#define CONFIG_PENDING 0x0001 /* c->pending is valid */
#define CONFIG_APPLYING 0x0002 /* c->applying is valid */
#define CONFIG_RESTARTING 0x0004 /* a thread is doing restarts */
/* Application configuration state */
struct app_config_ctx {
struct pevent_ctx *ctx; /* event context */
struct app_config info; /* application info */
void *cookie; /* application cookie */
int flags; /* state flags */
const struct structs_type *type; /* config structs type */
int num_ss; /* length of info.slist */
void *current; /* current configuration */
void *pending; /* pending new config */
void *applying; /* config being applied */
u_char *ss_flags; /* subsystem state flags */
char *xml_path; /* xml file pathname */
int xml_writeback; /* allow xml writeback */
void *xml_cache; /* cached xml file contents */
pthread_mutex_t mutex; /* mutex for config state */
struct pevent *timer; /* pending restart timer */
};
/************************************************************************
FUNCTIONS
************************************************************************/
/*
* Internal functions
*/
static const void *app_config_get_last(struct app_config_ctx *c);
static int app_config_equal(struct app_config_ctx *c,
const void *config1, const void *config2);
static void app_config_restart(struct app_config_ctx *c);
static void app_config_store(struct app_config_ctx *c);
static pevent_handler_t app_config_apply;
static structs_xmllog_t app_config_xml_logger;
/*
* Initialize the application configuration framework.
*/
struct app_config_ctx *
app_config_init(struct pevent_ctx *ctx,
const struct app_config *info, void *cookie)
{
struct app_config_ctx *c;
int i, j;
/* Sanity check */
for (i = 0; i <= info->version; i++) {
if (info->types[i] == NULL) {
errno = EINVAL;
return (NULL);
}
}
/* Check each subsystem has a unique name */
for (i = 0; info->slist[i] != NULL; i++) {
for (j = i + 1; info->slist[j] != NULL; j++) {
if (strcmp(info->slist[i]->name,
info->slist[j]->name) == 0) {
errno = EINVAL;
return (NULL);
}
}
}
/* Create new state object */
if ((c = MALLOC(MEM_TYPE, sizeof(*c))) == NULL) {
alogf(LOG_ERR, "%s: %m", "malloc");
return (NULL);
}
memset(c, 0, sizeof(*c));
c->ctx = ctx;
c->cookie = cookie;
/* Copy application config */
c->info = *info;
c->type = c->info.types[c->info.version];
c->info.slist = NULL;
/* Copy subsystem list */
for (c->num_ss = 0; info->slist[c->num_ss] != NULL; c->num_ss++);
if ((c->info.slist = MALLOC(MEM_TYPE,
c->num_ss * sizeof(*c->info.slist))) == NULL) {
alogf(LOG_ERR, "%s: %m", "malloc");
goto fail;
}
memcpy(c->info.slist,
info->slist, c->num_ss * sizeof(*c->info.slist));
/* Allocate subsystem flags list */
if ((c->ss_flags = MALLOC(MEM_TYPE,
c->num_ss * sizeof(*c->ss_flags))) == NULL) {
alogf(LOG_ERR, "%s: %m", "malloc");
goto fail;
}
memset(c->ss_flags, 0, c->num_ss * sizeof(*c->ss_flags));
/* Initialize mutex */
if ((errno = pthread_mutex_init(&c->mutex, NULL)) != 0) {
alogf(LOG_ERR, "%s: %m", "pthread_mutex_init");
goto fail;
}
/* Done */
return (c);
fail:
/* Clean up after failure */
FREE(MEM_TYPE, c->ss_flags);
FREE(MEM_TYPE, c->info.slist);
FREE(MEM_TYPE, c);
return (NULL);
}
/*
* Shutdown and free application configuration stuff.
*/
int
app_config_uninit(struct app_config_ctx **cp)
{
struct app_config_ctx *const c = *cp;
int r;
/* Sanity check */
if (c == NULL)
return (0);
/* Acquire mutex */
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
/* Check that nothing is happening and everything is shut down */
if (c->current != NULL
|| (c->flags & (CONFIG_PENDING|CONFIG_RESTARTING)) != 0) {
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
errno = EBUSY;
return (-1);
}
/* Nuke caller's reference, release mutex */
*cp = NULL;
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
/* Destroy config state */
pevent_unregister(&c->timer); /* not really necessary */
app_config_free(c, &c->xml_cache);
FREE(MEM_TYPE, c->xml_path);
pthread_mutex_destroy(&c->mutex);
FREE(MEM_TYPE, c->ss_flags);
FREE(MEM_TYPE, c->info.slist);
FREE(MEM_TYPE, c);
/* Done */
return (0);
}
/*
* Get the application cookie.
*/
void *
app_config_get_cookie(struct app_config_ctx *c)
{
return (c->cookie);
}
/*
* Get the configuration object type.
*/
const struct structs_type *
app_config_get_type(struct app_config_ctx *c)
{
return (c->type);
}
/*
* Load in application configuration from an XML file and apply it.
* The named file becomes the implicit file for app_config_reload().
*
* If the file does not exist, one is automatically created after
* calling the applications "getnew" routine.
*/
int
app_config_load(struct app_config_ctx *c, const char *path, int writeback)
{
void *old_config = NULL;
char *attrs = NULL;
u_int old_version;
FILE *fp = NULL;
struct stat sb;
int old_cstate;
char ebuf[128];
void *config;
int rtn = -1;
char *s;
int r;
/* Block thread from being canceled */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cstate);
/* Create a new configuration with the default values */
if ((config = app_config_new(c)) == NULL)
goto fail;
/* If configuration file is there but empty, remove it */
if (stat(path, &sb) == 0 && sb.st_size == 0) {
char resolved_path[MAXPATHLEN];
if (realpath(path, resolved_path) == NULL)
goto fail;
(void)unlink(resolved_path);
}
/* If configuration file doesn't exist, create one */
if (stat(path, &sb) == -1) {
/* Bail on any wierd errors */
if (errno != ENOENT)
goto fail;
/* Create new configuration */
if (c->info.getnew != NULL
&& (*c->info.getnew)(c, config) == -1)
goto fail;
/* Normalize it */
if (c->info.normalize != NULL)
(*c->info.normalize)(c, config);
goto ready;
}
/* Open XML config file */
if ((fp = fopen(path, "r")) == NULL)
goto fail;
/* Read top level XML attributes only */
r = structs_xml_input(NULL, APP_CONFIG_XML_TAG, &attrs,
TYPED_MEM_TEMP, fp, NULL, STRUCTS_XML_SCAN, app_config_xml_logger);
if (r == -1) {
alog(LOG_ERR, "error reading configuration from \"%s\"", path);
goto fail;
}
/* Find the version number attribute and parse version */
for (s = attrs, old_version = 0; *s != '\0'; s += strlen(s) + 1) {
u_long vers;
char *eptr;
int match;
match = (strcmp(s, ATTR_VERSION) == 0);
s += strlen(s) + 1;
if (!match)
continue;
vers = strtoul(s, &eptr, 10);
if (*s == '\0' || *eptr != '\0') {
alog(LOG_ERR, "\"%s\" contains invalid version \"%s\"",
path, s);
FREE(TYPED_MEM_TEMP, attrs);
errno = EPROGMISMATCH;
goto fail;
}
old_version = vers;
break;
}
FREE(TYPED_MEM_TEMP, attrs);
/* Sanity check version and rewind file */
if (old_version > c->info.version) {
alog(LOG_ERR, "\"%s\" contains newer version %d > %u",
path, old_version, c->info.version);
errno = EPROGMISMATCH;
goto fail;
}
if (fseek(fp, 0, SEEK_SET) == -1)
goto fail;
/* Upgrade older format if necessary */
if (old_version < c->info.version) {
const struct structs_type *const old_type
= c->info.types[old_version];
/* Do we have an upgrade method? */
if (c->info.upgrade == NULL) {
alog(LOG_ERR,
"\"%s\" contains obsolete version %d < %u",
path, old_version, c->info.version);
errno = EPROGMISMATCH;
goto fail;
}
/* Log it */
alog(LOG_INFO, "\"%s\" contains old version %d < %u, upgrading",
path, old_version, c->info.version);
/* Create an (uninitialized) old config structure */
if ((old_config = MALLOC(MEM_TYPE, old_type->size)) == NULL)
goto fail;
/* Read in the old version XML (and initialize) */
r = structs_xml_input(old_type, APP_CONFIG_XML_TAG, NULL,
TYPED_MEM_TEMP, fp, old_config,
STRUCTS_XML_UNINIT | STRUCTS_XML_LOOSE,
app_config_xml_logger);
fclose(fp);
fp = NULL;
if (r == -1) {
alog(LOG_ERR,
"error reading configuration from \"%s\"", path);
FREE(MEM_TYPE, old_config);
goto fail;
}
/* Upgrade from the old version to the new version */
r = (*c->info.upgrade)(c, old_config, old_version, config);
structs_free(old_type, NULL, old_config);
FREE(MEM_TYPE, old_config);
if (r == -1) {
alog(LOG_ERR,
"error upgrading configuration \"%s\" version"
" from %d -> %u", path,
old_version, c->info.version);
goto fail;
}
} else {
/* Version is correct, read in configuration */
r = structs_xml_input(c->type, APP_CONFIG_XML_TAG, NULL,
TYPED_MEM_TEMP, fp, config, STRUCTS_XML_LOOSE,
app_config_xml_logger);
fclose(fp);
fp = NULL;
if (r == -1) {
alog(LOG_ERR,
"error reading configuration from \"%s\"", path);
goto fail;
}
}
ready:
/* Remember path and writeback settings */
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
FREE(MEM_TYPE, c->xml_path);
if ((c->xml_path = STRDUP(MEM_TYPE, path)) == NULL)
alogf(LOG_ERR, "%s: %m", "strdup");
app_config_free(c, &c->xml_cache);
c->xml_writeback = writeback;
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
/* Apply configuration read from file */
if ((rtn = app_config_set(c, config, 0, ebuf, sizeof(ebuf))) == -1) {
alog(LOG_ERR, "error applying configuration from \"%s\": %s",
path, ebuf);
}
fail:
/* Done */
app_config_free(c, &config);
if (fp != NULL)
fclose(fp);
pthread_setcancelstate(old_cstate, NULL);
return (rtn);
}
/*
* Re-read the XML file passed to app_config_load() and reconfigure
* as appropriate.
*/
int
app_config_reload(struct app_config_ctx *c)
{
int writeback;
char *path;
int rtn;
int r;
/* Get the relevant info */
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
if (c->xml_path == NULL) {
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
errno = ENXIO;
return (-1);
}
if ((path = STRDUP(TYPED_MEM_TEMP, c->xml_path)) == NULL) {
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
return (-1);
}
writeback = c->xml_writeback;
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
/* Apply it */
rtn = app_config_load(c, path, writeback);
/* Done */
FREE(TYPED_MEM_TEMP, path);
return (rtn);
}
/*
* Function to create a configuration structure. This structure will
* have the application's default values.
*/
void *
app_config_new(struct app_config_ctx *c)
{
static int did_check;
void *config;
/* Create initialized structure */
if ((config = MALLOC(MEM_TYPE, c->type->size)) == NULL)
return (NULL);
if (structs_init(c->type, NULL, config) == -1) {
FREE(MEM_TYPE, config);
return (NULL);
}
/* Apply application defaults */
if (c->info.init != NULL && (*c->info.init)(c, config) == -1) {
structs_free(c->type, NULL, config);
FREE(MEM_TYPE, config);
return (NULL);
}
/* Sanity check that default configuration is really valid */
if (!did_check) {
char buf[512];
snprintf(buf, sizeof(buf), "generic problem");
if (c->info.checker != NULL
&& !(*c->info.checker)(c, config, buf, sizeof(buf))) {
alog(LOG_CRIT,
"default configuration is invalid: %s", buf);
}
did_check = 1;
}
return (config);
}
/*
* Get the application's most recently applied configuration.
*
* This assumes the mutex is held.
*/
static const void *
app_config_get_last(struct app_config_ctx *c)
{
return (((c->flags & CONFIG_PENDING) != 0) ? c->pending
: ((c->flags & CONFIG_APPLYING) != 0) ? c->applying
: c->current);
}
/*
* Compare two configurations, either of which may be NULL.
*/
static int
app_config_equal(struct app_config_ctx *c,
const void *config1, const void *config2)
{
if (config1 == NULL && config2 == NULL)
return (1);
if (config1 == NULL || config2 == NULL)
return (0);
return (structs_equal(c->type, NULL, config1, config2));
}
/*
* Get a copy of the application's current or pending configuration.
*/
void *
app_config_get(struct app_config_ctx *c, int pending)
{
void *copy;
int r;
/* Get copy of config */
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
copy = app_config_copy(c, pending ?
app_config_get_last(c) : c->current);
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
/* Done */
return (copy);
}
/*
* Get a copy of a configuration.
*/
void *
app_config_copy(struct app_config_ctx *c, const void *config)
{
void *copy;
if (config == NULL)
return (NULL);
if ((copy = MALLOC(MEM_TYPE, c->type->size)) == NULL) {
alogf(LOG_ERR, "%s: %m", "malloc");
return (NULL);
}
if (structs_get(c->type, NULL, config, copy) == -1) {
alogf(LOG_ERR, "%s: %m", "structs_get");
FREE(MEM_TYPE, copy);
return (NULL);
}
return (copy);
}
/*
* Free a configuration.
*/
void
app_config_free(struct app_config_ctx *c, void **configp)
{
void *const config = *configp;
if (config == NULL)
return;
*configp = NULL;
structs_free(c->type, NULL, config);
FREE(MEM_TYPE, config);
}
/*
* Change the application's current configuration.
*/
int
app_config_set(struct app_config_ctx *c,
const void *config0, u_long delay, char *ebuf, int emax)
{
struct pevent_info info;
char buf[512];
void *config;
int rtn = 0;
int r;
/* Allow NULL error buffer to be passed; initialize buffer */
if (ebuf == NULL) {
ebuf = buf;
emax = sizeof(buf);
}
snprintf(ebuf, emax, "unknown problem");
/* Copy config */
if (config0 == NULL)
config = NULL;
else if ((config = app_config_copy(c, config0)) == NULL)
return (-1);
/* Normalize it */
if (config != NULL && c->info.normalize != NULL)
(*c->info.normalize)(c, config);
/* Validate the new configuration */
if (config != NULL
&& c->info.checker != NULL
&& !(*c->info.checker)(c, config, ebuf, emax)) {
app_config_free(c, &config);
errno = EINVAL;
return (-1);
}
/* Acquire mutex */
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
/* Ignore new configs while a shutdown is pending */
if ((c->timer != NULL && c->pending == NULL)
|| ((c->flags & CONFIG_RESTARTING) != 0 && c->applying == NULL)) {
app_config_free(c, &config);
goto done;
}
/* See if new config is really new */
if (app_config_equal(c, config, app_config_get_last(c))) {
app_config_free(c, &config); /* same as what we got */
goto done;
}
/* Update pending configuration */
app_config_free(c, &c->pending);
c->pending = config;
c->flags |= CONFIG_PENDING;
/* If a previous restart thread is still running, we're done */
if ((c->flags & CONFIG_RESTARTING) != 0)
goto done;
/* If the restart timer is not running, need to start it */
if (c->timer == NULL)
goto start_timer;
/* Get time remaining on the restart timer */
if (pevent_get_info(c->timer, &info) == -1) {
alogf(LOG_ERR, "%s: %m", "pevent_get_info");
goto start_timer;
}
/* See if we need a shorter delay */
if (delay > INT_MAX || delay >= info.u.millis)
goto done; /* existing timeout is sufficient */
start_timer:
/* Cancel previous timer */
pevent_unregister(&c->timer);
/* Start restart timer */
if (pevent_register(c->ctx, &c->timer, PEVENT_OWN_THREAD,
&c->mutex, app_config_apply, c, PEVENT_TIME, delay) == -1) {
alogf(LOG_ERR, "%s: %m", "pevent_register");
rtn = -1;
goto done;
}
done:
/* Done */
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
return (rtn);
}
/*
* Write the current configuration back out to XML config file.
*
* This assumes c->current != NULL and the mutex is locked.
*/
static void
app_config_store(struct app_config_ctx *c)
{
char attrs[sizeof(ATTR_VERSION) + 32];
char *tpath = NULL;
FILE *fp = NULL;
int old_cstate;
/* Get configuration and XML pathname */
assert(c->xml_path != NULL);
/* Block thread from being canceled */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cstate);
/* Check if configuration is different from cached file contents */
if (c->xml_cache != NULL
&& structs_equal(c->type, NULL, c->current, c->xml_cache))
goto done;
/* Set version attribute */
snprintf(attrs, sizeof(attrs), "%s%c%u%c",
ATTR_VERSION, '\0', c->info.version, '\0');
/* Write out temporary new file */
ASPRINTF(TYPED_MEM_TEMP, &tpath, "%s.new", c->xml_path);
if (tpath == NULL) {
alogf(LOG_ERR, "%s: %m", "asprintf");
goto done;
}
if ((fp = fopen(tpath, "w")) == NULL) {
alogf(LOG_ERR, "%s: %m", tpath);
goto done;
}
if (structs_xml_output(c->type, APP_CONFIG_XML_TAG,
attrs, c->current, fp, NULL, STRUCTS_XML_FULL) == -1) {
alogf(LOG_ERR, "%s: %m", "structs_xml_output");
(void)unlink(tpath);
goto done;
}
fflush(fp);
fsync(fileno(fp));
fclose(fp);
fp = NULL;
/* Atomically overwrite exsting file with new file using rename(2) */
if (rename(tpath, c->xml_path) == -1) {
alogf(LOG_ERR, "%s(%s, %s): %m", "rename", tpath, c->xml_path);
(void)unlink(tpath);
goto done;
}
/* Cache file contents */
app_config_free(c, &c->xml_cache);
if ((c->xml_cache = app_config_copy(c, c->current)) == NULL)
alogf(LOG_ERR, "%s: %m", "app_config_copy");
done:
/* Clean up */
if (fp != NULL)
fclose(fp);
if (tpath != NULL)
FREE(TYPED_MEM_TEMP, tpath);
pthread_setcancelstate(old_cstate, NULL);
}
/*
* Entry point for restarter thread.
*
* The mutex will be acquired when we enter and released when we return.
*/
static void
app_config_apply(void *arg)
{
struct app_config_ctx *const c = arg;
/* Sanity checks */
assert((c->flags & CONFIG_APPLYING) == 0);
assert((c->flags & CONFIG_RESTARTING) == 0);
assert(c->applying == NULL);
/* Set flag indicating this thread is applying new configs */
c->flags |= CONFIG_RESTARTING;
/* Keep applying configurations until there are no more new ones */
while ((c->flags & CONFIG_PENDING) != 0) {
int r;
/* Make sure latest new configuration is different */
if (app_config_equal(c, c->pending, c->current)) {
app_config_free(c, &c->pending);
c->flags &= ~CONFIG_PENDING;
goto done;
}
/* Grab pending config and apply it */
c->applying = c->pending;
c->pending = NULL;
c->flags &= ~CONFIG_PENDING;
c->flags |= CONFIG_APPLYING;
/* Restart subsystems */
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
app_config_restart(c);
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
}
/* Save new configuration to disk */
if (c->xml_writeback && c->current != NULL)
app_config_store(c);
done:
/* Done */
c->flags &= ~CONFIG_RESTARTING;
}
/*
* Restart subsystems to effect the new configuration 'config'.
*
* This assumes the mutex is NOT locked.
*/
static void
app_config_restart(struct app_config_ctx *c)
{
int i;
int r;
/* Initialize actions */
for (i = 0; i < c->num_ss; i++)
c->ss_flags[i] &= ~(SS_NEED_STOP|SS_NEED_START);
/* Determine what needs to be shutdown and started up */
for (i = 0; i < c->num_ss; i++) {
const struct app_subsystem *const ss = c->info.slist[i];
/* Do simple analysis */
if (c->current != NULL
&& (c->ss_flags[i] & SS_RUNNING) != 0)
c->ss_flags[i] |= SS_NEED_STOP;
if (c->applying != NULL
&& (ss->willrun == NULL
|| (*ss->willrun)(c, ss, c->applying) != 0))
c->ss_flags[i] |= SS_NEED_START;
/* Optimize away unnecessary restarts */
if ((c->ss_flags[i] & (SS_NEED_STOP|SS_NEED_START))
== (SS_NEED_STOP|SS_NEED_START)) {
/* If either is NULL, the other isn't, so must do it */
if (c->current == NULL || c->applying == NULL)
goto no_optimize;
/* Check dependency list */
if (ss->deplist != NULL) {
int j;
for (j = 0; ss->deplist[j] != NULL; j++) {
if ((r = structs_equal(c->type,
ss->deplist[j], c->current,
c->applying)) != 1) {
if (r != -1)
goto no_optimize;
alogf(LOG_ERR, "%s(%s): %m",
"structs_equal",
ss->deplist[j]);
}
}
}
/* Check using subsystem's method */
if (ss->changed != NULL
&& (*ss->changed)(c, ss, c->current, c->applying))
goto no_optimize;
/* Optimize away restart because it's not needed */
c->ss_flags[i] &= ~(SS_NEED_STOP|SS_NEED_START);
no_optimize:;
}
}
/* Shut stuff down */
for (i = c->num_ss - 1; i >= 0; i--) {
const struct app_subsystem *const ss = c->info.slist[i];
if ((c->ss_flags[i] & SS_NEED_STOP) != 0) {
alog(LOG_DEBUG, "stopping subsystem \"%s\"", ss->name);
if (ss->stop != NULL)
(*ss->stop)(c, ss, c->current);
c->ss_flags[i] &= ~SS_RUNNING;
}
}
/* Update current configuration */
r = pthread_mutex_lock(&c->mutex);
assert(r == 0);
assert((c->flags & CONFIG_APPLYING) != 0);
assert((c->flags & CONFIG_RESTARTING) != 0);
app_config_free(c, &c->current);
c->current = c->applying;
c->applying = NULL;
c->flags &= ~CONFIG_APPLYING;
r = pthread_mutex_unlock(&c->mutex);
assert(r == 0);
/* If new config is NULL, we've just successfully shut down */
if (c->current == NULL)
return;
/* Start stuff up */
for (i = 0; i < c->num_ss; i++) {
const struct app_subsystem *const ss = c->info.slist[i];
if ((c->ss_flags[i] & SS_NEED_START) != 0) {
alog(LOG_DEBUG, "starting subsystem \"%s\"", ss->name);
if (ss->start != NULL
&& (*ss->start)(c, ss, c->current) == -1) {
alog(LOG_ERR, "subsystem \"%s\" startup failed",
ss->name);
continue;
}
c->ss_flags[i] |= SS_RUNNING;
}
}
}
/*
* Logger for XML parsing
*/
static void
app_config_xml_logger(int sev, const char *fmt, ...)
{
char buf[512];
va_list args;
snprintf(buf, sizeof(buf), "XML error: ");
va_start(args, fmt);
vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, args);
va_end(args);
alog(sev, "%s", buf);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>