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, suffix[16], *gz = NULL, *so = NULL; 54 size_t len; 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 len = PetscMax(4 * len, PETSC_MAX_PATH_LEN); 63 PetscCall(PetscMalloc1(len, &buf)); 64 par2 = buf; 65 PetscCall(PetscStrreplace(comm, libname, par2, len)); 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 /* see if library name does already not have suffix attached */ 81 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 82 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 83 PetscCall(PetscStrrstr(par2, suffix, &so)); 84 /* and attach the suffix if it is not there */ 85 if (!so) PetscCall(PetscStrcat(par2, suffix)); 86 87 /* restore the .gz suffix if it was there */ 88 if (gz) PetscCall(PetscStrcat(par2, ".gz")); 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 char libname[PETSC_MAX_PATH_LEN], par2[PETSC_MAX_PATH_LEN], suffix[16], *s; 127 char *basename, registername[128]; 128 PetscDLHandle handle; 129 PetscErrorCode (*func)(void) = NULL; 130 131 PetscFunctionBegin; 132 PetscValidCharPointer(path, 2); 133 PetscValidPointer(entry, 3); 134 135 *entry = NULL; 136 137 /* retrieve the library */ 138 PetscCall(PetscInfo(NULL, "Retrieving %s\n", path)); 139 PetscCall(PetscDLLibraryRetrieve(comm, path, par2, PETSC_MAX_PATH_LEN, &foundlibrary)); 140 PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate dynamic library:\n %s", path); 141 /* Eventually ./configure should determine if the system needs an executable dynamic library */ 142 #define PETSC_USE_NONEXECUTABLE_SO 143 #if !defined(PETSC_USE_NONEXECUTABLE_SO) 144 PetscCall(PetscTestFile(par2, 'x', &foundlibrary)); 145 PetscCheck(foundlibrary, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Dynamic library is not executable:\n %s\n %s", path, par2); 146 #endif 147 148 /* copy path and setup shared library suffix */ 149 PetscCall(PetscStrncpy(libname, path, PETSC_MAX_PATH_LEN)); 150 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 151 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 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(PetscStrcpy((*entry)->libname, libname)); 187 PetscFunctionReturn(PETSC_SUCCESS); 188 } 189 190 /*@C 191 PetscDLLibrarySym - Load a symbol from the dynamic link libraries. 192 193 Collective 194 195 Input Parameters: 196 + comm - communicator that will open the library 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 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 Will attempt to (retrieve and) open the library if it is not yet been opened. 211 212 @*/ 213 PetscErrorCode PetscDLLibrarySym(MPI_Comm comm, PetscDLLibrary *outlist, const char path[], const char insymbol[], void **value) 214 { 215 char libname[PETSC_MAX_PATH_LEN], suffix[16]; 216 char *symbol = NULL, *s; 217 PetscDLLibrary list = NULL, nlist, prev; 218 219 PetscFunctionBegin; 220 if (outlist) PetscValidPointer(outlist, 2); 221 if (path) PetscValidCharPointer(path, 3); 222 PetscValidCharPointer(insymbol, 4); 223 PetscValidPointer(value, 5); 224 225 if (outlist) list = *outlist; 226 *value = NULL; 227 228 PetscCall(PetscStrchr(insymbol, '(', &s)); 229 if (s) { 230 /* make copy of symbol so we can edit it in place */ 231 PetscCall(PetscStrallocpy(insymbol, &symbol)); 232 /* If symbol contains () then replace with a NULL, to support functionname() */ 233 PetscCall(PetscStrchr(symbol, '(', &s)); 234 s[0] = 0; 235 } else symbol = (char *)insymbol; 236 237 /* 238 Function name does include library 239 ------------------------------------- 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 */ 274 } else { 275 while (list) { 276 PetscCall(PetscDLSym(list->handle, symbol, value)); 277 if (*value) { 278 PetscCall(PetscInfo(NULL, "Loading symbol %s from dynamic library %s\n", symbol, list->libname)); 279 break; 280 } 281 list = list->next; 282 } 283 if (!*value) { 284 PetscCall(PetscDLSym(NULL, symbol, value)); 285 if (*value) PetscCall(PetscInfo(NULL, "Loading symbol %s from object code\n", symbol)); 286 } 287 } 288 289 if (symbol != insymbol) PetscCall(PetscFree(symbol)); 290 PetscFunctionReturn(PETSC_SUCCESS); 291 } 292 293 /*@C 294 PetscDLLibraryAppend - Appends another dynamic link library to the search list, to the end 295 of the search path. 296 297 Collective 298 299 Input Parameters: 300 + comm - MPI communicator 301 - path - name of the library 302 303 Output Parameter: 304 . outlist - list of libraries 305 306 Level: developer 307 308 Note: 309 if library is already in path will not add it. 310 311 If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run 312 when the library is opened. 313 314 .seealso: `PetscDLLibraryOpen()` 315 @*/ 316 PetscErrorCode PetscDLLibraryAppend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[]) 317 { 318 PetscDLLibrary list, prev; 319 size_t len; 320 PetscBool match, dir; 321 char program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN]; 322 char *libname, suffix[16], *s = NULL; 323 PetscToken token; 324 325 PetscFunctionBegin; 326 PetscValidPointer(outlist, 2); 327 328 /* is path a directory? */ 329 PetscCall(PetscTestDirectory(path, 'r', &dir)); 330 if (dir) { 331 PetscCall(PetscInfo(NULL, "Checking directory %s for dynamic libraries\n", path)); 332 PetscCall(PetscStrncpy(program, path, sizeof(program))); 333 PetscCall(PetscStrlen(program, &len)); 334 if (program[len - 1] == '/') { 335 PetscCall(PetscStrlcat(program, "*.", sizeof(program))); 336 } else { 337 PetscCall(PetscStrlcat(program, "/*.", sizeof(program))); 338 } 339 PetscCall(PetscStrlcat(program, PETSC_SLSUFFIX, sizeof(program))); 340 341 PetscCall(PetscLs(comm, program, found, 8 * PETSC_MAX_PATH_LEN, &dir)); 342 if (!dir) PetscFunctionReturn(PETSC_SUCCESS); 343 } else { 344 PetscCall(PetscStrncpy(found, path, PETSC_MAX_PATH_LEN)); 345 } 346 PetscCall(PetscStrncpy(suffix, ".", sizeof(suffix))); 347 PetscCall(PetscStrlcat(suffix, PETSC_SLSUFFIX, sizeof(suffix))); 348 349 PetscCall(PetscTokenCreate(found, '\n', &token)); 350 PetscCall(PetscTokenFind(token, &libname)); 351 while (libname) { 352 /* remove suffix from libname */ 353 PetscCall(PetscStrrstr(libname, suffix, &s)); 354 if (s) s[0] = 0; 355 /* see if library was already open then we are done */ 356 list = prev = *outlist; 357 match = PETSC_FALSE; 358 while (list) { 359 PetscCall(PetscStrcmp(list->libname, libname, &match)); 360 if (match) break; 361 prev = list; 362 list = list->next; 363 } 364 /* restore suffix from libname */ 365 if (s) s[0] = '.'; 366 if (!match) { 367 /* open the library and add to end of list */ 368 PetscCall(PetscDLLibraryOpen(comm, libname, &list)); 369 PetscCall(PetscInfo(NULL, "Appending %s to dynamic library search path\n", libname)); 370 if (!*outlist) *outlist = list; 371 else prev->next = list; 372 } 373 PetscCall(PetscTokenFind(token, &libname)); 374 } 375 PetscCall(PetscTokenDestroy(&token)); 376 PetscFunctionReturn(PETSC_SUCCESS); 377 } 378 379 /*@C 380 PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of 381 the search path. 382 383 Collective 384 385 Input Parameters: 386 + comm - MPI communicator 387 - path - name of the library 388 389 Output Parameter: 390 . outlist - list of libraries 391 392 Level: developer 393 394 Note: 395 If library is already in path will remove old reference. 396 397 @*/ 398 PetscErrorCode PetscDLLibraryPrepend(MPI_Comm comm, PetscDLLibrary *outlist, const char path[]) 399 { 400 PetscDLLibrary list, prev; 401 size_t len; 402 PetscBool match, dir; 403 char program[PETSC_MAX_PATH_LEN], found[8 * PETSC_MAX_PATH_LEN]; 404 char *libname, suffix[16], *s = NULL; 405 PetscToken token; 406 407 PetscFunctionBegin; 408 PetscValidPointer(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 473 474 Input Parameter: 475 . head - library list 476 477 Level: developer 478 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