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