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) PeNS
    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:   const char    *libname;
    321:   char           suffix[16], *s = NULL;
    322:   PetscToken     token;

    324:   PetscFunctionBegin;
    325:   PetscAssertPointer(outlist, 2);

    327:   /* is path a directory? */
    328:   PetscCall(PetscTestDirectory(path, 'r', &dir));
    329:   if (dir) {
    330:     PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
    331:     PetscCall(PetscStrncpy(program, path, sizeof(program)));
    332:     PetscCall(PetscStrlen(program, &len));
    333:     if (program[len - 1] == '/') {
    334:       PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
    335:     } else {
    336:       PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
    337:     }
    338:     PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));

    340:     PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
    341:     if (!dir) PetscFunctionReturn(PETSC_SUCCESS);
    342:   } else {
    343:     PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
    344:   }
    345:   PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
    346:   PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));

    348:   PetscCall(PetscTokenCreate(found, '\n', &token));
    349:   PetscCall(PetscTokenFind(token, &libname));
    350:   while (libname) {
    351:     /* remove suffix from libname */
    352:     PetscCall(PetscStrrstr(libname, suffix, &s));
    353:     if (s) s[0] = 0;
    354:     /* see if library was already open then we are done */
    355:     list = prev = *outlist;
    356:     match       = PETSC_FALSE;
    357:     while (list) {
    358:       PetscCall(PetscStrcmp(list->libname, libname, &match));
    359:       if (match) break;
    360:       prev = list;
    361:       list = list->next;
    362:     }
    363:     /* restore suffix from libname */
    364:     if (s) s[0] = '.';
    365:     if (!match) {
    366:       /* open the library and add to end of list */
    367:       PetscCall(PetscDLLibraryOpen(comm, libname, &list));
    368:       PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", libname));
    369:       if (!*outlist) *outlist = list;
    370:       else prev->next = list;
    371:     }
    372:     PetscCall(PetscTokenFind(token, &libname));
    373:   }
    374:   PetscCall(PetscTokenDestroy(&token));
    375:   PetscFunctionReturn(PETSC_SUCCESS);
    376: }

    378: /*@C
    379:   PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of the search list

    381:   Collective, No Fortran Support

    383:   Input Parameters:
    384: + comm - MPI communicator
    385: - path - name of the library

    387:   Output Parameter:
    388: . outlist - list of libraries

    390:   Level: developer

    392:   Note:
    393:   If library is already in the list it will remove the old reference.

    395: .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryAppend()`
    396: @*/
    397: PetscErrorCode PetscDLLibraryPrepend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[])
    398: {
    399:   PetscDLLibrary list, prev;
    400:   size_t         len;
    401:   PetscBool      match, dir;
    402:   char           program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN];
    403:   const char    *libname;
    404:   char           suffix[16], *s = NULL;
    405:   PetscToken     token;

    407:   PetscFunctionBegin;
    408:   PetscAssertPointer(outlist, 2);

    410:   /* is path a directory? */
    411:   PetscCall(PetscTestDirectory(path, 'r', &dir));
    412:   if (dir) {
    413:     PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path));
    414:     PetscCall(PetscStrncpy(program, path, sizeof(program)));
    415:     PetscCall(PetscStrlen(program, &len));
    416:     if (program[len - 1] == '/') {
    417:       PetscCall(PetscStrlcat(program, "*.", sizeof(program)));
    418:     } else {
    419:       PetscCall(PetscStrlcat(program, "/*.", sizeof(program)));
    420:     }
    421:     PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program)));

    423:     PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir));
    424:     if (!dir) PetscFunctionReturn(PETSC_SUCCESS);
    425:   } else {
    426:     PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN));
    427:   }

    429:   PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix)));
    430:   PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix)));

    432:   PetscCall(PetscTokenCreate(found, '\n', &token));
    433:   PetscCall(PetscTokenFind(token, &libname));
    434:   while (libname) {
    435:     /* remove suffix from libname */
    436:     PetscCall(PetscStrstr(libname, suffix, &s));
    437:     if (s) s[0] = 0;
    438:     /* see if library was already open and move it to the front */
    439:     prev  = NULL;
    440:     list  = *outlist;
    441:     match = PETSC_FALSE;
    442:     while (list) {
    443:       PetscCall(PetscStrcmp(list->libname, libname, &match));
    444:       if (match) {
    445:         PetscCall(PetscInfo(NULL, "Moving %s to begin of dynamic library search path\n", libname));
    446:         if (prev) prev->next = list->next;
    447:         if (prev) list->next = *outlist;
    448:         *outlist = list;
    449:         break;
    450:       }
    451:       prev = list;
    452:       list = list->next;
    453:     }
    454:     /* restore suffix from libname */
    455:     if (s) s[0] = '.';
    456:     if (!match) {
    457:       /* open the library and add to front of list */
    458:       PetscCall(PetscDLLibraryOpen(comm, libname, &list));
    459:       PetscCall(PetscInfo(NULL, "Prepending %s to dynamic library search path\n", libname));
    460:       list->next = *outlist;
    461:       *outlist   = list;
    462:     }
    463:     PetscCall(PetscTokenFind(token, &libname));
    464:   }
    465:   PetscCall(PetscTokenDestroy(&token));
    466:   PetscFunctionReturn(PETSC_SUCCESS);
    467: }

    469: /*@C
    470:   PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries.

    472:   Collective, No Fortran Support

    474:   Input Parameter:
    475: . list - library list

    477:   Level: developer

    479: .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryAppend()`,
    480:           `PetscDLLibraryPrepend()`
    481: @*/
    482: PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list)
    483: {
    484:   PetscBool      done = PETSC_FALSE;
    485:   PetscDLLibrary prev, tail;

    487:   PetscFunctionBegin;
    488:   if (!list) PetscFunctionReturn(PETSC_SUCCESS);
    489:   /* traverse the list in reverse order */
    490:   while (!done) {
    491:     if (!list->next) done = PETSC_TRUE;
    492:     prev = tail = list;
    493:     while (tail->next) {
    494:       prev = tail;
    495:       tail = tail->next;
    496:     }
    497:     prev->next = NULL;
    498:     /* close the dynamic library and free the space in entry data-structure*/
    499:     PetscCall(PetscInfo(NULL, "Closing dynamic library %s\n", tail->libname));
    500:     PetscCall(PetscDLClose(&tail->handle));
    501:     PetscCall(PetscFree(tail));
    502:   }
    503:   PetscFunctionReturn(PETSC_SUCCESS);
    504: }