/*
 * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
 * This software licensed under the GNU LGPL.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef PILS_PLUGIN_H
#  define PILS_PLUGIN_H
#  include <ltdl.h>

/* Glib headers generate warnings - so we make them go away */

#define		time	FOOtime
#define		index	FOOindex
#include	<glib.h>
#undef		index
#undef		time

/*****************************************************************************
 *	PILS - Universal Plugin and Interface loading system
 *****************************************************************************
 *
 * An Overview of PILS...
 *
 * PILS is fairly general and reasonably interesting plugin loading system.
 * We manage both plugins and their interfaces
 *
 * This plugin / interface management system is quite general, and should be
 * directly usable by basically any project on any platform on which it runs
 * - which should be many, since everything is build with automake
 * and libtool.
 *
 * Some terminology...
 *
 * There are two basic kinds of objects we deal with here:
 *
 * Plugins: dynamically loaded chunks of code which implement one or more
 *		interfaces.  The system treats all plugins as the same.
 *		In UNIX, these are dynamically loaded ".so" files.
 *
 * Interface: A set of functions which implement a particular capability
 * 		(or interface)
 * 	Generally interfaces are registered as part of a plugin.
 * 	The system treats all interfaces of the same type the same.
 * 	It is common to have exactly one interface inside of each plugin.
 * 	In this case, the interface name should match the plugin name.
 *
 * Each interface implementation exports certain functions for its clients
 * to use.   We refer to these those "Ops".  Every interface of the same type
 * "imports" the same interfaces from its interface manager,
 * and exports the same "Ops".
 *
 * Each interface implementation is provided certain interfaces which it
 * imports when it from its interface manager when it is registered.
 * We refer to these as "Imports".  Every interface of a given type
 * imports the same interfaces.
 *
 * The story with plugins is a little different...
 *
 * Every plugin exports a certain set of interfaces, regardless of what type
 * of interfaces is implemented by it.  These are described in the
 * PILPluginOps structure.
 *
 * Every plugin imports a certain set of interfaces, regardless of what type
 * of interfaces it may implement.  These are described by the
 * PILPluginImports structure.
 *
 * In the function parameters below, the following notation will
 * sometimes appear:
 *
 * (OP) == Output Parameter - a parameter which is modified by the
 * 	function being called
 *
 *
 *****************************************************************************
 *
 * The basic structures we maintain about plugins are as follows:
 *
 *	PILPlugin		The data which represents a plugin.
 *	PILPluginType		The data common to all plugins of a given type
 *	PILPluginUniv		The set of all plugin types in the Universe
 *					(well... at least *this* universe)
 *
 * The basic structures we maintain about interfaces are as follows:
 * 	PILInterface		The data which represents a interface
 * 	PILInterfaceType		The data which is common to all
 * 					interfaces of a given type
 *	PILPluginUniv		The set of all interface types in the Universe
 *					(well... at least *this* universe)
 *
 * Regarding "Universe"s.  It is our intent that a given program can deal
 * with plugins in more than one universe.  This might occur if you have two
 * independent libraries each of which uses the plugin loading environment
 * to manage their own independent interface components.  There should be
 * no restriction in creating a program which uses both of these libraries. 
 * At least that's what we hope ;-)
 *
 *
 ***************************************************************************
 * SOME MORE DETAILS ABOUT PLUGINS...
 ***************************************************************************
 *
 * Going back to more detailed data structures about plugins...
 *
 *	PILPluginImports		The set of standard functions all plugins
 *				import.
 *				This includes:
 *					register_plugin()
 *					unregister_plugin()
 *					register_interface()
 *					unregister_interface()
 *					load_plugin()
 *					log()	Preferred logging function
 *
 *	PILPluginOps		The set of standard operations all plugins
 *				export.
 *				This includes:
 *					pluginversion()
 *					pluginname()
 *					getdebuglevel()
 *					setdebuglevel()
 *					close()	    Prepare for unloading...
 *
 *	Although we treat plugins pretty much the same, they are still
 *	categorized into "types" - one type per directory.  These types
 *	generally correspond to interface types.
 *
 *	One can only cause a plugin to be loaded - not a interface.  But it is
 *	common to assume that loading a plugin named foo of type bar will
 *	cause a interface named foo of type bar to be registered.  If one
 *	wants to implement automatic plugin loading in a given interface type,
 *	this assumption is necessary.
 *
 *	The general way this works is...
 *
 *	- A request is made to load a particular plugin of a particular type.
 *
 *	- The plugin is loaded from the appropriate directory for plugins
 *		of that type.
 *
 *	- The ml_plugin_init() function is called once when the plugin is
 *		loaded.
 *
 *	The ml_plugin_init() function is passed a vector of functions which
 *		point to functions it can call to register itself, etc.
 *		(it's of type PILPluginImports)
 *
 * 	The ml_plugin_init function then uses this set of imported functions
 * 	to register itself and its interfaces.
 *
 * 	The mechanism of registering a interface is largely the same for
 * 	every interface.  However, the semantics of registering a interfaces
 * 	is determined by the interface manager for the particular type of
 * 	interface being discussed.
 *
 ***************************************************************************
 * SOME MORE DETAILS ABOUT PLUGINS...
 ***************************************************************************
 *
 *	There is only one built in type of interface.  That's the Interface
 *	manager interface.
 *	The interface manager for the interface of type "InterfaceMgr",
 *	named "InterfaceMgr" inserts itself into the system in order
 *	to bootstrap things...
 *
 *	When an attempt is made to register a interface of an unknown type,
 *	then the appropriate Interface manager is loaded automatically.
 *
 *	The name of an interface manager determines the type of
 *	interface it manages.
 *
 *	It handles requests for interfaces whose type is the same
 *	as its interface name.  If the interface manager's interface name
 *	is foo, then it is the interface manager for all interfaces whose
 *	type is foo.
 *
 * 	Types associated with interfaces of type Interface
 *
 *	PILInterfaceOps	The set of interfaces that every interface
 *				manager exports
 *	PILInterfaceImports	The set of interfaces which are supplied to
 *				(imported by) every interface of type
 *				Interface.  (that is, every interface
 *				manager).
 *
 *****************************************************************************
 *
 * Each plugin has only one entry point which is exported directly, regardless
 * of what kind of interface(s) it may implement...
 *
 * This entrypoint is named ml_plugin_init()	{more or less - see below}
 *
 * The ml_plugin_init() function is called once when the plugin is loaded.
 *
 *
 * All other function pointers are registered (exported) through parameters
 * passed to ml_plugin_init()
 *
 * It is the purpose of the Ml_plugin_init() to register the plugin,
 * and all the interfaces which this plugin implements.  A pointer to
 * the  registration function is in the parameters which are passed
 * to ml_plugin_init().
 *
 *****************************************************************************
 *
 * THINGS IN THIS DESIGN WHICH ARE PROBABLY BROKEN...
 *
 * It may also be the case that the plugin loading environment needs
 * to be able to have some kind of user_data passed to it which it can
 * also pass along to any interface ...
 *
 * Maybe this should be handled by a sort of global user_data registration
 * structure, so globals can be passed to interfaces when they're registered.
 *
 * A sort of "user_data" registry.  One for each interface type and one
 * for each interface...  Or maybe it could be even more flexible...
 *
 * This is all so that these nice pristene, beautiful concepts can come out
 * and work well in the real world where interfaces need to interact with
 * some kind of global system view, and with each other...
 *
 * Probably need some better way of managing interface versions, etc.
 *
 ****************************************************************************
 */

/*
 * If you want to use this funky export stuff, then you need to #define
 * PIL_PLUGINTYPE and PIL_PLUGIN *before* including this file.
 *
 * The way to use this stuff is to declare your primary entry point this way:
 *
 * This example is for an plugin of type "auth" named "sha1"
 *
 *	#define PIL_PLUGINTYPE	auth
 *	#define PIL_PLUGIN	sha1
 *	#include <upmls/PILPlugin.h>
 *
 *	static const char*	Ourpluginversion	(void);
 *	static const char*	Ourpluginname	(void);
 *	static int		Ourgetdebuglevel(void);
 *	static void		Oursetdebuglevel(int);
 *	static void		Ourclose	(PILPlugin*);
 *
 *	static struct PILPluginOps our_exported_plugin_operations =
 *	{	Ourpluginversion,
 *	,	Ourpluginname
 *	,	Ourgetdebuglevel
 *	,	Oursetdebuglevel
 *	,	Ourclose
 *	};
 *
 *	static const PILPluginImports*	PluginOps;
 *	static PILPlugin*		OurPlugin;
 *
 *	// Our plugin initialization and registration function
 *	// It gets called when the plugin gets loaded.
 *	PIL_rc
 *	PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
 *	{
 *		PluginOps = imports;
 *		OurPlugin = us;
 *
 *		// Register ourself as a plugin * /
 *		imports->register_plugin(us, &our_exported_plugin_operations);
 *
 *		// Register our interfaces
 *		imports->register_interface(us, "interfacetype", "interfacename"
 *			// Be sure and define "OurExports" and OurImports
 *			// above...
 *		,	&OurExports
 *		,	&OurImports);
 *		// Repeat for all interfaces in this plugin...
 *
 *	}
 *
 * Except for the PIL_PLUGINTYPE and the PIL_PLUGIN definitions, and changing
 * the names of various static variables and functions, every single plugin is
 * set up pretty much the same way
 *
 */

/*
 * No doubt there is a fancy preprocessor trick for avoiding these
 * duplications but I don't have time to figure it out.  Patches are
 * being accepted...
 */
#define	mlINIT_FUNC	_pil_plugin_init
#define mlINIT_FUNC_STR	"_pil_plugin_init"
#define PIL_INSERT	_LTX_
#define PIL_INSERT_STR	"_LTX_"

/*
 * snprintf-style format string for initialization entry point name:
 * 	arguments are: (plugintype, pluginname)
 */
#define	PIL_FUNC_FMT	"%s" PIL_INSERT_STR "%s" mlINIT_FUNC_STR

#ifdef __STDC__
#  define EXPORTHELPER1(plugintype, insert, pluginname, function)	\
	 plugintype##insert##pluginname##function
#else
#  define EXPORTHELPER1(plugintype, insert, pluginname, function)	\
 	plugintype/**/insert/**/pluginname/**/function
#endif

#define EXPORTHELPER2(a, b, c, d)    EXPORTHELPER1(a, b, c, d)
#define PIL_PLUGIN_INIT							\
	EXPORTHELPER2(PIL_PLUGINTYPE,PIL_INSERT,PIL_PLUGIN,mlINIT_FUNC)

/*
 *	Plugin loading return codes.  OK will always be zero.
 *
 *	There are many ways to fail, but only one kind of success ;-)
 */

typedef enum {
	PIL_OK=0,	/* Success */
	PIL_INVAL=1,	/* Invalid Parameters */
	PIL_BADTYPE=2,	/* Bad plugin/interface type */
	PIL_EXIST=3,	/* Duplicate Plugin/Interface name */
	PIL_OOPS=4,	/* Internal Error */
	PIL_NOPLUGIN=5,	/* No such plugin or Interface */
}PIL_rc;			/* Return code from Plugin fns*/

const char * PIL_strerror(PIL_rc rc);

typedef struct PILPluginImports_s	PILPluginImports;
typedef struct PILPluginOps_s		PILPluginOps;
typedef struct PILPlugin_s		PILPlugin;
typedef struct PILPluginUniv_s		PILPluginUniv;
typedef struct PILPluginType_s		PILPluginType;

typedef struct PILInterface_s		PILInterface;
typedef struct PILInterfaceImports_s	PILInterfaceImports;
typedef struct PILInterfaceUniv_s	PILInterfaceUniv;
typedef struct PILInterfaceType_s	PILInterfaceType;

typedef PIL_rc(*PILInterfaceFun)(PILInterface*, void* ud_interface);

#define	PIL_MAGIC_PLUGIN	0xFEEDBEEFUL
#define	PIL_MAGIC_PLUGINTYPE	0xFEEDCEEFUL
#define	PIL_MAGIC_PLUGINUNIV	0xFEEDDEEFUL
#define	PIL_MAGIC_INTERFACE	0xFEEDEEEFUL
#define	PIL_MAGIC_INTERFACETYPE	0xFEEDFEEFUL
#define	PIL_MAGIC_INTERFACEUNIV	0xFEED0EEFUL

#define IS_PILPLUGIN(s)		((s)->MagicNum == PIL_MAGIC_PLUGIN)
#define IS_PILPLUGINTYPE(s)	((s)->MagicNum == PIL_MAGIC_PLUGINTYPE)
#define IS_PILPLUGINUNIV(s)	((s)->MagicNum == PIL_MAGIC_PLUGINUNIV)
#define IS_PILINTERFACE(s)	((s)->MagicNum == PIL_MAGIC_INTERFACE)
#define IS_PILINTERFACETYPE(s)	((s)->MagicNum == PIL_MAGIC_INTERFACETYPE)
#define IS_PILINTERFACEUNIV(s)	((s)->MagicNum == PIL_MAGIC_INTERFACEUNIV)

/* This is the place where PILS installs its own plugins */
#define	PILS_BASE_PLUGINDIR	"/usr/lib64/pils/plugins"

/* The type of a Plugin Initialization Function */
typedef PIL_rc (*PILPluginInitFun) (PILPlugin*us
,		PILPluginImports* imports
,		void*	plugin_user_data);

/*
 * struct PILPluginOps_s (typedef PILPluginOps) defines the set of functions
 * exported by all plugins...
 */
struct PILPluginOps_s {
	const char*	(*pluginversion) (void);
	int		(*getdebuglevel) (void);
	void		(*setdebuglevel) (int);
	const char*	(*license) (void);
	const char*	(*licenseurl) (void);
	void		(*close) (PILPlugin*);
};

/*
 *	Logging levels for the "standard" log interface.
 */

typedef enum {
	PIL_FATAL= 1,	/* BOOM! Causes program to stop */
	PIL_CRIT	= 2,	/* Critical -- serious error */
	PIL_WARN	= 3,	/* Warning */
	PIL_INFO	= 4,	/* Informative message */
	PIL_DEBUG= 5,	/* Debug message */
}PILLogLevel;
typedef void (*PILLogFun)(PILLogLevel priority, const char * fmt, ...);

/*
 * struct PILPluginImports_s (typedef PILPluginImports) defines
 * the functions and capabilities that every plugin imports when it is loaded.
 */


struct PILPluginImports_s {
	PIL_rc	(*register_plugin)(PILPlugin* piinfo
	,	const PILPluginOps* commonops);
	PIL_rc	(*unregister_plugin)(PILPlugin* piinfo);
/*
 *	A little explanation of the close_func parameter to register_interface
 *	is in order.
 *
 *	It is an exported operation function, just like the Ops structure.
 *	However, the Ops vector is exported to applications that
 *	are using the interface. Unlike the Ops structure, close_func is
 *	exported only to the interface system, since applications shouldn't
 *	call it directly, but should manage the reference counts for the
 *	interfaces instead.
 *	The generic interface system doesn't have any idea how to call
 *	any functions in the operations vector.  So, it's a separate
 *	parameter for two good reasons.
 */
	PIL_rc	(*register_interface)(PILPlugin* piinfo
	,	const char *	interfacetype	/* Type of interface	*/
	,	const char *	interfacename	/* Name of interface	*/
	,	void*		Ops		/* Info (functions) exported
						   by this interface	*/
		/* Function to call to shut down this interface */
	,	PILInterfaceFun	close_func

	,	PILInterface**	interfaceid /* Interface id 	(OP)	*/
	,	void**		Imports
	,	void*		ud_interface);	/* interface user data */

	PIL_rc	(*unregister_interface)(PILInterface* interfaceid);
	PIL_rc	(*load_plugin)(PILPluginUniv* universe
	,	const char * plugintype, const char * pluginname
	,	void*	plugin_private);

	void	(*log)	(PILLogLevel priority, const char * fmt, ...);
	void*	(*alloc)(unsigned long size);
	void	(*mfree)(void* space);
	char*	(*mstrdup)(const char *s);
};

/*
 * Function for logging with the given logging function
 * The reason why it's here is so we can get printf arg checking
 * You can't get that when you call a function pointer directly.
 */
void PILCallLog(PILLogFun logfun, PILLogLevel priority, const char * fmt, ...)
	G_GNUC_PRINTF(3,4);

/*
 * EXPORTED INTERFACES...
 */

/* Create a new plugin universe - start the plugin loading system up */
PILPluginUniv*	NewPILPluginUniv(const char * baseplugindirectory);

/* Delete a plugin universe - shut the plugin loading system down */
/*	Best if used carefully ;-) */
void		DelPILPluginUniv(PILPluginUniv*);

/* Return a list of plugins of the given type */
char **		PILListPlugins(PILPluginUniv* u, const char *plugintype
,		int* plugincount /*can be NULL*/);

/* Free the plugin list returned by PILFreeListPlugins */
void		PILFreePluginList(char ** pluginlist);

/* Load the requested plugin */
PIL_rc		PILLoadPlugin(PILPluginUniv* piuniv
,		const char *	plugintype
,		const char *	pluginname
,		void *		pi_private);

/* Return  PIL_OK if the given  plugin exists */
PIL_rc		PILPluginExists(PILPluginUniv* piuniv
,		const char *	plugintype
,		const char *	pluginname);

/* Either or both of pitype and piname may be NULL */
void		PILSetDebugLevel(PILPluginUniv*u, const char * pitype
,		const char * piname
,		int level);

/* Neither pitype nor piname may be NULL */
int		PILGetDebugLevel(PILPluginUniv* u, const char * pitype
,		const char * piname);

PIL_rc		PILIncrIFRefCount(PILPluginUniv* piuniv
,		const char *	interfacetype
,		const char *	interfacename
,		int	plusminus);

int		PILGetIFRefCount(PILPluginUniv* piuniv
,		const char *	interfacetype
,		const char *	interfacename);

void PILLogMemStats(void);
/* The plugin/interface type of a interface manager */

#define	PI_IFMANAGER		"InterfaceMgr"
#define	PI_IFMANAGER_TYPE	InterfaceMgr

/*
 *      These functions are standard exported functions for all plugins.
 */
#define PIL_PLUGIN_BOILERPLATE(PluginVersion, DebugName, CloseName) \
/*								\
 * Prototypes for boilerplate functions				\
 */								\
static const char*      Ourpluginversion(void);			\
static int              GetOurDebugLevel(void);			\
static void             SetOurDebugLevel(int);			\
static void             CloseName(PILPlugin*);			\
static const char *	ReturnOurLicense(void);			\
static const char *	ReturnOurLicenseURL(void);		\
								\
								\
/*								\
 * Initialize Plugin Exports structure				\
 */								\
static PILPluginOps OurPIExports =				\
{	Ourpluginversion					\
,	GetOurDebugLevel					\
,	SetOurDebugLevel					\
,	ReturnOurLicense					\
,	ReturnOurLicenseURL					\
,	CloseName						\
};								\
/*								\
 * Definitions of boilerplate functions				\
 */								\
static const char*						\
Ourpluginversion(void)						\
{ return PluginVersion; }					\
								\
static int DebugName = 0;					\
								\
static int							\
GetOurDebugLevel(void)						\
{ return DebugName; }						\
								\
static void							\
SetOurDebugLevel(int level)					\
{ DebugName = level; }						\
								\
static const char *						\
ReturnOurLicense(void)						\
{ return PIL_PLUGINLICENSE; }					\
								\
static const char *						\
ReturnOurLicenseURL(void)					\
{ return PIL_PLUGINLICENSEURL; }

/* A few sample licenses and URLs.  We can easily add to this */

#define	LICENSE_GPL	 "gpl"
#define	URL_GPL		"http://www.fsf.org/licenses/gpl.html"

#define	LICENSE_LGPL	"lgpl"
#define	URL_LGPL	"http://www.fsf.org/licenses/lgpl.html"

#define	LICENSE_X11	"x11"
#define	URL_X11		"http://www.x.org/terms.htm"

#define	LICENSE_PUBDOM	"publicdomain"
#define	URL_PUBDOM	"file:///dev/null"

#define	LICENSE_MODBSD	"modbsd"
#define	URL_MODBSD	"http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5"

#define	LICENSE_OLDBSD	"origbsd"
#define	URL_OLDBSD	"http://www.xfree86.org/3.3.6/COPYRIGHT2.html#6"

#define	LICENSE_EXPAT	"expat"
#define	URL_EXPAT	"http://www.jclark.com/xml/copying.txt"

#define LICENSE_ZLIB	"zlib"
#define URL_ZLIB	"http://www.gzip.org/zlib/zlib_license.html"

#define	LICENSE_APACHE_10 "apache1_0"
#define	URL_APACHE_10	"http://www.apache.org/LICENSE-1.0"

#define	LICENSE_APACHE_11 "apache1_1"
#define	URL_APACHE_11	"http://www.apache.org/LICENSE-1.1"

#define	LICENSE_MPL	"mpl"
#define	URL_MPL		"http://www.mozilla.org/MPL/MPL-1.1.html"

#define	LICENSE_PROP	"proprietary"
#define	URL_PROP	""

#define	LICENSE_IBMPL	"ibmpl"
#define	URL_IBMPL	"http://oss.software.ibm.com/developerworks/opensource/license10.html"

#ifdef ENABLE_PIL_DEFS_PRIVATE
/* Perhaps these should be moved to a different header file */

/*
 * PILPluginType is the "class" for the basic plugin loading mechanism.
 *
 * To enable loading of plugins from a particular plugin type
 * one calls NewPILPluginType with the plugin type name, the plugin
 * base directory, and the set of functions to be imported to the plugin.
 *
 *
 * The general idea of these structures is as follows:
 *
 * The PILPluginUniv object contains information about all plugins of
 * all types.
 *
 * The PILPluginType object contains information about all the plugins of a
 * specific type.
 *
 * Note: for plugins which implement a single interface, the plugin type name
 * should be the same as the interface type name.
 *
 * For other plugins that implement more than one interface, one of
 * the interface names should normally match the plugin name.
 */


/*
 * struct PILPlugin_s (typedef PILPlugin) is the structure which
 * represents/defines a plugin, and is used to identify which plugin is
 * being referred to in various function calls.
 *
 * NOTE: It may be the case that this definition should be moved to
 * another header file - since no one ought to be messing with them anyway ;-)
 *
 * I'm not sure that we're putting the right stuff in here, either...
 */

struct PILPlugin_s {
	unsigned long	MagicNum;	
	char*		plugin_name;
	PILPluginType*	plugintype;	/* Parent structure */
	int		refcnt;		/* Reference count for this plugin */
	lt_dlhandle	dlhandle;	/* Reference to D.L. object */
	PILPluginInitFun dlinitfun;	/* Initialization function */
	const PILPluginOps* pluginops;	/* Exported plugin operations */

	void*		ud_plugin;	/* Plugin-Private data */
	/* Other stuff goes here ...  (?) */
};

/*
 *	PILPluginType		Information about all plugins of a given type.
 *					(i.e.,  in a given directory)
 *				(AKA struct PILPluginType_s)
 */

struct PILPluginType_s {
	unsigned long		MagicNum;	
	char *			plugintype;
	PILPluginUniv*		piuniv; /* The universe to which we belong */
	GHashTable*		Plugins;
				/* Key is plugin type, value is PILPlugin */

	char**	(*listplugins)(PILPluginType*, int* listlen);
};

/*
 *	PILPluginUniv (aka struct PILPluginUniv_s) is the structure which
 *	represents the universe of all PILPluginType objects.
 *	There is one PILPluginType object for each Plugin type.
 */

struct PILPluginUniv_s {
	unsigned long		MagicNum;	
	char **			rootdirlist;
			/* key is plugin type, data is PILPluginType* */
	GHashTable*		PluginTypes;
	struct PILInterfaceUniv_s*ifuniv; /* Universe of interfaces */
	PILPluginImports*	imports;
};

#  endif /* ENABLE_PIL_DEFS_PRIVATE */
#endif /*PILS_PLUGIN_H */


syntax highlighted by Code2HTML, v. 0.9.1