Actual source code: mpiuopen.c

  1: #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for popen() */
  2: /*
  3:       Some PETSc utility routines to add simple parallel IO capabilities
  4: */
  5: #include <petsc/private/logimpl.h>
  6: #include <errno.h>

  8: /*@
  9:   PetscFOpen - Has the first process in the MPI communicator open a file;
 10:   all others do nothing.

 12:   Logically Collective

 14:   Input Parameters:
 15: + comm - the MPI communicator
 16: . name - the filename
 17: - mode - the mode for `fopen()`, usually "w"

 19:   Output Parameter:
 20: . fp - the file pointer

 22:   Level: developer

 24:   Note:
 25:   `NULL`, "stderr" or "stdout" may be passed in as the filename

 27: .seealso: `PetscFClose()`, `PetscSynchronizedFGets()`, `PetscSynchronizedPrintf()`, `PetscSynchronizedFlush()`,
 28:           `PetscFPrintf()`
 29: @*/
 30: PetscErrorCode PetscFOpen(MPI_Comm comm, const char name[], const char mode[], FILE **fp)
 31: {
 32:   PetscMPIInt rank;
 33:   FILE       *fd;
 34:   char        fname[PETSC_MAX_PATH_LEN], tname[PETSC_MAX_PATH_LEN];

 36:   PetscFunctionBegin;
 37:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
 38:   if (rank == 0) {
 39:     PetscBool isstdout, isstderr;
 40:     PetscCall(PetscStrcmp(name, "stdout", &isstdout));
 41:     PetscCall(PetscStrcmp(name, "stderr", &isstderr));
 42:     if (isstdout || !name) fd = PETSC_STDOUT;
 43:     else if (isstderr) fd = PETSC_STDERR;
 44:     else {
 45:       PetscBool devnull = PETSC_FALSE;
 46:       PetscCall(PetscStrreplace(PETSC_COMM_SELF, name, tname, PETSC_MAX_PATH_LEN));
 47:       PetscCall(PetscFixFilename(tname, fname));
 48:       PetscCall(PetscStrbeginswith(fname, "/dev/null", &devnull));
 49:       if (devnull) PetscCall(PetscStrncpy(fname, "/dev/null", sizeof(fname)));
 50:       PetscCall(PetscInfo(0, "Opening file %s\n", fname));
 51:       fd = fopen(fname, mode);
 52:       PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open file %s", fname);
 53:     }
 54:   } else fd = NULL;
 55:   *fp = fd;
 56:   PetscFunctionReturn(PETSC_SUCCESS);
 57: }

 59: /*@
 60:   PetscFClose - Has MPI rank 0 in the communicator close a
 61:   file (usually obtained with `PetscFOpen()`; all others do nothing.

 63:   Logically Collective

 65:   Input Parameters:
 66: + comm - the MPI communicator
 67: - fd   - the file, opened with `PetscFOpen()`

 69:   Level: developer

 71: .seealso: `PetscFOpen()`
 72: @*/
 73: PetscErrorCode PetscFClose(MPI_Comm comm, FILE *fd)
 74: {
 75:   PetscMPIInt rank;
 76:   int         err;

 78:   PetscFunctionBegin;
 79:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
 80:   if (rank == 0 && fd != PETSC_STDOUT && fd != PETSC_STDERR) {
 81:     err = fclose(fd);
 82:     PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file");
 83:   }
 84:   PetscFunctionReturn(PETSC_SUCCESS);
 85: }

 87: static char PetscPOpenMachine[128] = "";

 89: /*@
 90:   PetscPClose - Closes (ends) a program on MPI rank 0 run with `PetscPOpen()`

 92:   Collective, but only MPI rank 0 does anything

 94:   Input Parameters:
 95: + comm - MPI communicator, only rank 0 performs the close
 96: - fd   - the file pointer where program input or output may be read or `NULL` if don't care

 98:   Level: intermediate

100:   Note:
101:   Does not work under Microsoft Windows

103: .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPOpen()`
104: @*/
105: PetscErrorCode PetscPClose(MPI_Comm comm, FILE *fd)
106: {
107: #if PetscDefined(HAVE_POPEN)
108:   PetscMPIInt rank;
109: #endif

111:   PetscFunctionBegin;
112: #if PetscDefined(HAVE_POPEN)
113:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
114:   if (rank == 0) {
115:     char buf[1024];
116:     while (fgets(buf, 1024, fd)); /* wait till it prints everything */
117:     (void)pclose(fd);
118:   }
119: #else
120:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "pclose() - routine is unavailable.");
121: #endif
122:   PetscFunctionReturn(PETSC_SUCCESS);
123: }

125: /*@
126:   PetscPOpen - Runs a program on MPI rank 0 and sends either its input or output to
127:   a file.

129:   Logically Collective, but only MPI rank 0 runs the command

131:   Input Parameters:
132: + comm    - MPI communicator, only processor zero runs the program
133: . machine - machine to run command on or `NULL`, or a string with 0 in first location
134: . program - name of program to run
135: - mode    - either "r" or "w"

137:   Output Parameter:
138: . fp - the file pointer where program input or output may be read or `NULL` if results are not needed

140:   Level: intermediate

142:   Notes:
143:   Use `PetscPClose()` to close the file pointer when you are finished with it

145:   Does not work under Microsoft Windows

147:   If machine is not provided will use the value set with `PetscPOpenSetMachine()` if that was provided, otherwise
148:   will use the machine running MPI rank 0 of the communicator

150:   The program string may contain ${DISPLAY}, ${HOMEDIRECTORY} or ${WORKINGDIRECTORY}; these
151:   will be replaced with relevant values.

153: .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpenSetMachine()`
154: @*/
155: PetscErrorCode PetscPOpen(MPI_Comm comm, const char machine[], const char program[], const char mode[], FILE **fp)
156: {
157: #if PetscDefined(HAVE_POPEN)
158:   PetscMPIInt rank;
159:   size_t      i, len, cnt;
160:   char        commandt[PETSC_MAX_PATH_LEN], command[PETSC_MAX_PATH_LEN];
161:   FILE       *fd;
162: #endif

164:   PetscFunctionBegin;
165: #if PetscDefined(HAVE_POPEN)
166:   /* all processors have to do the string manipulation because PetscStrreplace() is a collective operation */
167:   if (PetscPOpenMachine[0] || (machine && machine[0])) {
168:     PetscCall(PetscStrncpy(command, "ssh ", sizeof(command)));
169:     if (PetscPOpenMachine[0]) {
170:       PetscCall(PetscStrlcat(command, PetscPOpenMachine, sizeof(command)));
171:     } else {
172:       PetscCall(PetscStrlcat(command, machine, sizeof(command)));
173:     }
174:     PetscCall(PetscStrlcat(command, " \" export DISPLAY=${DISPLAY}; ", sizeof(command)));
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;

186:     PetscCall(PetscStrlcat(command, "\"", sizeof(command)));
187:   } else {
188:     PetscCall(PetscStrncpy(command, program, sizeof(command)));
189:   }

191:   PetscCall(PetscStrreplace(comm, command, commandt, 1024));

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: #else
200:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "popen() - system routine is unavailable.");
201: #endif
202:   PetscFunctionReturn(PETSC_SUCCESS);
203: }

205: /*@
206:   PetscPOpenSetMachine - Sets the name of the default machine to run `PetscPOpen()` calls on

208:   Logically Collective, but only the MPI process with rank 0 runs the command

210:   Input Parameter:
211: . machine - machine to run command on or `NULL` for the current machine

213:   Options Database Key:
214: . -popen_machine machine - run the process on this machine

216:   Level: intermediate

218: .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpen()`
219: @*/
220: PetscErrorCode PetscPOpenSetMachine(const char machine[])
221: {
222:   PetscFunctionBegin;
223:   if (machine) {
224:     PetscCall(PetscStrncpy(PetscPOpenMachine, machine, sizeof(PetscPOpenMachine)));
225:   } else {
226:     PetscPOpenMachine[0] = 0;
227:   }
228:   PetscFunctionReturn(PETSC_SUCCESS);
229: }