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