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 PetscErrorPrintf(" %s\n",libs->libname); 23 libs = libs->next; 24 } 25 PetscFunctionReturn(0); 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,*so; 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(0); 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 { 171 PetscCall(PetscInfo(NULL,"Dynamic library %s does not have lib prefix\n",libname)); 172 } 173 for (s=basename; *s; s++) if (*s == '-') *s = '_'; 174 PetscCall(PetscStrncpy(registername,"PetscDLLibraryRegister_",sizeof(registername))); 175 PetscCall(PetscStrlcat(registername,basename,sizeof(registername))); 176 PetscCall(PetscDLSym(handle,registername,(void**)&func)); 177 if (func) { 178 PetscCall(PetscInfo(NULL,"Loading registered routines from %s\n",libname)); 179 PetscCall((*func)()); 180 } else { 181 PetscCall(PetscInfo(NULL,"Dynamic library %s does not have symbol %s\n",libname,registername)); 182 } 183 184 PetscCall(PetscNew(entry)); 185 (*entry)->next = NULL; 186 (*entry)->handle = handle; 187 PetscCall(PetscStrcpy((*entry)->libname,libname)); 188 PetscFunctionReturn(0); 189 } 190 191 /*@C 192 PetscDLLibrarySym - Load a symbol from the dynamic link libraries. 193 194 Collective 195 196 Input Parameters: 197 + comm - communicator that will open the library 198 . outlist - list of already open libraries that may contain symbol (can be NULL and only the executable is searched for the function) 199 . path - optional complete library name (if provided checks here before checking outlist) 200 - insymbol - name of symbol 201 202 Output Parameter: 203 . value - if symbol not found then this value is set to NULL 204 205 Level: developer 206 207 Notes: 208 Symbol can be of the form 209 [/path/libname[.so.1.0]:]functionname[()] where items in [] denote optional 210 211 Will attempt to (retrieve and) open the library if it is not yet been opened. 212 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],*symbol,*s; 217 PetscDLLibrary nlist,prev,list = NULL; 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 {if (outlist) *outlist = nlist;} 263 264 done:; 265 PetscCall(PetscDLSym(nlist->handle,symbol,value)); 266 if (*value) { 267 PetscCall(PetscInfo(NULL,"Loading function %s from dynamic library %s\n",insymbol,path)); 268 } 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) { 286 PetscCall(PetscInfo(NULL,"Loading symbol %s from object code\n",symbol)); 287 } 288 } 289 } 290 291 if (symbol != insymbol) { 292 PetscCall(PetscFree(symbol)); 293 } 294 PetscFunctionReturn(0); 295 } 296 297 /*@C 298 PetscDLLibraryAppend - Appends another dynamic link library to the seach list, to the end 299 of the search path. 300 301 Collective 302 303 Input Parameters: 304 + comm - MPI communicator 305 - path - name of the library 306 307 Output Parameter: 308 . outlist - list of libraries 309 310 Level: developer 311 312 Notes: 313 if library is already in path will not add it. 314 315 If the library has the symbol PetscDLLibraryRegister_basename() in it then that function is automatically run 316 when the library is opened. 317 318 .seealso: `PetscDLLibraryOpen()` 319 @*/ 320 PetscErrorCode PetscDLLibraryAppend(MPI_Comm comm,PetscDLLibrary *outlist,const char path[]) 321 { 322 PetscDLLibrary list,prev; 323 size_t len; 324 PetscBool match,dir; 325 char program[PETSC_MAX_PATH_LEN],found[8*PETSC_MAX_PATH_LEN]; 326 char *libname,suffix[16],*s; 327 PetscToken token; 328 329 PetscFunctionBegin; 330 PetscValidPointer(outlist,2); 331 332 /* is path a directory? */ 333 PetscCall(PetscTestDirectory(path,'r',&dir)); 334 if (dir) { 335 PetscCall(PetscInfo(NULL,"Checking directory %s for dynamic libraries\n",path)); 336 PetscCall(PetscStrncpy(program,path,sizeof(program))); 337 PetscCall(PetscStrlen(program,&len)); 338 if (program[len-1] == '/') { 339 PetscCall(PetscStrlcat(program,"*.",sizeof(program))); 340 } else { 341 PetscCall(PetscStrlcat(program,"/*.",sizeof(program))); 342 } 343 PetscCall(PetscStrlcat(program,PETSC_SLSUFFIX,sizeof(program))); 344 345 PetscCall(PetscLs(comm,program,found,8*PETSC_MAX_PATH_LEN,&dir)); 346 if (!dir) PetscFunctionReturn(0); 347 } else { 348 PetscCall(PetscStrncpy(found,path,PETSC_MAX_PATH_LEN)); 349 } 350 PetscCall(PetscStrncpy(suffix,".",sizeof(suffix))); 351 PetscCall(PetscStrlcat(suffix,PETSC_SLSUFFIX,sizeof(suffix))); 352 353 PetscCall(PetscTokenCreate(found,'\n',&token)); 354 PetscCall(PetscTokenFind(token,&libname)); 355 while (libname) { 356 /* remove suffix from libname */ 357 PetscCall(PetscStrrstr(libname,suffix,&s)); 358 if (s) s[0] = 0; 359 /* see if library was already open then we are done */ 360 list = prev = *outlist; 361 match = PETSC_FALSE; 362 while (list) { 363 PetscCall(PetscStrcmp(list->libname,libname,&match)); 364 if (match) break; 365 prev = list; 366 list = list->next; 367 } 368 /* restore suffix from libname */ 369 if (s) s[0] = '.'; 370 if (!match) { 371 /* open the library and add to end of list */ 372 PetscCall(PetscDLLibraryOpen(comm,libname,&list)); 373 PetscCall(PetscInfo(NULL,"Appending %s to dynamic library search path\n",libname)); 374 if (!*outlist) *outlist = list; 375 else prev->next = list; 376 } 377 PetscCall(PetscTokenFind(token,&libname)); 378 } 379 PetscCall(PetscTokenDestroy(&token)); 380 PetscFunctionReturn(0); 381 } 382 383 /*@C 384 PetscDLLibraryPrepend - Add another dynamic library to search for symbols to the beginning of 385 the search path. 386 387 Collective 388 389 Input Parameters: 390 + comm - MPI communicator 391 - path - name of the library 392 393 Output Parameter: 394 . outlist - list of libraries 395 396 Level: developer 397 398 Notes: 399 If library is already in path will remove old reference. 400 401 @*/ 402 PetscErrorCode PetscDLLibraryPrepend(MPI_Comm comm,PetscDLLibrary *outlist,const char path[]) 403 { 404 PetscDLLibrary list,prev; 405 size_t len; 406 PetscBool match,dir; 407 char program[PETSC_MAX_PATH_LEN],found[8*PETSC_MAX_PATH_LEN]; 408 char *libname,suffix[16],*s; 409 PetscToken token; 410 411 PetscFunctionBegin; 412 PetscValidPointer(outlist,2); 413 414 /* is path a directory? */ 415 PetscCall(PetscTestDirectory(path,'r',&dir)); 416 if (dir) { 417 PetscCall(PetscInfo(NULL,"Checking directory %s for dynamic libraries\n",path)); 418 PetscCall(PetscStrncpy(program,path,sizeof(program))); 419 PetscCall(PetscStrlen(program,&len)); 420 if (program[len-1] == '/') { 421 PetscCall(PetscStrlcat(program,"*.",sizeof(program))); 422 } else { 423 PetscCall(PetscStrlcat(program,"/*.",sizeof(program))); 424 } 425 PetscCall(PetscStrlcat(program,PETSC_SLSUFFIX,sizeof(program))); 426 427 PetscCall(PetscLs(comm,program,found,8*PETSC_MAX_PATH_LEN,&dir)); 428 if (!dir) PetscFunctionReturn(0); 429 } else { 430 PetscCall(PetscStrncpy(found,path,PETSC_MAX_PATH_LEN)); 431 } 432 433 PetscCall(PetscStrncpy(suffix,".",sizeof(suffix))); 434 PetscCall(PetscStrlcat(suffix,PETSC_SLSUFFIX,sizeof(suffix))); 435 436 PetscCall(PetscTokenCreate(found,'\n',&token)); 437 PetscCall(PetscTokenFind(token,&libname)); 438 while (libname) { 439 /* remove suffix from libname */ 440 PetscCall(PetscStrstr(libname,suffix,&s)); 441 if (s) s[0] = 0; 442 /* see if library was already open and move it to the front */ 443 prev = NULL; 444 list = *outlist; 445 match = PETSC_FALSE; 446 while (list) { 447 PetscCall(PetscStrcmp(list->libname,libname,&match)); 448 if (match) { 449 PetscCall(PetscInfo(NULL,"Moving %s to begin of dynamic library search path\n",libname)); 450 if (prev) prev->next = list->next; 451 if (prev) list->next = *outlist; 452 *outlist = list; 453 break; 454 } 455 prev = list; 456 list = list->next; 457 } 458 /* restore suffix from libname */ 459 if (s) s[0] = '.'; 460 if (!match) { 461 /* open the library and add to front of list */ 462 PetscCall(PetscDLLibraryOpen(comm,libname,&list)); 463 PetscCall(PetscInfo(NULL,"Prepending %s to dynamic library search path\n",libname)); 464 list->next = *outlist; 465 *outlist = list; 466 } 467 PetscCall(PetscTokenFind(token,&libname)); 468 } 469 PetscCall(PetscTokenDestroy(&token)); 470 PetscFunctionReturn(0); 471 } 472 473 /*@C 474 PetscDLLibraryClose - Destroys the search path of dynamic libraries and closes the libraries. 475 476 Collective on PetscDLLibrary 477 478 Input Parameter: 479 . head - library list 480 481 Level: developer 482 483 @*/ 484 PetscErrorCode PetscDLLibraryClose(PetscDLLibrary list) 485 { 486 PetscBool done = PETSC_FALSE; 487 PetscDLLibrary prev,tail; 488 489 PetscFunctionBegin; 490 if (!list) PetscFunctionReturn(0); 491 /* traverse the list in reverse order */ 492 while (!done) { 493 if (!list->next) done = PETSC_TRUE; 494 prev = tail = list; 495 while (tail->next) { 496 prev = tail; 497 tail = tail->next; 498 } 499 prev->next = NULL; 500 /* close the dynamic library and free the space in entry data-structure*/ 501 PetscCall(PetscInfo(NULL,"Closing dynamic library %s\n",tail->libname)); 502 PetscCall(PetscDLClose(&tail->handle)); 503 PetscCall(PetscFree(tail)); 504 } 505 PetscFunctionReturn(0); 506 } 507