diff options
Diffstat (limited to '')
-rw-r--r-- | lib/pils/pils.c | 2152 |
1 files changed, 2152 insertions, 0 deletions
diff --git a/lib/pils/pils.c b/lib/pils/pils.c new file mode 100644 index 0000000..4243b22 --- /dev/null +++ b/lib/pils/pils.c @@ -0,0 +1,2152 @@ +/* + * Copyright (C) 2001 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 + * + */ +#include <lha_internal.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <stdarg.h> +#include <sys/stat.h> + +/* Dumbness... */ +#define time FooTimeParameter +#define index FooIndexParameter +# include <glib.h> +#undef time +#undef index + + +#define ENABLE_PIL_DEFS_PRIVATE +#define ENABLE_PLUGIN_MANAGER_PRIVATE + +#ifndef STRLEN_CONST +# define STRLEN_CONST(con) (sizeof(con)-1) +#endif + +#include <pils/interface.h> + +#define NEW(type) (g_new(type,1)) +#define ZAP(obj) memset(obj, 0, sizeof(*obj)) +#define DELETE(obj) {g_free(obj); obj = NULL;} + +#ifdef LTDL_SHLIB_EXT +# define PLUGINSUFFIX LTDL_SHLIB_EXT +#else +# define PLUGINSUFFIX ".so" +#endif + +static int PluginDebugLevel = 0; + +#define DEBUGPLUGIN (PluginDebugLevel > 0) + + + +static PIL_rc InterfaceManager_plugin_init(PILPluginUniv* univ); + +static char** PILPluginTypeListPlugins(PILPluginType* pitype, int* picount); +static PILInterface* FindIF(PILPluginUniv* universe, const char *iftype +, const char * ifname); +static PIL_rc PluginExists(const char * PluginPath); +static char * PILPluginPath(PILPluginUniv* universe, const char * plugintype +, const char * pluginname); + + +void DelPILPluginUniv(PILPluginUniv*); +/* + * These RmA* functions primarily called from hash_table_foreach, + * functions, so they have gpointer arguments. When calling + * them by hand, take special care to pass the right argument types. + * + * They all follow the same calling sequence though. It is: + * String name"*" type object + * "*" type object with the name given by 1st argument + * NULL + * + * For example: + * RmAPILPluginType takes + * string name + * PILPluginType* object with the given name. + */ +static gboolean RmAPILPluginType +( gpointer pitname /* Name of this plugin type */ +, gpointer pitype /* PILPluginType* */ +, gpointer notused +); +static void RemoveAPILPluginType(PILPluginType*); + +static PILPluginType* NewPILPluginType +( PILPluginUniv* pluginuniv +, const char * plugintype +); +static void DelPILPluginType(PILPluginType*); +/* + * These RmA* functions primarily called from hash_table_foreach, + * so they have gpointer arguments. When calling + * them by hand, take special care to pass the right argument types. + */ +static gboolean RmAPILPlugin +( gpointer piname /* Name of this plugin */ +, gpointer plugin /* PILPlugin* */ +, gpointer notused +); +static void RemoveAPILPlugin(PILPlugin*); + + +static PILPlugin* NewPILPlugin(PILPluginType* pitype + , const char * plugin_name + , lt_dlhandle dlhand + , PILPluginInitFun PluginSym); +static void DelPILPlugin(PILPlugin*); + +struct MemStat { + unsigned long news; + unsigned long frees; +}; + +static struct PluginStats { + struct MemStat plugin; + struct MemStat pitype; + struct MemStat piuniv; + struct MemStat interface; + struct MemStat interfacetype; + struct MemStat interfaceuniv; +}PILstats; + +#define STATNEW(t) {PILstats.t.news ++; } +#define STATFREE(t) {PILstats.t.frees ++; } + + + +static PILInterfaceUniv* NewPILInterfaceUniv(PILPluginUniv*); +static void DelPILInterfaceUniv(PILInterfaceUniv*); +/* + * These RmA* functions primarily called from hash_table_foreach, but + * not necessarily, so they have gpointer arguments. When calling + * them by hand, take special care to pass the right argument types. + */ +static gboolean RmAPILInterfaceType +( gpointer iftypename /* Name of this interface type */ +, gpointer iftype /* PILInterfaceType* */ +, gpointer notused +); +static void RemoveAPILInterfaceType(PILInterfaceType*, PILInterfaceType*); + +static PILInterfaceType* NewPILInterfaceType +( PILInterfaceUniv* +, const char * typename +, void* ifexports, void* user_data +); +static void DelPILInterfaceType(PILInterfaceType*); +/* + * These RmA* functions are designed to be called from + * hash_table_foreach, so they have gpointer arguments. When calling + * them by hand, take special care to pass the right argument types. + * They can be called from other places safely also. + */ +static gboolean RmAPILInterface +( gpointer ifname /* Name of this interface */ +, gpointer plugin /* PILInterface* */ +, gpointer notused +); +static PIL_rc RemoveAPILInterface(PILInterface*); +static void DelPILPluginType(PILPluginType*); + +static PILInterface* NewPILInterface +( PILInterfaceType* interfacetype +, const char* interfacename +, void * exports +, PILInterfaceFun closefun +, void* ud_interface +, PILPlugin* loading_plugin /* The plugin that loaded us */ +); +static void DelPILInterface(PILInterface*); +static PIL_rc close_ifmgr_interface(PILInterface*, void*); + + + + +/* + * For consistency, we show up as a plugin in our our system. + * + * Here are our exports as a plugin. + * + */ +static const char * PIL_PILPluginVersion(void); +static void PIL_PILPluginClose (PILPlugin*); +void PILpisysSetDebugLevel (int level); +int PILpisysGetDebugLevel(void); +static const char * PIL_PILPluginLicense (void); +static const char * PIL_PILPluginLicenseUrl (void); + +static const PILPluginOps PluginExports = +{ PIL_PILPluginVersion +, PILpisysGetDebugLevel +, PILpisysSetDebugLevel +, PIL_PILPluginLicense +, PIL_PILPluginLicenseUrl +, PIL_PILPluginClose +}; + +/* Prototypes for the functions that we export to every plugin */ +static PIL_rc PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* mops); +static PIL_rc PILunregister_plugin(PILPlugin* piinfo); +static PIL_rc +PILRegisterInterface +( PILPlugin* piinfo +, const char * interfacetype /* Type of interface */ +, const char * interfacename /* Name of interface */ +, void* Ops /* Ops exported by this interface */ +, PILInterfaceFun closefunc /* Ops exported by this interface */ +, PILInterface** interfaceid /* Interface id (OP) */ +, void** Imports /* Functions imported by */ + /* this interface (OP) */ +, void* ud_interface /* interface user data */ +); +static PIL_rc PILunregister_interface(PILInterface* interfaceid); +static void PILLog(PILLogLevel priority, const char * fmt, ...) + G_GNUC_PRINTF(2,3); + + +/* + * This is the set of functions that we export to every plugin + * + * That also makes it the set of functions that every plugin imports. + * + */ + +static PILPluginImports PILPluginImportSet = +{ PILregister_plugin /* register_plugin */ +, PILunregister_plugin /* unregister_plugin */ +, PILRegisterInterface /* register_interface */ +, RemoveAPILInterface /* unregister_interface */ +, PILLoadPlugin /* load_plugin */ +, PILLog /* Logging function */ +, g_malloc /* Malloc function */ +, g_realloc /* realloc function */ +, g_free /* Free function */ +, g_strdup /* Strdup function */ +}; + +static PIL_rc ifmgr_register_interface(PILInterface* newif + , void** imports); +static PIL_rc ifmgr_unregister_interface(PILInterface* interface); + +/* + * For consistency, the master interface manager is a interface in the + * system. Below is our set of exported Interface functions. + * + * Food for thought: This is the interface manager whose name is + * interface. This makes it the Interface Interface interface ;-) + * (or the Interface/Interface interface if you prefer) + */ + +static PILInterfaceOps IfExports = +{ ifmgr_register_interface +, ifmgr_unregister_interface +}; + + + +/* + * Below is the set of functions we export to every interface manager. + */ + +static int IfRefCount(PILInterface * ifh); +static int IfIncrRefCount(PILInterface*eifinfo,int plusminus); +static int PluginIncrRefCount(PILPlugin*eifinfo,int plusminus); +#if 0 +static int PluginRefCount(PILPlugin * ifh); +#endif +static void IfForceUnregister(PILInterface *eifinfo); +static void IfForEachClientRemove(PILInterface* manangerif + , gboolean(*f)(PILInterface* clientif, void * other) + , void* other); + +static PILInterfaceImports IFManagerImports = +{ IfRefCount +, IfIncrRefCount +, IfForceUnregister +, IfForEachClientRemove +}; +static void PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype); +static void PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv); +static void PILValidatePluginUniv(gpointer key, gpointer pitype, gpointer); +static void PILValidateInterface(gpointer key, gpointer interface, gpointer iftype); +static void PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv); +static void PILValidateInterfaceUniv(gpointer key, gpointer puniv, gpointer); + +/***************************************************************************** + * + * This code is for managing plugins, and interacting with them... + * + ****************************************************************************/ + +static PILPlugin* +NewPILPlugin( PILPluginType* pitype + , const char * plugin_name + , lt_dlhandle dlhand + , PILPluginInitFun PluginSym) +{ + PILPlugin* ret = NEW(PILPlugin); + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NewPILPlugin(0x%lx)", (unsigned long)ret); + } + + STATNEW(plugin); + ret->MagicNum = PIL_MAGIC_PLUGIN; + ret->plugin_name = g_strdup(plugin_name); + ret->plugintype = pitype; + ret->refcnt = 0; + ret->dlhandle = dlhand; + ret->dlinitfun = PluginSym; + PILValidatePlugin(ret->plugin_name, ret, pitype); + return ret; +} +static void +DelPILPlugin(PILPlugin*pi) +{ + + if (pi->refcnt > 0) { + PILLog(PIL_INFO, "DelPILPlugin: Non-zero refcnt"); + } + + if (pi->dlhandle) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Closing dlhandle for (%s/%s)" + , pi->plugintype->plugintype, pi->plugin_name); + } + lt_dlclose(pi->dlhandle); + }else if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NO dlhandle for (%s/%s)!" + , pi->plugintype->plugintype, pi->plugin_name); + } + DELETE(pi->plugin_name); + ZAP(pi); + DELETE(pi); + STATFREE(plugin); +} + + +static PILPluginType dummymlpitype = +{ PIL_MAGIC_PLUGINTYPE +, NULL /*plugintype*/ +, NULL /*piuniv*/ +, NULL /*Plugins*/ +, PILPluginTypeListPlugins /* listplugins */ +}; + +static PILPluginType* +NewPILPluginType(PILPluginUniv* pluginuniv + , const char * plugintype +) +{ + PILPluginType* ret = NEW(PILPluginType); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NewPILPlugintype(0x%lx)", (unsigned long)ret); + } + STATNEW(pitype); + + *ret = dummymlpitype; + + ret->plugintype = g_strdup(plugintype); + ret->piuniv = pluginuniv; + ret->Plugins = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(pluginuniv->PluginTypes + , g_strdup(ret->plugintype), ret); + PILValidatePluginType(ret->plugintype, ret, pluginuniv); + return ret; +} +static void +DelPILPluginType(PILPluginType*pitype) +{ + PILValidatePluginType(NULL, pitype, NULL); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "DelPILPluginType(%s)", pitype->plugintype); + } + + STATFREE(pitype); + g_hash_table_foreach_remove(pitype->Plugins, RmAPILPlugin, NULL); + g_hash_table_destroy(pitype->Plugins); + DELETE(pitype->plugintype); + ZAP(pitype); + DELETE(pitype); +} +/* + * These RmA* functions primarily called from hash_table_foreach, + * so they have gpointer arguments. This *not necessarily* clause + * is why they do the g_hash_table_lookup_extended call instead of + * just deleting the key. When called from outside, the key * + * may not be pointing at the key to actually free, but a copy + * of the key. + */ +static gboolean +RmAPILPlugin /* IsA GHFunc: required for g_hash_table_foreach_remove() */ +( gpointer piname /* Name of this plugin */ +, gpointer plugin /* PILPlugin* */ +, gpointer notused +) +{ + PILPlugin* Plugin = plugin; + PILPluginType* Pitype = Plugin->plugintype; + + PILValidatePlugin(piname, plugin, NULL); + PILValidatePluginType(NULL, Pitype, NULL); + g_assert(IS_PILPLUGIN(Plugin)); + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RmAPILPlugin(%s/%s)", Pitype->plugintype + , Plugin->plugin_name); + } + /* Normally called from g_hash_table_foreachremove or equivalent */ + + DelPILPlugin(plugin); + DELETE(piname); + return TRUE; +} + +static void +RemoveAPILPlugin(PILPlugin*Plugin) +{ + PILPluginType* Pitype = Plugin->plugintype; + gpointer key; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RemoveAPILPlugin(%s/%s)" + , Pitype->plugintype + , Plugin->plugin_name); + } + if (g_hash_table_lookup_extended(Pitype->Plugins + , Plugin->plugin_name, &key, (void*)&Plugin)) { + + g_hash_table_remove(Pitype->Plugins, key); + RmAPILPlugin(key, Plugin, NULL); + key = NULL; + Plugin = NULL; + + }else{ + g_assert_not_reached(); + } + if (g_hash_table_size(Pitype->Plugins) == 0) { + RemoveAPILPluginType(Pitype); + /* Pitype is now invalid */ + Pitype = NULL; + } +} + +PILPluginUniv* +NewPILPluginUniv(const char * basepluginpath) +{ + PILPluginUniv* ret = NEW(PILPluginUniv); + + /* The delimiter separating search path components */ + const char* path_delim = G_SEARCHPATH_SEPARATOR_S; + char * fullpath; + + STATNEW(piuniv); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NewPILPluginUniv(0x%lx)" + , (unsigned long)ret); + } + if (!g_path_is_absolute(basepluginpath)) { + DELETE(ret); + return(ret); + } + ret->MagicNum = PIL_MAGIC_PLUGINUNIV; + fullpath = g_strdup_printf("%s%s%s", basepluginpath + , path_delim, PILS_BASE_PLUGINDIR); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "PILS: Plugin path = %s", fullpath); + } + + /* Separate the root directory PATH into components */ + ret->rootdirlist = g_strsplit(fullpath, path_delim, 100); + g_free(fullpath); + + ret->PluginTypes = g_hash_table_new(g_str_hash, g_str_equal); + ret->imports = &PILPluginImportSet; + ret->ifuniv = NewPILInterfaceUniv(ret); + PILValidatePluginUniv(NULL, ret, NULL); + return ret; +} + +/* Change memory allocation functions immediately after creating universe */ +void +PilPluginUnivSetMemalloc(PILPluginUniv* u +, gpointer (*allocfun)(glib_size_t size) +, gpointer (*reallocfun)(gpointer ptr, glib_size_t size) +, void (*freefun)(void* space) +, char* (*strdupfun)(const char *s)) +{ + u->imports->alloc = allocfun; + u->imports->mrealloc = reallocfun; + u->imports->mfree = freefun; + u->imports->mstrdup = strdupfun; +} + + +/* Change logging functions - preferably right after creating universe */ +void +PilPluginUnivSetLog(PILPluginUniv* u +, void (*logfun) (PILLogLevel priority, const char * fmt, ...)) +{ + u->imports->log = logfun; +} + +void +DelPILPluginUniv(PILPluginUniv* piuniv) +{ + + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "DelPILPluginUniv(0x%lx)" + , (unsigned long)piuniv); + } + STATFREE(piuniv); + PILValidatePluginUniv(NULL, piuniv, NULL); + DelPILInterfaceUniv(piuniv->ifuniv); + piuniv->ifuniv = NULL; + g_hash_table_foreach_remove(piuniv->PluginTypes + , RmAPILPluginType, NULL); + g_hash_table_destroy(piuniv->PluginTypes); + g_strfreev(piuniv->rootdirlist); + ZAP(piuniv); + DELETE(piuniv); +} + +/* + * These RmA* functions primarily called from hash_table_foreach, + * so they have gpointer arguments. This *not necessarily* clause + * is why they do the g_hash_table_lookup_extended call instead of + * just deleting the key. When called from outside, the key * + * may not be pointing at the key to actually free, but a copy + * of the key. + */ +static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */ +RmAPILPluginType +( gpointer pitname /* Name of this plugin type "real" key */ +, gpointer pitype /* PILPluginType* */ +, gpointer notused +) +{ + PILPluginType* Plugintype = pitype; + + g_assert(IS_PILPLUGINTYPE(Plugintype)); + PILValidatePluginType(pitname, pitype, NULL); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RmAPILPluginType(%s)" + , Plugintype->plugintype); + } + /* + * This function is usually but not always called by + * g_hash_table_foreach_remove() + */ + + DelPILPluginType(pitype); + DELETE(pitname); + return TRUE; +} +static void +RemoveAPILPluginType(PILPluginType*Plugintype) +{ + PILPluginUniv* Pluginuniv = Plugintype->piuniv; + gpointer key; + if (g_hash_table_lookup_extended(Pluginuniv->PluginTypes + , Plugintype->plugintype, &key, (void*)&Plugintype)) { + + g_hash_table_remove(Pluginuniv->PluginTypes, key); + RmAPILPluginType(key, Plugintype, NULL); + }else{ + g_assert_not_reached(); + } +} + +/* + * InterfaceManager_plugin_init: Initialize the handling of + * "Interface Manager" interfaces. + * + * There are a few potential bootstrapping problems here ;-) + * + */ +static PIL_rc +InterfaceManager_plugin_init(PILPluginUniv* univ) +{ + PILPluginImports* imports = univ->imports; + PILPluginType* pitype; + PILInterface* ifinfo; + PILInterfaceType* iftype; + void* dontcare; + PILPlugin* ifmgr_plugin; + PIL_rc rc; + + + iftype = NewPILInterfaceType(univ->ifuniv, PI_IFMANAGER, &IfExports + , NULL); + + g_hash_table_insert(univ->ifuniv->iftypes + , g_strdup(PI_IFMANAGER), iftype); + + pitype = NewPILPluginType(univ, PI_IFMANAGER); + + g_hash_table_insert(univ->PluginTypes + , g_strdup(PI_IFMANAGER), pitype); + + ifmgr_plugin= NewPILPlugin(pitype, PI_IFMANAGER, NULL, NULL); + + g_hash_table_insert(pitype->Plugins + , g_strdup(PI_IFMANAGER), ifmgr_plugin); + + /* We can call register_plugin, since it doesn't depend on us... */ + rc = imports->register_plugin(ifmgr_plugin, &PluginExports); + if (rc != PIL_OK) { + PILLog(PIL_CRIT, "register_plugin() failed in init: %s" + , PIL_strerror(rc)); + return(rc); + } + /* + * Now, we're registering interfaces, and are into some deep + * Catch-22 if do it the "easy" way, since our code is + * needed in order to support interface loading for the type of + * interface we are (a Interface interface). + * + * So, instead of calling imports->register_interface(), we have to + * do the work ourselves here... + * + * Since no one should yet be registered to handle Interface + * interfaces, we need to bypass the hash table handler lookup + * that register_interface would do and call the function that + * register_interface would call... + * + */ + + /* The first argument is the PILInterfaceType* */ + ifinfo = NewPILInterface(iftype, PI_IFMANAGER, &IfExports + , close_ifmgr_interface, NULL, NULL); + ifinfo->ifmanager = iftype->ifmgr_ref = ifinfo; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "InterfaceManager_plugin_init(0x%lx/%s)" + , (unsigned long)ifinfo, ifinfo->interfacename); + } + PILValidatePluginUniv(NULL, univ, NULL); + ifmgr_register_interface(ifinfo, &dontcare); + PILValidatePluginUniv(NULL, univ, NULL); + + return(PIL_OK); +}/*InterfaceManager_plugin_init*/ + + +/* Return current IfIf "plugin" version (not very interesting for us) */ +static const char * +PIL_PILPluginVersion(void) +{ + return("1.0"); +} + +/* Return current IfIf debug level */ +int +PILpisysGetDebugLevel(void) +{ + return(PluginDebugLevel); +} + +/* Set current IfIf debug level */ +void +PILpisysSetDebugLevel (int level) +{ + PluginDebugLevel = level; +} +struct set_debug_helper { + const char * pitype; + const char * piname; + int level; +}; + +static void +PILSetDebugLeveltoPlugin(gpointer key, gpointer plugin, gpointer Helper) +{ + PILPlugin* p = plugin; + struct set_debug_helper* helper = Helper; + + p->pluginops->setdebuglevel(helper->level); +} + +static void +PILSetDebugLevelbyType(const void * key, gpointer plugintype, gpointer Helper) +{ + struct set_debug_helper* helper = Helper; + + + PILPluginType* t = plugintype; + + if (helper->piname == NULL) { + g_hash_table_foreach(t->Plugins, PILSetDebugLeveltoPlugin + , helper); + }else{ + PILPlugin* p = g_hash_table_lookup(t->Plugins + , helper->piname); + if (p != NULL) { + p->pluginops->setdebuglevel(helper->level); + } + } +} + +void +PILSetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname +, int level) +{ + struct set_debug_helper helper = {pitype, piname, level}; + + if (u == NULL) { + return; + } + + if (pitype == NULL) { + g_hash_table_foreach(u->PluginTypes + /* + * Reason for this next cast: + * SetDebugLevelbyType takes const gpointer + * arguments, unlike a GHFunc which doesn't. + */ + , (GHFunc)PILSetDebugLevelbyType + , &helper); + }else{ + PILPluginType* t = g_hash_table_lookup(u->PluginTypes + , pitype); + if (t != NULL) { + PILSetDebugLevelbyType(pitype, t, &helper); + } + } +} + + +int +PILGetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname) +{ + PILPluginType* t; + PILPlugin* p; + if ( u == NULL + || pitype == NULL + || (t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL + || (p = g_hash_table_lookup(t->Plugins, piname)) == NULL) { + return -1; + } + return p->pluginops->getdebuglevel(); +} + +/* Close/shutdown our PILPlugin (the interface manager interface plugin) */ +/* All our interfaces will have already been shut down and unregistered */ +static void +PIL_PILPluginClose (PILPlugin* plugin) +{ +} +static const char * +PIL_PILPluginLicense (void) +{ + return LICENSE_LGPL; +} +static const char * +PIL_PILPluginLicenseUrl (void) +{ + return URL_LGPL; +} + +/***************************************************************************** + * + * This code is for managing interfaces, and interacting with them... + * + ****************************************************************************/ + + +static PILInterface* +NewPILInterface(PILInterfaceType* interfacetype + , const char* interfacename + , void * exports + , PILInterfaceFun closefun + , void* ud_interface + , PILPlugin* loading_plugin) +{ + PILInterface* ret = NULL; + PILInterface* look = NULL; + + + if ((look = g_hash_table_lookup(interfacetype->interfaces + , interfacename)) != NULL) { + PILLog(PIL_DEBUG, "Deleting PILInterface!"); + DelPILInterface(look); + } + ret = NEW(PILInterface); + STATNEW(interface); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NewPILInterface(0x%lx)", (unsigned long)ret); + } + + if (ret) { + ret->MagicNum = PIL_MAGIC_INTERFACE; + ret->interfacetype = interfacetype; + ret->exports = exports; + ret->ud_interface = ud_interface; + ret->interfacename = g_strdup(interfacename); + ret->ifmanager = interfacetype->ifmgr_ref; + ret->loadingpi = loading_plugin; + g_hash_table_insert(interfacetype->interfaces + , g_strdup(ret->interfacename), ret); + + ret->if_close = closefun; + ret->refcnt = 1; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NewPILInterface(0x%lx:%s/%s)*** user_data: 0x%p *******" + , (unsigned long)ret + , interfacetype->typename + , ret->interfacename + , ud_interface); + } + } + return ret; +} +static void +DelPILInterface(PILInterface* intf) +{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "DelPILInterface(0x%lx/%s)" + , (unsigned long)intf, intf->interfacename); + } + STATFREE(interface); + DELETE(intf->interfacename); + ZAP(intf); + DELETE(intf); +} + +static PILInterfaceType* +NewPILInterfaceType(PILInterfaceUniv*univ, const char * typename +, void* ifeports, void* user_data) +{ + PILInterfaceType* ifmgr_types; + PILInterface* ifmgr_ref; + PILInterfaceType* ret = NEW(PILInterfaceType); + + + STATNEW(interfacetype); + ret->MagicNum = PIL_MAGIC_INTERFACETYPE; + ret->typename = g_strdup(typename); + ret->interfaces = g_hash_table_new(g_str_hash, g_str_equal); + ret->ud_if_type = user_data; + ret->universe = univ; + ret->ifmgr_ref = NULL; + + /* Now find the pointer to our if type in the Interface Universe */ + if ((ifmgr_types = g_hash_table_lookup(univ->iftypes, PI_IFMANAGER)) + != NULL) { + if ((ifmgr_ref=g_hash_table_lookup(ifmgr_types->interfaces + , typename)) != NULL) { + ret->ifmgr_ref = ifmgr_ref; + }else { + g_assert(strcmp(typename, PI_IFMANAGER) == 0); + } + }else { + g_assert(strcmp(typename, PI_IFMANAGER) == 0); + } + + /* Insert ourselves into our parent's table */ + g_hash_table_insert(univ->iftypes, g_strdup(typename), ret); + return ret; +} +static void +DelPILInterfaceType(PILInterfaceType*ift) +{ + PILInterfaceUniv* u = ift->universe; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "DelPILInterfaceType(%s)" + , ift->typename); + } + STATFREE(interfacetype); + + PILValidateInterfaceUniv(NULL, u, NULL); + + /* + * RmAPILInterface refuses to remove the interface for the + * Interface manager, because it must be removed last. + * + * Otherwise we won't be able to unregister interfaces + * for other types of objects, and we'll be very confused. + */ + + g_hash_table_foreach_remove(ift->interfaces, RmAPILInterface, NULL); + + PILValidateInterfaceUniv(NULL, u, NULL); + + if (g_hash_table_size(ift->interfaces) > 0) { + gpointer key, iftype; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "DelPILInterfaceType(%s): table size (%d)" + , ift->typename, g_hash_table_size(ift->interfaces)); + } + if (g_hash_table_lookup_extended(ift->interfaces + , PI_IFMANAGER, &key, &iftype)) { + DelPILInterface((PILInterface*)iftype); + DELETE(key); + } + } + DELETE(ift->typename); + g_hash_table_destroy(ift->interfaces); + ZAP(ift); + DELETE(ift); +} + +/* + * These RmA* functions primarily called from hash_table_foreach, + * so they have gpointer arguments. This *not necessarily* clause + * is why they do the g_hash_table_lookup_extended call instead of + * just deleting the key. When called from outside, the key * + * may not be pointing at the key to actually free, but a copy + * of the key. + */ +static gboolean /* IsAGHFunc: required for g_hash_table_foreach_remove() */ +RmAPILInterface +( gpointer ifname /* Name of this interface */ +, gpointer intf /* PILInterface* */ +, gpointer notused +) +{ + PILInterface* If = intf; + PILInterfaceType* Iftype = If->interfacetype; + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RmAPILInterface(0x%lx/%s)" + , (unsigned long)If, If->interfacename); + } + g_assert(IS_PILINTERFACE(If)); + + /* + * Don't remove the master interface manager this way, or + * Somebody will have a cow... + */ + if (If == If->ifmanager) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RmAPILInterface: skipping (%s)" + , If->interfacename); + } + return FALSE; + } + PILValidateInterface(ifname, If, Iftype); + PILValidateInterfaceType(NULL, Iftype, NULL); + + /* + * This function is usually but not always called by + * g_hash_table_foreach_remove() + */ + + PILunregister_interface(If); + PILValidateInterface(ifname, If, Iftype); + PILValidateInterfaceType(NULL, Iftype, NULL); + DELETE(ifname); + DelPILInterface(If); + return TRUE; +} +static PIL_rc +RemoveAPILInterface(PILInterface* pif) +{ + PILInterfaceType* Iftype = pif->interfacetype; + gpointer key; + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RemoveAPILInterface(0x%lx/%s)" + , (unsigned long)pif, pif->interfacename); + } + if (g_hash_table_lookup_extended(Iftype->interfaces + , pif->interfacename, &key, (void*)&pif)) { + g_assert(IS_PILINTERFACE(pif)); + g_hash_table_remove(Iftype->interfaces, key); + RmAPILInterface(key, pif, NULL); + }else{ + g_assert_not_reached(); + } + + if (g_hash_table_size(Iftype->interfaces) == 0) { + /* The generic plugin handler doesn't want us to + * delete it's types... + */ + if (Iftype->ifmgr_ref->refcnt <= 1) { + RemoveAPILInterfaceType(Iftype, NULL); + } + } + return PIL_OK; +} + + +/* Register a Interface Interface (Interface manager) */ +static PIL_rc +ifmgr_register_interface(PILInterface* intf +, void** imports) +{ + PILInterfaceType* ift = intf->interfacetype; + PILInterfaceUniv* ifuniv = ift->universe; + PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */ + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "Registering Implementation manager for" + " Interface type '%s'" + , intf->interfacename); + } + + ifops = intf->exports; + if (ifops->RegisterInterface == NULL + || ifops->UnRegisterInterface == NULL) { + PILLog(PIL_DEBUG, "ifmgr_register_interface(%s)" + ": NULL exported function pointer" + , intf->interfacename); + return PIL_INVAL; + } + + *imports = &IFManagerImports; + + if(g_hash_table_lookup(ifuniv->iftypes, intf->interfacename) == NULL){ + /* It registers itself into ifuniv automatically */ + NewPILInterfaceType(ifuniv,intf->interfacename, &IfExports + , NULL); + } + return PIL_OK; +} + +static gboolean +RemoveAllClients(PILInterface*interface, void * managerif) +{ + /* + * Careful! We can't remove ourselves this way... + * This gets taken care of as a special case in DelPILInterfaceUniv... + */ + if (managerif == interface) { + return FALSE; + } + PILunregister_interface(interface); + return TRUE; +} + +/* Unconditionally unregister a interface manager (InterfaceMgr Interface) */ +static PIL_rc +ifmgr_unregister_interface(PILInterface* interface) +{ + /* + * We need to unregister every interface we manage + */ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "ifmgr_unregister_interface(%s)" + , interface->interfacename); + } + + IfForEachClientRemove(interface, RemoveAllClients, interface); + return PIL_OK; +} + +/* Called to close the Interface manager for type Interface */ +static PIL_rc +close_ifmgr_interface(PILInterface* us, void* ud_interface) +{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "close_ifmgr_interface(%s)" + , us->interfacename); + } + /* Nothing much to do */ + return PIL_OK; +} + +/* Return the reference count for this interface */ +static int +IfRefCount(PILInterface * eifinfo) +{ + return eifinfo->refcnt; +} + +/* Modify the reference count for this interface */ +static int +IfIncrRefCount(PILInterface*eifinfo, int plusminus) +{ + if(DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "IfIncrRefCount(%d + %d )" + , eifinfo->refcnt, plusminus); + } + eifinfo->refcnt += plusminus; + if (eifinfo->refcnt <= 0) { + eifinfo->refcnt = 0; + /* Unregister this interface. */ + RemoveAPILInterface(eifinfo); + return 0; + } + return eifinfo->refcnt; +} + +#if 0 +static int +PluginRefCount(PILPlugin * pi) +{ + return pi->refcnt; +} +#endif + +static int +PluginIncrRefCount(PILPlugin*pi, int plusminus) +{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PluginIncrRefCount(%d + %d )" + , pi->refcnt, plusminus); + } + pi->refcnt += plusminus; + if (pi->refcnt <= 0) { + pi->refcnt = 0; + RemoveAPILPlugin(pi); + return 0; + } + return pi->refcnt; +} + +static PILInterface* +FindIF(PILPluginUniv* universe, const char *iftype, const char * ifname) +{ + PILInterfaceUniv* puniv; + PILInterfaceType* ptype; + + if (universe == NULL || (puniv = universe->ifuniv) == NULL + || (ptype=g_hash_table_lookup(puniv->iftypes, iftype))==NULL){ + return NULL; + } + return g_hash_table_lookup(ptype->interfaces, ifname); +} + +PIL_rc +PILIncrIFRefCount(PILPluginUniv* mu +, const char * interfacetype +, const char * interfacename +, int plusminus) +{ + PILInterface* intf = FindIF(mu, interfacetype, interfacename); + + if (intf) { + g_assert(IS_PILINTERFACE(intf)); + IfIncrRefCount(intf, plusminus); + return PIL_OK; + } + return PIL_NOPLUGIN; +} + +int +PILGetIFRefCount(PILPluginUniv* mu +, const char * interfacetype +, const char * interfacename) +{ + PILInterface* intf = FindIF(mu, interfacetype, interfacename); + + return IfRefCount(intf); +} + +static void +IfForceUnregister(PILInterface *id) +{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "IfForceUnRegister(%s)" + , id->interfacename); + } + RemoveAPILInterface(id); +} + +struct f_e_c_helper { + gboolean(*fun)(PILInterface* clientif, void * passalong); + void* passalong; +}; + +static gboolean IfForEachClientHelper(gpointer key +, gpointer iftype, gpointer helper_v); + +static gboolean +IfForEachClientHelper(gpointer unused, gpointer iftype, gpointer v) +{ + struct f_e_c_helper* s = (struct f_e_c_helper*)v; + + g_assert(IS_PILINTERFACE((PILInterface*)iftype)); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "IfForEachClientHelper(%s)" + , ((PILInterface*)iftype)->interfacename); + } + + return s->fun((PILInterface*)iftype, s->passalong); +} + + +static void +IfForEachClientRemove +( PILInterface* mgrif +, gboolean(*f)(PILInterface* clientif, void * passalong) +, void* passalong /* usually PILInterface* */ +) +{ + PILInterfaceType* mgrt; + PILInterfaceUniv* u; + const char * ifname; + PILInterfaceType* clientt; + + struct f_e_c_helper h = {f, passalong}; + + + if (mgrif == NULL || (mgrt = mgrif->interfacetype) == NULL + || (u = mgrt->universe) == NULL + || (ifname = mgrif->interfacename) == NULL) { + PILLog(PIL_WARN, "bad parameters to IfForEachClientRemove"); + return; + } + + if ((clientt = g_hash_table_lookup(u->iftypes, ifname)) == NULL) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "Interface manager [%s/%s] has no clients" + , PI_IFMANAGER, ifname); + } + return; + }; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "IfForEachClientRemove(%s:%s)" + , mgrt->typename, clientt->typename); + } + if (clientt->ifmgr_ref != mgrif) { + PILLog(PIL_WARN, "Bad ifmgr_ref ptr in PILInterfaceType"); + return; + } + + g_hash_table_foreach_remove(clientt->interfaces, IfForEachClientHelper + , &h); +} + +static PIL_rc +PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* commonops) +{ + piinfo->pluginops = commonops; + + return PIL_OK; +} + +static PIL_rc +PILunregister_plugin(PILPlugin* piinfo) +{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PILunregister_plugin(%s)" + , piinfo->plugin_name); + } + RemoveAPILPlugin(piinfo); + return PIL_OK; +} + +/* General logging function (not really UPPILS-specific) */ +static void +PILLog(PILLogLevel priority, const char * format, ...) +{ + va_list args; + GLogLevelFlags flags; + + switch(priority) { + case PIL_FATAL: flags = G_LOG_LEVEL_ERROR; + break; + case PIL_CRIT: flags = G_LOG_LEVEL_CRITICAL; + break; + + default: /* FALL THROUGH... */ + case PIL_WARN: flags = G_LOG_LEVEL_WARNING; + break; + + case PIL_INFO: flags = G_LOG_LEVEL_INFO; + break; + case PIL_DEBUG: flags = G_LOG_LEVEL_DEBUG; + break; + }; + va_start (args, format); + g_logv (G_LOG_DOMAIN, flags, format, args); + va_end (args); +} + +static const char * PIL_strerrmsgs [] = +{ "Success" +, "Invalid Parameters" +, "Bad plugin/interface type" +, "Duplicate entry (plugin/interface name/type)" +, "Oops happens" +, "No such plugin/interface/interface type" +}; + +const char * +PIL_strerror(PIL_rc rc) +{ + int irc = (int) rc; + static char buf[128]; + + if (irc < 0 || irc >= DIMOF(PIL_strerrmsgs)) { + snprintf(buf, sizeof(buf), "return code %d (?)", irc); + return buf; + } + return PIL_strerrmsgs[irc]; +} + +/* + * Returns the PATHname of the file containing the requested plugin + * This file handles PATH-like semantics from the rootdirlist. + * It is also might be the right place to put alias handing in the future... + */ +static char * +PILPluginPath(PILPluginUniv* universe, const char * plugintype +, const char * pluginname) +{ + char * PluginPath = NULL; + char ** spath_component; + + for (spath_component = universe->rootdirlist; *spath_component + ; ++ spath_component) { + + if (PluginPath) { + g_free(PluginPath); PluginPath=NULL; + } + + PluginPath = g_strdup_printf("%s%s%s%s%s%s" + , *spath_component + , G_DIR_SEPARATOR_S + , plugintype + , G_DIR_SEPARATOR_S + , pluginname + , PLUGINSUFFIX); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "PILS: Looking for %s/%s => [%s]" + , plugintype, pluginname, PluginPath); + } + + if (PluginExists(PluginPath) == PIL_OK) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "Plugin path for %s/%s => [%s]" + , plugintype, pluginname, PluginPath); + } + return PluginPath; + } + /* FIXME: Put alias file processing here... */ + } + + /* Can't find 'em all... */ + return PluginPath; +} + +static PIL_rc +PluginExists(const char * PluginPath) +{ + /* Make sure we can read and execute the plugin file */ + /* This test is nice, because dlopen reasons aren't return codes */ + + if (access(PluginPath, R_OK) != 0) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Plugin file %s does not exist" + , PluginPath); + } + return PIL_NOPLUGIN; + } + return PIL_OK; +} + +/* Return PIL_OK if the given plugin exists */ +PIL_rc +PILPluginExists(PILPluginUniv* piuniv +, const char * plugintype +, const char * pluginname) +{ + PIL_rc rc; + char * path = PILPluginPath(piuniv, plugintype, pluginname); + + if (path == NULL) { + return PIL_INVAL; + } + rc = PluginExists(path); + DELETE(path); + return rc; +} + +/* + * PILLoadPlugin() - loads a plugin into memory and calls the + * initial() entry point in the plugin. + * + * + * Method: + * + * Construct file name of plugin. + * See if plugin exists. If not, fail with PIL_NOPLUGIN. + * + * Search Universe for plugin type + * If found, search plugin type for pluginname + * if found, fail with PIL_EXIST. + * Otherwise, + * Create new Plugin type structure + * Use lt_dlopen() on plugin to get lt_dlhandle for it. + * + * Construct the symbol name of the initialization function. + * + * Use lt_dlsym() to find the pointer to the init function. + * + * Call the initialization function. + */ +PIL_rc +PILLoadPlugin(PILPluginUniv* universe, const char * plugintype +, const char * pluginname +, void* plugin_user_data) +{ + PIL_rc rc; + char * PluginPath; + char * PluginSym; + PILPluginType* pitype; + PILPlugin* piinfo; + lt_dlhandle dlhand; + PILPluginInitFun initfun; + + PluginPath = PILPluginPath(universe, plugintype, pluginname); + + if ((rc=PluginExists(PluginPath)) != PIL_OK) { + DELETE(PluginPath); + return rc; + } + + if((pitype=g_hash_table_lookup(universe->PluginTypes, plugintype)) + != NULL) { + if ((piinfo = g_hash_table_lookup + ( pitype->Plugins, pluginname)) != NULL) { + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Plugin %s already loaded" + , PluginPath); + } + DELETE(PluginPath); + return PIL_EXIST; + } + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PluginType %s already present" + , plugintype); + } + }else{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Creating PluginType for %s" + , plugintype); + } + /* Create a new PILPluginType object */ + pitype = NewPILPluginType(universe, plugintype); + } + + g_assert(pitype != NULL); + + /* + * At this point, we have a PILPluginType object and our + * plugin name is not listed in it. + */ + + dlhand = lt_dlopen(PluginPath); + + if (!dlhand) { + PILLog(PIL_WARN + , "lt_dlopen() failure on plugin %s/%s [%s]." + " Reason: [%s]" + , plugintype, pluginname + , PluginPath + , lt_dlerror()); + DELETE(PluginPath); + return PIL_NOPLUGIN; + } + DELETE(PluginPath); + /* Construct the magic init function symbol name */ + PluginSym = g_strdup_printf(PIL_FUNC_FMT + , plugintype, pluginname); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Plugin %s/%s init function: %s" + , plugintype, pluginname + , PluginSym); + } + + initfun = lt_dlsym(dlhand, PluginSym); + + if (initfun == NULL) { + PILLog(PIL_WARN + , "Plugin %s/%s init function (%s) not found" + , plugintype, pluginname, PluginSym); + DELETE(PluginSym); + lt_dlclose(dlhand); dlhand=NULL; + DelPILPluginType(pitype); + return PIL_NOPLUGIN; + } + DELETE(PluginSym); + /* + * Construct the new PILPlugin object + */ + piinfo = NewPILPlugin(pitype, pluginname, dlhand, initfun); + g_assert(piinfo != NULL); + g_hash_table_insert(pitype->Plugins, g_strdup(piinfo->plugin_name), piinfo); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Plugin %s/%s loaded and constructed." + , plugintype, pluginname); + } + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Calling init function in plugin %s/%s." + , plugintype, pluginname); + } + /* Save away the user_data for later */ + piinfo->ud_plugin = plugin_user_data; + /* initfun is allowed to change ud_plugin if they want */ + initfun(piinfo, universe->imports, plugin_user_data); + + return PIL_OK; +}/*PILLoadPlugin*/ + + + +#define REPORTERR(msg) PILLog(PIL_CRIT, "%s", msg) + +/* + * Register an interface. + * + * This function is exported to plugins for their use. + */ +static PIL_rc +PILRegisterInterface(PILPlugin* piinfo +, const char * interfacetype /* Type of interface */ +, const char * interfacename /* Name of interface */ +, void* Ops /* Info (functions) exported + by this interface */ +, PILInterfaceFun close_func /* Close function for interface */ +, PILInterface** interfaceid /* Interface id (OP) */ +, void** Imports /* Functions imported by + this interface (OP) */ +, void* ud_interface /* Optional user_data */ +) +{ + PILPluginUniv* piuniv; /* Universe this plugin is in */ + PILPluginType* pitype; /* Type of this plugin */ + PILInterfaceUniv* ifuniv; /* Universe this interface is in */ + PILInterfaceType*iftype; /* Type of this interface */ + PILInterface* ifinfo; /* Info about this Interface */ + + PILInterfaceType*ifmgrtype; /* PILInterfaceType for PI_IFMANAGER */ + PILInterface* ifmgrinfo; /* Interf info for "interfacetype" */ + const PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */ + /* of type "interfacetype" */ + PIL_rc rc; + + if ( piinfo == NULL + || (pitype = piinfo->plugintype) == NULL + || (piuniv = pitype->piuniv) == NULL + || (ifuniv = piuniv->ifuniv) == NULL + || ifuniv->iftypes == NULL + ) { + REPORTERR("bad parameters to PILRegisterInterface"); + return PIL_INVAL; + } + + /* Now we have lots of info, but not quite enough... */ + + if ((iftype = g_hash_table_lookup(ifuniv->iftypes, interfacetype)) + == NULL) { + + /* Try to autoload the needed interface handler */ + rc = PILLoadPlugin(piuniv, PI_IFMANAGER, interfacetype, NULL); + + /* See if the interface handler loaded like we expect */ + if ((iftype = g_hash_table_lookup(ifuniv->iftypes + , interfacetype)) == NULL) { + return PIL_BADTYPE; + } + } + if ((ifinfo = g_hash_table_lookup(iftype->interfaces, interfacename)) + != NULL) { + g_warning("Attempt to register duplicate interface: %s/%s" + , interfacetype, interfacename); + return PIL_EXIST; + } + /* + * OK... Now we know it is valid, and isn't registered... + * Let's locate the InterfaceManager registrar for this type + */ + if ((ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER)) + == NULL) { + REPORTERR("No " PI_IFMANAGER " type!"); + return PIL_OOPS; + } + if ((ifmgrinfo = g_hash_table_lookup(ifmgrtype->interfaces + , interfacetype)) == NULL) { + PILLog(PIL_CRIT + , "No interface manager for given type (%s) !" + , interfacetype); + return PIL_BADTYPE; + } + + ifops = ifmgrinfo->exports; + + /* Now we have all the information anyone could possibly want ;-) */ + + ifinfo = NewPILInterface(iftype, interfacename, Ops + , close_func, ud_interface, piinfo); + + g_assert(ifmgrinfo == ifinfo->ifmanager); + *interfaceid = ifinfo; + + /* Call the registration function for our interface type */ + rc = ifops->RegisterInterface(ifinfo, Imports); + + + /* Increment reference count of interface manager */ + IfIncrRefCount(ifmgrinfo, 1); + + /* Increment the ref count of the plugin that loaded us */ + PluginIncrRefCount(piinfo, 1); + + if (rc != PIL_OK) { + RemoveAPILInterface(ifinfo); + } + return rc; +} + +/* + * Method: + * + * Verify interface is valid. + * + * Call interface close function. + * + * Call interface manager unregister function + * + * Call RmAPILInterface to remove from InterfaceType table, and + * free interface object. + * + */ + +static PIL_rc +PILunregister_interface(PILInterface* id) +{ + PILInterfaceType* t; + PILInterfaceUniv* u; + PIL_rc rc; + PILInterface* ifmgr_info; /* Pointer to our interface handler */ + const PILInterfaceOps* exports; /* InterfaceManager operations for + * the type of interface we are + */ + + if ( id == NULL + || (t = id->interfacetype) == NULL + || (u = t->universe) == NULL + || id->interfacename == NULL) { + PILLog(PIL_WARN, "PILunregister_interface: bad interfaceid"); + return PIL_INVAL; + } + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PILunregister_interface(%s/%s)" + , t->typename, id->interfacename); + } + PILValidateInterface(NULL, id, t); + PILValidateInterfaceType(NULL, t, u); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "Calling InterfaceClose on %s/%s" + , t->typename, id->interfacename); + } + + /* Call the close function supplied by the interface */ + + if ((id->if_close != NULL) + && ((rc=id->if_close(id, id->ud_interface)) != PIL_OK)) { + PILLog(PIL_WARN, "InterfaceClose on %s/%s returned %s" + , t->typename, id->interfacename + , PIL_strerror(rc)); + } else { + rc = PIL_OK; + } + + /* Find the InterfaceManager that manages us */ + ifmgr_info = t->ifmgr_ref; + + g_assert(ifmgr_info != NULL); + + /* Find the exported functions from that IFIF */ + exports = ifmgr_info->exports; + + g_assert(exports != NULL && exports->UnRegisterInterface != NULL); + + /* Call the interface manager unregister function */ + exports->UnRegisterInterface(id); + + /* Decrement reference count of interface manager */ + IfIncrRefCount(ifmgr_info, -1); + /* This may make ifmgr_info invalid */ + ifmgr_info = NULL; + + /* Decrement the reference count of the plugin that loaded us */ + PluginIncrRefCount(id->loadingpi, -1); + + return rc; +} + +static PILInterfaceUniv* +NewPILInterfaceUniv(PILPluginUniv* piuniv) +{ + PILInterfaceUniv* ret = NEW(PILInterfaceUniv); + static int ltinityet = 0; + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "NewPILInterfaceUniv(0x%lx)" + , (unsigned long)ret); + } + if (!ltinityet) { + ltinityet=1; + lt_dlinit(); + } + STATNEW(interfaceuniv); + ret->MagicNum = PIL_MAGIC_INTERFACEUNIV; + /* Make the two universes point at each other */ + ret->piuniv = piuniv; + piuniv->ifuniv = ret; + + ret->iftypes = g_hash_table_new(g_str_hash, g_str_equal); + + InterfaceManager_plugin_init(piuniv); + return ret; +} + +static void +DelPILInterfaceUniv(PILInterfaceUniv* ifuniv) +{ + PILInterfaceType* ifmgrtype; + g_assert(ifuniv!= NULL && ifuniv->iftypes != NULL); + PILValidateInterfaceUniv(NULL, ifuniv, NULL); + + STATFREE(interfaceuniv); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "DelPILInterfaceUniv(0x%lx)" + , (unsigned long) ifuniv); + } + g_hash_table_foreach_remove(ifuniv->iftypes, RmAPILInterfaceType, NULL); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "DelPILInterfaceUniv: final cleanup"); + } + ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER); + RemoveAPILInterfaceType(ifmgrtype, ifmgrtype); + /* + * FIXME! need to delete the interface for PI_IFMANAGER last + * Right now, it seems to happen last, but I think that's + * coincidence... + */ + g_hash_table_destroy(ifuniv->iftypes); + ZAP(ifuniv); + DELETE(ifuniv); +} + +/* + * These RmA* functions primarily called from hash_table_foreach, + * so they have gpointer arguments. This *not necessarily* clause + * is why they do the g_hash_table_lookup_extended call instead of + * just deleting the key. When called from outside, the key + * may not be pointing at the key to actually free, but a copy + * of the key. + */ +static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */ +RmAPILInterfaceType +( gpointer typename /* Name of this interface type */ +, gpointer iftype /* PILInterfaceType* */ +, gpointer notused +) +{ + PILInterfaceType* Iftype = iftype; + PILInterfaceUniv* Ifuniv = Iftype->universe; + + /* + * We are not always called by g_hash_table_foreach_remove() + */ + + g_assert(IS_PILINTERFACETYPE(Iftype)); + PILValidateInterfaceUniv(NULL, Ifuniv, NULL); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RmAPILInterfaceType(%s)" + , (char*)typename); + } + if (iftype != notused + && strcmp(Iftype->typename, PI_IFMANAGER) == 0) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RmAPILInterfaceType: skipping (%s)" + , (char*)typename); + } + return FALSE; + } + + DelPILInterfaceType(iftype); + DELETE(typename); + + return TRUE; +} + +static void +RemoveAPILInterfaceType(PILInterfaceType*Iftype, PILInterfaceType* t2) +{ + PILInterfaceUniv* Ifuniv = Iftype->universe; + gpointer key; + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "RemoveAPILInterfaceType(%s)" + , Iftype->typename); + } + if (t2 != Iftype + && strcmp(Iftype->typename, PI_IFMANAGER) == 0) { + PILLog(PIL_DEBUG, "RemoveAPILInterfaceType: skipping (%s)" + , Iftype->typename); + return; + } + if (g_hash_table_lookup_extended(Ifuniv->iftypes + , Iftype->typename, &key, (gpointer)&Iftype)) { + + g_hash_table_remove(Ifuniv->iftypes, key); + RmAPILInterfaceType(key, Iftype, t2); + }else{ + g_assert_not_reached(); + } +} + +/* + * We need to write more functions: These include... + * + * Plugin functions: + * + * PILPluginPath() - returns path name for a given plugin + * + * PILPluginTypeList() - returns list of plugins of a given type + * + */ +static void free_dirlist(struct dirent** dlist, int n); + +static int qsort_string_cmp(const void *a, const void *b); + + +static void +free_dirlist(struct dirent** dlist, int n) +{ + int j; + for (j=0; j < n; ++j) { + if (dlist[j]) { + free(dlist[j]); + dlist[j] = NULL; + } + } + free(dlist); +} + +static int +qsort_string_cmp(const void *a, const void *b) +{ + return(strcmp(*(const char * const *)a, *(const char * const *)b)); +} + +#define FREE_DIRLIST(dlist, n) {free_dirlist(dlist, n); dlist = NULL;} + +static int +so_select (const struct dirent *dire) +{ + + const char obj_end [] = PLUGINSUFFIX; + const char *end = &dire->d_name[strlen(dire->d_name) + - (STRLEN_CONST(obj_end))]; + + + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "In so_select: %s.", dire->d_name); + } + if (end < dire->d_name) { + return 0; + } + if (strcmp(end, obj_end) == 0) { + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "FILE %s looks like a plugin name." + , dire->d_name); + } + return 1; + } + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "FILE %s Doesn't look like a plugin name [%s] " + "%zd %zd %s." + , dire->d_name, end + , sizeof(obj_end), strlen(dire->d_name) + , &dire->d_name[strlen(dire->d_name) + - (STRLEN_CONST(obj_end))]); + } + + return 0; +} + +/* Return (sorted) list of available plugin names */ +static char** +PILPluginTypeListPlugins(PILPluginType* pitype +, int * picount /* Can be NULL ... */) +{ + const char * piclass = pitype->plugintype; + unsigned plugincount = 0; + char ** result = NULL; + int initoff = 0; + char ** pelem; + + /* Return all the plugins in all the directories in the PATH */ + + for (pelem=pitype->piuniv->rootdirlist; *pelem; ++pelem) { + int j; + GString* path; + int dircount; + struct dirent** files; + + + path = g_string_new(*pelem); + g_assert(piclass != NULL); + if (piclass) { + if (g_string_append_c(path, G_DIR_SEPARATOR) == NULL + || g_string_append(path, piclass) == NULL) { + g_string_free(path, 1); path = NULL; + return(NULL); + } + } + + files = NULL; + errno = 0; + dircount = scandir(path->str, &files + , SCANSEL_CAST so_select, NULL); + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PILS: Examining directory [%s]" + ": [%d] files matching [%s] suffix found." + , path->str, dircount, PLUGINSUFFIX); + } + g_string_free(path, 1); path=NULL; + + if (dircount <= 0) { + if (files != NULL) { + FREE_DIRLIST(files, dircount); + files = NULL; + } + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG + , "PILS: skipping empty directory" + " in PILPluginTypeListPlugins()"); + } + continue; + } + + initoff = plugincount; + plugincount += dircount; + if (result == NULL) { + result = (char **) g_malloc((plugincount+1)*sizeof(char *)); + }else{ + result = (char **) g_realloc(result + , (plugincount+1)*sizeof(char *)); + } + + for (j=0; j < dircount; ++j) { + char* s; + unsigned slen = strlen(files[j]->d_name) + - STRLEN_CONST(PLUGINSUFFIX); + + s = g_malloc(slen+1); + strncpy(s, files[j]->d_name, slen); + s[slen] = EOS; + result[initoff+j] = s; + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PILS: plugin [%s] found" + , s); + } + } + FREE_DIRLIST(files, dircount); + files = NULL; + } + + if (picount != NULL) { + *picount = plugincount; + } + if (result) { + result[plugincount] = NULL; + /* Return them in sorted order... */ + qsort(result, plugincount, sizeof(char *), qsort_string_cmp); + }else{ + if (DEBUGPLUGIN) { + PILLog(PIL_DEBUG, "PILS: NULL return" + " from PILPluginTypeListPlugins()"); + } + } + + + return result; +} +/* Return (sorted) list of available plugin names */ +char** +PILListPlugins(PILPluginUniv* u, const char * pitype +, int * picount /* Can be NULL ... */) +{ + PILPluginType* t; + if ((t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL) { + if (picount) { + *picount = 0; + } + t = NewPILPluginType(u, pitype); + if (!t) { + return NULL; + } + } + return PILPluginTypeListPlugins(t, picount); +} + +void +PILFreePluginList(char ** pluginlist) +{ + char ** ml = pluginlist; + + if (!ml) { + return; + } + + while (*ml != NULL) { + DELETE(*ml); + } + DELETE(pluginlist); +} + + +static void +PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype) +{ + const char * Key = key; + const PILPlugin * Plugin = plugin; + + g_assert(IS_PILPLUGIN(Plugin)); + + g_assert(Key == NULL || strcmp(Key, Plugin->plugin_name) == 0); + + g_assert (Plugin->refcnt >= 0 ); + + /* g_assert (Plugin->pluginops != NULL ); */ + g_assert (strcmp(Key, PI_IFMANAGER) == 0 || Plugin->dlinitfun != NULL ); + g_assert (strcmp(Plugin->plugin_name, PI_IFMANAGER) == 0 + || Plugin->dlhandle != NULL); + g_assert(Plugin->plugintype != NULL); + g_assert(IS_PILPLUGINTYPE(Plugin->plugintype)); + g_assert(pitype == NULL || pitype == Plugin->plugintype); +} + +static void +PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv) +{ + char * Key = key; + PILPluginType * Pitype = pitype; + PILPluginUniv * Muniv = piuniv; + + g_assert(IS_PILPLUGINTYPE(Pitype)); + g_assert(Muniv == NULL || IS_PILPLUGINUNIV(Muniv)); + g_assert(Key == NULL || strcmp(Key, Pitype->plugintype) == 0); + g_assert(IS_PILPLUGINUNIV(Pitype->piuniv)); + g_assert(piuniv == NULL || piuniv == Pitype->piuniv); + g_assert(Pitype->Plugins != NULL); + g_hash_table_foreach(Pitype->Plugins, PILValidatePlugin, Pitype); +} +static void +PILValidatePluginUniv(gpointer key, gpointer piuniv, gpointer dummy) +{ + PILPluginUniv * Muniv = piuniv; + + g_assert(IS_PILPLUGINUNIV(Muniv)); + g_assert(Muniv->rootdirlist != NULL); + g_assert(Muniv->imports != NULL); + g_hash_table_foreach(Muniv->PluginTypes, PILValidatePluginType, piuniv); + PILValidateInterfaceUniv(NULL, Muniv->ifuniv, piuniv); +} +static void +PILValidateInterface(gpointer key, gpointer interface, gpointer iftype) +{ + char * Key = key; + PILInterface* Interface = interface; + g_assert(IS_PILINTERFACE(Interface)); + g_assert(Key == NULL || strcmp(Key, Interface->interfacename) == 0); + g_assert(IS_PILINTERFACETYPE(Interface->interfacetype)); + g_assert(iftype == NULL || iftype == Interface->interfacetype); + g_assert(Interface->ifmanager!= NULL); + g_assert(IS_PILINTERFACE(Interface->ifmanager)); + g_assert(strcmp(Interface->interfacetype->typename + , Interface->ifmanager->interfacename)== 0); + g_assert(Interface->exports != NULL); +} +static void +PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv) +{ + char * Key = key; + PILInterfaceType* Iftype = iftype; + g_assert(IS_PILINTERFACETYPE(Iftype)); + g_assert(Key == NULL || strcmp(Key, Iftype->typename) == 0); + g_assert(ifuniv == NULL || Iftype->universe == ifuniv); + g_assert(Iftype->interfaces != NULL); + g_assert(Iftype->ifmgr_ref != NULL); + g_assert(IS_PILINTERFACE(Iftype->ifmgr_ref)); + g_assert(Key == NULL || strcmp(Key, Iftype->ifmgr_ref->interfacename) == 0); + + g_hash_table_foreach(Iftype->interfaces, PILValidateInterface, iftype); +} +static void +PILValidateInterfaceUniv(gpointer key, gpointer ifuniv, gpointer piuniv) +{ + PILInterfaceUniv* Ifuniv = ifuniv; + PILPluginUniv* Pluginuniv = piuniv; + g_assert(IS_PILINTERFACEUNIV(Ifuniv)); + g_assert(Pluginuniv == NULL || IS_PILPLUGINUNIV(Pluginuniv)); + g_assert(piuniv == NULL || piuniv == Ifuniv->piuniv); + g_hash_table_foreach(Ifuniv->iftypes, PILValidateInterfaceType, ifuniv); +} + +#define PRSTAT(type) { \ + PILLog(PIL_INFO, "Plugin system objects (" #type "): " \ + "\tnew %ld free \%ld current %ld" \ + , PILstats.type.news \ + , PILstats.type.frees \ + , PILstats.type.news - PILstats.type.frees); \ +} +void +PILLogMemStats(void) +{ + PRSTAT(plugin); + PRSTAT(pitype); + PRSTAT(piuniv); + PRSTAT(interface); + PRSTAT(interfacetype); + PRSTAT(interfaceuniv); +} + +/* + * 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, ...) +{ + va_list args; + char * str; + int err = errno; + + va_start (args, fmt); + str = g_strdup_vprintf(fmt, args); + va_end (args); + logfun(priority, "%s", str); + g_free(str); + errno = err; +} |