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