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: 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: }
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.
31: Collective
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
38: Output Parameters:
39: + lname - actual name of the file on local filesystem if `found`
40: - found - true if the file exists
42: Level: developer
44: Notes:
45: [[<http,ftp>://hostname]/directoryname/]filename[.so.1.0]
47: ${PETSC_ARCH}, ${PETSC_DIR}, ${PETSC_LIB_DIR}, or ${any environmental variable}
48: occurring in directoryname and filename will be replaced with appropriate values.
50: .seealso: `PetscFileRetrieve()`
51: @*/
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;
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));
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;
79: PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found));
80: if (!*found) {
81: const char suffix[] = "." PETSC_SLSUFFIX;
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));
88: /* restore the .gz suffix if it was there */
89: if (gz) PetscCall(PetscStrlcat(par2, ".gz", blen));
91: /* and finally retrieve the file */
92: PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found));
93: }
95: PetscCall(PetscFree(buf));
96: PetscFunctionReturn(PETSC_SUCCESS);
97: }
99: /*@C
100: PetscDLLibraryOpen - Opens a PETSc dynamic link library
102: Collective, No Fortran Support
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
108: Output Parameter:
109: . entry - a PETSc dynamic link library entry
111: Level: developer
113: Notes:
114: [[<http,ftp>://hostname]/directoryname/]libbasename[.so.1.0]
116: If the library has the symbol `PetscDLLibraryRegister_basename()` in it then that function is automatically run
117: when the library is opened.
119: ${PETSC_ARCH} occurring in directoryname and filename
120: will be replaced with the appropriate value.
122: .seealso: `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`, `PetscDLLibraryRetrieve()`, `PetscDLLibrarySym()`, `PetscDLLibraryClose()`
123: @*/
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;
133: PetscFunctionBegin;
134: PetscAssertPointer(path, 2);
135: PetscAssertPointer(entry, 3);
137: *entry = NULL;
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
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;
161: /* open the dynamic library */
162: PetscCall(PetscInfo(NULL, "Opening dynamic library %s\n", libname));
163: PetscCall(PetscDLOpen(par2, PETSC_DL_DECIDE, &handle));
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: }
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: }
190: /*@C
191: PetscDLLibrarySym - Load a symbol from a list of dynamic link libraries.
193: Collective, No Fortran Support
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
201: Output Parameter:
202: . value - if symbol not found then this value is set to `NULL`
204: Level: developer
206: Notes:
207: Symbol can be of the form
208: [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional
210: It will attempt to (retrieve and) open the library if it is not yet been opened.
212: .seealso: `PetscDLLibrary`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `PetscDLLibraryClose()`
213: @*/
214: PetscErrorCode PetscDLLibrarySym(MPI_Comm comm, PetscDLLibrary *outlist, const char path[], const char insymbol[], void **value)
215: {
216: char libname[PETSC_MAX_PATH_LEN], suffix[16];
217: char *symbol = NULL, *s = NULL;
218: PetscDLLibrary list = NULL, nlist, prev;
220: PetscFunctionBegin;
221: if (outlist) PetscAssertPointer(outlist, 2);
222: if (path) PetscAssertPointer(path, 3);
223: PetscAssertPointer(insymbol, 4);
224: PetscAssertPointer(value, 5);
226: if (outlist) list = *outlist;
227: *value = NULL;
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;
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: }
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));
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: }
288: if (symbol != insymbol) PetscCall(PetscFree(symbol));
289: PetscFunctionReturn(PETSC_SUCCESS);
290: }
292: /*@C
293: PetscDLLibraryAppend - Appends another dynamic link library to the end of the search list
295: Collective, No Fortran Support
297: Input Parameters:
298: + comm - MPI communicator
299: - path - name of the library
301: Output Parameter:
302: . outlist - list of libraries
304: Level: developer
306: Note:
307: if library is already in path will not add it.
309: If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run
310: when the library is opened.
312: .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryPrepend()`
313: @*/
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: char *libname, suffix[16], *s = NULL;
321: PetscToken token;
323: PetscFunctionBegin;
324: PetscAssertPointer(outlist, 2);
326: /* is path a directory? */
327: PetscCall(PetscTestDirectory(path, 'r', &dir));
328: if (dir) {
329: PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
330: PetscCall(PetscStrncpy(program, path, sizeof(program)));
331: PetscCall(PetscStrlen(program, &len));
332: if (program[len - 1] == '/') {
333: PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
334: } else {
335: PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
336: }
337: PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));
339: PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
340: if (!dir) PetscFunctionReturn(PETSC_SUCCESS);
341: } else {
342: PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
343: }
344: PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
345: PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
347: PetscCall(PetscTokenCreate(found, '\n', &token));
348: PetscCall(PetscTokenFind(token, &libname));
349: while (libname) {
350: /* remove suffix from libname */
351: PetscCall(PetscStrrstr(libname, suffix, &s));
352: if (s) s[0] = 0;
353: /* see if library was already open then we are done */
354: list = prev = *outlist;
355: match = PETSC_FALSE;
356: while (list) {
357: PetscCall(PetscStrcmp(list->libname, libname, &match));
358: if (match) break;
359: prev = list;
360: list = list->next;
361: }
362: /* restore suffix from libname */
363: if (s) s[0] = '.';
364: if (!match) {
365: /* open the library and add to end of list */
366: PetscCall(PetscDLLibraryOpen(comm, libname, &list));
367: PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", libname));
368: if (!*outlist) *outlist = list;
369: else prev->next = list;
370: }
371: PetscCall(PetscTokenFind(token, &libname));
372: }
373: PetscCall(PetscTokenDestroy(&token));
374: PetscFunctionReturn(PETSC_SUCCESS);
375: }
377: /*@C
378: PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of the search list
380: Collective, No Fortran Support
382: Input Parameters:
383: + comm - MPI communicator
384: - path - name of the library
386: Output Parameter:
387: . outlist - list of libraries
389: Level: developer
391: Note:
392: If library is already in the list it will remove the old reference.
394: .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryAppend()`
395: @*/
396: PetscErrorCode PetscDLLibraryPrepend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[])
397: {
398: PetscDLLibrary list, prev;
399: size_t len;
400: PetscBool match, dir;
401: char program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN];
402: char *libname, suffix[16], *s = NULL;
403: PetscToken token;
405: PetscFunctionBegin;
406: PetscAssertPointer(outlist, 2);
408: /* is path a directory? */
409: PetscCall(PetscTestDirectory(path, 'r', &dir));
410: if (dir) {
411: PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
412: PetscCall(PetscStrncpy(program, path, sizeof(program)));
413: PetscCall(PetscStrlen(program, &len));
414: if (program[len - 1] == '/') {
415: PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
416: } else {
417: PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
418: }
419: PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));
421: PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
422: if (!dir) PetscFunctionReturn(PETSC_SUCCESS);
423: } else {
424: PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
425: }
427: PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
428: PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));
430: PetscCall(PetscTokenCreate(found, '\n', &token));
431: PetscCall(PetscTokenFind(token, &libname));
432: while (libname) {
433: /* remove suffix from libname */
434: PetscCall(PetscStrstr(libname, suffix, &s));
435: if (s) s[0] = 0;
436: /* see if library was already open and move it to the front */
437: prev = NULL;
438: list = *outlist;
439: match = PETSC_FALSE;
440: while (list) {
441: PetscCall(PetscStrcmp(list->libname, libname, &match));
442: if (match) {
443: PetscCall(PetscInfo(NULL, "Moving %s to begin of dynamic library search path\n", libname));
444: if (prev) prev->next = list->next;
445: if (prev) list->next = *outlist;
446: *outlist = list;
447: break;
448: }
449: prev = list;
450: list = list->next;
451: }
452: /* restore suffix from libname */
453: if (s) s[0] = '.';
454: if (!match) {
455: /* open the library and add to front of list */
456: PetscCall(PetscDLLibraryOpen(comm, libname, &list));
457: PetscCall(PetscInfo(NULL, "Prepending %s to dynamic library search path\n", libname));
458: list->next = *outlist;
459: *outlist = list;
460: }
461: PetscCall(PetscTokenFind(token, &libname));
462: }
463: PetscCall(PetscTokenDestroy(&token));
464: PetscFunctionReturn(PETSC_SUCCESS);
465: }
467: /*@C
468: PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries.
470: Collective, No Fortran Support
472: Input Parameter:
473: . list - library list
475: Level: developer
477: .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryAppend()`,
478: `PetscDLLibraryPrepend()`
479: @*/
480: PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list)
481: {
482: PetscBool done = PETSC_FALSE;
483: PetscDLLibrary prev, tail;
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: }