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: }