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 */
Petsc_DelTmpShared(MPI_Comm comm,PetscMPIInt keyval,void * count_val,void * extra_state)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 @*/
PetscGetTmp(MPI_Comm comm,char dir[],size_t len)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 @*/
PetscSharedTmp(MPI_Comm comm,PetscBool * shared)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 @*/
PetscSharedWorkingDirectory(MPI_Comm comm,PetscBool * shared)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 @*/
PetscFileRetrieve(MPI_Comm comm,const char url[],char localname[],size_t llen,PetscBool * found)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