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