xref: /petsc/src/sys/dll/dl.c (revision 009bbdc485cd9ad46be9940d3549e2dde9cdc322)
1 /*
2       Routines for opening dynamic link libraries (DLLs), keeping a searchable
3    path of DLLs, obtaining remote DLLs via a URL and opening them locally.
4 */
5 
6 #include <petscsys.h>
7 
8 /* ------------------------------------------------------------------------------*/
9 /*
10       Code to maintain a list of opened dynamic libraries and load symbols
11 */
12 struct _n_PetscDLLibrary {
13   PetscDLLibrary next;
14   PetscDLHandle  handle;
15   char           libname[PETSC_MAX_PATH_LEN];
16 };
17 
18 #undef __FUNCT__
19 #define __FUNCT__ "PetscDLLibraryPrintPath"
20 PetscErrorCode  PetscDLLibraryPrintPath(PetscDLLibrary libs)
21 {
22   PetscFunctionBegin;
23   while (libs) {
24     PetscErrorPrintf("  %s\n",libs->libname);
25     libs = libs->next;
26   }
27   PetscFunctionReturn(0);
28 }
29 
30 #undef __FUNCT__
31 #define __FUNCT__ "PetscDLLibraryRetrieve"
32 /*@C
33    PetscDLLibraryRetrieve - Copies a PETSc dynamic library from a remote location
34      (if it is remote), indicates if it exits and its local name.
35 
36      Collective on MPI_Comm
37 
38    Input Parameters:
39 +   comm - processors that are opening the library
40 -   libname - name of the library, can be relative or absolute
41 
42    Output Parameter:
43 +   name - actual name of file on local filesystem if found
44 .   llen - length of the name buffer
45 -   found - true if the file exists
46 
47    Level: developer
48 
49    Notes:
50    [[<http,ftp>://hostname]/directoryname/]filename[.so.1.0]
51 
52    ${PETSC_ARCH}, ${PETSC_DIR}, ${PETSC_LIB_DIR}, or ${any environmental variable}
53    occuring in directoryname and filename will be replaced with appropriate values.
54 @*/
55 PetscErrorCode  PetscDLLibraryRetrieve(MPI_Comm comm,const char libname[],char *lname,size_t llen,PetscBool  *found)
56 {
57   char           *buf,*par2,suffix[16],*gz,*so;
58   size_t         len;
59   PetscErrorCode ierr;
60 
61   PetscFunctionBegin;
62   /*
63      make copy of library name and replace $PETSC_ARCH etc
64      so we can add to the end of it to look for something like .so.1.0 etc.
65   */
66   ierr = PetscStrlen(libname,&len);CHKERRQ(ierr);
67   len  = PetscMax(4*len,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
68   ierr = PetscMalloc(len*sizeof(char),&buf);CHKERRQ(ierr);
69   par2 = buf;
70   ierr = PetscStrreplace(comm,libname,par2,len);CHKERRQ(ierr);
71 
72   /* temporarily remove .gz if it ends library name */
73   ierr = PetscStrrstr(par2,".gz",&gz);CHKERRQ(ierr);
74   if (gz) {
75     ierr = PetscStrlen(gz,&len);CHKERRQ(ierr);
76     if (len != 3) gz  = 0; /* do not end (exactly) with .gz */
77     else          *gz = 0; /* ends with .gz, so remove it   */
78   }
79   /* strip out .a from it if user put it in by mistake */
80   ierr = PetscStrlen(par2,&len);CHKERRQ(ierr);
81   if (par2[len-1] == 'a' && par2[len-2] == '.') par2[len-2] = 0;
82 
83   ierr = PetscFileRetrieve(comm,par2,lname,llen,found);CHKERRQ(ierr);
84   if (!(*found)) {
85     /* see if library name does already not have suffix attached */
86     ierr = PetscStrcpy(suffix,".");CHKERRQ(ierr);
87     ierr = PetscStrcat(suffix,PETSC_SLSUFFIX);CHKERRQ(ierr);
88     ierr = PetscStrrstr(par2,suffix,&so);CHKERRQ(ierr);
89     /* and attach the suffix if it is not there */
90     if (!so) { ierr = PetscStrcat(par2,suffix);CHKERRQ(ierr); }
91 
92     /* restore the .gz suffix if it was there */
93     if (gz) { ierr = PetscStrcat(par2,".gz");CHKERRQ(ierr); }
94 
95     /* and finally retrieve the file */
96     ierr = PetscFileRetrieve(comm,par2,lname,llen,found);CHKERRQ(ierr);
97   }
98 
99   ierr = PetscFree(buf);CHKERRQ(ierr);
100   PetscFunctionReturn(0);
101 }
102 
103 
104 #undef __FUNCT__
105 #define __FUNCT__ "PetscDLLibraryOpen"
106 /*@C
107    PetscDLLibraryOpen - Opens a PETSc dynamic link library
108 
109      Collective on MPI_Comm
110 
111    Input Parameters:
112 +   comm - processors that are opening the library
113 -   path - name of the library, can be relative or absolute
114 
115    Output Parameter:
116 .   entry - a PETSc dynamic link library entry
117 
118    Level: developer
119 
120    Notes:
121    [[<http,ftp>://hostname]/directoryname/]libbasename[.so.1.0]
122 
123    If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
124    when the library is opened.
125 
126    ${PETSC_ARCH} occuring in directoryname and filename
127    will be replaced with the appropriate value.
128 
129 .seealso: PetscLoadDynamicLibrary(), PetscDLLibraryAppend()
130 @*/
131 PetscErrorCode  PetscDLLibraryOpen(MPI_Comm comm,const char path[],PetscDLLibrary *entry)
132 {
133   PetscErrorCode ierr;
134   PetscBool      foundlibrary,match;
135   char           libname[PETSC_MAX_PATH_LEN],par2[PETSC_MAX_PATH_LEN],suffix[16],*s;
136   char           *basename,registername[128];
137   PetscDLHandle  handle;
138   PetscErrorCode (*func)(const char*) = NULL;
139   size_t         len;
140 
141   PetscFunctionBegin;
142   PetscValidCharPointer(path,2);
143   PetscValidPointer(entry,3);
144 
145   *entry = PETSC_NULL;
146 
147   /* retrieve the library */
148   ierr = PetscInfo1(0,"Retrieving %s\n",path);CHKERRQ(ierr);
149   ierr = PetscDLLibraryRetrieve(comm,path,par2,PETSC_MAX_PATH_LEN,&foundlibrary);CHKERRQ(ierr);
150   if (!foundlibrary) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate dynamic library:\n  %s\n",path);
151   /* Eventually ./configure should determine if the system needs an executable dynamic library */
152 #define PETSC_USE_NONEXECUTABLE_SO
153 #if !defined(PETSC_USE_NONEXECUTABLE_SO)
154   ierr  = PetscTestFile(par2,'x',&foundlibrary);CHKERRQ(ierr);
155   if (!foundlibrary) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Dynamic library is not executable:\n  %s\n  %s\n",path,par2);
156 #endif
157 
158   /* copy path and setup shared library suffix  */
159   ierr = PetscStrncpy(libname,path,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
160   ierr = PetscStrcpy(suffix,".");CHKERRQ(ierr);
161   ierr = PetscStrcat(suffix,PETSC_SLSUFFIX);CHKERRQ(ierr);
162   /* remove wrong suffixes from libname */
163   ierr = PetscStrrstr(libname,".gz",&s);CHKERRQ(ierr);
164   if (s && s[3] == 0) s[0] = 0;
165   ierr = PetscStrrstr(libname,".a",&s);CHKERRQ(ierr);
166   if (s && s[2] == 0) s[0] = 0;
167   /* remove shared suffix from libname */
168   ierr = PetscStrrstr(libname,suffix,&s);CHKERRQ(ierr);
169   if (s) s[0] = 0;
170 
171   /* open the dynamic library */
172   ierr = PetscInfo1(0,"Opening dynamic library %s\n",libname);CHKERRQ(ierr);
173   ierr = PetscDLOpen(par2,PETSC_DL_DECIDE,&handle);CHKERRQ(ierr);
174 
175   /* look for [path/]libXXXXX.YYY and extract out the XXXXXX */
176   ierr = PetscStrrchr(libname,'/',&basename);CHKERRQ(ierr); /* XXX Windows ??? */
177   if (!basename) basename = libname;
178   ierr = PetscStrncmp(basename,"lib",3,&match);CHKERRQ(ierr);
179   if (match) {
180     basename = basename + 3;
181   } else {
182     ierr = PetscInfo1(0,"Dynamic library %s does not have lib prefix\n",libname);CHKERRQ(ierr);
183   }
184   ierr = PetscStrlen(basename,&len);CHKERRQ(ierr);
185   ierr = PetscStrcpy(registername,"PetscDLLibraryRegister_");CHKERRQ(ierr);
186   ierr = PetscStrncat(registername,basename,len);CHKERRQ(ierr);
187   ierr = PetscDLSym(handle,registername,(void**)&func);CHKERRQ(ierr);
188   if (func) {
189     ierr = PetscInfo1(0,"Loading registered routines from %s\n",libname);CHKERRQ(ierr);
190     ierr = (*func)(libname);CHKERRQ(ierr);
191   } else {
192     ierr = PetscInfo2(0,"Dynamic library %s does not have symbol %s\n",libname,registername);CHKERRQ(ierr);
193   }
194 
195   ierr = PetscNew(struct _n_PetscDLLibrary,entry);CHKERRQ(ierr);
196   (*entry)->next   = 0;
197   (*entry)->handle = handle;
198   ierr = PetscStrcpy((*entry)->libname,libname);CHKERRQ(ierr);
199   PetscFunctionReturn(0);
200 }
201 
202 #undef __FUNCT__
203 #define __FUNCT__ "PetscDLLibrarySym"
204 /*@C
205    PetscDLLibrarySym - Load a symbol from the dynamic link libraries.
206 
207    Collective on MPI_Comm
208 
209    Input Parameter:
210 +  comm - communicator that will open the library
211 .  outlist - list of already open libraries that may contain symbol (can be PETSC_NULL and only the executable is searched for the function)
212 .  path     - optional complete library name (if provided checks here before checking outlist)
213 -  insymbol - name of symbol
214 
215    Output Parameter:
216 .  value - if symbol not found then this value is set to PETSC_NULL
217 
218    Level: developer
219 
220    Notes: Symbol can be of the form
221         [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional
222 
223         Will attempt to (retrieve and) open the library if it is not yet been opened.
224 
225 @*/
226 PetscErrorCode  PetscDLLibrarySym(MPI_Comm comm,PetscDLLibrary *outlist,const char path[],const char insymbol[],void **value)
227 {
228   char           libname[PETSC_MAX_PATH_LEN],suffix[16],*symbol,*s;
229   PetscDLLibrary nlist,prev,list = PETSC_NULL;
230   PetscErrorCode ierr;
231 
232   PetscFunctionBegin;
233   if (outlist) PetscValidPointer(outlist,2);
234   if (path) PetscValidCharPointer(path,3);
235   PetscValidCharPointer(insymbol,4);
236   PetscValidPointer(value,5);
237 
238   if (outlist) list   = *outlist;
239   *value = 0;
240 
241 
242   ierr = PetscStrchr(insymbol,'(',&s);CHKERRQ(ierr);
243   if (s) {
244     /* make copy of symbol so we can edit it in place */
245     ierr = PetscStrallocpy(insymbol,&symbol);CHKERRQ(ierr);
246     /* If symbol contains () then replace with a NULL, to support functionname() */
247     ierr = PetscStrchr(symbol,'(',&s);CHKERRQ(ierr);
248     s[0] = 0;
249   } else {
250     symbol = (char *)insymbol;
251   }
252 
253   /*
254        Function name does include library
255        -------------------------------------
256   */
257   if (path && path[0] != '\0') {
258     /* copy path and remove suffix from libname */
259     ierr = PetscStrncpy(libname,path,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
260     ierr = PetscStrcpy(suffix,".");CHKERRQ(ierr);
261     ierr = PetscStrcat(suffix,PETSC_SLSUFFIX);CHKERRQ(ierr);
262     ierr = PetscStrrstr(libname,suffix,&s);CHKERRQ(ierr);
263     if (s) s[0] = 0;
264     /* Look if library is already opened and in path */
265     prev  = 0;
266     nlist = list;
267     while (nlist) {
268       PetscBool  match;
269       ierr = PetscStrcmp(nlist->libname,libname,&match);CHKERRQ(ierr);
270       if (match) goto done;
271       prev  = nlist;
272       nlist = nlist->next;
273     }
274     /* open the library and append it to path */
275     ierr = PetscDLLibraryOpen(comm,path,&nlist);CHKERRQ(ierr);
276     ierr = PetscInfo1(0,"Appending %s to dynamic library search path\n",path);CHKERRQ(ierr);
277     if (prev) { prev->next = nlist; }
278     else      { *outlist   = nlist; }
279 
280   done:;
281     ierr = PetscDLSym(nlist->handle,symbol,value);CHKERRQ(ierr);
282     if (*value) {
283       ierr = PetscInfo2(0,"Loading function %s from dynamic library %s\n",insymbol,path);CHKERRQ(ierr);
284     }
285 
286   /*
287        Function name does not include library so search path
288        -----------------------------------------------------
289   */
290   } else {
291     while (list) {
292       ierr = PetscDLSym(list->handle,symbol,value);CHKERRQ(ierr);
293       if (*value) {
294         ierr = PetscInfo2(0,"Loading symbol %s from dynamic library %s\n",symbol,list->libname);CHKERRQ(ierr);
295         break;
296       }
297       list = list->next;
298     }
299     if (!*value) {
300       ierr = PetscDLSym(PETSC_NULL,symbol,value);CHKERRQ(ierr);
301       if (*value) {
302         ierr = PetscInfo1(0,"Loading symbol %s from object code\n",symbol);CHKERRQ(ierr);
303       }
304     }
305   }
306 
307   if (symbol != insymbol) {
308     ierr = PetscFree(symbol);CHKERRQ(ierr);
309   }
310   PetscFunctionReturn(0);
311 }
312 
313 #undef __FUNCT__
314 #define __FUNCT__ "PetscDLLibraryAppend"
315 /*@C
316      PetscDLLibraryAppend - Appends another dynamic link library to the seach list, to the end
317                 of the search path.
318 
319      Collective on MPI_Comm
320 
321      Input Parameters:
322 +     comm - MPI communicator
323 -     path - name of the library
324 
325      Output Parameter:
326 .     outlist - list of libraries
327 
328      Level: developer
329 
330      Notes: if library is already in path will not add it.
331 
332   If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
333       when the library is opened.
334 
335 .seealso: PetscDLLibraryOpen()
336 @*/
337 PetscErrorCode  PetscDLLibraryAppend(MPI_Comm comm,PetscDLLibrary *outlist,const char path[])
338 {
339   PetscDLLibrary list,prev;
340   PetscErrorCode ierr;
341   size_t         len;
342   PetscBool      match,dir;
343   char           program[PETSC_MAX_PATH_LEN],found[8*PETSC_MAX_PATH_LEN];
344   char           *libname,suffix[16],*s;
345   PetscToken     token;
346 
347   PetscFunctionBegin;
348   PetscValidPointer(outlist,2);
349 
350   /* is path a directory? */
351   ierr = PetscTestDirectory(path,'r',&dir);CHKERRQ(ierr);
352   if (dir) {
353     ierr = PetscInfo1(0,"Checking directory %s for dynamic libraries\n",path);CHKERRQ(ierr);
354     ierr  = PetscStrcpy(program,path);CHKERRQ(ierr);
355     ierr  = PetscStrlen(program,&len);CHKERRQ(ierr);
356     if (program[len-1] == '/') {
357       ierr  = PetscStrcat(program,"*.");CHKERRQ(ierr);
358     } else {
359       ierr  = PetscStrcat(program,"/*.");CHKERRQ(ierr);
360     }
361     ierr  = PetscStrcat(program,PETSC_SLSUFFIX);CHKERRQ(ierr);
362 
363     ierr = PetscLs(comm,program,found,8*PETSC_MAX_PATH_LEN,&dir);CHKERRQ(ierr);
364     if (!dir) PetscFunctionReturn(0);
365   } else {
366     ierr = PetscStrncpy(found,path,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
367   }
368   ierr = PetscStrcpy(suffix,".");CHKERRQ(ierr);
369   ierr = PetscStrcat(suffix,PETSC_SLSUFFIX);CHKERRQ(ierr);
370 
371   ierr = PetscTokenCreate(found,'\n',&token);CHKERRQ(ierr);
372   ierr = PetscTokenFind(token,&libname);CHKERRQ(ierr);
373   while (libname) {
374     /* remove suffix from libname */
375     ierr = PetscStrrstr(libname,suffix,&s);CHKERRQ(ierr);
376     if (s) s[0] = 0;
377     /* see if library was already open then we are done */
378     list  = prev = *outlist;
379     match = PETSC_FALSE;
380     while (list) {
381       ierr = PetscStrcmp(list->libname,libname,&match);CHKERRQ(ierr);
382       if (match) break;
383       prev = list;
384       list = list->next;
385     }
386     /* restore suffix from libname */
387     if (s) s[0] = '.';
388     if (!match) {
389       /* open the library and add to end of list */
390       ierr = PetscDLLibraryOpen(comm,libname,&list);CHKERRQ(ierr);
391       ierr = PetscInfo1(0,"Appending %s to dynamic library search path\n",libname);CHKERRQ(ierr);
392       if (!*outlist) {
393         *outlist   = list;
394       } else {
395         prev->next = list;
396       }
397     }
398     ierr = PetscTokenFind(token,&libname);CHKERRQ(ierr);
399   }
400   ierr = PetscTokenDestroy(&token);CHKERRQ(ierr);
401   PetscFunctionReturn(0);
402 }
403 
404 #undef __FUNCT__
405 #define __FUNCT__ "PetscDLLibraryPrepend"
406 /*@C
407      PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of
408                  the search path.
409 
410      Collective on MPI_Comm
411 
412      Input Parameters:
413 +     comm - MPI communicator
414 -     path - name of the library
415 
416      Output Parameter:
417 .     outlist - list of libraries
418 
419      Level: developer
420 
421      Notes: If library is already in path will remove old reference.
422 
423 @*/
424 PetscErrorCode  PetscDLLibraryPrepend(MPI_Comm comm,PetscDLLibrary *outlist,const char path[])
425 {
426   PetscDLLibrary list,prev;
427   PetscErrorCode ierr;
428   size_t         len;
429   PetscBool      match,dir;
430   char           program[PETSC_MAX_PATH_LEN],found[8*PETSC_MAX_PATH_LEN];
431   char           *libname,suffix[16],*s;
432   PetscToken     token;
433 
434   PetscFunctionBegin;
435   PetscValidPointer(outlist,2);
436 
437   /* is path a directory? */
438   ierr = PetscTestDirectory(path,'r',&dir);CHKERRQ(ierr);
439   if (dir) {
440     ierr = PetscInfo1(0,"Checking directory %s for dynamic libraries\n",path);CHKERRQ(ierr);
441     ierr  = PetscStrcpy(program,path);CHKERRQ(ierr);
442     ierr  = PetscStrlen(program,&len);CHKERRQ(ierr);
443     if (program[len-1] == '/') {
444       ierr  = PetscStrcat(program,"*.");CHKERRQ(ierr);
445     } else {
446       ierr  = PetscStrcat(program,"/*.");CHKERRQ(ierr);
447     }
448     ierr  = PetscStrcat(program,PETSC_SLSUFFIX);CHKERRQ(ierr);
449 
450     ierr = PetscLs(comm,program,found,8*PETSC_MAX_PATH_LEN,&dir);CHKERRQ(ierr);
451     if (!dir) PetscFunctionReturn(0);
452   } else {
453     ierr = PetscStrncpy(found,path,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
454   }
455 
456   ierr = PetscStrcpy(suffix,".");CHKERRQ(ierr);
457   ierr = PetscStrcat(suffix,PETSC_SLSUFFIX);CHKERRQ(ierr);
458 
459   ierr = PetscTokenCreate(found,'\n',&token);CHKERRQ(ierr);
460   ierr = PetscTokenFind(token,&libname);CHKERRQ(ierr);
461   while (libname) {
462     /* remove suffix from libname */
463     ierr = PetscStrstr(libname,suffix,&s);CHKERRQ(ierr);
464     if (s) s[0] = 0;
465     /* see if library was already open and move it to the front */
466     prev  = 0;
467     list  = *outlist;
468     match = PETSC_FALSE;
469     while (list) {
470       ierr = PetscStrcmp(list->libname,libname,&match);CHKERRQ(ierr);
471       if (match) {
472         ierr = PetscInfo1(0,"Moving %s to begin of dynamic library search path\n",libname);CHKERRQ(ierr);
473         if (prev) prev->next = list->next;
474         if (prev) list->next = *outlist;
475         *outlist = list;
476         break;
477       }
478       prev = list;
479       list = list->next;
480     }
481     /* restore suffix from libname */
482     if (s) s[0] = '.';
483     if (!match) {
484       /* open the library and add to front of list */
485       ierr = PetscDLLibraryOpen(comm,libname,&list);CHKERRQ(ierr);
486       ierr = PetscInfo1(0,"Prepending %s to dynamic library search path\n",libname);CHKERRQ(ierr);
487       list->next = *outlist;
488       *outlist   = list;
489     }
490     ierr = PetscTokenFind(token,&libname);CHKERRQ(ierr);
491   }
492   ierr = PetscTokenDestroy(&token);CHKERRQ(ierr);
493   PetscFunctionReturn(0);
494 }
495 
496 #undef __FUNCT__
497 #define __FUNCT__ "PetscDLLibraryClose"
498 /*@C
499      PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries.
500 
501     Collective on PetscDLLibrary
502 
503     Input Parameter:
504 .     head - library list
505 
506      Level: developer
507 
508 @*/
509 PetscErrorCode  PetscDLLibraryClose(PetscDLLibrary list)
510 {
511   PetscBool      done = PETSC_FALSE;
512   PetscDLLibrary prev,tail;
513   PetscErrorCode ierr;
514 
515   PetscFunctionBegin;
516   if (!list) PetscFunctionReturn(0);
517   /* traverse the list in reverse order */
518   while (!done) {
519     if (!list->next) done = PETSC_TRUE;
520     prev = tail = list;
521     while (tail->next) {
522       prev = tail;
523       tail = tail->next;
524     }
525     prev->next = 0;
526     /* close the dynamic library and free the space in entry data-structure*/
527     ierr = PetscInfo1(0,"Closing dynamic library %s\n",tail->libname);CHKERRQ(ierr);
528     ierr = PetscDLClose(&tail->handle);CHKERRQ(ierr);
529     ierr = PetscFree(tail);CHKERRQ(ierr);
530   };
531   PetscFunctionReturn(0);
532 }
533 
534