Rev 177 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 150 | pmbaty | 1 | // pipe.cpp -- somewhat portable pipe implementation that resembles POSIX popen()/pclose() |
| 2 | |||
| 3 | #include "common.h" |
||
| 4 | |||
| 5 | |||
| 6 | // definitions used in this module only |
||
| 7 | #define RD 0 |
||
| 8 | #define WR 1 |
||
| 9 | |||
| 10 | |||
| 11 | #ifdef _WIN32 |
||
| 12 | typedef struct pipe_s |
||
| 13 | { |
||
| 14 | PROCESS_INFORMATION procinfo; |
||
| 15 | int descriptors[2]; // RD and WR |
||
| 16 | } pipe_t; |
||
| 17 | #endif // _WIN32 |
||
| 18 | |||
| 19 | |||
| 20 | // function prototypes |
||
| 21 | FILE *pipe_open (const wchar_t *shell_command, const wchar_t *mode); |
||
| 22 | int pipe_close (FILE *stream); |
||
| 177 | pmbaty | 23 | int pipe_isalive (FILE *stream, int64_t *exit_code); |
| 150 | pmbaty | 24 | int pipe_hasdata (FILE *stream); |
| 25 | int pipe_read (FILE *stream, void *dstbuf, int nbytes); |
||
| 26 | int pipe_write (FILE *stream, void *srcbuf, int nbytes); |
||
| 27 | |||
| 28 | |||
| 29 | FILE *pipe_open (const wchar_t *shell_command, const wchar_t *mode) |
||
| 30 | { |
||
| 31 | // wrapper around popen(), and simple reimplementation of it for systems that don't have it |
||
| 32 | // mode shall be "r", "w", or "r+" |
||
| 33 | |||
| 34 | #ifdef _WIN32 |
||
| 35 | #define IT_TO_ME 0 |
||
| 36 | #define ME_TO_IT 1 |
||
| 37 | SECURITY_ATTRIBUTES security_attributes; |
||
| 38 | wchar_t current_path[MAX_PATH]; |
||
| 39 | wchar_t *command_pathname; |
||
| 40 | STARTUPINFO startup_info; |
||
| 41 | HANDLE hStdIO[2][2]; |
||
| 42 | pipe_t *pipe; |
||
| 43 | |||
| 44 | (void) mode; // all pipes are open read-write |
||
| 45 | |||
| 46 | command_pathname = (wchar_t *) malloc ((MAX_PATH + 4096) * sizeof (wchar_t)); // should be enough |
||
| 47 | if (command_pathname == NULL) |
||
| 48 | return (NULL); |
||
| 49 | |||
| 50 | // figure out command pathname out of full command string |
||
| 51 | wcscpy_s (command_pathname, MAX_PATH + 4096, shell_command); |
||
| 52 | if ((command_pathname[0] == L'"') && (wcschr (&command_pathname[1], L'"') != NULL)) |
||
| 53 | { |
||
| 54 | memmove (command_pathname, &command_pathname[1], (MAX_PATH + 4096 - 1) * sizeof (wchar_t)); |
||
| 55 | *wcschr (&command_pathname[1], L'"') = 0; |
||
| 56 | } |
||
| 57 | else if (wcschr (command_pathname, L' ') != NULL) |
||
| 58 | *wcschr (command_pathname, L' ') = 0; |
||
| 59 | |||
| 60 | // build the bidirectional I/O pipe |
||
| 61 | pipe = (pipe_t *) malloc (sizeof (pipe_t)); |
||
| 175 | pmbaty | 62 | memset (pipe, 0, sizeof (pipe_t)); |
| 150 | pmbaty | 63 | memset (&security_attributes, 0, sizeof (security_attributes)); // prepare the pipes' security attributes |
| 64 | security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES); |
||
| 65 | security_attributes.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited |
||
| 66 | hStdIO[0][0] = hStdIO[0][1] = hStdIO[1][0] = hStdIO[1][1] = NULL; |
||
| 67 | CreatePipe (&hStdIO[ME_TO_IT][RD], &hStdIO[ME_TO_IT][WR], &security_attributes, 0); // create a pipe for the child process's stdin |
||
| 68 | CreatePipe (&hStdIO[IT_TO_ME][RD], &hStdIO[IT_TO_ME][WR], &security_attributes, 0); // create a pipe for the child process's stdout |
||
| 69 | SetHandleInformation (hStdIO[ME_TO_IT][WR], HANDLE_FLAG_INHERIT, 0); // ensure the write handle to the pipe for STDIN is not inherited |
||
| 70 | SetHandleInformation (hStdIO[IT_TO_ME][RD], HANDLE_FLAG_INHERIT, 0); // ensure the read handle to the pipe for STDOUT is not inherited |
||
| 71 | pipe->descriptors[RD] = _open_osfhandle ((intptr_t) hStdIO[IT_TO_ME][RD], _O_BINARY); |
||
| 72 | pipe->descriptors[WR] = _open_osfhandle ((intptr_t) hStdIO[ME_TO_IT][WR], _O_BINARY | _O_APPEND); |
||
| 73 | |||
| 174 | pmbaty | 74 | // spawn the child process with redirected input and output |
| 150 | pmbaty | 75 | memset (&startup_info, 0, sizeof (startup_info)); |
| 194 | pmbaty | 76 | startup_info.cb = sizeof (STARTUPINFO); |
| 150 | pmbaty | 77 | startup_info.dwFlags = STARTF_USESTDHANDLES; |
| 78 | startup_info.hStdInput = hStdIO[ME_TO_IT][RD]; |
||
| 79 | startup_info.hStdOutput = hStdIO[IT_TO_ME][WR]; |
||
| 80 | startup_info.hStdError = hStdIO[IT_TO_ME][WR]; |
||
| 81 | GetCurrentDirectory (WCHAR_SIZEOF (current_path), current_path); |
||
| 82 | if (!CreateProcess (command_pathname, (wchar_t *) shell_command, NULL, NULL, true, CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, NULL, current_path, &startup_info, &pipe->procinfo)) |
||
| 83 | { |
||
| 84 | pipe_close ((FILE *) pipe); |
||
| 85 | free (command_pathname); |
||
| 86 | return (NULL); |
||
| 87 | } |
||
| 88 | |||
| 89 | // close the handles we won't use, free the command pathname memory space and return a pointer to our pipe object |
||
| 90 | CloseHandle (hStdIO[IT_TO_ME][WR]); |
||
| 91 | CloseHandle (hStdIO[ME_TO_IT][RD]); |
||
| 92 | #undef IT_TO_ME |
||
| 93 | #undef ME_TO_IT |
||
| 94 | free (command_pathname); |
||
| 95 | return ((FILE *) pipe); |
||
| 96 | #else // !_WIN32 |
||
| 97 | return (popen (shell_command, mode)); |
||
| 98 | #endif // _WIN32 |
||
| 99 | } |
||
| 100 | |||
| 101 | |||
| 102 | int pipe_close (FILE *stream) |
||
| 103 | { |
||
| 104 | // wrapper around pclose(), and simple reimplementation of it for systems that don't have it |
||
| 105 | |||
| 106 | #ifdef _WIN32 |
||
| 107 | HANDLE hRemoteThread; |
||
| 108 | HANDLE hProcessDup; |
||
| 109 | DWORD handle_flags; |
||
| 110 | DWORD thread_id; |
||
| 111 | DWORD exit_code; |
||
| 112 | pipe_t *pipe; |
||
| 113 | bool was_duplicated; |
||
| 114 | |||
| 115 | pipe = (pipe_t *) stream; // cast FILE * back into our own structure |
||
| 116 | |||
| 117 | // close all handles |
||
| 118 | if (pipe->descriptors[RD] != -1) _close (pipe->descriptors[RD]); pipe->descriptors[RD] = -1; |
||
| 119 | if (pipe->descriptors[WR] != -1) _close (pipe->descriptors[WR]); pipe->descriptors[WR] = -1; |
||
| 120 | |||
| 121 | // terminate process if it hasn't terminated by itself yet |
||
| 122 | exit_code = 0; |
||
| 123 | if (GetExitCodeProcess (pipe->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE)) |
||
| 124 | { |
||
| 125 | // taken from Dr. Dobbs. How to terminate any process cleanly: create a remote thread in it, |
||
| 126 | // and make its start address point right into kernel32's ExitProcess() function... |
||
| 127 | was_duplicated = (DuplicateHandle (GetCurrentProcess (), pipe->procinfo.hProcess, GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS, FALSE, 0) != 0); |
||
| 128 | if (!was_duplicated) |
||
| 129 | hProcessDup = pipe->procinfo.hProcess; |
||
| 130 | hRemoteThread = CreateRemoteThread (hProcessDup, NULL, 0, (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle (L"Kernel32"), "ExitProcess"), (void *) 0, 0, &thread_id); |
||
| 131 | if (hRemoteThread != NULL) |
||
| 132 | { |
||
| 133 | WaitForSingleObject (hProcessDup, INFINITE); // must wait process to terminate to guarantee that it has exited |
||
| 134 | CloseHandle (hRemoteThread); |
||
| 135 | } |
||
| 136 | else |
||
| 137 | TerminateProcess (pipe->procinfo.hProcess, 0); // if process refuses our remote thread, kill it |
||
| 138 | if (was_duplicated) |
||
| 139 | CloseHandle (hProcessDup); |
||
| 140 | } |
||
| 141 | |||
| 142 | // close process handles |
||
| 143 | if (pipe->procinfo.hProcess != NULL) |
||
| 144 | CloseHandle (pipe->procinfo.hProcess); |
||
| 145 | if (GetHandleInformation (pipe->procinfo.hThread, &handle_flags)) |
||
| 146 | CloseHandle (pipe->procinfo.hThread); |
||
| 147 | |||
| 148 | // and finally free the pipe structure |
||
| 149 | free (pipe); |
||
| 150 | return ((int) exit_code); |
||
| 151 | #else // !_WIN32 |
||
| 152 | return (pclose (stream)); |
||
| 153 | #endif // _WIN32 |
||
| 154 | } |
||
| 155 | |||
| 156 | |||
| 177 | pmbaty | 157 | int pipe_isalive (FILE *stream, int64_t *exit_code) |
| 150 | pmbaty | 158 | { |
| 159 | // wrapper around ioctl() on pipes for systems that don't have it |
||
| 160 | |||
| 161 | #ifdef _WIN32 |
||
| 177 | pmbaty | 162 | DWORD w32exitcode; |
| 175 | pmbaty | 163 | DWORD handle_flags; |
| 164 | if (((pipe_t *) stream)->procinfo.hProcess == 0) |
||
| 165 | return (1); // if the child process has not been fully started yet, assume the pipe is still alive |
||
| 166 | else if (((pipe_t *) stream)->procinfo.hProcess == INVALID_HANDLE_VALUE) |
||
| 167 | return (0); // if the child process handle has been explicitly marked invalid, assume it's already dead |
||
| 168 | else if (GetHandleInformation (((pipe_t *) stream)->procinfo.hProcess, &handle_flags) == 0) |
||
| 169 | { |
||
| 170 | ((pipe_t *) stream)->procinfo.hProcess = INVALID_HANDLE_VALUE; |
||
| 171 | return (0); // if the process handle is no longer valid but its handle is not marked as such, assume the child is dead |
||
| 172 | } |
||
| 177 | pmbaty | 173 | else if (GetExitCodeProcess (((pipe_t *) stream)->procinfo.hProcess, &w32exitcode) == 0) |
| 175 | pmbaty | 174 | return (1); // if GetExitCodeProcess() failed, *conservatively* assume the child is still alive |
| 177 | pmbaty | 175 | *exit_code = (int64_t) w32exitcode; |
| 176 | return (w32exitcode == STILL_ACTIVE); |
||
| 150 | pmbaty | 177 | #else // !_WIN32 |
| 177 | pmbaty | 178 | *exit_code = 0; |
| 150 | pmbaty | 179 | return (1); // UNIX doesn't provide a portable way for this |
| 180 | #endif // _WIN32 |
||
| 181 | } |
||
| 182 | |||
| 183 | |||
| 184 | int pipe_hasdata (FILE *stream) |
||
| 185 | { |
||
| 186 | // wrapper around ioctl() on pipes for systems that don't have it |
||
| 187 | |||
| 188 | #ifdef _WIN32 |
||
| 189 | return (!_eof (((pipe_t *) stream)->descriptors[RD])); |
||
| 190 | #else // !_WIN32 |
||
| 191 | return (feof (stream)); |
||
| 192 | #endif // _WIN32 |
||
| 193 | } |
||
| 194 | |||
| 195 | |||
| 196 | int pipe_read (FILE *stream, void *dstbuf, int nbytes) |
||
| 197 | { |
||
| 198 | // wrapper around fread() on pipes for systems that don't support it |
||
| 199 | |||
| 200 | #ifdef _WIN32 |
||
| 201 | return (_read (((pipe_t *) stream)->descriptors[RD], dstbuf, nbytes)); |
||
| 202 | #else // !_WIN32 |
||
| 203 | return (fread (dstbuf, 1, nbytes, stream)); |
||
| 204 | #endif // _WIN32 |
||
| 205 | } |
||
| 206 | |||
| 207 | |||
| 208 | int pipe_write (FILE *stream, void *srcbuf, int nbytes) |
||
| 209 | { |
||
| 210 | // wrapper around fwrite() on pipes for systems that don't support it |
||
| 211 | |||
| 212 | #ifdef _WIN32 |
||
| 213 | return (_write (((pipe_t *) stream)->descriptors[WR], srcbuf, nbytes)); |
||
| 214 | #else // !_WIN32 |
||
| 215 | return (fwrite (srcbuf, 1, nbytes, stream)); |
||
| 216 | #endif // _WIN32 |
||
| 217 | } |