/******************************************************************************/
/* */
/* X r d S y s P l u g i n . c c */
/* */
/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University */
/* All Rights Reserved */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD 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 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD 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 XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
// Bypass Solaris ELF madness
//
#ifdef __solaris__
#include
#if defined(_ILP32) && (_FILE_OFFSET_BITS != 32)
#undef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 32
#undef _LARGEFILE_SOURCE
#endif
#endif
#ifndef WIN32
#include
#if !defined(__APPLE__) && !defined(__CYGWIN__)
#include
#endif
#include
#include
#include
#include
#else
#include "XrdSys/XrdWin32.hh"
#endif
#include "XrdSys/XrdSysError.hh"
#include "XrdSys/XrdSysHeaders.hh"
#include "XrdSys/XrdSysPlugin.hh"
#include "XrdVersion.hh"
#include "XrdVersionPlugin.hh"
/******************************************************************************/
/* S t a t i c M e m b e r s */
/******************************************************************************/
struct XrdSysPlugin::PLlist *XrdSysPlugin::plList = 0;
/******************************************************************************/
/* D e s t r u c t o r */
/******************************************************************************/
XrdSysPlugin::~XrdSysPlugin()
{
if (libHandle) dlclose(libHandle);
if (libPath) free(libPath);
}
/******************************************************************************/
/* Private: b a d V e r s i o n */
/******************************************************************************/
XrdSysPlugin::cvResult XrdSysPlugin::badVersion(XrdVersionInfo &urInfo,
char mmv, int majv, int minv)
{
const char *path;
char buff1[512], buff2[128];
if (minv > 99) minv = 99;
snprintf(buff1, sizeof(buff1), "version %s is incompatible with %s "
"(must be %c= %d.%d.x)",
myInfo->vStr, urInfo.vStr, mmv, majv, minv);
path = msgSuffix(" in ", buff2, sizeof(buff2));
Inform(buff1, buff2, path, 0, 0, 1);
return cvBad;
}
/******************************************************************************/
/* Private: c h k V e r s i o n */
/******************************************************************************/
XrdSysPlugin::cvResult XrdSysPlugin::chkVersion(XrdVersionInfo &urInfo,
const char *pname,
void *lHandle)
{
static XrdVersionPlugin vInfo[] = {XrdVERSIONPLUGINRULES};
static XrdVersionPlugin vNote[] = {XrdVERSIONPLUGINMAXIMS};
XrdVersionPlugin *vinP;
char buff[1024], vName[256];
void *vP;
int i, n=0, pMajor, vMajor, pMinor, vMinor;
// If no version information supplied, skip version check
//
if (!myInfo) return cvNone;
// Check if we need to check the version here
//
i = 0;
while(vInfo[i].pName && strcmp(vInfo[i].pName, pname)) i++;
// If we didn't find it in the rules table then try to match the maxims
//
if (!vInfo[i].pName)
{i = 0; n = strlen(pname);
while(vNote[i].pName)
{if ((vNote[i].vPfxLen + vNote[i].vSfxLen <= n)
&& !strncmp(vNote[i].pName, pname, vNote[i].vPfxLen)
&& !strncmp(vNote[i].pName+vNote[i].vPfxLen,
pname + n - vNote[i].vSfxLen, vNote[i].vSfxLen)) break;
i++;
}
vinP = &vNote[i];
} else vinP = &vInfo[i];
if (!(vinP->pName)) return cvNone;
if ( vinP->vProcess == XrdVERSIONPLUGIN_DoNotChk) return cvDirty;
// Construct the version entry point
//
if (!n) n = strlen(pname);
if (n+sizeof(XrdVERSIONINFOSFX) > sizeof(vName))
return libMsg("Unable to generate version name for", "%s in ", pname);
strcpy(vName, pname); strcpy(vName+n, XrdVERSIONINFOSFX);
// Find the version number
//
if (!(vP = dlsym(lHandle, vName)))
{if (vinP->vProcess != XrdVERSIONPLUGIN_Required) return cvMissing;
return libMsg(dlerror()," required version information for %s in ",pname);
}
// Extract the version number from the plugin and do a quick check. We use
// memcpy to avoid instances where the symbol is wrongly defined. Make sure
// the version string ends with a null by copying one less byte than need be.
// The caller provided a struct that is gauranteed to end with nulls.
//
memcpy(static_cast( &urInfo ), vP, sizeof(XrdVersionInfo)-1);
// If version numbers are identical then we are done
//
if (myInfo->vNum == urInfo.vNum)
if (myInfo->vNum != XrdVNUMUNK
|| !strcmp(myInfo->vStr + (myInfo->vOpt & 0x0f)+1,
urInfo. vStr + (urInfo. vOpt & 0x0f)+1)) return cvClean;
// If the caller or plugin is unreleased, just issue a warning.
//
if (myInfo->vNum == XrdVNUMUNK || urInfo.vNum == XrdVNUMUNK)
{if (eDest)
{char mBuff[128];
sprintf(buff, "%s%s is using %s%s version",
(myInfo->vNum == XrdVNUMUNK ? "unreleased ":""),myInfo->vStr,
(urInfo.vNum == XrdVNUMUNK ? "unreleased ":""),urInfo.vStr);
msgSuffix(" in ", mBuff, sizeof(mBuff));
Inform(buff, mBuff, libPath);
}
return cvDirty;
}
// Extract version numbers
//
vMajor = XrdMajorVNUM(myInfo->vNum);
vMinor = XrdMinorVNUM(myInfo->vNum);
pMajor = XrdMajorVNUM(urInfo. vNum);
pMinor = XrdMinorVNUM(urInfo. vNum);
// The major version must always be compatible
//
if ((vinP->vMajLow >= 0 && pMajor < vinP->vMajLow)
|| (vinP->vMajLow < 0 && pMajor != vMajor))
return badVersion(urInfo, '>', vinP->vMajLow, vinP->vMinLow);
// The major version may not be greater than our versin
//
if (pMajor > vMajor) return badVersion(urInfo, '<', vMajor, vMinor);
// If we do not need to check minor versions then we are done
//
if (vinP->vMinLow > 99) return cvClean;
// In no case can the plug-in mnor version be greater than our version
//
if (pMajor == vMajor && pMinor > vMinor)
return badVersion(urInfo, '<', vMajor, vMinor);
// Verify compatible minor versions
//
if ((vinP->vMinLow >= 0 && pMinor >= vinP->vMinLow)
|| (vinP->vMinLow < 0 && pMinor == vMinor)) return cvClean;
// Incompatible versions
//
return badVersion(urInfo, '>', vinP->vMajLow, vinP->vMinLow);
}
/******************************************************************************/
/* Private: D L F l a g s */
/******************************************************************************/
int XrdSysPlugin::DLflags()
{
#if defined(__APPLE__)
return RTLD_FIRST;
#elif defined(__linux__)
return RTLD_NOW;
#else
return RTLD_NOW;
#endif
}
/******************************************************************************/
/* Private: F i n d */
/******************************************************************************/
void *XrdSysPlugin::Find(const char *libpath)
{
struct PLlist *plP = plList;
// Find the library in the preload list
//
while(plP && strcmp(libpath, plP->libPath)) plP = plP->next;
// Return result
//
return (plP ? plP->libHandle : 0);
}
/******************************************************************************/
/* g e t P l u g i n */
/******************************************************************************/
void *XrdSysPlugin::getPlugin(const char *pname, int optional)
{
return getPlugin(pname, optional, false);
}
void *XrdSysPlugin::getPlugin(const char *pname, int optional, bool global)
{
XrdVERSIONINFODEF(urInfo, unknown, XrdVNUMUNK, "");
void *ep, *myHandle;
cvResult cvRC;
int flags;
// If no path is given then we want to just search the executable. This is easy
// for some platforms and more difficult for others. So, we do the best we can.
//
if (libPath) flags = DLflags();
else { flags = RTLD_NOW;
#ifndef WIN32
flags|= global ? RTLD_GLOBAL : RTLD_LOCAL;
#else
if (global && eDest) eDest->Emsg("getPlugin",
"request for global symbols unsupported under Windows - ignored");
#endif
}
// Check if we should use the preload list
//
if (!(myHandle = libHandle) && plList) myHandle = Find(libPath);
// Open whatever it is we need to open
//
if (!myHandle)
{if ((myHandle = dlopen(libPath, flags))) libHandle = myHandle;
else {if (optional < 2) libMsg(dlerror(), " loading "); return 0;}
}
// Get the symbol. In the environment we have defined, null values are not
// allowed and we will issue an error.
//
if (!(ep = dlsym(myHandle, pname)))
{if (optional < 2) libMsg(dlerror(), " plugin %s in ", pname);
return 0;
}
// Check if we need to verify version compatability
//
if ((cvRC = chkVersion(urInfo, pname, myHandle)) == cvBad) return 0;
// Print the loaded version unless message is suppressed or not needed
//
if (libPath && optional < 2 && msgCnt
&& (cvRC == cvClean || cvRC == cvMissing))
{char buff[128];
msgSuffix(" from ", buff, sizeof(buff));
msgCnt--;
if (cvRC == cvClean)
{const char *wTxt=(urInfo.vNum == XrdVNUMUNK ? "unreleased ":0);
Inform("loaded ", wTxt, urInfo.vStr, buff, libPath);
}
else if (cvRC == cvMissing)
{Inform("loaded unversioned ", pname, buff, libPath);}
}
// All done
//
return ep;
}
/******************************************************************************/
/* Private: I n f o r m */
/******************************************************************************/
void XrdSysPlugin::Inform(const char *txt1, const char *txt2, const char *txt3,
const char *txt4, const char *txt5, int noHush)
{
const char *eTxt[] = {"Plugin ",txt1, txt2, txt3, txt4, txt5, 0};
char *bP;
int n, i, bL;
// Check if we should hush this messages (largely for client-side usage)
//
if (!noHush && getenv("XRDPIHUSH")) return;
// If we have a messaging object, use that
//
if (eDest)
{char buff[2048];
i = 1; bP = buff; bL = sizeof(buff);
while(bL > 1 && eTxt[i])
{n = snprintf(bP, bL, "%s", eTxt[i]);
bP += n; bL -= n; i++;
}
eDest->Say("Plugin ", buff);
return;
}
// If we have a buffer, set message in the buffer
//
if ((bP = eBuff))
{i = 0; bL = eBLen;
while(bL > 1 && eTxt[i])
{n = snprintf(bP, bL, "%s", eTxt[i]);
bP += n; bL -= n; i++;
}
}
}
/******************************************************************************/
/* Private: l i b M s g */
/******************************************************************************/
XrdSysPlugin::cvResult XrdSysPlugin::libMsg(const char *txt1, const char *txt2,
const char *mSym)
{
static const char fndg[] = "Finding";
static const int flen = sizeof("Finding");
const char *path;
char mBuff[512], nBuff[512];
// Check if this is a lookup or open issue. Trim message for the common case.
//
if (mSym)
{if (!txt1 || strstr(txt1, "undefined"))
{txt1 = "Unable to find ";
snprintf(nBuff, sizeof(nBuff), txt2, mSym);
} else {
strcpy(nBuff, fndg);
snprintf(nBuff+flen-1,sizeof(nBuff)-flen,txt2,mSym);
}
txt2 = nBuff;
}
else if (!txt1) txt1 = "Unknown system error!";
else if (strstr(txt1, "No such file")) txt1 = "No such file or directory";
else txt2 = " ";
// Spit out the message
//
path = msgSuffix(txt2, mBuff, sizeof(mBuff));
Inform(txt1, mBuff, path, 0, 0, 1);
return cvBad;
}
/******************************************************************************/
/* Private: m s g S u f f i x */
/******************************************************************************/
const char *XrdSysPlugin::msgSuffix(const char *Word, char *buff, int bsz)
{
if (libPath) snprintf(buff, bsz,"%s%s ", Word, libName);
else snprintf(buff, bsz,"%sexecutable image", Word);
return (libPath ? libPath : "");
}
/******************************************************************************/
/* P r e l o a d */
/******************************************************************************/
bool XrdSysPlugin::Preload(const char *path, char *ebuff, int eblen)
{
struct PLlist *plP;
void *myHandle;
// First see if this is already in the preload list
//
if (Find(path)) return true;
// Try to open the library
//
if (!(myHandle = dlopen(path, DLflags())))
{if (ebuff && eblen > 0)
{const char *dlMsg = dlerror();
snprintf(ebuff, eblen, "Plugin unable to load %s; %s", path,
(dlMsg ? dlMsg : "unknown system error"));
}
return false;
}
// Add the library handle
//
plP = new PLlist;
plP->libHandle = myHandle;
plP->libPath = strdup(path);
plP->next = plList;
plList = plP;
// All done
//
return true;
}
/******************************************************************************/
/* V e r C m p */
/******************************************************************************/
bool XrdSysPlugin::VerCmp(XrdVersionInfo &vInfo1,
XrdVersionInfo &vInfo2, bool noMsg)
{
const char *mTxt;
char v1buff[128], v2buff[128];
int unRel;
// Do a quick return if the version need not be checked or are equal
//
if (vInfo1.vNum <= 0 || vInfo1.vNum == vInfo2.vNum) return true;
// As it works out, many times two modules wind up in different shared
// libraries. For consistency we require that both major.minor version be the
// same unless either is unreleased (i.e. test). Issue warning if need be.
//
mTxt = (vInfo1.vNum == XrdVNUMUNK ? "unreleased " : "");
sprintf(v1buff, " %sversion %s", mTxt, vInfo1.vStr);
unRel = *mTxt;
mTxt = (vInfo2.vNum == XrdVNUMUNK ? "unreleased " : "");
sprintf(v2buff, " %sversion %s", mTxt, vInfo2.vStr);
unRel |= *mTxt;
if (unRel || vInfo1.vNum/100 == vInfo2.vNum/100) mTxt = "";
else mTxt = " which is incompatible!";
if (!noMsg)
cerr <<"Plugin: " <