Actual source code: fretrieve.c

  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>

 23: /*
 24:    Private routine to delete tmp/shared storage

 26:    This is called by MPI, not by users, when communicator attributes are deleted

 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: }

 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`

 42:   Collective

 44:   Input Parameters:
 45: + comm - MPI_Communicator that may share tmp
 46: - len  - length of string to hold name

 48:   Output Parameter:
 49: . dir - directory name

 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

 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

 61:   Level: developer

 63: .seealso: `PetscSharedTmp()`, `PetscSharedWorkingDirectory()`, `PetscGetWorkingDirectory()`, `PetscGetHomeDirectory()`
 64: @*/
 65: PetscErrorCode PetscGetTmp(MPI_Comm comm, char dir[], size_t len)
 66: {
 67:   PetscBool flg;

 69:   PetscFunctionBegin;
 70:   PetscCall(PetscOptionsGetenv(comm, "PETSC_TMP", dir, len, &flg));
 71:   if (!flg) PetscCall(PetscStrncpy(dir, "/tmp", len));
 72:   PetscFunctionReturn(PETSC_SUCCESS);
 73: }

 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.

 81:   Collective

 83:   Input Parameter:
 84: . comm - MPI_Communicator that may share tmp

 86:   Output Parameter:
 87: . shared - `PETSC_TRUE` or `PETSC_FALSE`

 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

 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

 99:   Level: developer

101:   Notes:
102:   Stores the status as a MPI attribute so it does not have
103:   to be redetermined each time.

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.

111:   This will be very slow on runs with a large number of processors since
112:   it requires O(p*p) file opens.

114:   If the environmental variable `PETSC_TMP` is set it will use this directory
115:   as the "tmp" directory.

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;

127:   PetscFunctionBegin;
128:   PetscCallMPI(MPI_Comm_size(comm, &size));
129:   if (size == 1) {
130:     *shared = PETSC_TRUE;
131:     PetscFunctionReturn(PETSC_SUCCESS);
132:   }

134:   PetscCall(PetscOptionsGetenv(comm, "PETSC_SHARED_TMP", NULL, 0, &flg));
135:   if (flg) {
136:     *shared = PETSC_TRUE;
137:     PetscFunctionReturn(PETSC_SUCCESS);
138:   }

140:   PetscCall(PetscOptionsGetenv(comm, "PETSC_NOT_SHARED_TMP", NULL, 0, &flg));
141:   if (flg) {
142:     *shared = PETSC_FALSE;
143:     PetscFunctionReturn(PETSC_SUCCESS);
144:   }

146:   if (Petsc_SharedTmp_keyval == MPI_KEYVAL_INVALID) PetscCallMPI(MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, Petsc_DelTmpShared, &Petsc_SharedTmp_keyval, NULL));

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];

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));

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:     }

163:     PetscCall(PetscStrlcat(filename, "/petsctestshared", sizeof(filename)));
164:     PetscCallMPI(MPI_Comm_rank(comm, &rank));

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;

187:       PetscCallMPI(MPIU_Allreduce(&cnt, &sum, 1, MPI_INT, MPI_SUM, comm));
188:       if (rank == i) unlink(filename);

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: }

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.

206:   Collective

208:   Input Parameter:
209: . comm - MPI_Communicator that may share working directory

211:   Output Parameter:
212: . shared - `PETSC_TRUE` or `PETSC_FALSE`

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

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

222:   Level: developer

224:   Notes:
225:   Stores the status as a MPI attribute so it does not have to be redetermined each time.

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.

234:   This will be very slow on runs with a large number of processors since it requires O(p*p) file opens.

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;

246:   PetscFunctionBegin;
247:   PetscCallMPI(MPI_Comm_size(comm, &size));
248:   if (size == 1) {
249:     *shared = PETSC_TRUE;
250:     PetscFunctionReturn(PETSC_SUCCESS);
251:   }

253:   PetscCall(PetscOptionsGetenv(comm, "PETSC_SHARED_WORKING_DIRECTORY", NULL, 0, &flg));
254:   if (flg) {
255:     *shared = PETSC_TRUE;
256:     PetscFunctionReturn(PETSC_SUCCESS);
257:   }

259:   PetscCall(PetscOptionsGetenv(comm, "PETSC_NOT_SHARED_WORKING_DIRECTORY", NULL, 0, &flg));
260:   if (flg) {
261:     *shared = PETSC_FALSE;
262:     PetscFunctionReturn(PETSC_SUCCESS);
263:   }

265:   if (Petsc_SharedWD_keyval == MPI_KEYVAL_INVALID) PetscCallMPI(MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, Petsc_DelTmpShared, &Petsc_SharedWD_keyval, NULL));

267:   PetscCallMPI(MPI_Comm_get_attr(comm, Petsc_SharedWD_keyval, (void **)&tagvalp, &iflg));
268:   if (!iflg) {
269:     char filename[PETSC_MAX_PATH_LEN];

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));

275:     PetscCall(PetscGetWorkingDirectory(filename, sizeof(filename) - 16));
276:     PetscCall(PetscStrlcat(filename, "/petsctestshared", sizeof(filename)));
277:     PetscCallMPI(MPI_Comm_rank(comm, &rank));

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;

300:       PetscCallMPI(MPIU_Allreduce(&cnt, &sum, 1, MPI_INT, MPI_SUM, comm));
301:       if (rank == i) unlink(filename);

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: }

314: /*@C
315:   PetscFileRetrieve - Obtains a file from a URL or a compressed file
316:   and copies into local disk space as uncompressed.

318:   Collective

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`

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

329:   Level: developer

331:   Note:
332:   if the file already exists locally this function just returns without downloading it.

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;

344:   PetscFunctionBegin;
345:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
346:   if (rank == 0) {
347:     *found = PETSC_FALSE;

349:     PetscCall(PetscStrstr(url, ".gz", &par));
350:     if (par) {
351:       PetscCall(PetscStrlen(par, &len));
352:       if (len == 3) compressed = PETSC_TRUE;
353:     }

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);

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:     }

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:     }

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;

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;

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, MPIU_BOOL, 0, comm));
449:   PetscCallMPI(MPI_Bcast(localname, (PetscMPIInt)llen, MPI_CHAR, 0, comm));
450:   PetscFunctionReturn(PETSC_SUCCESS);
451: }