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 + name - 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 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 PetscValidCharPointer(path, 2); 135 PetscValidPointer(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:\n %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:\n %s\n %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 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) 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) PetscValidPointer(outlist, 2); 222 if (path) PetscValidCharPointer(path, 3); 223 PetscValidCharPointer(insymbol, 4); 224 PetscValidPointer(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 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()`, `PetscDLLibraryOpen()`, `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; 322 323 PetscFunctionBegin; 324 PetscValidPointer(outlist, 2); 325 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))); 338 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))); 346 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 } 376 377 /*@C 378 PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of the search list 379 380 Collective 381 382 Input Parameters: 383 + comm - MPI communicator 384 - path - name of the library 385 386 Output Parameter: 387 . outlist - list of libraries 388 389 Level: developer 390 391 Note: 392 If library is already in the list it will remove the old reference. 393 394 .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `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; 404 405 PetscFunctionBegin; 406 PetscValidPointer(outlist, 2); 407 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))); 420 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 } 426 427 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 428 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 429 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 } 466 467 /*@C 468 PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries. 469 470 Collective 471 472 Input Parameter: 473 . head - library list 474 475 Level: developer 476 477 .seealso: `PetscDLLibrary`, `PetscDLLibraryOpen()`, `PetscLoadDynamicLibrary()`, `PetscDLLibraryRetrieve()`, `PetscDLLibraryOpen()`, `PetscDLLibraryAppend()`, 478 `PetscDLLibraryPrepend()` 479 @*/ 480 PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list) 481 { 482 PetscBool done = PETSC_FALSE; 483 PetscDLLibrary prev, tail; 484 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 } 503