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