summaryrefslogtreecommitdiffstats
path: root/modules.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules.c')
-rw-r--r--modules.c693
1 files changed, 542 insertions, 151 deletions
diff --git a/modules.c b/modules.c
index 406cf32a..84f017e6 100644
--- a/modules.c
+++ b/modules.c
@@ -1,24 +1,32 @@
/* modules.c
* This is the implementation of syslogd modules object.
- * This object handles plug-ins and buil-in modules of all kind.
+ * This object handles plug-ins and build-in modules of all kind.
+ *
+ * Modules are reference-counted. Anyone who access a module must call
+ * Use() before any function is accessed and Release() when he is done.
+ * When the reference count reaches 0, rsyslog unloads the module (that
+ * may be changed in the future to cache modules). Rsyslog does NOT
+ * unload modules with a reference count > 0, even if the unload
+ * method is called!
*
* File begun on 2007-07-22 by RGerhards
*
* Copyright 2007 Rainer Gerhards and Adiscon GmbH.
*
- * 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 file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * Rsyslog 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.
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
@@ -40,10 +48,117 @@
#include "syslogd.h"
#include "cfsysline.h"
#include "modules.h"
+#include "errmsg.h"
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */
static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */
-static int bCfsyslineInitialized = 0;
+
+/* config settings */
+uchar *pModDir = NULL; /* read-only after startup */
+
+
+#ifdef DEBUG
+/* we add some home-grown support to track our users (and detect who does not free us). In
+ * the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11
+ */
+
+/* add a user to the current list of users (always at the root) */
+static void
+modUsrAdd(modInfo_t *pThis, char *pszUsr)
+{
+ modUsr_t *pUsr;
+
+ BEGINfunc
+ if((pUsr = calloc(1, sizeof(modUsr_t))) == NULL)
+ goto finalize_it;
+
+ if((pUsr->pszFile = strdup(pszUsr)) == NULL) {
+ free(pUsr);
+ goto finalize_it;
+ }
+
+ if(pThis->pModUsrRoot != NULL) {
+ pUsr->pNext = pThis->pModUsrRoot;
+ }
+ pThis->pModUsrRoot = pUsr;
+
+finalize_it:
+ ENDfunc;
+}
+
+
+/* remove a user from the current user list
+ * rgerhards, 2008-03-11
+ */
+static void
+modUsrDel(modInfo_t *pThis, char *pszUsr)
+{
+ modUsr_t *pUsr;
+ modUsr_t *pPrev = NULL;
+
+ for(pUsr = pThis->pModUsrRoot ; pUsr != NULL ; pUsr = pUsr->pNext) {
+ if(!strcmp(pUsr->pszFile, pszUsr))
+ break;
+ else
+ pPrev = pUsr;
+ }
+
+ if(pUsr == NULL) {
+ dbgprintf("oops - tried to delete user %s from module %s and it wasn't registered as one...\n",
+ pszUsr, pThis->pszName);
+ } else {
+ if(pPrev == NULL) {
+ /* This was at the root! */
+ pThis->pModUsrRoot = pUsr->pNext;
+ } else {
+ pPrev->pNext = pUsr->pNext;
+ }
+ /* free ressources */
+ free(pUsr->pszFile);
+ free(pUsr);
+ pUsr = NULL; /* just to make sure... */
+ }
+}
+
+
+/* print a short list all all source files using the module in question
+ * rgerhards, 2008-03-11
+ */
+static void
+modUsrPrint(modInfo_t *pThis)
+{
+ modUsr_t *pUsr;
+
+ for(pUsr = pThis->pModUsrRoot ; pUsr != NULL ; pUsr = pUsr->pNext) {
+ dbgprintf("\tmodule %s is currently in use by file %s\n",
+ pThis->pszName, pUsr->pszFile);
+ }
+}
+
+
+/* print all loaded modules and who is accessing them. This is primarily intended
+ * to be called at end of run to detect "module leaks" and who is causing them.
+ * rgerhards, 2008-03-11
+ */
+//static void
+void
+modUsrPrintAll(void)
+{
+ modInfo_t *pMod;
+
+ BEGINfunc
+ for(pMod = pLoadedModules ; pMod != NULL ; pMod = pMod->pNext) {
+ dbgprintf("printing users of loadable module %s, refcount %u, ptr %p, type %d\n", pMod->pszName, pMod->uRefCnt, pMod, pMod->eType);
+ modUsrPrint(pMod);
+ }
+ ENDfunc
+}
+
+#endif /* #ifdef DEBUG */
/* Construct a new module object
@@ -70,22 +185,29 @@ static rsRetVal moduleConstruct(modInfo_t **pThis)
*/
static void moduleDestruct(modInfo_t *pThis)
{
+ assert(pThis != NULL);
if(pThis->pszName != NULL)
free(pThis->pszName);
- if(pThis->pModHdlr != NULL)
+ if(pThis->pModHdlr != NULL) {
+# ifdef VALGRIND
+# warning "dlclose disabled for valgrind"
+# else
dlclose(pThis->pModHdlr);
+# endif
+ }
+
free(pThis);
}
-/* The followind function is the queryEntryPoint for host-based entry points.
+/* The following function is the queryEntryPoint for host-based entry points.
* Modules may call it to get access to core interface functions. Please note
* that utility functions can be accessed via shared libraries - at least this
* is my current shool of thinking.
* Please note that the implementation as a query interface allows to take
* care of plug-in interface version differences. -- rgerhards, 2007-07-31
*/
-rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
+static rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
{
DEFiRet;
@@ -94,11 +216,23 @@ rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
if(!strcmp((char*) name, "regCfSysLineHdlr")) {
*pEtryPoint = regCfSysLineHdlr;
+ } else if(!strcmp((char*) name, "objGetObjInterface")) {
+ *pEtryPoint = objGetObjInterface;
+ } else {
+ *pEtryPoint = NULL; /* to be on the safe side */
+ ABORT_FINALIZE(RS_RET_ENTRY_POINT_NOT_FOUND);
}
- if(iRet == RS_RET_OK)
- iRet = (*pEtryPoint == NULL) ? RS_RET_NOT_FOUND : RS_RET_OK;
- return iRet;
+finalize_it:
+ RETiRet;
+}
+
+
+/* get the name of a module
+ */
+static uchar *modGetName(modInfo_t *pThis)
+{
+ return((pThis->pszName == NULL) ? (uchar*) "" : pThis->pszName);
}
@@ -108,23 +242,16 @@ rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
* rgerhards, 2007-07-24
* TODO: the actual state name is not yet pulled
*/
-uchar *modGetStateName(modInfo_t *pThis)
+static uchar *modGetStateName(modInfo_t *pThis)
{
return(modGetName(pThis));
}
-/* get the name of a module
- */
-uchar *modGetName(modInfo_t *pThis)
-{
- return((pThis->pszName == NULL) ? (uchar*) "" : pThis->pszName);
-}
-
-
/* Add a module to the loaded module linked list
*/
-static inline void addModToList(modInfo_t *pThis)
+static inline void
+addModToList(modInfo_t *pThis)
{
assert(pThis != NULL);
@@ -132,6 +259,7 @@ static inline void addModToList(modInfo_t *pThis)
pLoadedModules = pLoadedModulesLast = pThis;
} else {
/* there already exist entries */
+ pThis->pPrev = pLoadedModulesLast;
pLoadedModulesLast->pNext = pThis;
pLoadedModulesLast = pThis;
}
@@ -145,7 +273,7 @@ static inline void addModToList(modInfo_t *pThis)
* returned - then, the list is empty.
* rgerhards, 2007-07-23
*/
-modInfo_t *modGetNxt(modInfo_t *pThis)
+static modInfo_t *GetNxt(modInfo_t *pThis)
{
modInfo_t *pNew;
@@ -158,14 +286,20 @@ modInfo_t *modGetNxt(modInfo_t *pThis)
}
-/* this function is like modGetNxt(), but it returns pointers to
- * output modules only. As we currently deal just with output modules,
+/* this function is like GetNxt(), but it returns pointers to
+ * modules of specific type only. As we currently deal just with output modules,
* it is a dummy, to be filled with real code later.
* rgerhards, 2007-07-24
*/
-modInfo_t *omodGetNxt(modInfo_t *pThis)
+static modInfo_t *GetNxtType(modInfo_t *pThis, eModType_t rqtdType)
{
- return(modGetNxt(pThis));
+ modInfo_t *pMod = pThis;
+
+ do {
+ pMod = GetNxt(pMod);
+ } while(!(pMod == NULL || pMod->eType == rqtdType)); /* warning: do ... while() */
+
+ return pMod;
}
@@ -177,112 +311,90 @@ modInfo_t *omodGetNxt(modInfo_t *pThis)
* been destroyed. In the case of output modules, this happens when the
* rule set is being destroyed. When we implement other module types, we
* need to think how we handle it there (and if we have any instance data).
+ * rgerhards, 2008-03-10: reject unload request if the module has a reference
+ * count > 0.
*/
-static rsRetVal modPrepareUnload(modInfo_t *pThis)
+static rsRetVal
+modPrepareUnload(modInfo_t *pThis)
{
DEFiRet;
void *pModCookie;
assert(pThis != NULL);
- /* WARNING - the current code does NOT work and causes an abort - this is acceptable right now
- * as I am DEVELOPING the working code and will NOT release until it is there. If you use a
- * CVS snapshot, be aware of this limitation. For now, you can just remove everything up to
- * (but not including) the END DEVEL comment. That will do the trick. rgerhards, 2007-11-21
- */
+ if(pThis->uRefCnt > 0) {
+ dbgprintf("rejecting unload of module '%s' because it has a refcount of %d\n",
+ pThis->pszName, pThis->uRefCnt);
+ ABORT_FINALIZE(RS_RET_MODULE_STILL_REFERENCED);
+ }
+
CHKiRet(pThis->modGetID(&pModCookie));
pThis->modExit(); /* tell the module to get ready for unload */
CHKiRet(unregCfSysLineHdlrs4Owner(pModCookie));
- /* END DEVEL */
-
finalize_it:
- return iRet;
+ RETiRet;
}
/* Add an already-loaded module to the module linked list. This function does
* everything needed to fully initialize the module.
*/
-rsRetVal doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)()), uchar *name, void *pModHdlr)
+static rsRetVal
+doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_t*), uchar *name, void *pModHdlr)
{
DEFiRet;
- modInfo_t *pNew;
+ modInfo_t *pNew = NULL;
+ rsRetVal (*modGetType)(eModType_t *pType);
assert(modInit != NULL);
- if(bCfsyslineInitialized == 0) {
- /* we need to initialize the cfsysline subsystem first */
- CHKiRet(cfsyslineInit());
- bCfsyslineInitialized = 1;
+ if((iRet = moduleConstruct(&pNew)) != RS_RET_OK) {
+ pNew = NULL;
+ ABORT_FINALIZE(iRet);
}
- if((iRet = moduleConstruct(&pNew)) != RS_RET_OK)
- return iRet;
-
- if((iRet = (*modInit)(1, &pNew->iIFVers, &pNew->modQueryEtryPt, queryHostEtryPt)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
+ CHKiRet((*modInit)(CURR_MOD_IF_VERSION, &pNew->iIFVers, &pNew->modQueryEtryPt, queryHostEtryPt, pNew));
- if(pNew->iIFVers != 1) {
- moduleDestruct(pNew);
- return RS_RET_MISSING_INTERFACE;
+ if(pNew->iIFVers != CURR_MOD_IF_VERSION) {
+ ABORT_FINALIZE(RS_RET_MISSING_INTERFACE);
}
+ /* We now poll the module to see what type it is. We do this only once as this
+ * can never change in the lifetime of an module. -- rgerhards, 2007-12-14
+ */
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getType", &modGetType));
+ CHKiRet((iRet = (*modGetType)(&pNew->eType)) != RS_RET_OK);
+ dbgprintf("module of type %d being loaded.\n", pNew->eType);
+
/* OK, we know we can successfully work with the module. So we now fill the
- * rest of the data elements.
+ * rest of the data elements. First we load the interfaces common to all
+ * module types.
*/
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"doAction", &pNew->mod.om.doAction)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"parseSelectorAct", &pNew->mod.om.parseSelectorAct)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature",
- &pNew->isCompatibleWithFeature)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"dbgPrintInstInfo",
- &pNew->dbgPrintInstInfo)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"getWriteFDForSelect", &pNew->getWriteFDForSelect)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"onSelectReadyWrite", &pNew->onSelectReadyWrite)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"needUDPSocket", &pNew->needUDPSocket)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"tryResume", &pNew->tryResume)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"modGetID", &pNew->modGetID)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
- }
- if((iRet = (*pNew->modQueryEtryPt)((uchar*)"modExit", &pNew->modExit)) != RS_RET_OK) {
- moduleDestruct(pNew);
- return iRet;
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modGetID", &pNew->modGetID));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modExit", &pNew->modExit));
+
+ /* ... and now the module-specific interfaces */
+ switch(pNew->eType) {
+ case eMOD_IN:
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"runInput", &pNew->mod.im.runInput));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"willRun", &pNew->mod.im.willRun));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"afterRun", &pNew->mod.im.afterRun));
+ break;
+ case eMOD_OUT:
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"dbgPrintInstInfo", &pNew->dbgPrintInstInfo));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"doAction", &pNew->mod.om.doAction));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"parseSelectorAct", &pNew->mod.om.parseSelectorAct));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature", &pNew->isCompatibleWithFeature));
+ CHKiRet((*pNew->modQueryEtryPt)((uchar*)"tryResume", &pNew->tryResume));
+ break;
+ case eMOD_LIB:
+ break;
}
pNew->pszName = (uchar*) strdup((char*)name); /* we do not care if strdup() fails, we can accept that */
pNew->pModHdlr = pModHdlr;
- pNew->eType = eMOD_OUT; /* TODO: take this from module */
/* TODO: take this from module */
if(pModHdlr == NULL)
pNew->eLinkType = eMOD_LINK_STATIC;
@@ -293,18 +405,24 @@ rsRetVal doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)())
addModToList(pNew);
finalize_it:
- return iRet;
+ if(iRet != RS_RET_OK) {
+ if(pNew != NULL)
+ moduleDestruct(pNew);
+ }
+
+ RETiRet;
}
/* Print loaded modules. This is more or less a
* debug or test aid, but anyhow I think it's worth it...
* This only works if the dbgprintf() subsystem is initialized.
+ * TODO: update for new input modules!
*/
-void modPrintList(void)
+static void modPrintList(void)
{
modInfo_t *pMod;
- pMod = modGetNxt(NULL);
+ pMod = GetNxt(NULL);
while(pMod != NULL) {
dbgprintf("Loaded Module: Name='%s', IFVersion=%d, ",
(char*) modGetName(pMod), pMod->iIFVers);
@@ -316,8 +434,8 @@ void modPrintList(void)
case eMOD_IN:
dbgprintf("input");
break;
- case eMOD_FILTER:
- dbgprintf("filter");
+ case eMOD_LIB:
+ dbgprintf("library");
break;
}
dbgprintf(" module.\n");
@@ -328,70 +446,343 @@ void modPrintList(void)
dbgprintf("\tdbgPrintInstInfo: 0x%lx\n", (unsigned long) pMod->dbgPrintInstInfo);
dbgprintf("\tfreeInstance: 0x%lx\n", (unsigned long) pMod->freeInstance);
dbgprintf("\n");
- pMod = modGetNxt(pMod); /* done, go next */
+ pMod = GetNxt(pMod); /* done, go next */
}
}
-/* unload all modules and free module linked list
- * rgerhards, 2007-08-09
+/* unlink and destroy a module. The caller must provide a pointer to the module
+ * itself as well as one to its immediate predecessor.
+ * rgerhards, 2008-02-26
*/
-rsRetVal modUnloadAndDestructAll(void)
+static rsRetVal
+modUnlinkAndDestroy(modInfo_t **ppThis)
{
DEFiRet;
- modInfo_t *pMod;
- modInfo_t *pModPrev;
+ modInfo_t *pThis;
- pMod = modGetNxt(NULL);
- while(pMod != NULL) {
- pModPrev = pMod;
- pMod = modGetNxt(pModPrev); /* get next */
- /* now we can destroy the previous module */
- dbgprintf("Unloading module %s\n", modGetName(pModPrev));
- modPrepareUnload(pModPrev);
- moduleDestruct(pModPrev);
+ assert(ppThis != NULL);
+ pThis = *ppThis;
+ assert(pThis != NULL);
+
+ /* first check if we are permitted to unload */
+ if(pThis->eType == eMOD_LIB) {
+ if(pThis->uRefCnt > 0) {
+ dbgprintf("module %s NOT unloaded because it still has a refcount of %u\n",
+ pThis->pszName, pThis->uRefCnt);
+# ifdef DEBUG
+ //modUsrPrintAll();
+# endif
+ ABORT_FINALIZE(RS_RET_MODULE_STILL_REFERENCED);
+ }
+ }
+
+ /* we need to unlink the module before we can destruct it -- rgerhards, 2008-02-26 */
+ if(pThis->pPrev == NULL) {
+ /* module is root, so we need to set a new root */
+ pLoadedModules = pThis->pNext;
+ } else {
+ pThis->pPrev->pNext = pThis->pNext;
+ }
+
+ if(pThis->pNext == NULL) {
+ pLoadedModulesLast = pThis->pPrev;
+ } else {
+ pThis->pNext->pPrev = pThis->pPrev;
}
- return iRet;
+ /* finally, we are ready for the module to go away... */
+ dbgprintf("Unloading module %s\n", modGetName(pThis));
+ CHKiRet(modPrepareUnload(pThis));
+ *ppThis = pThis->pNext;
+
+ moduleDestruct(pThis);
+
+finalize_it:
+ RETiRet;
}
-rsRetVal modUnloadAndDestructDynamic(void)
+/* unload all loaded modules of a specific type (use eMOD_ALL if you want to
+ * unload all module types). The unload happens only if the module is no longer
+ * referenced. So some modules may survive this call.
+ * rgerhards, 2008-03-11
+ */
+static rsRetVal
+modUnloadAndDestructAll(eModLinkType_t modLinkTypesToUnload)
{
DEFiRet;
- modInfo_t *pMod;
- modInfo_t *pModPrev;
+ modInfo_t *pModCurr; /* module currently being processed */
+
+ pModCurr = GetNxt(NULL);
+ while(pModCurr != NULL) {
+ if(modLinkTypesToUnload == eMOD_LINK_ALL || pModCurr->eLinkType == modLinkTypesToUnload) {
+ if(modUnlinkAndDestroy(&pModCurr) == RS_RET_MODULE_STILL_REFERENCED) {
+ pModCurr = GetNxt(pModCurr);
+ }
+ /* Note: if the module was successfully unloaded, it has updated the
+ * pModCurr pointer to the next module. So we do NOT need to advance
+ * to the next module on successful unload.
+ */
+ } else {
+ pModCurr = GetNxt(pModCurr);
+ }
+ }
- pLoadedModulesLast = NULL;
+# ifdef DEBUG
+ if(pLoadedModules != NULL) {
+ dbgprintf("modules still loaded after module.UnloadAndDestructAll:\n");
+ modUsrPrintAll();
+ }
+# endif
- pMod = modGetNxt(NULL);
- while(pMod != NULL) {
- pModPrev = pMod;
- pMod = modGetNxt(pModPrev); /* get next */
- /* now we can destroy the previous module */
- if(pModPrev->eLinkType != eMOD_LINK_STATIC) {
- dbgprintf("Unloading module %s\n", modGetName(pModPrev));
- modPrepareUnload(pModPrev);
- moduleDestruct(pModPrev);
- } else {
- pLoadedModulesLast = pModPrev;
+ RETiRet;
+}
+
+
+/* load a module and initialize it, based on doModLoad() from conf.c
+ * rgerhards, 2008-03-05
+ * varmojfekoj added support for dynamically loadable modules on 2007-08-13
+ * rgerhards, 2007-09-25: please note that the non-threadsafe function dlerror() is
+ * called below. This is ok because modules are currently only loaded during
+ * configuration file processing, which is executed on a single thread. Should we
+ * change that design at any stage (what is unlikely), we need to find a
+ * replacement.
+ */
+static rsRetVal
+Load(uchar *pModName)
+{
+ DEFiRet;
+
+ size_t iPathLen, iModNameLen;
+ uchar szPath[PATH_MAX];
+ uchar *pModNameCmp;
+ int bHasExtension;
+ void *pModHdlr, *pModInit;
+ modInfo_t *pModInfo;
+
+ assert(pModName != NULL);
+ dbgprintf("Requested to load module '%s'\n", pModName);
+
+ iModNameLen = strlen((char *) pModName);
+ if(iModNameLen > 3 && !strcmp((char *) pModName + iModNameLen - 3, ".so")) {
+ iModNameLen -= 3;
+ bHasExtension = TRUE;
+ } else
+ bHasExtension = FALSE;
+
+ pModInfo = GetNxt(NULL);
+ while(pModInfo != NULL) {
+ if(!strncmp((char *) pModName, (char *) (pModNameCmp = modGetName(pModInfo)), iModNameLen) &&
+ (!*(pModNameCmp + iModNameLen) || !strcmp((char *) pModNameCmp + iModNameLen, ".so"))) {
+ dbgprintf("Module '%s' already loaded\n", pModName);
+ ABORT_FINALIZE(RS_RET_OK);
}
+ pModInfo = GetNxt(pModInfo);
}
- /* Note: the last modules pNext pointer is now invalid
- * (except if the last module was not touched, what is highly
- * unlikely. We simply fix this be setting it to NULL. After all,
- * it is the last module ;). This bug had some severe effects in
- * v3, but none in v2 because in v2 the list was never again
- * traversed before a new one was added. But even in v2 it may cause
- * a segfault if the number of loaded modules changed between HUPs.
- * rgerhards, 2008-02-26
- */
- if(pLoadedModulesLast != NULL)
- pLoadedModulesLast->pNext = NULL;
+ /* now build our load module name */
+ if(*pModName == '/') {
+ *szPath = '\0'; /* we do not need to append the path - its already in the module name */
+ iPathLen = 0;
+ } else {
+ *szPath = '\0';
+ strncat((char *) szPath, (pModDir == NULL) ? _PATH_MODDIR : (char*) pModDir, sizeof(szPath) - 1);
+ iPathLen = strlen((char*) szPath);
+ if((szPath[iPathLen - 1] != '/')) {
+ if((iPathLen <= sizeof(szPath) - 2)) {
+ szPath[iPathLen++] = '/';
+ szPath[iPathLen] = '\0';
+ } else {
+ errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ }
+ }
+
+ /* ... add actual name ... */
+ strncat((char *) szPath, (char *) pModName, sizeof(szPath) - iPathLen - 1);
+
+ /* now see if we have an extension and, if not, append ".so" */
+ if(!bHasExtension) {
+ /* we do not have an extension and so need to add ".so"
+ * TODO: I guess this is highly importable, so we should change the
+ * algo over time... -- rgerhards, 2008-03-05
+ */
+ /* ... so now add the extension */
+ strncat((char *) szPath, ".so", sizeof(szPath) - strlen((char*) szPath) - 1);
+ iPathLen += 3;
+ }
- return iRet;
+ if(iPathLen + strlen((char*) pModName) >= sizeof(szPath)) {
+ errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* complete load path constructed, so ... GO! */
+ dbgprintf("loading module '%s'\n", szPath);
+ if(!(pModHdlr = dlopen((char *) szPath, RTLD_NOW))) {
+ errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlopen: %s\n", szPath, dlerror());
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ if(!(pModInit = dlsym(pModHdlr, "modInit"))) {
+ errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlsym: %s\n", szPath, dlerror());
+ dlclose(pModHdlr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr)) != RS_RET_OK) {
+ errmsg.LogError(NO_ERRCODE, "could not load module '%s', rsyslog error %d\n", szPath, iRet);
+ dlclose(pModHdlr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+finalize_it:
+ RETiRet;
}
-/*
- * vi:set ai:
+
+
+/* set the default module load directory. A NULL value may be provided, in
+ * which case any previous value is deleted but no new one set. The caller-provided
+ * string is duplicated. If it needs to be freed, that's the caller's duty.
+ * rgerhards, 2008-03-07
+ */
+static rsRetVal
+SetModDir(uchar *pszModDir)
+{
+ DEFiRet;
+
+ dbgprintf("setting default module load directory '%s'\n", pszModDir);
+ if(pModDir != NULL) {
+ free(pModDir);
+ }
+
+ pModDir = (uchar*) strdup((char*)pszModDir);
+
+ RETiRet;
+}
+
+
+/* Reference-Counting object access: add 1 to the current reference count. Must be
+ * called by anyone interested in using a module. -- rgerhards, 20080-03-10
+ */
+static rsRetVal
+Use(char *srcFile, modInfo_t *pThis)
+{
+ DEFiRet;
+
+ assert(pThis != NULL);
+ pThis->uRefCnt++;
+ dbgprintf("source file %s requested reference for module '%s', reference count now %u\n",
+ srcFile, pThis->pszName, pThis->uRefCnt);
+
+# ifdef DEBUG
+ modUsrAdd(pThis, srcFile);
+# endif
+
+ RETiRet;
+
+}
+
+
+/* Reference-Counting object access: subract one from the current refcount. Must
+ * by called by anyone who no longer needs a module. If count reaches 0, the
+ * module is unloaded. -- rgerhards, 20080-03-10
+ */
+static rsRetVal
+Release(char *srcFile, modInfo_t **ppThis)
+{
+ DEFiRet;
+ modInfo_t *pThis;
+
+ assert(ppThis != NULL);
+ pThis = *ppThis;
+ assert(pThis != NULL);
+ if(pThis->uRefCnt == 0) {
+ /* oops, we are already at 0? */
+ dbgprintf("internal error: module '%s' already has a refcount of 0 (released by %s)!\n",
+ pThis->pszName, srcFile);
+ } else {
+ --pThis->uRefCnt;
+ dbgprintf("file %s released module '%s', reference count now %u\n",
+ srcFile, pThis->pszName, pThis->uRefCnt);
+# ifdef DEBUG
+ modUsrDel(pThis, srcFile);
+ modUsrPrint(pThis);
+# endif
+ }
+
+ if(pThis->uRefCnt == 0) {
+ /* we have a zero refcount, so we must unload the module */
+ dbgprintf("module '%s' has zero reference count, unloading...\n", pThis->pszName);
+ modUnlinkAndDestroy(&pThis);
+ /* we must NOT do a *ppThis = NULL, because ppThis now points into freed memory!
+ * If in doubt, see obj.c::ReleaseObj() for how we are called.
+ */
+ }
+
+ RETiRet;
+
+}
+
+
+/* exit our class
+ * rgerhards, 2008-03-11
+ */
+BEGINObjClassExit(module, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(module)
+ /* release objects we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+
+# ifdef DEBUG
+ modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */
+# endif
+ENDObjClassExit(module)
+
+
+/* queryInterface function
+ * rgerhards, 2008-03-05
+ */
+BEGINobjQueryInterface(module)
+CODESTARTobjQueryInterface(module)
+ if(pIf->ifVersion != moduleCURR_IF_VERSION) { /* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->GetNxt = GetNxt;
+ pIf->GetNxtType = GetNxtType;
+ pIf->GetName = modGetName;
+ pIf->GetStateName = modGetStateName;
+ pIf->PrintList = modPrintList;
+ pIf->UnloadAndDestructAll = modUnloadAndDestructAll;
+ pIf->doModInit = doModInit;
+ pIf->SetModDir = SetModDir;
+ pIf->Load = Load;
+ pIf->Use = Use;
+ pIf->Release = Release;
+finalize_it:
+ENDobjQueryInterface(module)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-03-05
+ */
+BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ uchar *pModPath;
+
+ /* use any module load path specified in the environment */
+ if((pModPath = (uchar*) getenv("RSYSLOG_MODDIR")) != NULL) {
+ SetModDir(pModPath);
+ }
+
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDObjClassInit(module)
+
+/* vi:set ai:
*/