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