Rev 150 | Rev 175 | Go to most recent revision | 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); |
||
| 23 | int pipe_isalive (FILE *stream); |
||
| 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)); |
||
| 62 | memset (&security_attributes, 0, sizeof (security_attributes)); // prepare the pipes' security attributes |
||
| 63 | security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES); |
||
| 64 | security_attributes.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited |
||
| 65 | hStdIO[0][0] = hStdIO[0][1] = hStdIO[1][0] = hStdIO[1][1] = NULL; |
||
| 66 | CreatePipe (&hStdIO[ME_TO_IT][RD], &hStdIO[ME_TO_IT][WR], &security_attributes, 0); // create a pipe for the child process's stdin |
||
| 67 | CreatePipe (&hStdIO[IT_TO_ME][RD], &hStdIO[IT_TO_ME][WR], &security_attributes, 0); // create a pipe for the child process's stdout |
||
| 68 | SetHandleInformation (hStdIO[ME_TO_IT][WR], HANDLE_FLAG_INHERIT, 0); // ensure the write handle to the pipe for STDIN is not inherited |
||
| 69 | SetHandleInformation (hStdIO[IT_TO_ME][RD], HANDLE_FLAG_INHERIT, 0); // ensure the read handle to the pipe for STDOUT is not inherited |
||
| 70 | pipe->descriptors[RD] = _open_osfhandle ((intptr_t) hStdIO[IT_TO_ME][RD], _O_BINARY); |
||
| 71 | pipe->descriptors[WR] = _open_osfhandle ((intptr_t) hStdIO[ME_TO_IT][WR], _O_BINARY | _O_APPEND); |
||
| 72 | |||
| 174 | pmbaty | 73 | // spawn the child process with redirected input and output |
| 150 | pmbaty | 74 | memset (&startup_info, 0, sizeof (startup_info)); |
| 75 | startup_info.cb = sizeof (STARTUPINFOA); |
||
| 76 | startup_info.dwFlags = STARTF_USESTDHANDLES; |
||
| 77 | startup_info.hStdInput = hStdIO[ME_TO_IT][RD]; |
||
| 78 | startup_info.hStdOutput = hStdIO[IT_TO_ME][WR]; |
||
| 79 | startup_info.hStdError = hStdIO[IT_TO_ME][WR]; |
||
| 80 | GetCurrentDirectory (WCHAR_SIZEOF (current_path), current_path); |
||
| 81 | 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)) |
||
| 82 | { |
||
| 83 | pipe_close ((FILE *) pipe); |
||
| 84 | free (command_pathname); |
||
| 85 | return (NULL); |
||
| 86 | } |
||
| 87 | |||
| 88 | // close the handles we won't use, free the command pathname memory space and return a pointer to our pipe object |
||
| 89 | CloseHandle (hStdIO[IT_TO_ME][WR]); |
||
| 90 | CloseHandle (hStdIO[ME_TO_IT][RD]); |
||
| 91 | #undef IT_TO_ME |
||
| 92 | #undef ME_TO_IT |
||
| 93 | free (command_pathname); |
||
| 94 | return ((FILE *) pipe); |
||
| 95 | #else // !_WIN32 |
||
| 96 | return (popen (shell_command, mode)); |
||
| 97 | #endif // _WIN32 |
||
| 98 | } |
||
| 99 | |||
| 100 | |||
| 101 | int pipe_close (FILE *stream) |
||
| 102 | { |
||
| 103 | // wrapper around pclose(), and simple reimplementation of it for systems that don't have it |
||
| 104 | |||
| 105 | #ifdef _WIN32 |
||
| 106 | HANDLE hRemoteThread; |
||
| 107 | HANDLE hProcessDup; |
||
| 108 | DWORD handle_flags; |
||
| 109 | DWORD thread_id; |
||
| 110 | DWORD exit_code; |
||
| 111 | pipe_t *pipe; |
||
| 112 | bool was_duplicated; |
||
| 113 | |||
| 114 | pipe = (pipe_t *) stream; // cast FILE * back into our own structure |
||
| 115 | |||
| 116 | // close all handles |
||
| 117 | if (pipe->descriptors[RD] != -1) _close (pipe->descriptors[RD]); pipe->descriptors[RD] = -1; |
||
| 118 | if (pipe->descriptors[WR] != -1) _close (pipe->descriptors[WR]); pipe->descriptors[WR] = -1; |
||
| 119 | |||
| 120 | // terminate process if it hasn't terminated by itself yet |
||
| 121 | exit_code = 0; |
||
| 122 | if (GetExitCodeProcess (pipe->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE)) |
||
| 123 | { |
||
| 124 | // taken from Dr. Dobbs. How to terminate any process cleanly: create a remote thread in it, |
||
| 125 | // and make its start address point right into kernel32's ExitProcess() function... |
||
| 126 | was_duplicated = (DuplicateHandle (GetCurrentProcess (), pipe->procinfo.hProcess, GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS, FALSE, 0) != 0); |
||
| 127 | if (!was_duplicated) |
||
| 128 | hProcessDup = pipe->procinfo.hProcess; |
||
| 129 | hRemoteThread = CreateRemoteThread (hProcessDup, NULL, 0, (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle (L"Kernel32"), "ExitProcess"), (void *) 0, 0, &thread_id); |
||
| 130 | if (hRemoteThread != NULL) |
||
| 131 | { |
||
| 132 | WaitForSingleObject (hProcessDup, INFINITE); // must wait process to terminate to guarantee that it has exited |
||
| 133 | CloseHandle (hRemoteThread); |
||
| 134 | } |
||
| 135 | else |
||
| 136 | TerminateProcess (pipe->procinfo.hProcess, 0); // if process refuses our remote thread, kill it |
||
| 137 | if (was_duplicated) |
||
| 138 | CloseHandle (hProcessDup); |
||
| 139 | } |
||
| 140 | |||
| 141 | // close process handles |
||
| 142 | if (pipe->procinfo.hProcess != NULL) |
||
| 143 | CloseHandle (pipe->procinfo.hProcess); |
||
| 144 | if (GetHandleInformation (pipe->procinfo.hThread, &handle_flags)) |
||
| 145 | CloseHandle (pipe->procinfo.hThread); |
||
| 146 | |||
| 147 | // and finally free the pipe structure |
||
| 148 | free (pipe); |
||
| 149 | return ((int) exit_code); |
||
| 150 | #else // !_WIN32 |
||
| 151 | return (pclose (stream)); |
||
| 152 | #endif // _WIN32 |
||
| 153 | } |
||
| 154 | |||
| 155 | |||
| 156 | int pipe_isalive (FILE *stream) |
||
| 157 | { |
||
| 158 | // wrapper around ioctl() on pipes for systems that don't have it |
||
| 159 | |||
| 160 | #ifdef _WIN32 |
||
| 161 | DWORD exit_code; |
||
| 162 | return (GetExitCodeProcess (((pipe_t *) stream)->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE)); |
||
| 163 | #else // !_WIN32 |
||
| 164 | return (1); // UNIX doesn't provide a portable way for this |
||
| 165 | #endif // _WIN32 |
||
| 166 | } |
||
| 167 | |||
| 168 | |||
| 169 | int pipe_hasdata (FILE *stream) |
||
| 170 | { |
||
| 171 | // wrapper around ioctl() on pipes for systems that don't have it |
||
| 172 | |||
| 173 | #ifdef _WIN32 |
||
| 174 | return (!_eof (((pipe_t *) stream)->descriptors[RD])); |
||
| 175 | #else // !_WIN32 |
||
| 176 | return (feof (stream)); |
||
| 177 | #endif // _WIN32 |
||
| 178 | } |
||
| 179 | |||
| 180 | |||
| 181 | int pipe_read (FILE *stream, void *dstbuf, int nbytes) |
||
| 182 | { |
||
| 183 | // wrapper around fread() on pipes for systems that don't support it |
||
| 184 | |||
| 185 | #ifdef _WIN32 |
||
| 186 | return (_read (((pipe_t *) stream)->descriptors[RD], dstbuf, nbytes)); |
||
| 187 | #else // !_WIN32 |
||
| 188 | return (fread (dstbuf, 1, nbytes, stream)); |
||
| 189 | #endif // _WIN32 |
||
| 190 | } |
||
| 191 | |||
| 192 | |||
| 193 | int pipe_write (FILE *stream, void *srcbuf, int nbytes) |
||
| 194 | { |
||
| 195 | // wrapper around fwrite() on pipes for systems that don't support it |
||
| 196 | |||
| 197 | #ifdef _WIN32 |
||
| 198 | return (_write (((pipe_t *) stream)->descriptors[WR], srcbuf, nbytes)); |
||
| 199 | #else // !_WIN32 |
||
| 200 | return (fwrite (srcbuf, 1, nbytes, stream)); |
||
| 201 | #endif // _WIN32 |
||
| 202 | } |