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 */ 5 6 #include <petsc/private/petscimpl.h> 7 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 }; 16 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 } 26 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. 30 31 Collective 32 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 37 38 Output Parameters: 39 + lname - actual name of the file on local filesystem if `found` 40 - found - true if the file exists 41 42 Level: developer 43 44 Notes: 45 [[<http,ftp>://hostname]/directoryname/]filename[.so.1.0] 46 47 ${PETSC_ARCH}, ${PETSC_DIR}, ${PETSC_LIB_DIR}, or ${any environmental variable} 48 occurring in directoryname and filename will be replaced with appropriate values. 49 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; 56 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)); 67 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; 78 79 PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found)); 80 if (!*found) { 81 const char suffix[] = "." PETSC_SLSUFFIX; 82 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)); 87 88 /* restore the .gz suffix if it was there */ 89 if (gz) PetscCall(PetscStrlcat(par2, ".gz", blen)); 90 91 /* and finally retrieve the file */ 92 PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found)); 93 } 94 95 PetscCall(PetscFree(buf)); 96 PetscFunctionReturn(PETSC_SUCCESS); 97 } 98 99 /*@C 100 PetscDLLibraryOpen - Opens a PETSc dynamic link library 101 102 Collective, No Fortran Support 103 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 107 108 Output Parameter: 109 . entry - a PETSc dynamic link library entry 110 111 Level: developer 112 113 Notes: 114 [[<http,ftp>://hostname]/directoryname/]libbasename[.so.1.0] 115 116 If the library has the symbol `PetscDLLibraryRegister_basename()` in it then that function is automatically run 117 when the library is opened. 118 119 ${PETSC_ARCH} occurring in directoryname and filename 120 will be replaced with the appropriate value. 121 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; 132 133 PetscFunctionBegin; 134 PetscAssertPointer(path, 2); 135 PetscAssertPointer(entry, 3); 136 137 *entry = NULL; 138 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 149 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; 160 161 /* open the dynamic library */ 162 PetscCall(PetscInfo(NULL, "Opening dynamic library %s\n", libname)); 163 PetscCall(PetscDLOpen(par2, PETSC_DL_DECIDE, &handle)); 164 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 } 182 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 } 189 190 /*@C 191 PetscDLLibrarySym - Load a symbol from a list of dynamic link libraries. 192 193 Collective, No Fortran Support 194 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 200 201 Output Parameter: 202 . value - if symbol not found then this value is set to `NULL` 203 204 Level: developer 205 206 Notes: 207 Symbol can be of the form 208 [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional 209 210 It will attempt to (retrieve and) open the library if it is not yet been opened. 211 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; 219 220 PetscFunctionBegin; 221 if (outlist) PetscAssertPointer(outlist, 2); 222 if (path) PetscAssertPointer(path, 3); 223 PetscAssertPointer(insymbol, 4); 224 PetscAssertPointer(value, 5); 225 226 if (outlist) list = *outlist; 227 *value = NULL; 228 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; 237 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 } 265 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)); 269 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 } 287 288 if (symbol != insymbol) PetscCall(PetscFree(symbol)); 289 PetscFunctionReturn(PETSC_SUCCESS); 290 } 291 292 /*@C 293 PetscDLLibraryAppend - Appends another dynamic link library to the end of the search list 294 295 Collective, No Fortran Support 296 297 Input Parameters: 298 + comm - MPI communicator 299 - path - name of the library 300 301 Output Parameter: 302 . outlist - list of libraries 303 304 Level: developer 305 306 Note: 307 if library is already in path will not add it. 308 309 If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run 310 when the library is opened. 311 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; 323 324 PetscFunctionBegin; 325 PetscAssertPointer(outlist, 2); 326 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))); 339 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))); 347 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 } 377 378 /*@C 379 PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of the search list 380 381 Collective, No Fortran Support 382 383 Input Parameters: 384 + comm - MPI communicator 385 - path - name of the library 386 387 Output Parameter: 388 . outlist - list of libraries 389 390 Level: developer 391 392 Note: 393 If library is already in the list it will remove the old reference. 394 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; 406 407 PetscFunctionBegin; 408 PetscAssertPointer(outlist, 2); 409 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))); 422 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 } 428 429 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 430 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 431 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 } 468 469 /*@C 470 PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries. 471 472 Collective, No Fortran Support 473 474 Input Parameter: 475 . list - library list 476 477 Level: developer 478 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; 486 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 } 505