XRootD
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"
56 #include "XrdSys/XrdSysHeaders.hh"
57 #include "XrdSys/XrdSysPlatform.hh"
58 #include "XrdSys/XrdSysPlugin.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 
66 struct 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 
82 XrdSysPlugin::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 
106 XrdSysPlugin::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 
223 int 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 
238 void *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 
255 void *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 
299 void *XrdSysPlugin::getPlugin(const char *pname, int optional)
300 {
301  return getPlugin(pname, optional, false);
302 }
303 
304 void *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 
350 void 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 
389 XrdSysPlugin::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 
424 const 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 
435 bool 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 
472 bool 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 XrdSysError eDest(0,"crypto_")
#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
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
Definition: XrdSysError.cc:141
void * getPlugin(const char *pname, int optional=0)
static bool Preload(const char *path, char *ebuff=0, int eblen=0)
~XrdSysPlugin()
Destructor.
Definition: XrdSysPlugin.cc:72
void * getLibrary(bool allMsgs=true, bool global=false)
static bool VerCmp(XrdVersionInfo &vInf1, XrdVersionInfo &vInf2, bool noMsg=false)
XrdVERSIONINFODEF(myVersion, cmsclient, XrdVNUMBER, XrdVERSION)