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 /* 10 Code to maintain a list of opened dynamic libraries and load symbols 11 */ 12 struct _n_PetscDLLibrary { 13 PetscDLLibrary next; 14 PetscDLHandle handle; 15 char libname[PETSC_MAX_PATH_LEN]; 16 }; 17 18 PetscErrorCode PetscDLLibraryPrintPath(PetscDLLibrary libs) 19 { 20 PetscFunctionBegin; 21 while (libs) { 22 PetscCall(PetscErrorPrintf(" %s\n", libs->libname)); 23 libs = libs->next; 24 } 25 PetscFunctionReturn(PETSC_SUCCESS); 26 } 27 28 /*@C 29 PetscDLLibraryRetrieve - Copies a PETSc dynamic library from a remote location 30 (if it is remote), indicates if it exits and its local name. 31 32 Collective 33 34 Input Parameters: 35 + comm - processors that are opening the library 36 - libname - name of the library, can be relative or absolute 37 38 Output Parameters: 39 + name - actual name of file on local filesystem if found 40 . llen - length of the name buffer 41 - found - true if the file exists 42 43 Level: developer 44 45 Notes: 46 [[<http,ftp>://hostname]/directoryname/]filename[.so.1.0] 47 48 ${PETSC_ARCH}, ${PETSC_DIR}, ${PETSC_LIB_DIR}, or ${any environmental variable} 49 occurring in directoryname and filename will be replaced with appropriate values. 50 @*/ 51 PetscErrorCode PetscDLLibraryRetrieve(MPI_Comm comm, const char libname[], char *lname, size_t llen, PetscBool *found) 52 { 53 char *buf, *par2, *gz = NULL, *so = NULL; 54 size_t len, blen; 55 56 PetscFunctionBegin; 57 /* 58 make copy of library name and replace $PETSC_ARCH etc 59 so we can add to the end of it to look for something like .so.1.0 etc. 60 */ 61 PetscCall(PetscStrlen(libname, &len)); 62 blen = PetscMax(4 * len, PETSC_MAX_PATH_LEN); 63 PetscCall(PetscMalloc1(blen, &buf)); 64 par2 = buf; 65 PetscCall(PetscStrreplace(comm, libname, par2, blen)); 66 67 /* temporarily remove .gz if it ends library name */ 68 PetscCall(PetscStrrstr(par2, ".gz", &gz)); 69 if (gz) { 70 PetscCall(PetscStrlen(gz, &len)); 71 if (len != 3) gz = NULL; /* do not end (exactly) with .gz */ 72 else *gz = 0; /* ends with .gz, so remove it */ 73 } 74 /* strip out .a from it if user put it in by mistake */ 75 PetscCall(PetscStrlen(par2, &len)); 76 if (par2[len - 1] == 'a' && par2[len - 2] == '.') par2[len - 2] = 0; 77 78 PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found)); 79 if (!(*found)) { 80 const char suffix[] = "." PETSC_SLSUFFIX; 81 82 /* see if library name does already not have suffix attached */ 83 PetscCall(PetscStrrstr(par2, suffix, &so)); 84 /* and attach the suffix if it is not there */ 85 if (!so) PetscCall(PetscStrlcat(par2, suffix, blen)); 86 87 /* restore the .gz suffix if it was there */ 88 if (gz) PetscCall(PetscStrlcat(par2, ".gz", blen)); 89 90 /* and finally retrieve the file */ 91 PetscCall(PetscFileRetrieve(comm, par2, lname, llen, found)); 92 } 93 94 PetscCall(PetscFree(buf)); 95 PetscFunctionReturn(PETSC_SUCCESS); 96 } 97 98 /*@C 99 PetscDLLibraryOpen - Opens a PETSc dynamic link library 100 101 Collective 102 103 Input Parameters: 104 + comm - processors that are opening the library 105 - path - name of the library, can be relative or absolute 106 107 Output Parameter: 108 . entry - a PETSc dynamic link library entry 109 110 Level: developer 111 112 Notes: 113 [[<http,ftp>://hostname]/directoryname/]libbasename[.so.1.0] 114 115 If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run 116 when the library is opened. 117 118 ${PETSC_ARCH} occurring in directoryname and filename 119 will be replaced with the appropriate value. 120 121 .seealso: `PetscLoadDynamicLibrary()`, `PetscDLLibraryAppend()` 122 @*/ 123 PetscErrorCode PetscDLLibraryOpen(MPI_Comm comm, const char path[], PetscDLLibrary *entry) 124 { 125 PetscBool foundlibrary, match; 126 const char suffix[] = "." PETSC_SLSUFFIX; 127 char libname[PETSC_MAX_PATH_LEN], par2[PETSC_MAX_PATH_LEN], *s; 128 char *basename, registername[128]; 129 PetscDLHandle handle; 130 PetscErrorCode (*func)(void) = NULL; 131 132 PetscFunctionBegin; 133 PetscValidCharPointer(path, 2); 134 PetscValidPointer(entry, 3); 135 136 *entry = NULL; 137 138 /* retrieve the library */ 139 PetscCall(PetscInfo(NULL, "Retrieving %s\n", path)); 140 PetscCall(PetscDLLibraryRetrieve(comm, path, par2, PETSC_MAX_PATH_LEN, &foundlibrary)); 141 PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate dynamic library:\n %s", path); 142 /* Eventually ./configure should determine if the system needs an executable dynamic library */ 143 #define PETSC_USE_NONEXECUTABLE_SO 144 #if !defined(PETSC_USE_NONEXECUTABLE_SO) 145 PetscCall(PetscTestFile(par2, 'x', &foundlibrary)); 146 PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Dynamic library is not executable:\n %s\n %s", path, par2); 147 #endif 148 149 /* copy path and setup shared library suffix */ 150 PetscCall(PetscStrncpy(libname, path, sizeof(libname))); 151 /* remove wrong suffixes from libname */ 152 PetscCall(PetscStrrstr(libname, ".gz", &s)); 153 if (s && s[3] == 0) s[0] = 0; 154 PetscCall(PetscStrrstr(libname, ".a", &s)); 155 if (s && s[2] == 0) s[0] = 0; 156 /* remove shared suffix from libname */ 157 PetscCall(PetscStrrstr(libname, suffix, &s)); 158 if (s) s[0] = 0; 159 160 /* open the dynamic library */ 161 PetscCall(PetscInfo(NULL, "Opening dynamic library %s\n", libname)); 162 PetscCall(PetscDLOpen(par2, PETSC_DL_DECIDE, &handle)); 163 164 /* look for [path/]libXXXXX.YYY and extract out the XXXXXX */ 165 PetscCall(PetscStrrchr(libname, '/', &basename)); /* XXX Windows ??? */ 166 if (!basename) basename = libname; 167 PetscCall(PetscStrncmp(basename, "lib", 3, &match)); 168 if (match) basename = basename + 3; 169 else PetscCall(PetscInfo(NULL, "Dynamic library %s does not have lib prefix\n", libname)); 170 for (s = basename; *s; s++) 171 if (*s == '-') *s = '_'; 172 PetscCall(PetscStrncpy(registername, "PetscDLLibraryRegister_", sizeof(registername))); 173 PetscCall(PetscStrlcat(registername, basename, sizeof(registername))); 174 PetscCall(PetscDLSym(handle, registername, (void **)&func)); 175 if (func) { 176 PetscCall(PetscInfo(NULL, "Loading registered routines from %s\n", libname)); 177 PetscCall((*func)()); 178 } else { 179 PetscCall(PetscInfo(NULL, "Dynamic library %s does not have symbol %s\n", libname, registername)); 180 } 181 182 PetscCall(PetscNew(entry)); 183 (*entry)->next = NULL; 184 (*entry)->handle = handle; 185 PetscCall(PetscStrncpy((*entry)->libname, libname, sizeof((*entry)->libname))); 186 PetscFunctionReturn(PETSC_SUCCESS); 187 } 188 189 /*@C 190 PetscDLLibrarySym - Load a symbol from the dynamic link libraries. 191 192 Collective 193 194 Input Parameters: 195 + comm - communicator that will open the library 196 . outlist - list of already open libraries that may contain symbol (can be NULL and only the executable is searched for the function) 197 . path - optional complete library name (if provided checks here before checking outlist) 198 - insymbol - name of symbol 199 200 Output Parameter: 201 . value - if symbol not found then this value is set to NULL 202 203 Level: developer 204 205 Notes: 206 Symbol can be of the form 207 [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional 208 209 Will attempt to (retrieve and) open the library if it is not yet been opened. 210 211 @*/ 212 PetscErrorCode PetscDLLibrarySym(MPI_Comm comm, PetscDLLibrary *outlist, const char path[], const char insymbol[], void **value) 213 { 214 char libname[PETSC_MAX_PATH_LEN], suffix[16]; 215 char *symbol = NULL, *s = NULL; 216 PetscDLLibrary list = NULL, nlist, prev; 217 218 PetscFunctionBegin; 219 if (outlist) PetscValidPointer(outlist, 2); 220 if (path) PetscValidCharPointer(path, 3); 221 PetscValidCharPointer(insymbol, 4); 222 PetscValidPointer(value, 5); 223 224 if (outlist) list = *outlist; 225 *value = NULL; 226 227 PetscCall(PetscStrchr(insymbol, '(', &s)); 228 if (s) { 229 /* make copy of symbol so we can edit it in place */ 230 PetscCall(PetscStrallocpy(insymbol, &symbol)); 231 /* If symbol contains () then replace with a NULL, to support functionname() */ 232 PetscCall(PetscStrchr(symbol, '(', &s)); 233 s[0] = 0; 234 } else symbol = (char *)insymbol; 235 236 /* 237 Function name does include library 238 ------------------------------------- 239 */ 240 if (path && path[0] != '\0') { 241 /* copy path and remove suffix from libname */ 242 PetscCall(PetscStrncpy(libname, path, PETSC_MAX_PATH_LEN)); 243 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 244 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 245 PetscCall(PetscStrrstr(libname, suffix, &s)); 246 if (s) s[0] = 0; 247 /* Look if library is already opened and in path */ 248 prev = NULL; 249 nlist = list; 250 while (nlist) { 251 PetscBool match; 252 PetscCall(PetscStrcmp(nlist->libname, libname, &match)); 253 if (match) goto done; 254 prev = nlist; 255 nlist = nlist->next; 256 } 257 /* open the library and append it to path */ 258 PetscCall(PetscDLLibraryOpen(comm, path, &nlist)); 259 PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", path)); 260 if (prev) prev->next = nlist; 261 else { 262 if (outlist) *outlist = nlist; 263 } 264 265 done:; 266 PetscCall(PetscDLSym(nlist->handle, symbol, value)); 267 if (*value) PetscCall(PetscInfo(NULL, "Loading function %s from dynamic library %s\n", insymbol, path)); 268 269 /* 270 Function name does not include library so search path 271 ----------------------------------------------------- 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 search list, to the end 294 of the search path. 295 296 Collective 297 298 Input Parameters: 299 + comm - MPI communicator 300 - path - name of the library 301 302 Output Parameter: 303 . outlist - list of libraries 304 305 Level: developer 306 307 Note: 308 if library is already in path will not add it. 309 310 If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run 311 when the library is opened. 312 313 .seealso: `PetscDLLibraryOpen()` 314 @*/ 315 PetscErrorCode PetscDLLibraryAppend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[]) 316 { 317 PetscDLLibrary list, prev; 318 size_t len; 319 PetscBool match, dir; 320 char program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN]; 321 char *libname, suffix[16], *s = NULL; 322 PetscToken token; 323 324 PetscFunctionBegin; 325 PetscValidPointer(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 380 the search path. 381 382 Collective 383 384 Input Parameters: 385 + comm - MPI communicator 386 - path - name of the library 387 388 Output Parameter: 389 . outlist - list of libraries 390 391 Level: developer 392 393 Note: 394 If library is already in path will remove old reference. 395 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 char *libname, suffix[16], *s = NULL; 404 PetscToken token; 405 406 PetscFunctionBegin; 407 PetscValidPointer(outlist, 2); 408 409 /* is path a directory? */ 410 PetscCall(PetscTestDirectory(path, 'r', &dir)); 411 if (dir) { 412 PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path)); 413 PetscCall(PetscStrncpy(program, path, sizeof(program))); 414 PetscCall(PetscStrlen(program, &len)); 415 if (program[len - 1] == '/') { 416 PetscCall(PetscStrlcat(program, "*.", sizeof(program))); 417 } else { 418 PetscCall(PetscStrlcat(program, "/*.", sizeof(program))); 419 } 420 PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program))); 421 422 PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir)); 423 if (!dir) PetscFunctionReturn(PETSC_SUCCESS); 424 } else { 425 PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN)); 426 } 427 428 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 429 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 430 431 PetscCall(PetscTokenCreate(found, '\n', &token)); 432 PetscCall(PetscTokenFind(token, &libname)); 433 while (libname) { 434 /* remove suffix from libname */ 435 PetscCall(PetscStrstr(libname, suffix, &s)); 436 if (s) s[0] = 0; 437 /* see if library was already open and move it to the front */ 438 prev = NULL; 439 list = *outlist; 440 match = PETSC_FALSE; 441 while (list) { 442 PetscCall(PetscStrcmp(list->libname, libname, &match)); 443 if (match) { 444 PetscCall(PetscInfo(NULL, "Moving %s to begin of dynamic library search path\n", libname)); 445 if (prev) prev->next = list->next; 446 if (prev) list->next = *outlist; 447 *outlist = list; 448 break; 449 } 450 prev = list; 451 list = list->next; 452 } 453 /* restore suffix from libname */ 454 if (s) s[0] = '.'; 455 if (!match) { 456 /* open the library and add to front of list */ 457 PetscCall(PetscDLLibraryOpen(comm, libname, &list)); 458 PetscCall(PetscInfo(NULL, "Prepending %s to dynamic library search path\n", libname)); 459 list->next = *outlist; 460 *outlist = list; 461 } 462 PetscCall(PetscTokenFind(token, &libname)); 463 } 464 PetscCall(PetscTokenDestroy(&token)); 465 PetscFunctionReturn(PETSC_SUCCESS); 466 } 467 468 /*@C 469 PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries. 470 471 Collective 472 473 Input Parameter: 474 . head - library list 475 476 Level: developer 477 478 @*/ 479 PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list) 480 { 481 PetscBool done = PETSC_FALSE; 482 PetscDLLibrary prev, tail; 483 484 PetscFunctionBegin; 485 if (!list) PetscFunctionReturn(PETSC_SUCCESS); 486 /* traverse the list in reverse order */ 487 while (!done) { 488 if (!list->next) done = PETSC_TRUE; 489 prev = tail = list; 490 while (tail->next) { 491 prev = tail; 492 tail = tail->next; 493 } 494 prev->next = NULL; 495 /* close the dynamic library and free the space in entry data-structure*/ 496 PetscCall(PetscInfo(NULL, "Closing dynamic library %s\n", tail->libname)); 497 PetscCall(PetscDLClose(&tail->handle)); 498 PetscCall(PetscFree(tail)); 499 } 500 PetscFunctionReturn(PETSC_SUCCESS); 501 } 502