1 /* 2 Code for opening and closing files. 3 */ 4 #include <petscsys.h> 5 #if defined(PETSC_HAVE_PWD_H) 6 #include <pwd.h> 7 #endif 8 #include <ctype.h> 9 #include <sys/stat.h> 10 #if defined(PETSC_HAVE_UNISTD_H) 11 #include <unistd.h> 12 #endif 13 #if defined(PETSC_HAVE_SYS_UTSNAME_H) 14 #include <sys/utsname.h> 15 #endif 16 #include <fcntl.h> 17 #include <time.h> 18 #if defined(PETSC_HAVE_SYS_SYSTEMINFO_H) 19 #include <sys/systeminfo.h> 20 #endif 21 #include <petsc/private/petscimpl.h> 22 23 /* 24 Private routine to delete tmp/shared storage 25 26 This is called by MPI, not by users, when communicator attributes are deleted 27 28 */ 29 static PetscMPIInt MPIAPI Petsc_DelTmpShared(MPI_Comm comm, PetscMPIInt keyval, void *count_val, void *extra_state) 30 { 31 PetscFunctionBegin; 32 PetscCallReturnMPI(PetscInfo(NULL, "Deleting tmp/shared data in an MPI_Comm %ld\n", (long)comm)); 33 PetscCallReturnMPI(PetscFree(count_val)); 34 PetscFunctionReturn(MPI_SUCCESS); 35 } 36 37 // "Unknown section 'Environmental Variables'" 38 // PetscClangLinter pragma disable: -fdoc-section-header-unknown 39 /*@C 40 PetscGetTmp - Gets the name of the "tmp" directory, often this is `/tmp` 41 42 Collective 43 44 Input Parameters: 45 + comm - MPI_Communicator that may share tmp 46 - len - length of string to hold name 47 48 Output Parameter: 49 . dir - directory name 50 51 Options Database Keys: 52 + -shared_tmp - indicates the directory is known to be shared among the MPI processes 53 . -not_shared_tmp - indicates the directory is known to be not shared among the MPI processes 54 - -tmp tmpdir - name of the directory you wish to use as tmp 55 56 Environmental Variables: 57 + `PETSC_SHARED_TMP` - indicates the directory is known to be shared among the MPI processes 58 . `PETSC_NOT_SHARED_TMP` - indicates the directory is known to be not shared among the MPI processes 59 - `PETSC_TMP` - name of the directory you wish to use as tmp 60 61 Level: developer 62 63 .seealso: `PetscSharedTmp()`, `PetscSharedWorkingDirectory()`, `PetscGetWorkingDirectory()`, `PetscGetHomeDirectory()` 64 @*/ 65 PetscErrorCode PetscGetTmp(MPI_Comm comm, char dir[], size_t len) 66 { 67 PetscBool flg; 68 69 PetscFunctionBegin; 70 PetscCall(PetscOptionsGetenv(comm, "PETSC_TMP", dir, len, &flg)); 71 if (!flg) PetscCall(PetscStrncpy(dir, "/tmp", len)); 72 PetscFunctionReturn(PETSC_SUCCESS); 73 } 74 75 // "Unknown section 'Environmental Variables'" 76 // PetscClangLinter pragma disable: -fdoc-section-header-unknown 77 /*@ 78 PetscSharedTmp - Determines if all processors in a communicator share a 79 tmp directory or have different ones. 80 81 Collective 82 83 Input Parameter: 84 . comm - MPI_Communicator that may share tmp 85 86 Output Parameter: 87 . shared - `PETSC_TRUE` or `PETSC_FALSE` 88 89 Options Database Keys: 90 + -shared_tmp - indicates the directory is known to be shared among the MPI processes 91 . -not_shared_tmp - indicates the directory is known to be not shared among the MPI processes 92 - -tmp tmpdir - name of the directory you wish to use as tmp 93 94 Environmental Variables: 95 + `PETSC_SHARED_TMP` - indicates the directory is known to be shared among the MPI processes 96 . `PETSC_NOT_SHARED_TMP` - indicates the directory is known to be not shared among the MPI processes 97 - `PETSC_TMP` - name of the directory you wish to use as tmp 98 99 Level: developer 100 101 Notes: 102 Stores the status as a MPI attribute so it does not have 103 to be redetermined each time. 104 105 Assumes that all processors in a communicator either 106 1) have a common tmp or 107 2) each has a separate tmp 108 eventually we can write a fancier one that determines which processors 109 share a common tmp. 110 111 This will be very slow on runs with a large number of processors since 112 it requires O(p*p) file opens. 113 114 If the environmental variable `PETSC_TMP` is set it will use this directory 115 as the "tmp" directory. 116 117 .seealso: `PetscGetTmp()`, `PetscSharedWorkingDirectory()`, `PetscGetWorkingDirectory()`, `PetscGetHomeDirectory()` 118 @*/ 119 PetscErrorCode PetscSharedTmp(MPI_Comm comm, PetscBool *shared) 120 { 121 PetscMPIInt size, rank, *tagvalp, sum, cnt, i; 122 PetscBool flg; 123 PetscMPIInt iflg; 124 FILE *fd; 125 int err; 126 127 PetscFunctionBegin; 128 PetscCallMPI(MPI_Comm_size(comm, &size)); 129 if (size == 1) { 130 *shared = PETSC_TRUE; 131 PetscFunctionReturn(PETSC_SUCCESS); 132 } 133 134 PetscCall(PetscOptionsGetenv(comm, "PETSC_SHARED_TMP", NULL, 0, &flg)); 135 if (flg) { 136 *shared = PETSC_TRUE; 137 PetscFunctionReturn(PETSC_SUCCESS); 138 } 139 140 PetscCall(PetscOptionsGetenv(comm, "PETSC_NOT_SHARED_TMP", NULL, 0, &flg)); 141 if (flg) { 142 *shared = PETSC_FALSE; 143 PetscFunctionReturn(PETSC_SUCCESS); 144 } 145 146 if (Petsc_SharedTmp_keyval == MPI_KEYVAL_INVALID) PetscCallMPI(MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, Petsc_DelTmpShared, &Petsc_SharedTmp_keyval, NULL)); 147 148 PetscCallMPI(MPI_Comm_get_attr(comm, Petsc_SharedTmp_keyval, (void **)&tagvalp, &iflg)); 149 if (!iflg) { 150 char filename[PETSC_MAX_PATH_LEN], tmpname[PETSC_MAX_PATH_LEN]; 151 152 /* This communicator does not yet have a shared tmp attribute */ 153 PetscCall(PetscMalloc1(1, &tagvalp)); 154 PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_SharedTmp_keyval, tagvalp)); 155 156 PetscCall(PetscOptionsGetenv(comm, "PETSC_TMP", tmpname, 238, &flg)); 157 if (!flg) { 158 PetscCall(PetscStrncpy(filename, "/tmp", sizeof(filename))); 159 } else { 160 PetscCall(PetscStrncpy(filename, tmpname, sizeof(filename))); 161 } 162 163 PetscCall(PetscStrlcat(filename, "/petsctestshared", sizeof(filename))); 164 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 165 166 /* each processor creates a /tmp file and all the later ones check */ 167 /* this makes sure no subset of processors is shared */ 168 *shared = PETSC_FALSE; 169 for (i = 0; i < size - 1; i++) { 170 if (rank == i) { 171 fd = fopen(filename, "w"); 172 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open test file %s", filename); 173 err = fclose(fd); 174 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file"); 175 } 176 PetscCallMPI(MPI_Barrier(comm)); 177 if (rank >= i) { 178 fd = fopen(filename, "r"); 179 if (fd) cnt = 1; 180 else cnt = 0; 181 if (fd) { 182 err = fclose(fd); 183 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file"); 184 } 185 } else cnt = 0; 186 187 PetscCallMPI(MPIU_Allreduce(&cnt, &sum, 1, MPI_INT, MPI_SUM, comm)); 188 if (rank == i) unlink(filename); 189 190 if (sum == size) { 191 *shared = PETSC_TRUE; 192 break; 193 } else PetscCheck(sum == 1, PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Subset of processes share /tmp "); 194 } 195 *tagvalp = (int)*shared; 196 PetscCall(PetscInfo(NULL, "processors %s %s\n", *shared ? "share" : "do NOT share", flg ? tmpname : "/tmp")); 197 } else *shared = (PetscBool)*tagvalp; 198 PetscFunctionReturn(PETSC_SUCCESS); 199 } 200 201 // "Unknown section 'Environmental Variables'" 202 // PetscClangLinter pragma disable: -fdoc-section-header-unknown 203 /*@ 204 PetscSharedWorkingDirectory - Determines if all processors in a communicator share a working directory or have different ones. 205 206 Collective 207 208 Input Parameter: 209 . comm - MPI_Communicator that may share working directory 210 211 Output Parameter: 212 . shared - `PETSC_TRUE` or `PETSC_FALSE` 213 214 Options Database Keys: 215 + -shared_working_directory - indicates the directory is known to be shared among the MPI processes 216 - -not_shared_working_directory - indicates the directory is known to be not shared among the MPI processes 217 218 Environmental Variables: 219 + `PETSC_SHARED_WORKING_DIRECTORY` - indicates the directory is known to be shared among the MPI processes 220 - `PETSC_NOT_SHARED_WORKING_DIRECTORY` - indicates the directory is known to be not shared among the MPI processes 221 222 Level: developer 223 224 Notes: 225 Stores the status as a MPI attribute so it does not have to be redetermined each time. 226 227 Assumes that all processors in a communicator either 228 .vb 229 1) have a common working directory or 230 2) each has a separate working directory 231 .ve 232 eventually we can write a fancier one that determines which processors share a common working directory. 233 234 This will be very slow on runs with a large number of processors since it requires O(p*p) file opens. 235 236 .seealso: `PetscGetTmp()`, `PetscSharedTmp()`, `PetscGetWorkingDirectory()`, `PetscGetHomeDirectory()` 237 @*/ 238 PetscErrorCode PetscSharedWorkingDirectory(MPI_Comm comm, PetscBool *shared) 239 { 240 PetscMPIInt size, rank, *tagvalp, sum, cnt, i; 241 PetscBool flg; 242 PetscMPIInt iflg; 243 FILE *fd; 244 int err; 245 246 PetscFunctionBegin; 247 PetscCallMPI(MPI_Comm_size(comm, &size)); 248 if (size == 1) { 249 *shared = PETSC_TRUE; 250 PetscFunctionReturn(PETSC_SUCCESS); 251 } 252 253 PetscCall(PetscOptionsGetenv(comm, "PETSC_SHARED_WORKING_DIRECTORY", NULL, 0, &flg)); 254 if (flg) { 255 *shared = PETSC_TRUE; 256 PetscFunctionReturn(PETSC_SUCCESS); 257 } 258 259 PetscCall(PetscOptionsGetenv(comm, "PETSC_NOT_SHARED_WORKING_DIRECTORY", NULL, 0, &flg)); 260 if (flg) { 261 *shared = PETSC_FALSE; 262 PetscFunctionReturn(PETSC_SUCCESS); 263 } 264 265 if (Petsc_SharedWD_keyval == MPI_KEYVAL_INVALID) PetscCallMPI(MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, Petsc_DelTmpShared, &Petsc_SharedWD_keyval, NULL)); 266 267 PetscCallMPI(MPI_Comm_get_attr(comm, Petsc_SharedWD_keyval, (void **)&tagvalp, &iflg)); 268 if (!iflg) { 269 char filename[PETSC_MAX_PATH_LEN]; 270 271 /* This communicator does not yet have a shared attribute */ 272 PetscCall(PetscMalloc1(1, &tagvalp)); 273 PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_SharedWD_keyval, tagvalp)); 274 275 PetscCall(PetscGetWorkingDirectory(filename, sizeof(filename) - 16)); 276 PetscCall(PetscStrlcat(filename, "/petsctestshared", sizeof(filename))); 277 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 278 279 /* each processor creates a file and all the later ones check */ 280 /* this makes sure no subset of processors is shared */ 281 *shared = PETSC_FALSE; 282 for (i = 0; i < size - 1; i++) { 283 if (rank == i) { 284 fd = fopen(filename, "w"); 285 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open test file %s", filename); 286 err = fclose(fd); 287 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file"); 288 } 289 PetscCallMPI(MPI_Barrier(comm)); 290 if (rank >= i) { 291 fd = fopen(filename, "r"); 292 if (fd) cnt = 1; 293 else cnt = 0; 294 if (fd) { 295 err = fclose(fd); 296 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file"); 297 } 298 } else cnt = 0; 299 300 PetscCallMPI(MPIU_Allreduce(&cnt, &sum, 1, MPI_INT, MPI_SUM, comm)); 301 if (rank == i) unlink(filename); 302 303 if (sum == size) { 304 *shared = PETSC_TRUE; 305 break; 306 } else PetscCheck(sum == 1, PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Subset of processes share working directory"); 307 } 308 *tagvalp = (int)*shared; 309 } else *shared = (PetscBool)*tagvalp; 310 PetscCall(PetscInfo(NULL, "processors %s working directory\n", (*shared) ? "shared" : "do NOT share")); 311 PetscFunctionReturn(PETSC_SUCCESS); 312 } 313 314 /*@C 315 PetscFileRetrieve - Obtains a file from a URL or a compressed file 316 and copies into local disk space as uncompressed. 317 318 Collective 319 320 Input Parameters: 321 + comm - processors accessing the file 322 . url - name of file, including entire URL (with or without .gz) 323 - llen - length of `localname` 324 325 Output Parameters: 326 + localname - name of local copy of file - valid on only process zero 327 - found - if found or retrieved the file - valid on all processes 328 329 Level: developer 330 331 Note: 332 if the file already exists locally this function just returns without downloading it. 333 334 .seealso: `PetscDLLibraryRetrieve()` 335 @*/ 336 PetscErrorCode PetscFileRetrieve(MPI_Comm comm, const char url[], char localname[], size_t llen, PetscBool *found) 337 { 338 char buffer[PETSC_MAX_PATH_LEN], *par = NULL, *tlocalname = NULL, name[PETSC_MAX_PATH_LEN]; 339 FILE *fp; 340 PetscMPIInt rank; 341 size_t len = 0; 342 PetscBool flg1, flg2, flg3, flg4, download, compressed = PETSC_FALSE; 343 344 PetscFunctionBegin; 345 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 346 if (rank == 0) { 347 *found = PETSC_FALSE; 348 349 PetscCall(PetscStrstr(url, ".gz", &par)); 350 if (par) { 351 PetscCall(PetscStrlen(par, &len)); 352 if (len == 3) compressed = PETSC_TRUE; 353 } 354 355 PetscCall(PetscStrncmp(url, "ftp://", 6, &flg1)); 356 PetscCall(PetscStrncmp(url, "http://", 7, &flg2)); 357 PetscCall(PetscStrncmp(url, "file://", 7, &flg3)); 358 PetscCall(PetscStrncmp(url, "https://", 8, &flg4)); 359 download = (PetscBool)(flg1 || flg2 || flg3 || flg4); 360 361 if (!download && !compressed) { 362 PetscCall(PetscStrncpy(localname, url, llen)); 363 PetscCall(PetscTestFile(url, 'r', found)); 364 if (*found) { 365 PetscCall(PetscInfo(NULL, "Found file %s\n", url)); 366 } else { 367 PetscCall(PetscInfo(NULL, "Did not find file %s\n", url)); 368 } 369 goto done; 370 } 371 372 /* look for uncompressed file in requested directory */ 373 if (compressed) { 374 PetscCall(PetscStrncpy(localname, url, llen)); 375 PetscCall(PetscStrstr(localname, ".gz", &par)); 376 *par = 0; /* remove .gz extension */ 377 PetscCall(PetscTestFile(localname, 'r', found)); 378 if (*found) goto done; 379 } 380 381 /* look for file in current directory */ 382 PetscCall(PetscStrrchr(url, '/', &tlocalname)); 383 PetscCall(PetscStrncpy(localname, tlocalname, llen)); 384 if (compressed) { 385 PetscCall(PetscStrstr(localname, ".gz", &par)); 386 *par = 0; /* remove .gz extension */ 387 } 388 PetscCall(PetscTestFile(localname, 'r', found)); 389 if (*found) goto done; 390 391 if (download) { 392 /* local file is not already here so use curl to get it */ 393 PetscCall(PetscStrncpy(localname, tlocalname, llen)); 394 PetscCall(PetscStrncpy(buffer, "curl --fail --silent --show-error ", sizeof(buffer))); 395 PetscCall(PetscStrlcat(buffer, url, sizeof(buffer))); 396 PetscCall(PetscStrlcat(buffer, " > ", sizeof(buffer))); 397 PetscCall(PetscStrlcat(buffer, localname, sizeof(buffer))); 398 #if defined(PETSC_HAVE_POPEN) 399 PetscCall(PetscPOpen(PETSC_COMM_SELF, NULL, buffer, "r", &fp)); 400 PetscCall(PetscPClose(PETSC_COMM_SELF, fp)); 401 #else 402 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot run external programs on this machine"); 403 #endif 404 PetscCall(PetscTestFile(localname, 'r', found)); 405 if (*found) { 406 FILE *fd; 407 char buf[1024], *str, *substring; 408 409 /* check if the file didn't exist so it downloaded an HTML message instead */ 410 fd = fopen(localname, "r"); 411 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscTestFile() indicates %s exists but fopen() cannot open it", localname); 412 str = fgets(buf, sizeof(buf) - 1, fd); 413 while (str) { 414 PetscCall(PetscStrstr(buf, "<!DOCTYPE html>", &substring)); 415 PetscCheck(!substring, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unable to download %s it does not appear to exist at this URL, dummy HTML file was downloaded", url); 416 PetscCall(PetscStrstr(buf, "Not Found", &substring)); 417 PetscCheck(!substring, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unable to download %s it does not appear to exist at this URL, dummy HTML file was downloaded", url); 418 str = fgets(buf, sizeof(buf) - 1, fd); 419 } 420 fclose(fd); 421 } 422 } else if (compressed) { 423 PetscCall(PetscTestFile(url, 'r', found)); 424 if (!*found) goto done; 425 PetscCall(PetscStrncpy(localname, url, llen)); 426 } 427 if (compressed) { 428 PetscCall(PetscStrrchr(localname, '/', &tlocalname)); 429 PetscCall(PetscStrncpy(name, tlocalname, PETSC_MAX_PATH_LEN)); 430 PetscCall(PetscStrstr(name, ".gz", &par)); 431 *par = 0; /* remove .gz extension */ 432 /* uncompress file */ 433 PetscCall(PetscStrncpy(buffer, "gzip -c -d ", sizeof(buffer))); 434 PetscCall(PetscStrlcat(buffer, localname, sizeof(buffer))); 435 PetscCall(PetscStrlcat(buffer, " > ", sizeof(buffer))); 436 PetscCall(PetscStrlcat(buffer, name, sizeof(buffer))); 437 #if defined(PETSC_HAVE_POPEN) 438 PetscCall(PetscPOpen(PETSC_COMM_SELF, NULL, buffer, "r", &fp)); 439 PetscCall(PetscPClose(PETSC_COMM_SELF, fp)); 440 #else 441 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot run external programs on this machine"); 442 #endif 443 PetscCall(PetscStrncpy(localname, name, llen)); 444 PetscCall(PetscTestFile(localname, 'r', found)); 445 } 446 } 447 done: 448 PetscCallMPI(MPI_Bcast(found, 1, MPI_C_BOOL, 0, comm)); 449 PetscCallMPI(MPI_Bcast(localname, (PetscMPIInt)llen, MPI_CHAR, 0, comm)); 450 PetscFunctionReturn(PETSC_SUCCESS); 451 } 452