xref: /petsc/src/sys/fileio/mpiuopen.c (revision df4cd43f92eaa320656440c40edb1046daee8f75)
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; No Fortran Support
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 .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(PetscStrcpy(fname, "/dev/null"));
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 the first processor in the communicator close a
62     file; all others do nothing.
63 
64     Logically Collective; No Fortran Support
65 
66     Input Parameters:
67 +   comm - the 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 processor zero run with `PetscPOpen()`
93 
94      Collective, but only process 0 runs the command
95 
96    Input Parameters:
97 +   comm - MPI communicator, only processor zero runs the program
98 -   fp - 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 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 processor zero and sends either its input or output to
124           a file.
125 
126      Logically Collective, but only process 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 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 don't care
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 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 node zero 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(PetscStrcpy(command, "ssh "));
163     if (PetscPOpenMachine[0]) {
164       PetscCall(PetscStrcat(command, PetscPOpenMachine));
165     } else {
166       PetscCall(PetscStrcat(command, machine));
167     }
168     PetscCall(PetscStrcat(command, " \" export DISPLAY=${DISPLAY}; "));
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(PetscStrcat(command, "\""));
181   } else {
182     PetscCall(PetscStrcpy(command, program));
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 process 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(PetscStrcpy(PetscPOpenMachine, machine));
216   } else {
217     PetscPOpenMachine[0] = 0;
218   }
219   PetscFunctionReturn(PETSC_SUCCESS);
220 }
221 
222 #endif
223