/*
 *
 *   (C) Copyright IBM Corp. 2001, 2003
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Module: plugfuncs.h
 */

#ifndef EVMS_PLUGFUNCS_H_INCLUDED
#define EVMS_PLUGFUNCS_H_INCLUDED 1

#include <common.h>
#include <options.h>
#include <enginestructs.h>
#include <ece.h>


/* Maximum length of a user message. */
#define MAX_USER_MESSAGE_LEN    10240


#define ENGINE_SERVICES_API_MAJOR_VERION  13
#define ENGINE_SERVICES_API_MINOR_VERION  0
#define ENGINE_SERVICES_API_PATCH_LEVEL   0

/*
 * For all can_????() functions, the function returns 0 if "yes", else a reason
 * code.
 */

typedef struct engine_functions_s {

	/*
	 * Get the version of the plug-in API that this Engine provides.
	 */
	void (*get_engine_plugin_api_version)(evms_version_t * version);

	/*
	 * Get a list of the user space plug-ins that are loaded, optionally
	 * filtering by type and flags.  If the type parameter is not 0, only
	 * plug-ins of that type will be returned.  If type is 0, all plug-ins
	 * will be returned.  See common.h for the definitions of
	 * plugin_search_flags_t.
	 */
	int (*get_plugin_list)(plugin_type_t         type,
			       plugin_search_flags_t flags,
			       list_anchor_t       * plugins);

	/*
	 * Get the plugin_record_t for a given plug-in ID.
	 */
	int (*get_plugin_by_ID)(plugin_id_t         plugin_id,
				plugin_record_t * * plugin);

	/*
	 * Get the plugin_record_t for a given plug-in short name.
	 */
	int (*get_plugin_by_name)(char              * plugin_short_name,
				  plugin_record_t * * plugin);

	/*
	 * Get a list of volumes, optionally filtering by FSIM.  If FSIM is
	 * specified, only volumes managed by that FSIM will be returned.  If
	 * FSIM is NULL, all volumes will be returned.
	 */
	int (*get_volume_list)(plugin_record_t     * fsim,
			       storage_container_t * disk_group,
			       volume_search_flags_t flags,
			       list_anchor_t       * volume_list);

	/*
	 * Get a list of objects, optionally filtering by object type, data
	 * type, and plug-in.  See the object_type_t, data_type_t, and
	 * object_search_flags_t enums in common.h.  If object_type is 0,
	 * objects of any type will be returned.  If data_type is 0, objects of
	 * any data type will be returned.  If plugin is NULL, objects managed
	 * by any plug-in will be returned.
	 */
	int (*get_object_list)(object_type_t         object_type,
			       data_type_t           data_type,
			       plugin_record_t     * plugin,
			       storage_container_t * disk_group,
			       object_search_flags_t flags,
			       list_anchor_t       * objects);

	/*
	 * Get a list of storage containers, optionally filtering by plug-in.
	 * If plugin is specified, only containers managed by that plug-in
	 * will be returned.  If plugin is NULL, all containers will be
	 * returned.
	 */
	int (*get_container_list)(plugin_record_t        * plugin,
				  storage_container_t    * disk_group,
				  container_search_flags_t flags,
				  list_anchor_t          * container_list);

	/*
	 * Allocate a storage_object_t for a logical disk structure.
	 */
	int (*allocate_logical_disk)(char               * name,
				     storage_object_t * * new_disk);

	/*
	 * Free a storage_object_t for a logical disk.
	 */
	int (*free_logical_disk)(storage_object_t * disk);

	/*
	 * Allocate a storage_object_t for a disk_segment.  The caller is
	 * responsible for putting the storage_object_t for the logical disk
	 * from which this segment comes into the child_objects list in the
	 * storage_object_t for the segment.  Additionally, the caller must add
	 * the storage_object_t for the disk segment to the parent_objects list
	 * in the storage_object_t for the logical disk.
	 */
	int (*allocate_segment)(char               * name,
				storage_object_t * * new_segment);

	/*
	 * Free a storage_object_t for a disk_segment.
	 */
	int (*free_segment)(storage_object_t * segment);

	/*
	 * Allocate a storage_container_t structure.  The caller fills in the
	 * objects_consumed and objects_produced lists in the container.  The
	 * caller fills in the appropriate consuming_container and
	 * producing_container fields in the storage_object_t(s) that are
	 * consumed or produced by the container.
	 */
	int (*allocate_container)(char                  * name,
				  storage_container_t * * new_container);

	/*
	 * Free a storage_container_t structure.
	 */
	int (*free_container)(storage_container_t * container);

	/*
	 * Allocate a storage_object_t for a storage_region.  The caller is
	 * responsible for putting the storage_object_t from which this region
	 * comes into the child_objects list in the storage_object_t for the
	 * region.  Additionally, the caller must add the storage_object_t for
	 * the region to the parent_objects list in the storage_object_t from
	 * which this region comes.
	 */
	int (*allocate_region)(char               * name,
			       storage_object_t * * new_region);

	/*
	 * Free the storage_region structure.
	 */
	int (*free_region)(storage_object_t * region);

	/*
	 * Allocate a storage_object_t for an EVMS object.  The caller is
	 * responsible for putting the storage_object_t from which this EVMS
	 * object comes into the child_objects list in the storage_object_t for
	 * the EVMS object.  Additionally, the caller must add the
	 * storage_object_t for the EVMS object to the parent_objects list in
	 * the storage_object_t from which this EVMS object comes.
	 */
	int (*allocate_evms_object)(char               * name,
				    storage_object_t * * new_object);

	/*
	 * Free a storage_object_t for an EVMS object.
	 */
	int (*free_evms_object)(storage_object_t * object);

	/*
	 * Free any kind of storage object.  The Engine will do the right
	 * thing based on the object's object_type field.
	 */
	int (*free_storage_object)(storage_object_t * object);

	/*
	 * Open the device node for an object and return the file handle.
	 * On error, return -(error code).
	 */
	int (*open_object)(storage_object_t * object,
			   int                oflags,
			   ...);

	/*
	 * Read from an object's device node.  Return the number of bytes read
	 * or -(error code).
	 */
	int32_t (*read_object)(storage_object_t * object,
			       int                fd,
			       void             * buffer,
			       int32_t            bytes,
			       u_int64_t          offset);

	/*
	 * Write to an object's device node.  Return the number of bytes written
	 * or -(error code).
	 */
	int32_t (*write_object)(storage_object_t * object,
				int                fd,
				void             * buffer,
				int32_t            bytes,
				u_int64_t          offset);

	/*
	 * Send an ioctl to the object's device node.  Return error code.
	 */
	int (*ioctl_object)(storage_object_t * object,
			    int fd,
			    unsigned long int command,
			    ...);

	/*
	 * Close the file descriptor for an object's device node.
	 */
	int (*close_object)(storage_object_t * object,
			    int                fd);

	/*
	 * Open the device node for an volume and return the file handle.
	 * On error, return -(error code).
	 */
	int (*open_volume)(logical_volume_t * volume,
			   int                oflags,
			   ...);

	/*
	 * Read from an volume's device node.  Return the number of bytes read
	 * or -(error code).
	 */
	int32_t (*read_volume)(logical_volume_t * volume,
			       int                fd,
			       void             * buffer,
			       int32_t            bytes,
			       u_int64_t          offset);

	/*
	 * Write to an volume's device node.  Return the number of bytes written
	 * or -(error code).
	 */
	int32_t (*write_volume)(logical_volume_t * volume,
				int                fd,
				void             * buffer,
				int32_t            bytes,
				u_int64_t          offset);

	/*
	 * Send an ioctl to the volume's device node.  Return error code.
	 */
	int (*ioctl_volume)(logical_volume_t * volume,
			    int fd,
			    unsigned long int command,
			    ...);

	/*
	 * Close the file descriptor for an volume's device node.
	 */
	int (*close_volume)(logical_volume_t * volume,
			    int                fd);

	/*
	 * engine_alloc is the generic memory allocation service provided by the
	 * Engine.  For any memory that plug-ins return to the Engine, the
	 * plug-in must use the same malloc() that the Engine uses so that the
	 * Engine can properly free() the memory.  To assist the plug-ins, the
	 * Engine provides a common allocation function which the plug-ins can
	 * use so that all memory allocations are managed by the same memory
	 * manager.  Memory will be zero filled.
	 */
	void * (*engine_alloc)(u_int32_t size);

	/*
	 * engine_realloc is the generic memory reallocation service provided by
	 * the Engine.
	 */
	void * (*engine_realloc)(void * pBuf,
				 u_int32_t size);

	/*
	 * engine_free is the generic memory deallocation service provided by
	 * the Engine.
	 */
	void (*engine_free)(void *);

	/*
	 * Check if the Engine is in the process of committing changes.
	 */
	boolean (*commit_in_progress)(void);

	/*
	 * Write data to the Engine's log file.
	 */
	int (*write_log_entry)(debug_level_t     level,
			       plugin_record_t * plugin,
			       char            * fmt,
			       ...) __PRINTF_ATTRIBUTE__(3,4);

	/*
	 * Calculate a 32-bit CRC for a buffer of a given size.
	 * On the first call to calculate_CRC() the CRC parameter must be
	 * 0xffffffff.
	 * calculate_CRC() can be called multiple times to get the CRC for an
	 * aggregate of buffers.  To do so, subsequent calls set the CRC
	 * parameter to the resulting CRC that was returned from the previous
	 * call.  To calculate a new CRC, the CRC parameter must be set to
	 * 0xffffffff.
	 */
	u_int32_t (*calculate_CRC)(u_int32_t crc,
				   void    * buffer,
				   u_int32_t buffer_size);

	/*
	 * Calculate a checksum on a buffer of given size.  This Engine service
	 * actually issues an ioctl() to the EVMS kernel to use the kernel's
	 * checksum function so that checksums are consistent with the runtime
	 * code.  An error code is returned if the ioctl to the kernel fails.
	 * "insum" is the initial checksum value, useful if you are doing a
	 * single checksum on a series of multiple data blocks.
	 */
	int (*calculate_checksum)(unsigned char * buffer,
				  int             buffer_size,
				  unsigned int    insum,
				  unsigned int  * outsum);

	/*
	 * Add sectors that are to be written with zeros to the Engine's Kill
	 * Sector list.  Should only be called by device managers
	 */
	int (*add_sectors_to_kill_list)(storage_object_t * disk,	/* Disk on which the sectors reside */
					lba_t              lba,		/* Sector number of the first sector */
									/* to wipe out */
					sector_count_t     count);	/* Number of sectors to wipe out */


	/*
	 * Check to make sure this name is valid and no other object has the
	 * same name.
	 */
	int (*validate_name)(char * name);

	/*
	 * Register the name for an object.  The Engine will make sure that
	 * there is no other object with the same name.  If the name is not
	 * valid (e.g., it's too long) or another object has already registered
	 * the name, an error will be returned.
	 */
	int (*register_name)(char * name);

	/*
	 * Unregister the name of an object.
	 */
	int (*unregister_name)(char * name);

	/*
	 * Ask all the parent objects of this object if they can handle this
	 * object expanding by the specified amount.  Parent plug-ins may modify
	 * the size according to any constrains they have.  If the size has not
	 * been changed by any of the parents, the Engine will return 0.  If all
	 * the parents don't return an error but the size has been updated, the
	 * Engine will return EAGAIN.
	 */
	int (*can_expand_by)(storage_object_t * object,
			     sector_count_t   * delta_size);

	/*
	 * Ask all the parent objects of this object if they can handle this
	 * object shrinking by the specified amount.  Parent plug-ins may modify
	 * the size according to any constrains they have.  If the size has not
	 * been changed by any of the parents, the Engine will return 0.  If all
	 * the parents don't return an error but the size has been updated, the
	 * Engine will return EAGAIN.
	 */
	int (*can_shrink_by)(storage_object_t * object,
			     sector_count_t   * delta_size);

	/*
	 * Send a message to the user interface.  This service can be used in
	 * three ways.
	 *
	 * 1) Send a notification message to the user expecting no response.
	 *
	 * user_message(plugin_record, NULL, NULL, message_fmt, ...);
	 *
	 * 2) Ask a question and get one item selected from a list of two or
	 *    more items.
	 *
	 * char * choices = {string1, string2, ..., NULL};
	 * user_message(plugin_record, &answer, choices, message_fmt, ...);
	 *
	 * The "choices" parameter is a NULL terminated array of strings that
	 * describe each of the choices.  "*answer" *must* be initialized to the
	 * default response.  The UI will present the message and the choices to
	 * the user.  On return, *answer will contain the index of the selected
	 * choice string.
	 */
	int (*user_message)(plugin_record_t * plugin,
			    int             * answer,
			    char          * * choice_text,
			    char            * message_fmt,
			    ...) __PRINTF_ATTRIBUTE__(4,5);

	/*
	 * user_communication() uses the option_descriptor_t structures to
	 * convey a group of choices to the user.  Use this service when you
	 * have a complex list of things to ask of the user, e.g., they are of
	 * several different types (strings, ints, etc), they have constraints
	 * on their selection, or they may have dependencies on each other.
	 *
	 * The Engine will create a EVMS_Task_Message task for the UI.  The UI
	 * will use the task when calling the evms_get_option_descriptor(),
	 * evms_set_option_value(), etc.  APIs for getting and setting options.
	 * Your plug-in will be called on its set_option() function with the
	 * task context.  The action will be EVMS_Task_Message, the task object
	 * will be set to the object_instance parameter that you provide on the
	 * call to user_communication().
	 *
	 * The "message_text" will be treated by the UI as a title for the
	 * options that are presented.  "options" is an array of
	 * option_descriptor_t structures.  Each of the option descriptors
	 * *must* have an initial value.
	 */
	int (*user_communication)(void                * object_instance,
				  char                * message_text,
				  option_desc_array_t * options);

	/*
	 * Start, update, or close a progress indicator for the user.  See the
	 * description in common.h for how the progress_t structures are used.
	 */
	int (*progress)(progress_t * progress);

	/*
	 * Can this object be renamed?  The Engine will figure out if there are
	 * any restrictions that would prevent the object from being renamed,
	 * e.g., the object is the topmost object of a compatibility volume (the
	 * volume name will have been derived from the object) and the volume is
	 * mounted.  The Engine won't allow a volume that is mounted to be
	 * renamed.  If the object cannot be renamed, the Engine will return an
	 * error code that (hopefully) gives some indication as to why the
	 * rename is not allowed.  Plug-ins call this Engine service before
	 * allowing their object name to be changed by a set_info() call.
	 */
	int (*can_rename)(storage_object_t * object);

	/*
	 * Can this parent object take on the child object as a new child?
	 */
	int (*can_adopt_child)(storage_object_t * parent,
			       storage_object_t * child);

	/*
	 * Do all the fixups necessary to make the child object a child of the
	 * parent object.
	 */
	int (*adopt_child)(storage_object_t * parent,
			   storage_object_t * child);

	/*
	 * Do all the fixups necessary to make the child object a top object.
	 */
	int (*orphan_child)(storage_object_t * child);

	/*
	 * Is this volume mounted?  If you want to know the name of the mount
	 * point, specify a location in mount_name where the service will place
	 * a pointer to malloced memory that contains the mount point name.
	 * Remember to free the string when you are finished with it.  If you do
	 * not want to know the mount point and not have the hassle of freeing
	 * the memory, specify NULL for mount_name.
	 */
	boolean (*is_mounted)(char   * volume_name,
			      char * * mount_name);

	/*
	 * Assign an FSIM to a volume.  FSIMs can use this service to claim
	 * control of a volume.  For example, an FSIM for a journaling file
	 * system may want to claim another volume for an external log.  The
	 * Engine will return an error code if there is any reason the FSIM
	 * cannot be assigned to the volume, such as the volume already being
	 * owned by another FSIM.
	 * An FSIM does not use this service as part of the processing of a call
	 * to the FSIM's probe() function.  The Engine will  automatically
         * assign the FSIM to a volume if it returns 0 on a call to probe().
	 */
	int (*assign_fsim_to_volume)(plugin_record_t  * fsim,
				     logical_volume_t * volume);

	/*
	 * Unassign an FSIM from a volume.  FSIMs can use this service to
	 * release control of a volume.  For example, on unmkfs_setup() an FSIM
	 * for a journaling file system may want to release its claim on another
	 * volume that it used for an external log.
	 */
	int (*unassign_fsim_from_volume)(logical_volume_t * volume);

	/*
	 * Get the mode in which the Engine was opened.
	 */
	engine_mode_t (*get_engine_mode)(void);

	/*
	 * Device-Mapper common services
	 */

	/*
	 * Get the current DM ioctl version.
	 */
	int (*dm_get_version)(void);

	/*
	 * Allocate a DM target with the appropriate dm_type_data structure for
	 * the specified type. Specify num_devs for the total number of devices
	 * when allocating a DM_TARGET_STRIPE, MIRROR or MULTIPATH, and specify
	 * num_groups for the number of groups when allocating a MULTIPATH. In
	 * all other cases, the num_devs and num_groups parameters will be
	 * ignored.
	 */
	dm_target_t * (*dm_allocate_target)(dm_target_type type,
					    u_int64_t      start,
					    u_int64_t      length,
					    u_int32_t      num_devs,
					    u_int32_t      num_groups);
	/*
	 * Construct a list of targets to be passed to activate or reactivate.
	 */
	void (*dm_add_target)(dm_target_t   * target,
			      dm_target_t * * target_list);

	/*
	 * Deallocate a list of DM targets.  This assumes the list was created
	 * using calls to dm_allocate_target() and dm_add_target().
	 */
	void (*dm_deallocate_targets)(dm_target_t * target_list);

	/*
	 * Suspend or resume a DM device.
	 * suspend = TRUE  -> suspend
	 * suspend = FALSE -> resume
	 */
	int (*dm_suspend)(storage_object_t * object,
			  int                suspend);

	/*
	 * Suspend or resume a DM device representing an EVMS volume.
	 * suspend = TRUE  -> suspend
	 * suspend = FALSE -> resume
	 * (This should only ever be used by the snapshot plugin.)
	 */
	int (*dm_suspend_volume)(logical_volume_t * volume,
				 int                suspend);

	/*
	 * Set/unset a global flag indicating a device has been suspended.
	 */
	void (*dm_set_suspended_flag)(int suspended);

	/*
	 * Activate an EVMS object as a device-mapper device, or change the
	 * mapping for an already active object.
	 */
	int (*dm_activate)(storage_object_t * object,
			   dm_target_t      * target_list);

	/*
	 * Deactivate a device-mapper device representing an EVMS object.
	 */
	int (*dm_deactivate)(storage_object_t * object);

	/*
	 * Assign a new name to a Device-Mapper device. This API does not
	 * actually change the name within the object. The caller can change
	 * the object's name before or after calling this API.
	 */
	int (*dm_rename)(storage_object_t * object,
			 char * old_name,
			 char * new_name);

	/*
	 * Create a Device-Mapper device with no initial mapping. This API is
	 * only available when running DM interface version 4.
	 */
	int (*dm_create)(storage_object_t * object);

	/*
	 * Get the device status from the kernel, and update the object's
	 * information appropriately.
	 */
	int (*dm_update_status)(storage_object_t * object);

	/*
	 * Return a list of targets representing an active device's current
	 * mapping.
	 */
	int (*dm_get_targets)(storage_object_t * object,
			      dm_target_t    * * target_list);

	/*
	 * Load new mappings for a Device-Mapper device. This API is only
	 * available when running DM interface version 4, and will behave
	 * very similarly to dm_activate on an active object.
	 */
	int (*dm_load_targets)(storage_object_t * object,
			       dm_target_t      * target_list);

	/*
	 * Clear a set of targets from a Device-Mapper device before those
	 * targets have been activated. This API is only available when
	 * running DM interface version 4.
	 */
	int (*dm_clear_targets)(storage_object_t * object);

	/*
	 * Return a string with status info for the specified object.
	 */
	int (*dm_get_info)(storage_object_t * object,
			   char           * * info);

	/*
	 * Get a list of all active Device-Mapper devices. This API is only
	 * available when running DM interface version 4.
	 */
	int (*dm_get_devices)(dm_device_list_t * * device_list);

	/*
	 * Deallocate a list of dm_device_list_t structures that is returned
	 * from a call to dm_get_devices().
	 */
	void (*dm_deallocate_device_list)(dm_device_list_t *device_list);

	/*
	 * Wait for an event from a Device-Mapper device. Must specify the
	 * event-number to wait for. After the wait completes, the next
	 * available event-number will be filled in, and a string containing
	 * the status-info is returned (same format as returned from
	 * dm_get_info). This API is only available when running DM interface
	 * version 4.
	 */
	int (*dm_wait)(storage_object_t * object,
		       unsigned int * event_nr,
		       char ** info);

	/*
	 * Are we are running on a 2.4 kernel?
	 *
	 * On 2.4 kernels, device sizes are stored in kilobytes, not sectors.
	 * When creating a device-mapper device that has a target with an odd
	 * number of sectors, we need to round-down child object size to the
	 * nearest kilobyte, or the device-mapper kernel will reject the
	 * creation of the device saying the underlying device is too small.
	 */
	boolean (*is_2_4_kernel)(void);

	/*
	 * Configuration file common services
	 */

	/*
	 * Get the boolean value assigned for a "key".  The Engine understands
	 * "0", "1", "on", "off", "yes", "no", "true", and "false".  The check
	 * for the string values is not case sensitive.
	 */
	int (*get_config_bool)(char    * key,
			       boolean * value);

	/*
	 * Get a 32-bit value assigned to a "key".
	 */
	int (*get_config_uint32)(char      * key,
				 u_int32_t * value);

	/*
	 * Get an array of 32-bit values assigned to a "key".
	 */
	int (*get_config_uint32_array)(char              * key,
				       int               * count,
				       const u_int32_t * * array);

	/*
	 * Get a 64-bit value assigned to a "key".
	 */
	int (*get_config_uint64)(char      * key,
				 u_int64_t * value);

	/*
	 * Get an array of 64-bit values assigned to a "key".
	 */
	int (*get_config_uint64_array)(char              * key,
				       int               * count,
				       const u_int64_t * * array);

	/*
	 * Get a string assigned to a "key".
	 */
	int (*get_config_string)(char         * key,
				 const char * * value);

	/*
	 * Get an array of strings assigned to a "key".
	 */
	int (*get_config_string_array)(char                 * key,
				       int                  * count,
				       const char * const * * array);

	/*
	 * Get the cluster ID.
	 */
	int (*get_clusterid)(ece_clusterid_t * clusterid);

	/*
	 * Get the Engine's current cluster node ID.
	 */
	int (*get_nodeid)(ece_nodeid_t * nodeid);

	/*
	 * Get the ASCII representation for a node ID.  If the length of the
	 * buffer is too small, ENOSPC is returned and *len is set to the
	 * size needed.
	 */
	int (*nodeid_to_string)(ece_nodeid_t * nodeid,
				char         * string,
				uint         * len);

	/*
	 * Get the a node ID for an ASCII string.
	 */
	int (*string_to_nodeid)(const char   * string,
				ece_nodeid_t * nodeid);

	/*
	 * Get a list of nodes, filtering on the flags.
	 */
	int (*get_node_list)(node_search_flags_t   flags,
			     const node_list_t * * node_list);

	/*
	 * Are all volumes created from this object unmounted?
	 * If not, return a pointer to a volume that is mounted.
	 * If vol is NULL then no volume will be returned.
	 */
	boolean (*is_offline)(storage_object_t   * obj,
			      logical_volume_t * * vol);

	/*
	 * Copy a source object to a target object.
	 * The source and target must be offline.
	 */
	int (*offline_copy)(copy_job_t * copy_job);

	/*
	 * Can online copies be done?
	 */
	boolean (*can_online_copy)(void);

	/*
	 * Create the mirror object for online copy.
	 */
	int (*copy_setup)(copy_job_t * copy_job);

	/*
	 * Start the copying.
	 */
	int (*copy_start)(copy_job_t * copy_job);

	/*
	 * Wait for the copying to finish.  This call will not return until
	 * the copy is complete or an error is encountered.
	 */
	int (*copy_wait)(copy_job_t * copy_job);

	/*
	 * Destroy the mirror object.
	 */
	int (*copy_cleanup)(copy_job_t * copy_job);

	/*
	 * Get the current quorum status.
	 */
	boolean (*have_quorum)(void);

	/*
         * Get a string that describes the error number.  The Engine will
	 * determine if the error is a standard Linux error code or a handle
	 * manager error code and will determine the appropriate string to
	 * return.  If the error code does not fall within the ranges for the
	 * above classes of errors, the Engine will return "Unknown error
	 * code".  As with the C strerror(), the strings do not have a
	 * terminating '\n'.
	 */
	const char * (*strerror)(int err_num);

	/*
	 * Exec a program for a given volume.  The Engine will determine
	 * if the execvp() should be done on the local machine or routed
	 * to a remote node in a cluster.
	 */
	pid_t (*fork_and_execvp)(logical_volume_t * vol,
				 char             * argv[],
				 int                stdin_pipe[2],
				 int                stdout_pipe[2],
				 int                stderr_pipe[2]);

	/*
	 * Run the list of objects through the discovery process.
	 */
	int (*discover)(list_anchor_t objects);

	/*
	 * Can the list of objects and everything made from them be discarded?
	 */
	int (*can_discard)(list_anchor_t objects);

	/*
	 * Discard the list of objects.  The Engine will find all the objects,
	 * containers, and volumes that are made from the list of objects and
	 * will process them from the top down, calling each thing's plug-in
	 * to discard the thing.
	 */
	int (*discard)(list_anchor_t objects);

	/*
	 * Mark the objects for rediscovery on the other nodes in the cluster.
	 */
	int (*rediscover_objects)(list_anchor_t objects);

	/*
	 * Mark the containers for rediscovery on the other nodes in the cluster.
	 */
	int (*rediscover_containers)(list_anchor_t containers);

	/*
	 * Allocate an anchor for a list.
	 */
	list_anchor_t (*allocate_list)(void);

	/*
	 * How many elements are in the list?
	 */
	uint (*list_count)(list_anchor_t list);

	/*
	 * Does this list have no elements?
	 */
	boolean (*list_empty)(list_anchor_t list);

	/*
	 * Find a given thing in the list.  If the thing is in the list, return
	 * the element corresponding to the thing.  If the thing is not in the
	 * list, return NULL.
	 * The "compare" function is used for finding the thing in the list.
	 * Each thing in the list will be compared against the thing passed to
	 * find_in_list().  When the compare function returns 0, find_in_list()
	 * will know that it has found the thing and will return the element
	 * corresponding to the thing.  If "compare" is NULL, find_in_list()
	 * will compare the given thing pointer agains the thing pointers in
	 * the list.
	 */
	list_element_t (*find_in_list)(list_anchor_t      list,
				       void             * thing,
				       compare_function_t compare);

	/*
	 * Delete all the elements in the list, but leave the list anchor.
	 */
	void (*delete_all_elements)(list_anchor_t list);

	/*
	 * Destroy the list -- free the list anchor and all the list elements.
	 */
	void (*destroy_list)(list_anchor_t list);

	/*
	 * Get the thing corresponding to element.
	 */
	void * (*get_thing)(list_element_t element);

	/*
	 * Get the element after the given element in the list.  If the given
	 * element is the last one in the list, return NULL.
	 */
	list_element_t (*next_element)(list_element_t element);

	/*
	 * Get the next thing in the list.
	 * On input, *element points to an element in the list.
	 * on return, *element will be set to the next element (NULL if past
	 * the end of the list), and the thing for the *element will be
	 * returned (return NULL if *element is NULL).
	 */
	void * (*next_thing)(list_element_t * element);

	/*
	 * Get the element before the given element in the list.
	 */
	list_element_t (*previous_element)(list_element_t element);

	/*
	 * Get the previous thing in the list.
	 * On input, *element points to an element in the list.
	 * on return, *element will be set to the preveios element (NULL if
	 * past the beginning of the list), and the thing for the *element will
	 * be returned (return NULL if *element is NULL).
	 */
	void * (*previous_thing)(list_element_t * element);

	/*
	 * Get the first thing in the list.  If element is not NULL, *element
	 * will be set to the element for the first thing.
	 */
	void * (*first_thing)(list_anchor_t    list,
			      list_element_t * element);

	/*
	 * Get the last thing in the list.  If elemenet is not NULL, *element
	 * will be set to the element for the last thing.
	 */
	void * (*last_thing)(list_anchor_t    list,
			     list_element_t * element);

	/*
	 * Insert an element (with its thing) into the list.  The element MUST
	 * NOT be in any list.  The flags indicate where and how the element is
	 * inserted.  If the insert succeeds, the element is returned, else NULL
	 * is returned.
	 * If flags say INSERT_AFTER, the element will be inserted after the
	 * ref_element.  If ref_element is NULL, the element will be inserted at
	 * the end of the list.
	 * If flags say INSERT_BEFORE, the element will be inserted before the
	 * ref_element.  If ref_element is NULL, the element will be inserted at
	 * the beginning of the list.
	 * If the EXCLUSIVE_INSERT flag is set, the Engine will make sure there
	 * is only one element in the list that contains the thing.  If there is
	 * another element in the list that has the same thing as the element to
	 * be inserted, the element in that is already the list is returned.
	 */
	list_element_t (*insert_element)(list_anchor_t  list,
					 list_element_t element,
					 insert_flags_t flags,
					 list_element_t ref_element);

	/*
	 * Insert a thing into the list.  The flags indicate where and how the
	 * thing is inserted.  The list element for the new thing is returned.
	 * If flags say INSERT_AFTER, the thing will be inserted after the given
	 * element.  If ref_element is NULL, the thing will be inserted at the
	 * end of the list.
	 * If flags say INSERT_BEFORE, the thing will be inserted before the
	 * given element.  If ref_element is NULL, the thing will be inserted at
	 * the beginning of the list.
	 * If the EXCLUSIVE_INSERT flag is set, the Engine will make sure there
	 * is only one element in the list that contains the thing.
	 */
	list_element_t (*insert_thing)(list_anchor_t  list,
				       void         * thing,
				       insert_flags_t flags,
				       list_element_t ref_element);

	/*
	 * Remove the given element from its list.  The element is not freed.
	 */
	void (*remove_element)(list_element_t element);

	/*
	 * Remove the given element from its list, if it is in one, and free it.
	 */
	void (*delete_element)(list_element_t element);

	/*
	 * Remove the element for the given thing from the list.
	 * If the thing appears multiple times in the list, all occurrences will
	 * be removed.  The element(s) associated with the thing is/are freed.
	 */
	void (*remove_thing)(list_anchor_t list,
			     void        * thing);

	/*
	 * Replace the thing in the list with the new_thing.  If thing
	 * appears multiple times in the list, all occurrences will be
	 * replaced.  The function will return ENOENT if thing is not
	 * found in the list.
	 */
	int (*replace_thing)(list_anchor_t list,
			     void        * thing,
			     void        * new_thing);

	/*
	 * Make a copy of the given list.
	 */
	list_anchor_t (*copy_list)(list_anchor_t list);

	/*
	 * Append a copy of the elements in list2 to the end of list1.
	 */
	int (*concatenate_lists)(list_anchor_t list1,
				 list_anchor_t list2);

	/*
	 * Merge two lists together using the "compare" function.  If "compare"
	 * is NULL, list2 will be appended to list1.  In either case, all the
	 * elements from list2 will end up in list1, and list2 will be empty.
	 * Can return ENOMEM if it fails to allocate memory needed to do the
	 * merge.
	 */
	int (*merge_lists)(list_anchor_t      list1,
			   list_anchor_t      list2,
			   compare_function_t compare);

	/*
	 * Sort the elements in the list.
	 * Can return ENOMEM if it fails to allocate memory needed to do the
	 * sort.
	 */
	int (*sort_list)(list_anchor_t      list,
			 compare_function_t compare);

} engine_functions_t;


#define ENGINE_PLUGIN_API_MAJOR_VERION  12
#define ENGINE_PLUGIN_API_MINOR_VERION  0
#define ENGINE_PLUGIN_API_PATCH_LEVEL   0

typedef struct plugin_functions_s {
	int (*setup_evms_plugin)(engine_functions_t * functions);

	void (*cleanup_evms_plugin)(void);

	/*
	 * Can you apply your plug-in to the input_object?  If yes, return the
	 * size of the object you would create.
	 * The Engine will only call this function on EVMS feature plug-ins.
	 * Other plug-ins may choose whether or not to support this API.
	 */
	int (*can_add_feature)(storage_object_t * input_object,
			       sector_count_t   * size);

	/*
	 * Can you delete this object?
	 */
	int (*can_delete)(storage_object_t * object);

	/*
	 * Can you unassign your plug-in from this object?
	 */
	int (*can_unassign)(storage_object_t * object);

	/*
	 * Can you expand this object?  If yes, build an expand_object_info_t
	 * and add it to the expand_points list.  If you can't expand, but allow
	 * one of your children to expand, call can_expand on whichever child
	 * you will allow to expand.  If you can not handle expanding below you,
	 * do not pass the command down to your child.
	 */
	int (*can_expand)(storage_object_t * object,
			  sector_count_t   * expand_limit,	/* a delta size */
			  list_anchor_t      expand_points);	/* of type expand_object_info_t, */
								/* tag = EXPAND_OBJECT_TAG       */

	/*
	 * Can you allow your child object to expand by "size"?  Return 0 if
	 * yes, else an error code.  "size" is the delta expand BY size, not the
	 * resulting size.  Update the "size" if your object would expand by a
	 * different delta size when your child object expanded by the given
	 * size.
	 */
	int (*can_expand_by)(storage_object_t * object,
			     sector_count_t   * size);

	/*
	 * Can you shrink this object?  If yes, build a shrink_object_info_t and
	 * add it to the shrink_points list.  If you can't shrink, but allow one
	 * of your children to shrink, call can_shrink on whichever child you
	 * will allow to shrink.  If you can not handle shrinking below you, do
	 * not pass the command down to your child.
	 */
	int (*can_shrink)(storage_object_t * object,
			  sector_count_t   * shrink_limit,	/* a delta size */
			  list_anchor_t      shrink_points);	/* of type shrink_object_info_t, */
								/* tag = SHRINK_OBJECT_TAG       */


	/*
	 * Can you allow your child object to shrink by "size"?  Return 0 if
	 * yes, else an error code.  "size" is the delta shrink BY size, not the
	 * resulting size.  Update the "size" if your object would shrink by a
	 * different delta size when your child object shrunk by the given size.
	 */
	int (*can_shrink_by)(storage_object_t * object,
			     sector_count_t   * size);

	/*
	 * Can this object's child be replaced?
	 * If new_child is NULL, the question is simply: can the child object be
	 * replaced?
	 * If new_child is not NULL, the question is: can you replace this child
	 * with new_child?
	 */
	int (*can_replace_child)(storage_object_t * object,
				 storage_object_t * child,
				 storage_object_t * new_child);

	/*
	 * Will you allow your object to be made into a volume?  (We don't see
	 * any reason why you wouldn't.) Will you allow a volume to be removed
	 * off the top of your object?  The "flag" parameter says whether the
	 * volume is to be created (TRUE) or removed (FALSE).
	 */
	int (*can_set_volume)(storage_object_t * object,
			      boolean            flag);

	/*
	 * Claim objects by removing them from the list.  Create a
	 * storage_object_t for the object you are discovering, fill in the
	 * appropriate fields and put the new object on the output_objects list.
	 * If you do not claim an object from the input list, then just
	 * copy/move it to the output list.  The input list can be modified at
	 * will.  The output list must contain all the storage objects in the
	 * system after yours are discovered, i.e., it is the input list, minus
	 * the objects you claim, plus the objects you produce.
	 */
	int (*discover)(list_anchor_t input_objects,
			list_anchor_t output_objects,
			boolean final_call);

	/*
	 * Create storage_object_t(s) from the list of objects using the given
	 * options.  Return the newly allocated storage_object_t(s) in
	 * new_objects list.
	 */
	int (*create)(list_anchor_t    input_objects,
		      option_array_t * options,
		      list_anchor_t    output_objects);

	/*
	 * Assign your plug-in to produce storage objects from the given storage
	 * object.  This function makes sense mainly for segment managers that
	 * are assigned to disks (or segments).
	 */
	int (*assign)(storage_object_t * object,
		      option_array_t   * options);

	/*
	 * Forget about these objects.  Don't delete them.  Just clean up any
	 * data structures you may have associated with them.  The Engine will
	 * call to deactivate the objects durring commit.
	 */
	int (*discard)(list_anchor_t objects);

	/*
	 * Delete the object.  Free any privately allocated data.  Remove your
	 * parent pointer from your child objects.  Do any cleanup necessary to
	 * remove your plug-in from your child objects.  Put your object's
	 * children from the object's child_objects list_anchor_t onto the list_anchor_t
	 * provided in the second parameter.  Call the Engine's free_?????t() to
	 * free the object.
	 */
	int (*delete)(storage_object_t * object,
		      list_anchor_t      child_objects);

	/*
	 * Unassign your plug-in from producing storage objects from the given
	 * storage object.  This function makes sense mainly for segment
	 * managers that are assigned to disks (or segments).
	 */
	int (*unassign)(storage_object_t * object);

	/*
	 * If the "object" is not the "expand_object", then your child is going
	 * to expand.  Do any necessary work to get ready for your child to
	 * expand, e.g., read in metadata, then call expand() on your child
	 * object which will expand.  Upon return from the call to your child's
	 * expand(), do any work necessary to adjust this object to account for
	 * the child object's new size, e.g., update the location of metadata.
	 *
	 * If the "object" is the same as the "expand_object", then this is the
	 * object targeted for expanding.  Expand the object according to the
	 * input_objects given and the options selected.
	 */
	int (*expand)(storage_object_t * object,
		      storage_object_t * expand_object,
		      list_anchor_t      input_objects,
		      option_array_t   * options);

	/*
	 * If the "object" is not the "shrink_object", then your child is going
	 * to shrink.  Do any necessary work to get ready for your child to
	 * shrink, e.g., read in metadata, then call shrink() on your child
	 * object which will shrink.  Upon return from the call to your child's
	 * shrink(), do any work necessary to adjust this object to account for
	 * the child object's new size, e.g., update the location of metadata.
	 *
	 * If the "object" is the same as the "shrink_object", then this is the
	 * object targeted for shrinking.  Shrink the object according to the
	 * input_objects given and the options selected.
	 */
	int (*shrink)(storage_object_t * object,
		      storage_object_t * shrink_object,
		      list_anchor_t      input_objects,
		      option_array_t   * options);

	/*
	 * Replace the object's child with the new child object.
	 */
	int (*replace_child)(storage_object_t * object,
			     storage_object_t * child,
			     storage_object_t * new_child);

	/*
	 * This call notifies you that your object is being made into (or part
	 * of) a volume or that your object is no longer part of a volume.  The
	 * "flag" parameter indicates whether the volume is being created (TRUE)
	 * or removed (FALSE).
	 */
	void (*set_volume)(storage_object_t * object,
			   boolean            flag);

	/*
	 * Put sectors on the kill list.  The plug-in translates the lsn and
	 * count into lsn(s) and count(s) for its child object(s) and calls the
	 * child object's add_sectors_to_kill_list().
	 * The Device Manager calls the Engine's add_sectors_to_kill_list
	 * service to put the sectors on the Engine's kill list.
	 */
	int (*add_sectors_to_kill_list)(storage_object_t * object,
					lsn_t              lsn,
					sector_count_t     count);

	/*
	 * Write your plug-ins data, e.g., feature header and feature metadata,
	 * to disk.  Clear the SOFLAG_DIRTY in the storage_object_t(s).
	 * Committing changes in done in several phases.  "phase" says which
	 * phase of the commit is being performed.
	 *
	 * Write your first copy of metadata during the FIRST_METADATA_WRITE
	 * phase; write your second copy of metadata (if you have one) during
	 * the SECOND_METADATA_WRITE phase.
	 */
	int (*commit_changes)(storage_object_t * object,
			      uint               phase);

	/*
	 * Make the storage object active.
	 */
	int (*activate)(storage_object_t * object);

	/*
	 * Deactivate the storage object.
	 */
	int (*deactivate)(storage_object_t * object);

	/*
	 * Return the total number of supported options for the specified task.
	 */
	int (*get_option_count)(task_context_t * context);

	/*
	 * Fill in the initial list of acceptable objects.  Fill in the minimum
	 * and maximum number of objects that must/can be selected.  Set up all
	 * initial values in the option_descriptors in the context record for
	 * the given task.  Some fields in the option_descriptor may be
	 * dependent on a selected object.  Leave such fields blank for now, and
	 * fill in during the set_objects call.
	 */
	int (*init_task)(task_context_t * context);

	/*
	 * Examine the specified value, and determine if it is valid for the
	 * task and option_descriptor index.  If it is acceptable, set that
	 * value in the appropriate entry in the option_descriptor.  The value
	 * may be adjusted if necessary/allowed.  If so, set the effect return
	 * value accordingly.
	 */
	int (*set_option)(task_context_t * context,
			  u_int32_t        index,
			  value_t        * value,
			  task_effect_t  * effect);

	/*
	 * Validate the objects in the selected_objects list in the task
	 * context.
	 * Remove from the selected objects lists any objects which are not
	 * acceptable.  For unacceptable objects, create a declined_handle_t
	 * structure with the reason why it is not acceptable, and add it to the
	 * declined_objects list.  Modify the acceptable_objects list in the
	 * task context as necessary based on the selected objects and the
	 * current settings of the options.  Modify any option settings as
	 * necessary based on the selected objects.  Return the appropriate
	 * task_effect_t settings if the object list(s), minimum or maximum
	 * objects selected, or option settings have changed.
	 */
	int (*set_objects)(task_context_t * context,
			   list_anchor_t    declined_objects,	 /* of type declined_handle_t */
			   task_effect_t  * effect);

	/*
	 * Return any additional information that you wish to provide about the
	 * object.  The Engine provides an external API to get the information
	 * stored in the storage_object_t.  This call is to get any other
	 * information about the object that is not specified in the
	 * storage_object_t.  Any piece of information you wish to provide must
	 * be in an extended_info_t structure.  Use the Engine's engine_alloc()
	 * to allocate the memory for the extended_info_t.  Also use
	 * engine_alloc() to allocate any strings that may go into the
	 * extended_info_t.  Then use engine_alloc() to allocate an
	 * extended_info_array_t with enough entries for the number of
	 * extended_info_t structures you are returning.  Fill in the array and
	 * return it in *info.
	 *
	 * If you have extended_info_t descriptors that themselves may have more
	 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE
	 * flag in the extended_info_t flags field.  If the caller wants more
	 * information about a particular extended_info_t item, this API will be
	 * called with a pointer to the storage_object_t and with a pointer to
	 * the name of the extended_info_t item.  In that case, return an
	 * extended_info_array_t with further information about the item.  Each
	 * of those items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
	 * set if you desire.  It is your responsibility to give the items
	 * unique names so that you know which item the caller is asking
	 * additional information for.  If info_name is NULL, the caller just
	 * wants top level information about the object.
	 */
	int (*get_info)(storage_object_t        * object,
			char                    * info_name,
			extended_info_array_t * * info);

	/*
	 * Apply the settings of the options to the given object.
	 */
	int (*set_info)(storage_object_t * object,
			option_array_t   * options);

	/*
	 * Return any additional information that you wish to provide about your
	 * plug-in.  The Engine provides an external API to get the information
	 * stored in the plugin_record_t.  This call is to get any other
	 * information about the plug-in that is not specified in the
	 * plugin_record_t.  Any piece of information you wish to provide must
	 * be in an extended_info_t structure.  Use the Engine's engine_alloc()
	 * to allocate the memory for the extended_info_t.  Also use
	 * engine_alloc() to allocate any strings that may go into the
	 * extended_info_t.  Then use engine_alloc() to allocate an
	 * extended_info_array_t with enough entries for the number of
	 * extended_info_t structures you are returning.  Fill in the array and
	 * return it in *info.
	 *
	 * If you have extended_info_t descriptors that themselves may have more
	 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE
	 * flag in the extended_info_t flags field.  If the caller wants more
	 * information about a particular extended_info_t item, this API will be
	 * called with a pointer to the storage_object_t and with a pointer to
	 * the name of the extended_info_t item.  In that case, return an
	 * extended_info_array_t with further information about the item.  Each
	 * of those items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
	 * set if you desire.  It is your responsibility to give the items
	 * unique names so that you know which item the caller is asking
	 * additional information for.  If info_name is NULL, the caller just
	 * wants top level information about the object.
	 */
	int (*get_plugin_info)(char                    * info_name,
			       extended_info_array_t * * info);

	/*
	 * Convert lsn and count to lsn and count on the child object(s) and
	 * call the read function of child objects.
	 */
	int (*read)(storage_object_t * object,
		    lsn_t              lsn,
		    sector_count_t     count,
		    void             * buffer);

	/*
	 * Convert lsn and count to lsn and count on the child object(s) and
	 * call the write function of child objects.
	 */
	int (*write)(storage_object_t * object,
		     lsn_t              lsn,
		     sector_count_t     count,
		     void             * buffer);

	/*
	 * Return an array of plug-in functions that you support for this
	 * object.
	 */
	int (*get_plugin_functions)(storage_object_t        * object,
				    function_info_array_t * * actions);

	/*
	 * Execute the plug-in function on the object.
	 */
	int (*plugin_function)(storage_object_t * object,
			       task_action_t      action,
			       list_anchor_t      objects,
			       option_array_t   * options);

} plugin_functions_t;


#define ENGINE_FSIM_API_MAJOR_VERION  10
#define ENGINE_FSIM_API_MINOR_VERION  0
#define ENGINE_FSIM_API_PATCH_LEVEL   0

typedef struct fsim_functions_s {
	int (*setup_evms_plugin)(engine_functions_t * functions);

	void (*cleanup_evms_plugin)(void);

	/*
	 * Does this FSIM manage the file system on this volume?
	 * Return 0 for "yes", else a reason code.
	 */
	int (*probe)(logical_volume_t * volume);

	/*
	 * Get the current size of the file system on this volume.
	 */
	int (*get_fs_size)(logical_volume_t * volume,
			   sector_count_t   * fs_size);

	/*
	 * Get the file system size limits for this volume.
	 */
	int (*get_fs_limits)(logical_volume_t * volume,
			     sector_count_t   * fs_min_size,
			     sector_count_t   * fs_max_size,
			     sector_count_t   * vol_max_size);

	/*
	 * Can you install your file system on this volume?
	 */
	int (*can_mkfs)(logical_volume_t * volume);

	/*
	 * Can you remove your file system from this volume?
	 */
	int (*can_unmkfs)(logical_volume_t * volume);

	/*
	 * Can you fsck this volume?
	 */
	int (*can_fsck)(logical_volume_t * volume);

	/*
	 * Can you defrag this volume?
	 */
	int (*can_defrag)(logical_volume_t * volume);

	/*
	 * Can you expand this volume by the amount specified?
	 *
	 * If your file system cannot handle expansion at all, return an
	 * error code that indicates why it cannot be expanded.
	 *
	 * If your file system can expand but cannot handle having unused
	 * space after the end of your file system, adjust the *delta_size
	 * to the maximum you allow and return 0.
	 *
	 * If your file system cannot fill the resulting size but your file
	 * system can handle extra unused space after the end of the file
	 * system, then do not change the *delta_size and return 0.
	 */
	int (*can_expand_by)(logical_volume_t * volume,
			     sector_count_t   * delta_size);

	/*
	 * Can you shrink this volume by the amount specified?
	 *
	 * If your file system cannot handle shrinking at all, return an
	 * error code that indicates why it cannot be shrunk.
	 *
	 * If your file system can shrink but the *delta_size is too much to
	 * shrink by, adjust the *delta_size to the maximum shrinkage you allow
	 * and return 0.
	 */
	int (*can_shrink_by)(logical_volume_t * volume,
			     sector_count_t   * delta_size);

	/*
	 * mkfs has been scheduled.  Do any setup work such as claiming another
	 * volume for an external log.
	 */
	int (*mkfs_setup)(logical_volume_t * volume,
			  option_array_t   * options);

	/*
	 * Install your file system on the volume.
	 */
	int (*mkfs)(logical_volume_t * volume,
		    option_array_t   * options);

	/*
	 * Forget about this volume.  Don't remove the file system.  Just clean
	 * up any data structures you may have associated with it.
	 */
	int (*discard)(logical_volume_t * volume);

	/*
	 * unmkfs has been scheduled.  Do any setup work such as releasing
	 * another volume that was used for an external log.
	 */
	int (*unmkfs_setup)(logical_volume_t * volume);

	/*
	 * Remove your file system from the volume.  This could be as simple as
	 * wiping out critical sectors, such as a superblock, so that you will
	 * no longer detect that your file system is installed on the volume.
	 */
	int (*unmkfs)(logical_volume_t * volume);

	/*
	 * Run fsck on the volume.
	 */
	int (*fsck)(logical_volume_t * volume,
		    option_array_t   * options);

	/*
	 * Defragment on the volume.
	 */
	int (*defrag)(logical_volume_t * volume,
		      option_array_t   * options);

	/*
	 * Expand the volume to new_size.  If the volume is not expanded exactly
	 * to new_size, set new_sie to the new_size of the volume.
	 */
	int (*expand)(logical_volume_t * volume,
		      sector_count_t   * new_size);

	/*
	 * Shrink the volume to new_size.  If the volume is not expanded
	 * exactly to new_size, set new_size to the new_size of the volume.
	 */
	int (*shrink)(logical_volume_t * volume,
		      sector_count_t     requested_size,
		      sector_count_t   * new_size);

	/*
	 * Return the total number of supported options for the specified task.
	 */
	int (*get_option_count)(task_context_t * context);

	/*
	 * Fill in the initial list of acceptable objects.  Fill in the minimum
	 * and maximum number of objects that must/can be selected.  Set up all
	 * initial values in the option_descriptors in the context record for
	 * the given task.  Some fields in the option_descriptor may be
	 * dependent on a selected object.  Leave such fields blank for now, and
	 * fill in during the set_objects call.
	 */
	int (*init_task)(task_context_t * context);

	/*
	 * Examine the specified value, and determine if it is valid for the
	 * task and option_descriptor index.  If it is acceptable, set that
	 * value in the appropriate entry in the option_descriptor.  The value
	 * may be adjusted if necessary/allowed.  If so, set the effect return
	 * value accordingly.
	 */
	int (*set_option)(task_context_t * context,
			  u_int32_t        index,
			  value_t        * value,
			  task_effect_t  * effect);

	/*
	 * Validate the volumes in the selected_objects list in the task
	 * context.
	 *
	 * Remove from the selected objects lists any volumes which are not
	 * acceptable.  For unacceptable volumes, create a declined_handle_t
	 * structure with the reason why it is not acceptable, and add it to the
	 * declined_volumes list.  Modify the acceptable_objects list in the
	 * task context as necessary based on the selected objects and the
	 * current settings of the options.  Modify any option settings as
	 * necessary based on the selected objects.  Return the appropriate
	 * task_effect_t settings if the object list(s), minimum or maximum
	 * objects selected, or option settings have changed.
	 */
	int (*set_volumes)(task_context_t * context,
			   list_anchor_t    declined_volumes,	 /* of type declined_handle_t */
			   task_effect_t  * effect);


	/*
	 * Return any additional information that you wish to provide about the
	 * volume.  The Engine provides an external API to get the information
	 * stored in the logical_volume_t.  This call is to get any other
	 * information about the volume that is not specified in the
	 * logical_volume_t.  Any piece of information you wish to provide must
	 * be in an extended_info_t structure.  Use the Engine's engine_alloc()
	 * to allocate the memory for the extended_info_t.  Also use
	 * engine_alloc() to allocate any strings that may go into the
	 * extended_info_t.  Then use engine_alloc() to allocate an
	 * extended_info_array_t with enough entries for the number of
	 * extended_info_t structures you are returning.  Fill in the array and
	 * return it in *info.
	 *
	 * If you have extended_info_t descriptors that themselves may have more
	 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE
	 * flag in the extended_info_t flags field.  If the caller wants more
	 * information about a particular extended_info_t item, this API will be
	 * called with a pointer to the storage_object_t and with a pointer to
	 * the name of the extended_info_t item.  In that case, return an
	 * extended_info_array_t with further information about the item.  Each
	 * of those items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
	 * set if you desire.  It is your responsibility to give the items
	 * unique names so that you know which item the caller is asking
	 * additional information for.  If info_name is NULL, the caller just
	 * wants top level information about the object.
	 */
	int (*get_volume_info)(logical_volume_t        * volume,
			       char                    * info_name,
			       extended_info_array_t * * info);

	/*
	 * Apply the settings of the options to the given volume.
	 */
	int (*set_volume_info)(logical_volume_t * volume,
			       option_array_t   * options);

	/*
	 * Return any additional information that you wish to provide about your
	 * plug-in.  The Engine provides an external API to get the information
	 * stored in the plugin_record_t.  This call is to get any other
	 * information about the plug-in that is not specified in the
	 * plugin_record_t.  Any piece of information you wish to provide must
	 * be in an extended_info_t structure.  Use the Engine's engine_alloc()
	 * to allocate the memory for the extended_info_t.  Also use
	 * engine_alloc() to allocate any strings that may go into the
	 * extended_info_t.  Then use engine_alloc() to allocate an
	 * extended_info_array_t with enough entries for the number of
	 * extended_info_t structures you are returning.  Fill in the array and
	 * return it in *info.
	 *
	 * If you have extended_info_t descriptors that themselves may have more
	 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE
	 * flag in the extended_info_t flags field.  If the caller wants more
	 * information about a particular extended_info_t item, this API will be
	 * called with a pointer to the storage_object_t and with a pointer to
	 * the name of the extended_info_t item.  In that case, return an
	 * extended_info_array_t with further information about the item.  Each
	 * of those items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
	 * set if you desire.  It is your responsibility to give the items
	 * unique names so that you know which item the caller is asking
	 * additional information for.  If info_name is NULL, the caller just
	 * wants top level information about the object.
	 */
	int (*get_plugin_info)(char                    * info_name,
			       extended_info_array_t * * info);

	/*
	 * Return an array of plug-in functions that you support for this
	 * volume.
	 */
	int (*get_plugin_functions)(logical_volume_t        * volume,
				    function_info_array_t * * actions);

	/*
	 * Execute the plug-in function on the volume.
	 */
	int (*plugin_function)(logical_volume_t * volume,
			       task_action_t      action,
			       list_anchor_t      objects,
			       option_array_t   * options);

} fsim_functions_t;


#define ENGINE_CONTAINER_API_MAJOR_VERION  9
#define ENGINE_CONTAINER_API_MINOR_VERION  0
#define ENGINE_CONTAINER_API_PATCH_LEVEL   0

typedef struct container_functions_s {

	/*
	 * Can you create a container from this list of data segments?
	 */
	int (*can_create_container)(list_anchor_t objects);

	/*
	 * Can you destroy the container?  You must check to be sure that no
	 * regions are exported from this container.
	 */
	int (*can_delete_container)(storage_container_t * container);

	/*
	 * Can you add this object to the container?
	 * Return 0 if you can, else return an error code.
	 */
	int (*can_add_object)(storage_object_t    * object,
			      storage_container_t * container);

	/*
	 * Can you remove this object from the container that currently consumes
	 * it?  Return 0 if you can, else return an error code.
	 */
	int (*can_remove_object)(storage_object_t * object);

	/*
	 * Create and fill in the container adding newly created unallocated
	 * objects produced as appropriate.  The plug-in must claim the objects,
	 * as it does in discovery.  Mark the container dirty.  Must use
	 * allocate_container engine API to allocate the container structure.
	 */
	int (*create_container)(list_anchor_t           objects,
				option_array_t        * options,
				storage_container_t * * container);

	/*
	 * Engine will remove the object from its current container before
	 * calling this API.  Claim the object and add it to a container
	 * objects_consumed list.  Mark the container dirty.  Update/allocate
	 * the unallocated object that is exported from the container.  If
	 * container is NULL, add the object to default (or unassigned)
	 * container.
	 */
	int (*add_object)(storage_object_t    * object,
			  storage_container_t * container,
			  option_array_t      * options);

	/*
	 * Transfer the object from its current container to the specified
	 * container.  Mark the container dirty.  If container is NULL, transfer
	 * the object to the default (or unassigned) container.
	 */
	int (*transfer_object)(storage_object_t    * object,
			       storage_container_t * container,
			       option_array_t      * options);

	/*
	 * Forget about this container.  Don't delete it.  Just clean up any
	 * data structures you may have associated with it.
	 */
	int (*discard_container)(storage_container_t * container);

	/*
	 * Remove object from its current container.  Make sure there are no
	 * allocated objects produced by the container that are using space in
	 * the object.  Does not destroy the segment.
	 */
	int (*remove_object)(storage_object_t * object);

	/*
	 * Destroy the container.  Make sure there are no allocated objects
	 * being produced by the container.  Put your consumed objects from the
	 * container's objects_consumed list_anchor_t onto the list_anchor_t provided in the
	 * second parameter.  Free any private data, then use the Engine's
	 * free_container() to deallocate the container object.
	 */
	int (*delete_container)(storage_container_t * container,
				list_anchor_t         objects_consumed);

	/*
	 * Write any container metadata, to disk.  Clear the SCFLAG_DIRTY in
	 * the container.
	 *
	 * Committing changes in done in several (two for now) phases.  "phase"
	 * says which phase of the commit is being performed.
	 *
	 * Write your first copy of metadata during the FIRST_METADATA_WRITE
	 * phase; write your second copy of metadata (if you have one) during
	 * the SECOND_METADATA_WRITE phase.
	 */
	int (*commit_container_changes)(storage_container_t * container,
					uint                  phase);

	/*
	 * Return any additional information that you wish to provide about the
	 * container.  The Engine provides an external API to get the information
	 * stored in the storage_container_t.  This call is to get any other
	 * information about the container that is not specified in the
	 * storage_container_t.  Any piece of information you wish to provide
	 * must be in an extended_info_t structure.  Use the Engine's
	 * engine_alloc() to allocate the memory for the extended_info_t.  Also
	 * use engine_alloc() to allocate any strings that may go into the
	 * extended_info_t.  Then use engine_alloc() to allocate an
	 * extended_info_array_t with enough entries for the number of
	 * extended_info_t structures you are returning.  Fill in the array and
	 * return it in *info.
	 *
	 * If you have extended_info_t descriptors that themselves may have more
	 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE
	 * flag in the extended_info_t flags field.  If the caller wants more
	 * information about a particular extended_info_t item, this API will be
	 * called with a pointer to the storage_container_t and with a pointer
	 * to the name of the extended_info_t item.  In that case, return an
	 * extended_info_array_t with further information about the item.  Each
	 * of those items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
	 * set if you desire.  It is your responsibility to give the items
	 * unique names so that you know which item the caller is asking
	 * additional information for.  If info_name is NULL, the caller just
	 * wants top level information about the object.
	 */
	int (*get_container_info)(storage_container_t     * container,
				  char                    * info_name,
				  extended_info_array_t * * info);

	/*
	 * Apply the settings of the options to the given container.
	 */
	int (*set_container_info)(storage_container_t * container,
				  option_array_t      * options);

	/*
	 * Return an array of plug-in functions that you support for this
	 * container.
	 */
	int (*get_plugin_functions)(storage_container_t     * container,
				    function_info_array_t * * actions);

	/*
	 * Execute the plug-in function on the container.
	 */
	int (*plugin_function)(storage_container_t * container,
			       task_action_t         action,
			       list_anchor_t         objects,
			       option_array_t      * options);

} container_functions_t;


#define ENGINE_CLUSTER_API_MAJOR_VERION  1
#define ENGINE_CLUSTER_API_MINOR_VERION  0
#define ENGINE_CLUSTER_API_PATCH_LEVEL   0

typedef struct cluster_functions_s {

	int (*setup_evms_plugin)(engine_functions_t * functions);

	void (*cleanup_evms_plugin)(void);

	int (*register_callback)(ece_callback_type_t type,
				 ece_cb_t            cb);

	int (*unregister_callback)(ece_cb_t cb);

	int (*send_msg)(ece_msg_t * msg);

	int (*get_clusterid)(ece_clusterid_t * clusterid);

	int (*get_my_nodeid)(ece_nodeid_t * nodeid);

	int (*get_num_config_nodes)(uint * num_nodes);

	int (*get_all_nodes)(uint         * num_nodes,
			     ece_nodeid_t * nodes);

	int (*get_membership)(ece_event_t * membership_event);

	/*
	 * Get the ASCII representation for a node ID.
	 */
	int (*nodeid_to_string)(const ece_nodeid_t * nodeid,
				char         * string,
				uint         * len);

	/*
	 * Get the a node ID for and ASCII string.
	 */
	int (*string_to_nodeid)(const char   * string,
				ece_nodeid_t * nodeid);

	/*
	 * Get the ASCII representation for a cluster ID.
	 */
	int (*clusterid_to_string)(ece_clusterid_t * clusterid,
				   char            * string,
				   uint              len);

	/*
	 * Get the a cluster ID for and ASCII string.
	 */
	int (*string_to_clusterid)(char            * string,
				   ece_clusterid_t * clusterid);

	/*
	 * Return any additional information that you wish to provide about your
	 * plug-in.  The Engine provides an external API to get the information
	 * stored in the plugin_record_t.  This call is to get any other
	 * information about the plug-in that is not specified in the
	 * plugin_record_t.  Any piece of information you wish to provide must
	 * be in an extended_info_t structure.  Use the Engine's engine_alloc()
	 * to allocate the memory for the extended_info_t.  Also use
	 * engine_alloc() to allocate any strings that may go into the
	 * extended_info_t.  Then use engine_alloc() to allocate an
	 * extended_info_array_t with enough entries for the number of
	 * extended_info_t structures you are returning.  Fill in the array and
	 * return it in *info.
	 *
	 * If you have extended_info_t descriptors that themselves may have more
	 * extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE
	 * flag in the extended_info_t flags field.  If the caller wants more
	 * information about a particular extended_info_t item, this API will be
	 * called with a pointer to the storage_object_t and with a pointer to
	 * the name of the extended_info_t item.  In that case, return an
	 * extended_info_array_t with further information about the item.  Each
	 * of those items may have the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
	 * set if you desire.  It is your responsibility to give the items
	 * unique names so that you know which item the caller is asking
	 * additional information for.  If info_name is NULL, the caller just
	 * wants top level information about the object.
	 */
	int (*get_plugin_info)(char                    * info_name,
			       extended_info_array_t * * info);

	/*
	 * Return an array of plug-in functions that you support for this node.
	 */
	int (*get_plugin_functions)(ece_nodeid_t            * nodeid,
				    function_info_array_t * * actions);

	/*
	 * Execute the plug-in function.
	 */
	int (*plugin_function)(ece_nodeid_t        * nodeid,
			       task_action_t         action,
			       list_anchor_t         objects,
			       option_array_t      * options);

} cluster_functions_t;

#endif

