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