xref: /petsc/src/sys/fileio/mpiuopen.c (revision 98d129c30f3ee9fdddc40fdbc5a989b7be64f888)
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)); /* wait till it prints everything */
116     (void)pclose(fd);
117   }
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 /*@C
122   PetscPOpen - Runs a program on MPI rank 0 and sends either its input or output to
123   a file.
124 
125   Logically Collective, but only MPI rank 0 runs the command
126 
127   Input Parameters:
128 + comm    - MPI communicator, only processor zero runs the program
129 . machine - machine to run command on or `NULL`, or a string with 0 in first location
130 . program - name of program to run
131 - mode    - either "r" or "w"
132 
133   Output Parameter:
134 . fp - the file pointer where program input or output may be read or `NULL` if results are not needed
135 
136   Level: intermediate
137 
138   Notes:
139   Use `PetscPClose()` to close the file pointer when you are finished with it
140 
141   Does not work under Microsoft Windows
142 
143   If machine is not provided will use the value set with `PetsPOpenSetMachine()` if that was provided, otherwise
144   will use the machine running MPI rank 0 of the communicator
145 
146   The program string may contain ${DISPLAY}, ${HOMEDIRECTORY} or ${WORKINGDIRECTORY}; these
147   will be replaced with relevant values.
148 
149 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpenSetMachine()`
150 @*/
151 PetscErrorCode PetscPOpen(MPI_Comm comm, const char machine[], const char program[], const char mode[], FILE **fp)
152 {
153   PetscMPIInt rank;
154   size_t      i, len, cnt;
155   char        commandt[PETSC_MAX_PATH_LEN], command[PETSC_MAX_PATH_LEN];
156   FILE       *fd;
157 
158   PetscFunctionBegin;
159   /* all processors have to do the string manipulation because PetscStrreplace() is a collective operation */
160   if (PetscPOpenMachine[0] || (machine && machine[0])) {
161     PetscCall(PetscStrncpy(command, "ssh ", sizeof(command)));
162     if (PetscPOpenMachine[0]) {
163       PetscCall(PetscStrlcat(command, PetscPOpenMachine, sizeof(command)));
164     } else {
165       PetscCall(PetscStrlcat(command, machine, sizeof(command)));
166     }
167     PetscCall(PetscStrlcat(command, " \" export DISPLAY=${DISPLAY}; ", sizeof(command)));
168     /*
169         Copy program into command but protect the " with a \ in front of it
170     */
171     PetscCall(PetscStrlen(command, &cnt));
172     PetscCall(PetscStrlen(program, &len));
173     for (i = 0; i < len; i++) {
174       if (program[i] == '\"') command[cnt++] = '\\';
175       command[cnt++] = program[i];
176     }
177     command[cnt] = 0;
178 
179     PetscCall(PetscStrlcat(command, "\"", sizeof(command)));
180   } else {
181     PetscCall(PetscStrncpy(command, program, sizeof(command)));
182   }
183 
184   PetscCall(PetscStrreplace(comm, command, commandt, 1024));
185 
186   PetscCallMPI(MPI_Comm_rank(comm, &rank));
187   if (rank == 0) {
188     PetscCall(PetscInfo(NULL, "Running command :%s\n", commandt));
189     PetscCheck((fd = popen(commandt, mode)), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot run command %s", commandt);
190     if (fp) *fp = fd;
191   }
192   PetscFunctionReturn(PETSC_SUCCESS);
193 }
194 
195 /*@C
196   PetscPOpenSetMachine - Sets the name of the default machine to run `PetscPOpen()` calls on
197 
198   Logically Collective, but only the MPI process with rank 0 runs the command
199 
200   Input Parameter:
201 . machine - machine to run command on or `NULL` for the current machine
202 
203   Options Database Key:
204 . -popen_machine <machine> - run the process on this machine
205 
206   Level: intermediate
207 
208 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpen()`
209 @*/
210 PetscErrorCode PetscPOpenSetMachine(const char machine[])
211 {
212   PetscFunctionBegin;
213   if (machine) {
214     PetscCall(PetscStrncpy(PetscPOpenMachine, machine, sizeof(PetscPOpenMachine)));
215   } else {
216     PetscPOpenMachine[0] = 0;
217   }
218   PetscFunctionReturn(PETSC_SUCCESS);
219 }
220 
221 #endif
222