XRootD
Loading...
Searching...
No Matches
XrdSysPlugin.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d S y s P l u g i n . c c */
4/* */
5/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* All Rights Reserved */
7/* Produced by Andrew Hanushevsky for Stanford University under contract */
8/* DE-AC02-76-SFO0515 with the Department of Energy */
9/* */
10/* This file is part of the XRootD software suite. */
11/* */
12/* XRootD is free software: you can redistribute it and/or modify it under */
13/* the terms of the GNU Lesser General Public License as published by the */
14/* Free Software Foundation, either version 3 of the License, or (at your */
15/* option) any later version. */
16/* */
17/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20/* License for more details. */
21/* */
22/* You should have received a copy of the GNU Lesser General Public License */
23/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25/* */
26/* The copyright holder's institutional names and contributor's names may not */
27/* be used to endorse or promote products derived from this software without */
28/* specific prior written permission of the institution or contributor. */
29/******************************************************************************/
30
31// Bypass Solaris ELF madness
32//
33#ifdef __solaris__
34#include <sys/isa_defs.h>
35#if defined(_ILP32) && (_FILE_OFFSET_BITS != 32)
36#undef _FILE_OFFSET_BITS
37#define _FILE_OFFSET_BITS 32
38#undef _LARGEFILE_SOURCE
39#endif
40#endif
41
42#ifndef WIN32
43#include <dlfcn.h>
44#if !defined(__APPLE__) && !defined(__CYGWIN__)
45#include <link.h>
46#endif
47#include <cstdio>
48#include <strings.h>
49#include <sys/types.h>
50#include <cerrno>
51#else
52#include "XrdSys/XrdWin32.hh"
53#endif
54
55#include "XrdSys/XrdSysError.hh"
59#include "XrdVersion.hh"
60#include "XrdVersionPlugin.hh"
61
62/******************************************************************************/
63/* S t a t i c M e m b e r s */
64/******************************************************************************/
65
66struct XrdSysPlugin::PLlist *XrdSysPlugin::plList = 0;
67
68/******************************************************************************/
69/* D e s t r u c t o r */
70/******************************************************************************/
71
73{
74 if (libHandle) dlclose(libHandle);
75 if (libPath) free(libPath);
76}
77
78/******************************************************************************/
79/* Private: b a d V e r s i o n */
80/******************************************************************************/
81
82XrdSysPlugin::cvResult XrdSysPlugin::badVersion(XrdVersionInfo &urInfo,
83 char mmv, int majv, int minv)
84{
85 const char *path;
86 char buff1[512], buff2[128];
87
88 if (minv < 0) strcpy(buff2, "y");
89 else sprintf(buff2, "%d", minv);
90
91 snprintf(buff1, sizeof(buff1), "version %s is incompatible with %s "
92 "(must be %c= %d.%s.x)",
93 myInfo->vStr, urInfo.vStr, mmv, majv, buff2);
94
95 path = msgSuffix(" in ", buff2, sizeof(buff2));
96
97 Inform(buff1, buff2, path, 0, 0, 1);
98
99 return cvBad;
100}
101
102/******************************************************************************/
103/* Private: c h k V e r s i o n */
104/******************************************************************************/
105
106XrdSysPlugin::cvResult XrdSysPlugin::chkVersion(XrdVersionInfo &urInfo,
107 const char *pname,
108 void *lHandle)
109{
110 static XrdVersionPlugin vInfo[] = {XrdVERSIONPLUGINRULES};
111 static XrdVersionPlugin vNote[] = {XrdVERSIONPLUGINMAXIMS};
112 XrdVersionPlugin *vinP;
113 char buff[1024], vName[256];
114 void *vP;
115 int i, n=0, pMajor, vMajor, pMinor, vMinor;
116
117// If no version information supplied, skip version check
118//
119 if (!myInfo) return cvNone;
120
121// Check if we need to check the version here
122//
123 i = 0;
124 while(vInfo[i].pName && strcmp(vInfo[i].pName, pname)) i++;
125
126// If we didn't find it in the rules table then try to match the maxims
127//
128 if (!vInfo[i].pName)
129 {i = 0; n = strlen(pname);
130 while(vNote[i].pName)
131 {if ((vNote[i].vPfxLen + vNote[i].vSfxLen <= n)
132 && !strncmp(vNote[i].pName, pname, vNote[i].vPfxLen)
133 && !strncmp(vNote[i].pName+vNote[i].vPfxLen,
134 pname + n - vNote[i].vSfxLen, vNote[i].vSfxLen)) break;
135 i++;
136 }
137 vinP = &vNote[i];
138 } else vinP = &vInfo[i];
139
140 if (!(vinP->pName)) return cvNone;
141 if ( vinP->vProcess == XrdVERSIONPLUGIN_DoNotChk) return cvDirty;
142
143// Construct the version entry point
144//
145 if (!n) n = strlen(pname);
146 if (n+sizeof(XrdVERSIONINFOSFX) > sizeof(vName))
147 return libMsg("Unable to generate version name for", "%s in ", pname);
148 strcpy(vName, pname); strcpy(vName+n, XrdVERSIONINFOSFX);
149
150// Find the version number
151//
152 if (!(vP = dlsym(lHandle, vName)))
153 {if (vinP->vProcess != XrdVERSIONPLUGIN_Required) return cvMissing;
154 return libMsg(dlerror()," required version information for %s in ",pname);
155 }
156
157// Extract the version number from the plugin and do a quick check. We use
158// memcpy to avoid instances where the symbol is wrongly defined. Make sure
159// the version string ends with a null by copying one less byte than need be.
160// The caller provided a struct that is gauranteed to end with nulls.
161//
162 memcpy(static_cast<void*>( &urInfo ), vP, sizeof(XrdVersionInfo)-1);
163
164// If version numbers are identical then we are done
165//
166 if (myInfo->vNum == urInfo.vNum)
167 if (myInfo->vNum != XrdVNUMUNK
168 || !strcmp(myInfo->vStr + (myInfo->vOpt & 0x0f)+1,
169 urInfo. vStr + (urInfo. vOpt & 0x0f)+1)) return cvClean;
170
171// If the caller or plugin is unreleased, just issue a warning.
172//
173 if (myInfo->vNum == XrdVNUMUNK || urInfo.vNum == XrdVNUMUNK)
174 {if (eDest)
175 {char mBuff[128];
176 snprintf(buff, sizeof(buff), "%s%s is using %s%s version",
177 (myInfo->vNum == XrdVNUMUNK ? "unreleased ":""),myInfo->vStr,
178 (urInfo.vNum == XrdVNUMUNK ? "unreleased ":""),urInfo.vStr);
179 msgSuffix(" in ", mBuff, sizeof(mBuff));
180 Inform(buff, mBuff, libPath);
181 }
182 return cvDirty;
183 }
184
185// Extract version numbers
186//
187 vMajor = XrdMajorVNUM(myInfo->vNum);
188 vMinor = XrdMinorVNUM(myInfo->vNum);
189 pMajor = XrdMajorVNUM(urInfo. vNum);
190 pMinor = XrdMinorVNUM(urInfo. vNum);
191
192// The major version must always be compatible
193//
194 if (vinP->vMajLow >= 0 && pMajor < vinP->vMajLow)
195 return badVersion(urInfo, '>', vinP->vMajLow, vinP->vMinLow);
196
197 if (vinP->vMajLow < 0 && pMajor != vMajor)
198 return badVersion(urInfo, '=', vMajor, -1);
199
200// The plugin version may not be greater than our version)
201//
202 if (pMajor > vMajor || (pMajor == vMajor && pMinor > vMinor))
203 return badVersion(urInfo, '<', vMajor, vMinor);
204
205// If we do not need to check minor versions then we are done
206//
207 if (vinP->vMinLow < 0) return cvClean;
208
209// Verify compatible minor versions
210//
211 if (pMajor == vinP->vMajLow && pMinor < vinP->vMinLow)
212 return badVersion(urInfo, '>', vinP->vMajLow, vinP->vMinLow);
213
214// Compatible versions
215//
216 return cvClean;
217}
218
219/******************************************************************************/
220/* Private: D L F l a g s */
221/******************************************************************************/
222
223int XrdSysPlugin::DLflags()
224{
225#if defined(__APPLE__)
226 return RTLD_FIRST;
227#elif defined(__linux__)
228 return RTLD_NOW;
229#else
230 return RTLD_NOW;
231#endif
232}
233
234/******************************************************************************/
235/* Private: F i n d */
236/******************************************************************************/
237
238void *XrdSysPlugin::Find(const char *libpath)
239{
240 struct PLlist *plP = plList;
241
242// Find the library in the preload list
243//
244 while(plP && strcmp(libpath, plP->libPath)) plP = plP->next;
245
246// Return result
247//
248 return (plP ? plP->libHandle : 0);
249}
250
251/******************************************************************************/
252/* g e t L i b r a r y */
253/******************************************************************************/
254
255void *XrdSysPlugin::getLibrary(bool allMsgs, bool global)
256{
257 void *myHandle;
258 int flags;
259
260// Check if we should use the preload list
261//
262 if (!(myHandle = libHandle) && plList) myHandle = Find(libPath);
263
264// If already open, return the handle
265//
266 if (myHandle) return myHandle;
267
268// If no path is given then we want to just search the executable. This is easy
269// for some platforms and more difficult for others. So, we do the best we can.
270//
271 if (libPath) flags = DLflags();
272 else { flags = RTLD_NOW;
273#ifndef WIN32
274 flags|= global ? RTLD_GLOBAL : RTLD_LOCAL;
275#else
276 if (global && eDest) eDest->Emsg("getPlugin",
277 "request for global symbols unsupported under Windows - ignored");
278#endif
279 }
280
281// Try to open this library or the executable image
282//
283 if ((myHandle = dlopen(libPath, flags))) libHandle = myHandle;
284 else {const char *eTxt = dlerror();
285 if (strcasestr(eTxt, "no such file")) errno = ENOENT;
286 else errno = ENOEXEC;
287 if (allMsgs || errno != ENOENT) libMsg(eTxt, " loading ");
288 }
289
290// All done
291//
292 return myHandle;
293}
294
295/******************************************************************************/
296/* g e t P l u g i n */
297/******************************************************************************/
298
299void *XrdSysPlugin::getPlugin(const char *pname, int optional)
300{
301 return getPlugin(pname, optional, false);
302}
303
304void *XrdSysPlugin::getPlugin(const char *pname, int optional, bool global)
305{
306 XrdVERSIONINFODEF(urInfo, unknown, XrdVNUMUNK, "");
307 void *ep, *myHandle;
308 cvResult cvRC;
309
310// Open whatever it is we need to open
311//
312 if (!(myHandle = getLibrary(optional < 2, global))) return 0;
313
314// Get the symbol. In the environment we have defined, null values are not
315// allowed and we will issue an error.
316//
317 if (!(ep = dlsym(myHandle, pname)))
318 {if (optional < 2) libMsg(dlerror(), " symbol %s in ", pname);
319 return 0;
320 }
321
322// Check if we need to verify version compatibility
323//
324 if ((cvRC = chkVersion(urInfo, pname, myHandle)) == cvBad) return 0;
325
326// Print the loaded version unless message is suppressed or not needed
327//
328 if (libPath && optional < 2 && msgCnt
329 && (cvRC == cvClean || cvRC == cvMissing))
330 {char buff[128];
331 msgSuffix(" from ", buff, sizeof(buff));
332 msgCnt--;
333 if (cvRC == cvClean)
334 {const char *wTxt=(urInfo.vNum == XrdVNUMUNK ? "unreleased ":"");
335 Inform("loaded ", wTxt, urInfo.vStr, buff, libPath);
336 }
337 else if (cvRC == cvMissing)
338 {Inform("loaded unversioned ", pname, buff, libPath);}
339 }
340
341// All done
342//
343 return ep;
344}
345
346/******************************************************************************/
347/* Private: I n f o r m */
348/******************************************************************************/
349
350void XrdSysPlugin::Inform(const char *txt1, const char *txt2, const char *txt3,
351 const char *txt4, const char *txt5, int noHush)
352{
353 const char *eTxt[] = {"Plugin ",txt1, txt2, txt3, txt4, txt5, 0};
354 char *bP;
355 int n, i, bL;
356
357// Check if we should hush this messages (largely for client-side usage)
358//
359 if (!noHush && getenv("XRDPIHUSH")) return;
360
361// If we have a messaging object, use that
362//
363 if (eDest)
364 {char buff[2048];
365 i = 1; bP = buff; bL = sizeof(buff);
366 while(bL > 1 && eTxt[i])
367 {n = snprintf(bP, bL, "%s", eTxt[i]);
368 bP += n; bL -= n; i++;
369 }
370 eDest->Say("Plugin ", buff);
371 return;
372 }
373
374// If we have a buffer, set message in the buffer
375//
376 if ((bP = eBuff))
377 {i = 0; bL = eBLen;
378 while(bL > 1 && eTxt[i])
379 {n = snprintf(bP, bL, "%s", eTxt[i]);
380 bP += n; bL -= n; i++;
381 }
382 }
383}
384
385/******************************************************************************/
386/* Private: l i b M s g */
387/******************************************************************************/
388
389XrdSysPlugin::cvResult XrdSysPlugin::libMsg(const char *txt1, const char *txt2,
390 const char *mSym)
391{
392 static const char fndg[] = "Finding";
393 static const int flen = sizeof("Finding");
394 const char *path;
395 char mBuff[512], nBuff[512];
396
397// Check if this is a lookup or open issue. Trim message for the common case.
398//
399 if (mSym)
400 {if (!txt1 || strstr(txt1, "undefined"))
401 {txt1 = "Unable to find";
402 snprintf(nBuff, sizeof(nBuff), txt2, mSym);
403 } else {
404 strcpy(nBuff, fndg);
405 snprintf(nBuff+flen-1,sizeof(nBuff)-flen,txt2,mSym);
406 }
407 txt2 = nBuff;
408 }
409 else if (!txt1) txt1 = "Unknown system error!";
410 else if (strstr(txt1, "No such file")) txt1 = "No such file or directory";
411 else txt2 = " ";
412
413// Spit out the message
414//
415 path = msgSuffix(txt2, mBuff, sizeof(mBuff));
416 Inform(txt1, mBuff, path, 0, 0, 1);
417 return cvBad;
418}
419
420/******************************************************************************/
421/* Private: m s g S u f f i x */
422/******************************************************************************/
423
424const char *XrdSysPlugin::msgSuffix(const char *Word, char *buff, int bsz)
425{
426 if (libPath) snprintf(buff, bsz,"%s%s ", Word, libName);
427 else snprintf(buff, bsz,"%sexecutable image", Word);
428 return (libPath ? libPath : "");
429}
430
431/******************************************************************************/
432/* P r e l o a d */
433/******************************************************************************/
434
435bool XrdSysPlugin::Preload(const char *path, char *ebuff, int eblen)
436{
437 struct PLlist *plP;
438 void *myHandle;
439
440// First see if this is already in the preload list
441//
442 if (Find(path)) return true;
443
444// Try to open the library
445//
446 if (!(myHandle = dlopen(path, DLflags())))
447 {if (ebuff && eblen > 0)
448 {const char *dlMsg = dlerror();
449 snprintf(ebuff, eblen, "Plugin unable to load %s; %s", path,
450 (dlMsg ? dlMsg : "unknown system error"));
451 }
452 return false;
453 }
454
455// Add the library handle
456//
457 plP = new PLlist;
458 plP->libHandle = myHandle;
459 plP->libPath = strdup(path);
460 plP->next = plList;
461 plList = plP;
462
463// All done
464//
465 return true;
466}
467
468/******************************************************************************/
469/* V e r C m p */
470/******************************************************************************/
471
472bool XrdSysPlugin::VerCmp(XrdVersionInfo &vInfo1,
473 XrdVersionInfo &vInfo2, bool noMsg)
474{
475 const char *mTxt;
476 char v1buff[128], v2buff[128];
477 int unRel;
478
479// Do a quick return if the version need not be checked or are equal
480//
481 if (vInfo1.vNum <= 0 || vInfo1.vNum == vInfo2.vNum) return true;
482
483// As it works out, many times two modules wind up in different shared
484// libraries. For consistency we require that both major.minor version be the
485// same unless either is unreleased (i.e. test). Issue warning if need be.
486//
487 mTxt = (vInfo1.vNum == XrdVNUMUNK ? "unreleased " : "");
488 sprintf(v1buff, " %sversion %s", mTxt, vInfo1.vStr);
489 unRel = *mTxt;
490
491 mTxt = (vInfo2.vNum == XrdVNUMUNK ? "unreleased " : "");
492 sprintf(v2buff, " %sversion %s", mTxt, vInfo2.vStr);
493 unRel |= *mTxt;
494
495 if (unRel || vInfo1.vNum/100 == vInfo2.vNum/100) mTxt = "";
496 else mTxt = " which is incompatible!";
497
498 if (!noMsg)
499 std::cerr <<"Plugin: " <<v1buff <<" is using " <<v2buff <<mTxt <<std::endl;
500
501 return (*mTxt == 0);
502}
static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION)
#define XrdVERSIONPLUGINRULES
int vProcess
version: <0 skip, =0 optional, >0 required
#define XrdVERSIONPLUGIN_DoNotChk
const char * pName
-> plugin object creator function name
#define XrdVERSIONPLUGIN_Required
short vMajLow
Lowest compatible major version number.
short vMinLow
Lowest compatible minor (< 0 don't check).
#define XrdVERSIONPLUGINMAXIMS
void * getPlugin(const char *pname, int optional=0)
static bool Preload(const char *path, char *ebuff=0, int eblen=0)
~XrdSysPlugin()
Destructor.
void * getLibrary(bool allMsgs=true, bool global=false)
static bool VerCmp(XrdVersionInfo &vInf1, XrdVersionInfo &vInf2, bool noMsg=false)