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