
/*
 * 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>
 */

#ifndef _PDEL_CONFIG_APP_CONFIG_H_
#define _PDEL_CONFIG_APP_CONFIG_H_

/************************************************************************
				DEFINITIONS
************************************************************************/

struct app_subsystem;
struct app_config_ctx;
struct pevent_ctx;

/* XML document tag for app_config configurations */
#define APP_CONFIG_XML_TAG	"config"

/* Methods exported by a subsystem */
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);

/* Descriptor for a 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 */
};

/*
 * Methods exported by an application:
 *
 * init		Initialize a config structure. The structure will already
 *		be initialized with the values provided by the structs type.
 *		This method is so any other default values can be applied.
 *
 * getnew	This is used when no existing configuration is found for
 *		the system (i.e., after a factory reset). The argument
 *		provided is a configuration structure already initialized
 *		using the "init" method. The "getnew" method should apply
 *		any further initialization needed for this specific system.
 *
 * checker	This method is the gatekeeper for applying new configurations.
 *		If this method returns zero, the configuration is rejected.
 *		An appropriate error message may be put in "errbuf". Make
 *		sure your application does not automatically generate any
 *		configurations that fail to pass this method, otherwise
 *		you get into a stuck state.
 *
 * normalize	Configurations may contain redundant and/or derived info.
 *		This method (if defined) can be used to normalize a config
 *		struture. This method is called before "checker".
 *
 * upgrade	When the configuration structure changes, and you still
 *		want to be able to read in XML created from the older
 *		versions of the configuration structure, defining this
 *		method gives you a way to specify how to upgrade information
 *		the old format to the new one.
 */
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);

/*
 * Descriptor for an application's configuration information.
 *
 * Automatic configuration upgrade feature:
 *
 * types[version] is a structs type for the applications configuration
 * information structure. All previous pointers in the types[] array point
 * to types for older versions of the configuration structure. When an
 * older version is encountered in an XML file, it is read in using the
 * corresponding old structs type and converted to the current version
 * using the "upgrade" method. So versions are numbered 0, 1, .... Each
 * time the configuration structure is changed, "version" should be
 * bumped and the new type added to the "types" array. Version numbers
 * are not allowed to go backwards.
 */
struct app_config {
	u_int				version;/* current config version # */
	const struct structs_type	**types;/* types for all versions */
	const struct app_subsystem	**slist;/* list of subsystems */
	app_config_init_t		*init;	/* initialize config defaults */
	app_config_getnew_t		*getnew;/* generate new config */
	app_config_checker_t		*checker;/* validate a config struct */
	app_config_normalize_t		*normalize; /* normalize a config */
	app_config_upgrade_t		*upgrade;/* upgrade a config */
};

/************************************************************************
			    BUILT-IN SUBSYSTEMS
************************************************************************/

__BEGIN_DECLS

/*
 * PID file subsystem template.
 *
 * Copy this structure and set arg to be a const char * pointing to the
 * name of the item in the config structure containing the pathname to
 * the PID file. If this pathname is NULL/empty, no PID file is created.
 */
extern const struct app_subsystem	app_config_pidfile_subsystem;

/*
 * Directory subsystem template.
 *
 * Copy this structure and set arg to be a const char * pointing to the
 * name of the item in the config structure containing the directory to
 * chdir(2) into. If this pathname is NULL/empty, no change is made.
 */
extern const struct app_subsystem	app_config_directory_subsystem;

/*
 * "Curconf" subsystem template.
 *
 * Copy this structure and set arg to be a 'struct my_config **' pointer
 * pointing to the application's 'curconf' variable, which at all times
 * points to a (read-only) copy of the currently active configuration object.
 * This subsystem should usually be first in the subsystem list.
 */
extern const struct app_subsystem	app_config_curconf_subsystem;

__END_DECLS

/*
 * Alog logging subsystem template.
 *
 * Copy this structure and set arg to be a pointer to an instance
 * of the structure below.
 */
struct app_config_alog_info {
	const char	*name;		/* name of alog_channel_config_type */
	int		channel;	/* alog channel */
};

__BEGIN_DECLS
extern const struct app_subsystem	app_config_alog_subsystem;
__END_DECLS

/************************************************************************
				FUNCTIONS
************************************************************************/

__BEGIN_DECLS

/*
 * Initialize the application's configuration type and list of subsystems.
 * Also sets the XML tag for reading and writing the configuration and
 * the user application cookie.
 *
 * This must be called first.
 */
extern struct	app_config_ctx *app_config_init(struct pevent_ctx *ctx,
			const struct app_config *info, void *cookie);

/*
 * Reverse the effects of app_config_init().
 *
 * All subsystems must be shutdown and no new configurations
 * may be pending.
 */
extern int	app_config_uninit(struct app_config_ctx **ctxp);

/*
 * Get application cookie.
 */
extern void	*app_config_get_cookie(struct app_config_ctx *ctx);

/*
 * Initialize application configuration from an XML file.
 *
 * This reads in the configuration and then calls app_config_set()
 * with a delay of one millisecond.
 *
 * If "writeback" is zero, no writebacks will ever occur.
 */
extern int	app_config_load(struct app_config_ctx *ctx,
			const char *path, int allow_writeback);

/*
 * Re-read the XML file passed to app_config_load() and reconfigure
 * as appropriate.
 *
 * This reads in the configuration and then calls app_config_set()
 * with a delay of one millisecond.
 */
extern int	app_config_reload(struct app_config_ctx *c);

/*
 * Function to create a configuration structure. This structure will
 * have the application "default" values.
 */
extern void	*app_config_new(struct app_config_ctx *c);

/*
 * Change the application's current configuration to be a copy of "config"
 * after a delay of at most "delay" milliseconds.
 *
 * A NULL configuration shuts everything down. Any configs passed to
 * app_config_set() subsequent to passing a NULL config, but before
 * the shutdown operation has completed, are ignored. This guarantees
 * that a shutdown really does shut everything down.
 *
 * If an app_config_load() was previously called with "allow_writeback" true,
 * then a non-NULL configuration will be written back out to the XML file.
 *
 * If the configuration is invalid, -1 is returned with errno == EINVAL
 * and the buffer (if not NULL) is filled in with the reason.
 */
extern int	app_config_set(struct app_config_ctx *ctx,
			const void *config, u_long delay, char *ebuf, int emax);

/*
 * Get a copy of the application's current or pending configuration.
 */
extern void	*app_config_get(struct app_config_ctx *ctx, int pending);

/*
 * Get the configuration object type.
 */
extern const	struct structs_type *app_config_get_type(
			struct app_config_ctx *ctx);

/*
 * Get a copy of a configuration.
 */
extern void	*app_config_copy(struct app_config_ctx *ctx,
			const void *config);

/*
 * Free a configuration.
 */
extern void	app_config_free(struct app_config_ctx *ctx, void **configp);

__END_DECLS

#endif	/* _PDEL_CONFIG_APP_CONFIG_H_ */

