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