#include
<sys/types.h>
#include
<pdel/config/app_config.h>
struct app_config_ctx *
app_config_init
(struct
pevent_ctx *ctx, const
struct app_config *info,
void *cookie);
int
app_config_uninit
(struct
app_config_ctx **ctxp);
void *
app_config_get_cookie
(struct
app_config_ctx *ctx);
int
app_config_load
(struct
app_config_ctx *ctx,
const char *path,
int allow_writeback);
int
app_config_reload
(struct
app_config_ctx *ctx);
void *
app_config_new
(struct
app_config_ctx *ctx);
int
app_config_set
(struct
app_config_ctx *ctx,
const void *config,
u_long delay,
char *ebuf,
int emax);
void *
app_config_get
(struct
app_config_ctx *ctx, int
pending);
const struct structs_type *
app_config_get_type
(struct
app_config_ctx *ctx);
void *
app_config_copy
(struct
app_config_ctx *ctx,
const void *config);
void
app_config_free
(struct
app_config_ctx *ctx, void
**configp);
extern const struct app_subsystem
app_config_alog_subsystem;
extern const struct app_subsystem
app_config_curconf_subsystem;
extern const struct app_subsystem
app_config_directory_subsystem;
extern const struct app_subsystem
app_config_pidfile_subsystem;
These functions implement an application configuration
framework.
The app_config
model assumes that the
application's configuration is stored in a single
configuration
object, which can be any data structure that is describable by a
structs(3)
type. The configuration can be stored in an XML file which is automatically
updated.
The application itself consists of one or more
subsystems. A
subsystem is an abstract activity that can be started or stopped and whose
behavior depends on (some part of) the configuration object. When the
configuration object is changed, those subsystems that require it are
automatically stopped and then restarted with the new configuration.
The application may provide methods for:
- Creating a new, default configuration object
- Initializing a new configuration object for the current system
- Checking a configuration object for overall validity
- Normalizing a configuration object
- Upgrading a configuration object from an old version
Each subsystem may provide methods for:
- Starting the subsystem
- Stopping the subsystem
- Determining if the subsystem will run
- Determining if the subsystem needs to be restarted
In the steady state, there is a single currently active
configuration object. Only those subsystems that are supposed to be running
are. All running subsystems are running with their configurations based on
(i.e., consistent with) the active configuration object.
When there needs to be a configuration change, a new configuration
object is constructed and the app_config
library is
told to apply the new configuration object. After a configurable delay (for
hysteresis), those subsystems affected by the change are stopped, the new
configuration object is installed in place of the old one, and the stopped
subsystems are restarted.
The app_config
library guarantees that
only "valid" configurations may be applied, where the meaning of
"valid" is determined by the application.
A subsystem is defined as anything that can be started and/or
stopped. A subsystem typically depends on some portion of the configuration
object such that it must be stopped and restarted when that portion
changes.
A subsystem is defined by a struct
app_subsystem
:
struct app_subsystem {
const char *name; /* name, null to end list */
void *arg; /* opaque subsystem argument */
app_ss_startup_t *start; /* start subsystem */
app_ss_shutdown_t *stop; /* stop subsystem */
app_ss_willrun_t *willrun; /* will subsystem run? */
app_ss_changed_t *changed; /* subsystem config changed? */
const char **deplist; /* config items dependent on */
};
The name is currently used only for
debugging, but must be NULL
to terminate the list of
subsystems (see below). The arg is ignored and is for
the application's private use. The start,
stop, willrun, and
changed fields must be pointers to functions having
these types:
typedef int app_ss_startup_t(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config);
typedef void app_ss_shutdown_t(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config);
typedef int app_ss_willrun_t(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config);
typedef int app_ss_changed_t(struct app_config_ctx *ctx,
const struct app_subsystem *ss,
const void *config1, const void *config2);
start
()
starts the subsystem; config points to a copy of the
current (i.e., new) configuration object. start
()
should return zero if successful, or else -1 with
errno set on failure. In the latter case, the
stop
() method will not be called.
stop
()
stops the subsystem; config points to a copy of the
current configuration object.
willrun
()
should return non-zero if the subsystem needs to run at all.
config points to a copy of the current (i.e., new)
configuration object.
changed
()
determines if the subsystem needs to be restarted during a configuration
change from config1 to config2.
It should return 1 if so, zero otherwise.
In the above methods, the configuration object argument(s) become
invalid after the method returns.
Alternately, or in conjunction with the
changed
()
method, the deplist may point to a
NULL
terminated list of
structs(3)
names of fields in the configuration object on which this subsystem depends.
The subsystem will automatically be restarted if any of the named fields
differ between config1 and
config2, as determined by
structs_equal(3).
All four of the above subsystem methods are optional
and may be specified as NULL
. In the case of
startup
()
and
shutdown
(),
NULL
means "do nothing".
willrun
() being NULL
is
equivalent to it always returning 1. changed
() being
NULL
is equivalent to it always returning 0.
deplist being NULL
is
equivalent to an empty list.
An application itself is described by a struct
app_config
:
struct app_config {
u_int version; /* current version # */
const struct structs_type **types; /* all version types */
const struct app_subsystem **slist; /* list of subsystems */
app_config_init_t *init; /* initialize defaults */
app_config_getnew_t *getnew; /* generate new config */
app_config_checker_t *checker; /* validate a config */
app_config_normalize_t *normalize; /* normalize a config */
app_config_upgrade_t *upgrade; /* upgrade a config */
};
The list of subsystems supported by the application is pointed to
by slist. This list must be terminated with an entry
whose name is NULL
.
Subsystems are always started in the order they are listed in
slist, and they are always shutdown in the reverse
order.
The version is the configuration object
version number (the first version is zero), and types
points to an array of version + 1 pointers to
structs(3)
types for the configuration object, where types[i]
is the
structs(3)
type for version i of the configuration object.
The remaining fields are pointers to functions having these
types:
typedef int app_config_init_t(struct app_config_ctx *ctx,
void *config);
typedef int app_config_getnew_t(struct app_config_ctx *ctx,
void *config);
typedef int app_config_checker_t(struct app_config_ctx *ctx,
const void *config, char *errbuf, size_t ebufsize);
typedef void app_config_normalize_t(struct app_config_ctx *ctx,
void *config);
typedef int app_config_upgrade_t(struct app_config_ctx *ctx,
const void *old_conf, u_int old_version,
void *new_conf);
If the default configuration object is not equal to what
is provided by
structs_init(3),
then init
()
may be implemented. It should further modify the
config as appropriate to get the generic default
configuration. init
() returns zero on success, or -1
on error with errno set appropriately.
getnew
()
is invoked when no existing configuration is found by
app_config_load
() (see below). The
config is as returned by
init
(). getnew
() should
apply any further initialization required for this particular system.
getnew
() returns zero on success, or -1 on error
with errno set appropriately.
The distinction between
init
() and
getnew
() is somewhat subtle:
init
() simply initializes a new configuration
object. It may be invoked many times during the normal operation of the
application as configuration objects are needed.
getnew
() is only invoked once, at the beginning of
application startup, when there is no previously saved configuration found.
Therefore, the behavior of init
() should not be
affected by the "environment", while the behavior of
getnew
() often is.
checker
()
determines whether the config is valid, returning 1 if
so or 0 if not. In the latter case, it may print an error message (including
'\0') into the buffer errbuf, which has size
ebufsize (see
snprintf(3)).
normalize
()
gives the application a chance to normalize an otherwise valid configuration
object. This is useful when the configuration object contains redundant
information, or information that can be represented in more than one
way.
All configurations that are applied by
app_config
are guaranteed to have been checked and
normalized. All configurations passed to
checker
()
are guaranteed to have been passed through
normalize
() first.
Note: the configurations returned by
init
() and
getnew
() must be valid according to
checker.
upgrade
()
is invoked when an older version of the configuration object is read in from
an XML file. The configuration version number is stored as the
"version" attribute of the XML document element.
old_conf is the old object, which has version
old_version, and new_conf is a
newly initialized configuration object of the current version.
upgrade
() should copy the configuration information
from old_conf to new_conf.
A quick and dirty way to do this when most of the fields are the
same is to use
structs_traverse(3)
to list the fields in the old configuration object,
structs_get_string(3)
to get their ASCII values, and
structs_set_string(3)
to set the same values in the new configuration object.
app_config_init
()
should be called at application startup time to initialize
app_config
for the application described by
info. A
pevent(3)
context ctx must be supplied.
app_config_init
() returns an application context,
with which all configuration and subsystems are associated. Multiple
independent application contexts may exist at the same time. The
cookie is saved along with the context but is
otherwise ignored.
app_config_uninit
()
should be called at application shutdown time to release resources allocated
by app_config
. It may only be called when all
subsystems are shutdown (i.e., the current configuration object pointer is
NULL)
. This enables
app_config_init
() to be called again, if so
desired.
Upon return from
app_config_uninit
(),
*ctxp will be set to NULL
. If
*ctxp is already equal to NULL
when app_config_uninit
() is invoked, nothing
happens.
app_config_get_cookie
()
retrieves the application cookie provided to
app_config_init
().
app_config_load
()
reads in an application configuration object from the XML file at
path and applies it, making it the current
configuration. If path is empty or non-existent, a new
configuration object is created using the application's
getnew
() method.
If the file contains an old version of the configuration object,
it is automatically upgraded to the current version. If
allow_writeback is non-zero, then
path is remembered and the file is updated (i.e.,
rewritten) every time the application configuration object changes. Updates
are done atomically by creating a temporary file with the suffix
".new" and renaming it (see
rename(2)).
In theory, one call to
app_config_load
()
in an application's
main
()
routine is all that is required to get things going.
app_config_reload
()
reloads the configuration file previously specified to
app_config_load
() and applies it. This would be the
typical response to receiving a SIGHUP
signal.
app_config_new
()
creates a new configuration object with the application's default values as
specified by the application's init
() method. The
returned pointer should be cast to the appropriate type. The caller is
responsible for eventually freeing the returned configuration object by
calling app_config_free
().
app_config_set
()
changes the application's current configuration to be a copy of the
configuration pointed to by config. If this
configuration is invalid, -1 is returned with errno
set to EINVAL
, and if ebuf is
not NULL
, the buffer pointed to by
ebuf and having size emax is
filled in with a '\0'-terminated error message.
app_config_set
() may also return -1 with
errno set to other values in the case of system
errors.
If config is
NULL
, all running subsystems will be shut down. Any
configurations passed to
app_config_set
()
subsequent to passing a NULL
configuration, but
before the shutdown operation has completed, are ignored. This guarantees
that a NULL
configuration does actually shutdown the
application.
The new configuration (or shutdown) takes
effect after a delay of delay milliseconds after
app_config_set
()
has successfully returned zero. The appropriate subsystem
stop
(), and then start
()
methods are invoked serially from a new thread.
app_config_get
()
returns a copy of the current or pending configuration object. The returned
pointer should be cast to the appropriate type. If
pending is zero, then the configuration object
currently in use is copied. Otherwise, the configuration object most
recently applied via app_config_set
() is copied.
These will be different when there is a pending, but not yet applied,
configuration. The caller is responsible for eventually freeing the returned
configuration object by calling
app_config_free
().
app_config_get_type
()
returns the
structs(3)
for the application configuration object.
app_config_copy
()
copies a configuration object. The returned pointer should be cast to the
appropriate type. The caller is responsible for eventually freeing the
returned configuration object by calling
app_config_free
().
app_config_free
()
destroys the configuration object pointed to by
*configp. Upon return, *configp
will be set to NULL
. If
*configp is already NULL
when
app_config_free
() is invoked, nothing happens.
The app_config
library comes with some
predefined subsystem templates.
app_config_alog_subsystem handles
configuring error logging for an application. To use
app_config_alog_subsystem, copy the structure and set
the arg field to point to a struct
app_config_alog_info
:
struct app_config_alog_info {
const char *name; /* field name */
int channel; /* alog channel */
};
The name should be the
structs(3)
field name of the field in the configuration object that configures logging
for the
alog(3)
logging channel channel. This field should be a
struct alog_config
.
app_config_curconf_subsystem is useful when
the application needs efficient access to the currently active
configuration. This subsystem assumes that there is a global pointer
variable (call it curconf) which by definition always
points to a read-only copy of the currently active configuration. For
example, if the application's configuration object is a
struct my_config
, then curconf
would be defined as:
const struct my_config *const curconf;
Then the function of
app_config_curconf_subsystem is to automatically keep
this variable up to date. (The const
keywords
reflect the application's point of view: the first is because the structure
is read-only, while the second is because the pointer itself is
read-only.)
To use app_config_curconf_subsystem, copy
the structure and set the arg field to point to the
application's curconf pointer variable. Typically the
app_config_curconf_subsystem will be first in the list
of subsystems, so that curconf is always updated
before any other subsystem starts. Then at any time
*curconf can be examined for the currently active
configuration.
app_config_directory_subsystem handles
configuring the current working directory for the process. To use
app_config_directory_subsystem, copy the structure and
set the arg field to point to a string containing the
structs(3)
name of the field in the configuration object that contains the directory
name. If this name is not the empty string, then the current working
directory will be set according to the value of this field.
app_config_pidfile_subsystem handles
"PID files", i.e., exclusive application lock files into which the
process ID is written. These guard against two instances of the same
application running at the same time. To use
app_config_pidfile_subsystem, copy the structure and
set the arg field to point to a string containing the
structs(3)
name of the field in the configuration object that contains the PID file
pathname.