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 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 Fortran Note: 29 This routine is not supported in Fortran. 30 31 .seealso: `PetscFClose()`, `PetscSynchronizedFGets()`, `PetscSynchronizedPrintf()`, `PetscSynchronizedFlush()`, 32 `PetscFPrintf()` 33 @*/ 34 PetscErrorCode PetscFOpen(MPI_Comm comm, const char name[], const char mode[], FILE **fp) 35 { 36 PetscMPIInt rank; 37 FILE *fd; 38 char fname[PETSC_MAX_PATH_LEN], tname[PETSC_MAX_PATH_LEN]; 39 40 PetscFunctionBegin; 41 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 42 if (rank == 0) { 43 PetscBool isstdout, isstderr; 44 PetscCall(PetscStrcmp(name, "stdout", &isstdout)); 45 PetscCall(PetscStrcmp(name, "stderr", &isstderr)); 46 if (isstdout || !name) fd = PETSC_STDOUT; 47 else if (isstderr) fd = PETSC_STDERR; 48 else { 49 PetscBool devnull; 50 PetscCall(PetscStrreplace(PETSC_COMM_SELF, name, tname, PETSC_MAX_PATH_LEN)); 51 PetscCall(PetscFixFilename(tname, fname)); 52 PetscCall(PetscStrbeginswith(fname, "/dev/null", &devnull)); 53 if (devnull) PetscCall(PetscStrcpy(fname, "/dev/null")); 54 PetscCall(PetscInfo(0, "Opening file %s\n", fname)); 55 fd = fopen(fname, mode); 56 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open file %s", fname); 57 } 58 } else fd = NULL; 59 *fp = fd; 60 PetscFunctionReturn(0); 61 } 62 63 /*@C 64 PetscFClose - Has the first processor in the communicator close a 65 file; all others do nothing. 66 67 Logically Collective 68 69 Input Parameters: 70 + comm - the communicator 71 - fd - the file, opened with PetscFOpen() 72 73 Level: developer 74 75 Fortran Note: 76 This routine is not supported in Fortran. 77 78 .seealso: `PetscFOpen()` 79 @*/ 80 PetscErrorCode PetscFClose(MPI_Comm comm, FILE *fd) 81 { 82 PetscMPIInt rank; 83 int err; 84 85 PetscFunctionBegin; 86 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 87 if (rank == 0 && fd != PETSC_STDOUT && fd != PETSC_STDERR) { 88 err = fclose(fd); 89 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on file"); 90 } 91 PetscFunctionReturn(0); 92 } 93 94 #if defined(PETSC_HAVE_POPEN) 95 static char PetscPOpenMachine[128] = ""; 96 97 /*@C 98 PetscPClose - Closes (ends) a program on processor zero run with `PetscPOpen()` 99 100 Collective, but only process 0 runs the command 101 102 Input Parameters: 103 + comm - MPI communicator, only processor zero runs the program 104 - fp - the file pointer where program input or output may be read or NULL if don't care 105 106 Level: intermediate 107 108 Note: 109 Does not work under Windows 110 111 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPOpen()` 112 @*/ 113 PetscErrorCode PetscPClose(MPI_Comm comm, FILE *fd) 114 { 115 PetscMPIInt rank; 116 117 PetscFunctionBegin; 118 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 119 if (rank == 0) { 120 char buf[1024]; 121 while (fgets(buf, 1024, fd)) 122 ; /* wait till it prints everything */ 123 (void)pclose(fd); 124 } 125 PetscFunctionReturn(0); 126 } 127 128 /*@C 129 PetscPOpen - Runs a program on processor zero and sends either its input or output to 130 a file. 131 132 Logically Collective, but only process 0 runs the command 133 134 Input Parameters: 135 + comm - MPI communicator, only processor zero runs the program 136 . machine - machine to run command on or NULL, or string with 0 in first location 137 . program - name of program to run 138 - mode - either r or w 139 140 Output Parameter: 141 . fp - the file pointer where program input or output may be read or NULL if don't care 142 143 Level: intermediate 144 145 Notes: 146 Use `PetscPClose()` to close the file pointer when you are finished with it 147 148 Does not work under Windows 149 150 If machine is not provided will use the value set with `PetsPOpenSetMachine()` if that was provided, otherwise 151 will use the machine running node zero of the communicator 152 153 The program string may contain ${DISPLAY}, ${HOMEDIRECTORY} or ${WORKINGDIRECTORY}; these 154 will be replaced with relevant values. 155 156 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpenSetMachine()` 157 @*/ 158 PetscErrorCode PetscPOpen(MPI_Comm comm, const char machine[], const char program[], const char mode[], FILE **fp) 159 { 160 PetscMPIInt rank; 161 size_t i, len, cnt; 162 char commandt[PETSC_MAX_PATH_LEN], command[PETSC_MAX_PATH_LEN]; 163 FILE *fd; 164 165 PetscFunctionBegin; 166 /* all processors have to do the string manipulation because PetscStrreplace() is a collective operation */ 167 if (PetscPOpenMachine[0] || (machine && machine[0])) { 168 PetscCall(PetscStrcpy(command, "ssh ")); 169 if (PetscPOpenMachine[0]) { 170 PetscCall(PetscStrcat(command, PetscPOpenMachine)); 171 } else { 172 PetscCall(PetscStrcat(command, machine)); 173 } 174 PetscCall(PetscStrcat(command, " \" export DISPLAY=${DISPLAY}; ")); 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; 185 186 PetscCall(PetscStrcat(command, "\"")); 187 } else { 188 PetscCall(PetscStrcpy(command, program)); 189 } 190 191 PetscCall(PetscStrreplace(comm, command, commandt, 1024)); 192 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 PetscFunctionReturn(0); 200 } 201 202 /*@C 203 PetscPOpenSetMachine - Sets the name of the default machine to run `PetscPOpen()` calls on 204 205 Logically Collective, but only process 0 runs the command 206 207 Input Parameter: 208 . machine - machine to run command on or NULL for the current machine 209 210 Options Database Key: 211 . -popen_machine <machine> - run the process on this machine 212 213 Level: intermediate 214 215 .seealso: `PetscFOpen()`, `PetscFClose()`, `PetscPClose()`, `PetscPOpen()` 216 @*/ 217 PetscErrorCode PetscPOpenSetMachine(const char machine[]) 218 { 219 PetscFunctionBegin; 220 if (machine) { 221 PetscCall(PetscStrcpy(PetscPOpenMachine, machine)); 222 } else { 223 PetscPOpenMachine[0] = 0; 224 } 225 PetscFunctionReturn(0); 226 } 227 228 #endif 229