xref: /petsc/src/sys/fileio/mpiuopen.c (revision 66af8762ec03dbef0e079729eb2a1734a35ed7ff)
1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for popen() */
2 /*
3       Some PETSc utility routines to add simple parallel IO capabilities
4 */
5 #include <petscsys.h>
6 #include <petsc/private/logimpl.h>
7 #include <errno.h>
8 
9 /*@C
10   PetscFOpen - Has the first process in the MPI communicator open a file;
11   all others do nothing.
12 
13   Logically Collective; No Fortran Support
14 
15   Input Parameters:
16 + comm - the MPI communicator
17 . name - the filename
18 - mode - the mode for `fopen()`, usually "w"
19 
20   Output Parameter:
21 . fp - the file pointer
22 
23   Level: developer
24 
25   Note:
26   `NULL`, "stderr" or "stdout" may be passed in as the filename
27 
28 .seealso: `PetscFClose()`, `PetscSynchronizedFGets()`, `PetscSynchronizedPrintf()`, `PetscSynchronizedFlush()`,
29           `PetscFPrintf()`
30 @*/
31 PetscErrorCode PetscFOpen(MPI_Comm comm, const char name[], const char mode[], FILE **fp)
32 {
33   PetscMPIInt rank;
34   FILE       *fd;
35   char        fname[PETSC_MAX_PATH_LEN], tname[PETSC_MAX_PATH_LEN];
36 
37   PetscFunctionBegin;
38   PetscCallMPI(MPI_Comm_rank(comm, &rank));
39   if (rank == 0) {
40     PetscBool isstdout, isstderr;
41     PetscCall(PetscStrcmp(name, "stdout", &isstdout));
42     PetscCall(PetscStrcmp(name, "stderr", &isstderr));
43     if (isstdout || !name) fd = PETSC_STDOUT;
44     else if (isstderr) fd = PETSC_STDERR;
45     else {
46       PetscBool devnull = PETSC_FALSE;
47       PetscCall(PetscStrreplace(PETSC_COMM_SELF, name, tname, PETSC_MAX_PATH_LEN));
48       PetscCall(PetscFixFilename(tname, fname));
49       PetscCall(PetscStrbeginswith(fname, "/dev/null", &devnull));
50       if (devnull) PetscCall(PetscStrncpy(fname, "/dev/null", sizeof(fname)));
51       PetscCall(PetscInfo(0, "Opening file %s\n", fname));
52       fd = fopen(fname, mode);
53       PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open file %s", fname);
54     }
55   } else fd = NULL;
56   *fp = fd;
57   PetscFunctionReturn(PETSC_SUCCESS);
58 }
59 
60 /*@C
61   PetscFClose - Has MPI rank 0 in the communicator close a
62   file (usually obtained with `PetscFOpen()`; all others do nothing.
63 
64   Logically Collective; No Fortran Support
65 
66   Input Parameters:
67 + comm - the MPI communicator
68 - fd   - the file, opened with `PetscFOpen()`
69 
70   Level: developer
71 
72 .seealso: `PetscFOpen()`
73 @*/
74 PetscErrorCode PetscFClose(MPI_Comm comm, FILE *fd)
75 {
76   PetscMPIInt rank;
77   int         err;
78 
79   PetscFunctionBegin;
80   PetscCallMPI(MPI_Comm_rank(comm, &rank));
81   if (rank == 0 && fd != PETSC_STDOUT && fd != PETSC_STDERR) {
82     err = fclose(fd);
83     PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file");
84   }
85   PetscFunctionReturn(PETSC_SUCCESS);
86 }
87 
88 #if defined(PETSC_HAVE_POPEN)
89 static char PetscPOpenMachine[128] = "";
90 
91 /*@C
92   PetscPClose - Closes (ends) a program on MPI rank 0 run with `PetscPOpen()`
93 
94   Collective, but only MPI rank 0 does anything
95 
96   Input Parameters:
97 + comm - MPI communicator, only rank 0 performs the close
98 - fd   - the file pointer where program input or output may be read or `NULL` if don't care
99 
100   Level: intermediate
101 
102   Note:
103   Does not work under Microsoft Windows
104 
105 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPOpen()`
106 @*/
107 PetscErrorCode PetscPClose(MPI_Comm comm, FILE *fd)
108 {
109   PetscMPIInt rank;
110 
111   PetscFunctionBegin;
112   PetscCallMPI(MPI_Comm_rank(comm, &rank));
113   if (rank == 0) {
114     char buf[1024];
115     while (fgets(buf, 1024, fd))
116       ; /* wait till it prints everything */
117     (void)pclose(fd);
118   }
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 /*@C
123   PetscPOpen - Runs a program on MPI rank 0 and sends either its input or output to
124   a file.
125 
126   Logically Collective, but only MPI rank 0 runs the command
127 
128   Input Parameters:
129 + comm    - MPI communicator, only processor zero runs the program
130 . machine - machine to run command on or `NULL`, or a string with 0 in first location
131 . program - name of program to run
132 - mode    - either "r" or "w"
133 
134   Output Parameter:
135 . fp - the file pointer where program input or output may be read or `NULL` if results are not needed
136 
137   Level: intermediate
138 
139   Notes:
140   Use `PetscPClose()` to close the file pointer when you are finished with it
141 
142   Does not work under Microsoft Windows
143 
144   If machine is not provided will use the value set with `PetsPOpenSetMachine()` if that was provided, otherwise
145   will use the machine running MPI rank 0 of the communicator
146 
147   The program string may contain ${DISPLAY}, ${HOMEDIRECTORY} or ${WORKINGDIRECTORY}; these
148   will be replaced with relevant values.
149 
150 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpenSetMachine()`
151 @*/
152 PetscErrorCode PetscPOpen(MPI_Comm comm, const char machine[], const char program[], const char mode[], FILE **fp)
153 {
154   PetscMPIInt rank;
155   size_t      i, len, cnt;
156   char        commandt[PETSC_MAX_PATH_LEN], command[PETSC_MAX_PATH_LEN];
157   FILE       *fd;
158 
159   PetscFunctionBegin;
160   /* all processors have to do the string manipulation because PetscStrreplace() is a collective operation */
161   if (PetscPOpenMachine[0] || (machine && machine[0])) {
162     PetscCall(PetscStrncpy(command, "ssh ", sizeof(command)));
163     if (PetscPOpenMachine[0]) {
164       PetscCall(PetscStrlcat(command, PetscPOpenMachine, sizeof(command)));
165     } else {
166       PetscCall(PetscStrlcat(command, machine, sizeof(command)));
167     }
168     PetscCall(PetscStrlcat(command, " \" export DISPLAY=${DISPLAY}; ", sizeof(command)));
169     /*
170         Copy program into command but protect the " with a \ in front of it
171     */
172     PetscCall(PetscStrlen(command, &cnt));
173     PetscCall(PetscStrlen(program, &len));
174     for (i = 0; i < len; i++) {
175       if (program[i] == '\"') command[cnt++] = '\\';
176       command[cnt++] = program[i];
177     }
178     command[cnt] = 0;
179 
180     PetscCall(PetscStrlcat(command, "\"", sizeof(command)));
181   } else {
182     PetscCall(PetscStrncpy(command, program, sizeof(command)));
183   }
184 
185   PetscCall(PetscStrreplace(comm, command, commandt, 1024));
186 
187   PetscCallMPI(MPI_Comm_rank(comm, &rank));
188   if (rank == 0) {
189     PetscCall(PetscInfo(NULL, "Running command :%s\n", commandt));
190     PetscCheck((fd = popen(commandt, mode)), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot run command %s", commandt);
191     if (fp) *fp = fd;
192   }
193   PetscFunctionReturn(PETSC_SUCCESS);
194 }
195 
196 /*@C
197   PetscPOpenSetMachine - Sets the name of the default machine to run `PetscPOpen()` calls on
198 
199   Logically Collective, but only MPI rank 0 runs the command
200 
201   Input Parameter:
202 . machine - machine to run command on or `NULL` for the current machine
203 
204   Options Database Key:
205 . -popen_machine <machine> - run the process on this machine
206 
207   Level: intermediate
208 
209 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpen()`
210 @*/
211 PetscErrorCode PetscPOpenSetMachine(const char machine[])
212 {
213   PetscFunctionBegin;
214   if (machine) {
215     PetscCall(PetscStrncpy(PetscPOpenMachine, machine, sizeof(PetscPOpenMachine)));
216   } else {
217     PetscPOpenMachine[0] = 0;
218   }
219   PetscFunctionReturn(PETSC_SUCCESS);
220 }
221 
222 #endif
223