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 Code to maintain a list of opened dynamic libraries and load symbols
10 */
11 struct _n_PetscDLLibrary {
12 PetscDLLibrary next;
13 PetscDLHandle handle;
14 char libname[PETSC_MAX_PATH_LEN];
15 };
16
PetscDLLibraryPrintPath(PetscDLLibrary libs)17 PetscErrorCode PetscDLLibraryPrintPath(PetscDLLibrary libs)
18 {
19 PetscFunctionBegin;
20 while (libs) {
21 PetscCall(PetscErrorPrintf(" %s\n", libs->libname));
22 libs = libs->next;
23 }
24 PetscFunctionReturn(PETSC_SUCCESS);
25 }
26
27 /*@C
28 PetscDLLibraryRetrieve - Copies a PETSc dynamic library from a remote location
29 (if it is remote), then indicates if it exits and its local name.
30
31 Collective
32
33 Input Parameters:
34 + comm - MPI processes that will be opening the library
35 . libname - name of the library, can be a relative or absolute path and be a URL
36 - llen - length of the `name` buffer
37
38 Output Parameters:
39 + lname - actual name of the file on local filesystem if `found`
40 - found - true if the file exists
41
42 Level: developer
43
44 Notes:
45 [[<http,ftp>://hostname]/directoryname/]filename[.so.1.0]
46
47 ${PETSC_ARCH}, ${PETSC_DIR}, ${PETSC_LIB_DIR}, or ${any environmental variable}
48 occurring in directoryname and filename will be replaced with appropriate values.
49
50 .seealso: `PetscFileRetrieve()`
51 @*/
PetscDLLibraryRetrieve(MPI_Comm comm,const char libname[],char lname[],size_t llen,PetscBool * found)52 PetscErrorCode PetscDLLibraryRetrieve(MPI_Comm comm, const char libname[], char lname[], size_t llen, PetscBool *found)
53 {
54 char *buf, *par2, *gz = NULL, *so = NULL;
55 size_t len, blen;
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 PetscCall(PetscStrlen(libname, &len));
63 blen = PetscMax(4 * len, PETSC_MAX_PATH_LEN);
64 PetscCall(PetscMalloc1(blen, &buf));
65 par2 = buf;
66 PetscCall(PetscStrreplace(comm, libname, par2, blen));
67
68 /* temporarily remove .gz if it ends library name */
69 PetscCall(PetscStrrstr(par2, ".gz", &gz));
70 if (gz) {
71 PetscCall(PetscStrlen(gz, &len));
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 PetscCall(PetscStrlen(par2, &len));
77 if (par2[len - 1] == 'a' && par2[len - 2] == '.') par2[len - 2] = 0;
78
79 PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found));
80 if (!*found) {
81 const char suffix[] = "." PETSC_SLSUFFIX;
82
83 /* see if library name does already not have suffix attached */
84 PetscCall(PetscStrrstr(par2, suffix, &so));
85 /* and attach the suffix if it is not there */
86 if (!so) PetscCall(PetscStrlcat(par2, suffix, blen));
87
88 /* restore the .gz suffix if it was there */
89 if (gz) PetscCall(PetscStrlcat(par2, ".gz", blen));
90
91 /* and finally retrieve the file */
92 PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found));
93 }
94
95 PetscCall(PetscFree(buf));
96 PetscFunctionReturn(PETSC_SUCCESS);
97 }
98
99 /*@C
100 PetscDLLibraryOpen - Opens a PETSc dynamic link library
101
102 Collective, No Fortran Support
103
104 Input Parameters:
105 + comm - MPI processes that are opening the library
106 - path - name of the library, can be a relative or absolute path
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: `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`, `PetscDLLibraryRetrieve()`, `PetscDLLibrarySym()`, `PetscDLLibraryClose()`
123 @*/
PetscDLLibraryOpen(MPI_Comm comm,const char path[],PetscDLLibrary * entry)124 PetscErrorCode PetscDLLibraryOpen(MPI_Comm comm, const char path[], PetscDLLibrary *entry)
125 {
126 PetscBool foundlibrary, match;
127 const char suffix[] = "." PETSC_SLSUFFIX;
128 char libname[PETSC_MAX_PATH_LEN], par2[PETSC_MAX_PATH_LEN], *s;
129 char *basename, registername[128];
130 PetscDLHandle handle;
131 PetscErrorCode (*func)(void) = NULL;
132
133 PetscFunctionBegin;
134 PetscAssertPointer(path, 2);
135 PetscAssertPointer(entry, 3);
136
137 *entry = NULL;
138
139 /* retrieve the library */
140 PetscCall(PetscInfo(NULL, "Retrieving %s\n", path));
141 PetscCall(PetscDLLibraryRetrieve(comm, path, par2, PETSC_MAX_PATH_LEN, &foundlibrary));
142 PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate dynamic library: %s", 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 PetscCall(PetscTestFile(par2, 'x', &foundlibrary));
147 PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Dynamic library is not executable: %s %s", path, par2);
148 #endif
149
150 /* copy path and setup shared library suffix */
151 PetscCall(PetscStrncpy(libname, path, sizeof(libname)));
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(PetscStrncpy((*entry)->libname, libname, sizeof((*entry)->libname)));
187 PetscFunctionReturn(PETSC_SUCCESS);
188 }
189
190 /*@C
191 PetscDLLibrarySym - Load a symbol from a list of dynamic link libraries.
192
193 Collective, No Fortran Support
194
195 Input Parameters:
196 + comm - the MPI communicator that will load the symbol
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 it 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 It will attempt to (retrieve and) open the library if it is not yet been opened.
211
212 .seealso: `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `PetscDLLibraryClose()`
213 @*/
PetscDLLibrarySym(MPI_Comm comm,PetscDLLibrary * outlist,const char path[],const char insymbol[],void ** value)214 PetscErrorCode PetscDLLibrarySym(MPI_Comm comm, PetscDLLibrary *outlist, const char path[], const char insymbol[], void **value) PeNS
215 {
216 char libname[PETSC_MAX_PATH_LEN], suffix[16];
217 char *symbol = NULL, *s = NULL;
218 PetscDLLibrary list = NULL, nlist, prev;
219
220 PetscFunctionBegin;
221 if (outlist) PetscAssertPointer(outlist, 2);
222 if (path) PetscAssertPointer(path, 3);
223 PetscAssertPointer(insymbol, 4);
224 PetscAssertPointer(value, 5);
225
226 if (outlist) list = *outlist;
227 *value = NULL;
228
229 PetscCall(PetscStrchr(insymbol, '(', &s));
230 if (s) {
231 /* make copy of symbol so we can edit it in place */
232 PetscCall(PetscStrallocpy(insymbol, &symbol));
233 /* If symbol contains () then replace with a NULL, to support functionname() */
234 PetscCall(PetscStrchr(symbol, '(', &s));
235 s[0] = 0;
236 } else symbol = (char *)insymbol;
237
238 /*
239 Function name does include library
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 } else {
274 while (list) {
275 PetscCall(PetscDLSym(list->handle, symbol, value));
276 if (*value) {
277 PetscCall(PetscInfo(NULL, "Loading symbol %s from dynamic library %s\n", symbol, list->libname));
278 break;
279 }
280 list = list->next;
281 }
282 if (!*value) {
283 PetscCall(PetscDLSym(NULL, symbol, value));
284 if (*value) PetscCall(PetscInfo(NULL, "Loading symbol %s from object code\n", symbol));
285 }
286 }
287
288 if (symbol != insymbol) PetscCall(PetscFree(symbol));
289 PetscFunctionReturn(PETSC_SUCCESS);
290 }
291
292 /*@C
293 PetscDLLibraryAppend - Appends another dynamic link library to the end of the search list
294
295 Collective, No Fortran Support
296
297 Input Parameters:
298 + comm - MPI communicator
299 - path - name of the library
300
301 Output Parameter:
302 . outlist - list of libraries
303
304 Level: developer
305
306 Note:
307 if library is already in path will not add it.
308
309 If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
310 when the library is opened.
311
312 .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryPrepend()`
313 @*/
PetscDLLibraryAppend(MPI_Comm comm,PetscDLLibrary * outlist,const char path[])314 PetscErrorCode PetscDLLibraryAppend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[])
315 {
316 PetscDLLibrary list, prev;
317 size_t len;
318 PetscBool match, dir;
319 char program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN];
320 const char *libname;
321 char suffix[16], *s = NULL;
322 PetscToken token;
323
324 PetscFunctionBegin;
325 PetscAssertPointer(outlist, 2);
326
327 /* is path a directory? */
328 PetscCall(PetscTestDirectory(path, 'r', &dir));
329 if (dir) {
330 PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
331 PetscCall(PetscStrncpy(program, path, sizeof(program)));
332 PetscCall(PetscStrlen(program, &len));
333 if (program[len - 1] == '/') {
334 PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
335 } else {
336 PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
337 }
338 PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));
339
340 PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
341 if (!dir) PetscFunctionReturn(PETSC_SUCCESS);
342 } else {
343 PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
344 }
345 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
346 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
347
348 PetscCall(PetscTokenCreate(found, '\n', &token));
349 PetscCall(PetscTokenFind(token, &libname));
350 while (libname) {
351 /* remove suffix from libname */
352 PetscCall(PetscStrrstr(libname, suffix, &s));
353 if (s) s[0] = 0;
354 /* see if library was already open then we are done */
355 list = prev = *outlist;
356 match = PETSC_FALSE;
357 while (list) {
358 PetscCall(PetscStrcmp(list->libname, libname, &match));
359 if (match) break;
360 prev = list;
361 list = list->next;
362 }
363 /* restore suffix from libname */
364 if (s) s[0] = '.';
365 if (!match) {
366 /* open the library and add to end of list */
367 PetscCall(PetscDLLibraryOpen(comm, libname, &list));
368 PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", libname));
369 if (!*outlist) *outlist = list;
370 else prev->next = list;
371 }
372 PetscCall(PetscTokenFind(token, &libname));
373 }
374 PetscCall(PetscTokenDestroy(&token));
375 PetscFunctionReturn(PETSC_SUCCESS);
376 }
377
378 /*@C
379 PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of the search list
380
381 Collective, No Fortran Support
382
383 Input Parameters:
384 + comm - MPI communicator
385 - path - name of the library
386
387 Output Parameter:
388 . outlist - list of libraries
389
390 Level: developer
391
392 Note:
393 If library is already in the list it will remove the old reference.
394
395 .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryAppend()`
396 @*/
PetscDLLibraryPrepend(MPI_Comm comm,PetscDLLibrary * outlist,const char path[])397 PetscErrorCode PetscDLLibraryPrepend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[])
398 {
399 PetscDLLibrary list, prev;
400 size_t len;
401 PetscBool match, dir;
402 char program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN];
403 const char *libname;
404 char suffix[16], *s = NULL;
405 PetscToken token;
406
407 PetscFunctionBegin;
408 PetscAssertPointer(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, No Fortran Support
473
474 Input Parameter:
475 . list - library list
476
477 Level: developer
478
479 .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryAppend()`,
480 `PetscDLLibraryPrepend()`
481 @*/
PetscDLLibraryClose(PetscDLLibrary list)482 PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list)
483 {
484 PetscBool done = PETSC_FALSE;
485 PetscDLLibrary prev, tail;
486
487 PetscFunctionBegin;
488 if (!list) PetscFunctionReturn(PETSC_SUCCESS);
489 /* traverse the list in reverse order */
490 while (!done) {
491 if (!list->next) done = PETSC_TRUE;
492 prev = tail = list;
493 while (tail->next) {
494 prev = tail;
495 tail = tail->next;
496 }
497 prev->next = NULL;
498 /* close the dynamic library and free the space in entry data-structure*/
499 PetscCall(PetscInfo(NULL, "Closing dynamic library %s\n", tail->libname));
500 PetscCall(PetscDLClose(&tail->handle));
501 PetscCall(PetscFree(tail));
502 }
503 PetscFunctionReturn(PETSC_SUCCESS);
504 }
505