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