1 /* 2 * gdb2 - gdb output splitter 3 * 4 * Copyright (C) 2000,2001 Nedko Arnaoudov <nedkohome@atia.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22 #include "ph.h" 23 24 #define GDB_INITIAL_COMMAND "gdb.exe" 25 #define PARAMS_SEPARATOR " " 26 #define PARAMS_SEPARATOR_LEN strlen(PARAMS_SEPARATOR); 27 28 void DisplayError(const char *pszAPI); 29 void ReadAndHandleOutput(HANDLE hPipeRead); 30 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut, 31 HANDLE hChildStdIn, 32 HANDLE hChildStdErr, 33 char *pchCommandLine); 34 35 DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam); 36 HANDLE hChildProcess = NULL; 37 HANDLE hStdIn = NULL; // Handle to parents std input. 38 BOOL bRunThread = TRUE; 39 40 int main(int argc, char* argv[]) 41 { 42 HANDLE hOutputReadTmp,hOutputRead,hOutputWrite; 43 HANDLE hInputWriteTmp,hInputRead,hInputWrite; 44 HANDLE hErrorWrite; 45 HANDLE hThread; 46 DWORD ThreadId; 47 SECURITY_ATTRIBUTES sa; 48 49 // Set up the security attributes struct. 50 sa.nLength= sizeof(SECURITY_ATTRIBUTES); 51 sa.lpSecurityDescriptor = NULL; 52 sa.bInheritHandle = TRUE; 53 54 55 // Create the child output pipe. 56 if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0)) 57 DisplayError("CreatePipe"); 58 59 60 // Create a duplicate of the output write handle for the std error 61 // write handle. This is necessary in case the child application 62 // closes one of its std output handles. 63 if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite, 64 GetCurrentProcess(),&hErrorWrite,0, 65 TRUE,DUPLICATE_SAME_ACCESS)) 66 DisplayError("DuplicateHandle"); 67 68 69 // Create the child input pipe. 70 if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0)) 71 DisplayError("CreatePipe"); 72 73 74 // Create new output read handle and the input write handles. Set 75 // the Properties to FALSE. Otherwise, the child inherits the 76 // properties and, as a result, non-closeable handles to the pipes 77 // are created. 78 if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp, 79 GetCurrentProcess(), 80 &hOutputRead, // Address of new handle. 81 0,FALSE, // Make it uninheritable. 82 DUPLICATE_SAME_ACCESS)) 83 DisplayError("DupliateHandle"); 84 85 if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp, 86 GetCurrentProcess(), 87 &hInputWrite, // Address of new handle. 88 0,FALSE, // Make it uninheritable. 89 DUPLICATE_SAME_ACCESS)) 90 DisplayError("DupliateHandle"); 91 92 93 // Close inheritable copies of the handles you do not want to be 94 // inherited. 95 if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle"); 96 if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle"); 97 98 99 // Get std input handle so you can close it and force the ReadFile to 100 // fail when you want the input thread to exit. 101 if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) == 102 INVALID_HANDLE_VALUE ) 103 DisplayError("GetStdHandle"); 104 105 SetConsoleTitle("gdb control console"); 106 107 size_t size = strlen(GDB_INITIAL_COMMAND)+PARAMS_SEPARATOR_LEN; 108 109 // get parameters length 110 for (int i = 1 ; i < argc ; i++) 111 size += strlen(argv[i])+PARAMS_SEPARATOR_LEN; 112 113 size++; // terminating null 114 115 char *pszCommandLine = new char [size]; 116 if (!pszCommandLine) 117 { 118 printf("out of memory.\n"); 119 return -1; 120 } 121 122 strcpy(pszCommandLine,GDB_INITIAL_COMMAND); 123 for (int i = 1 ; i < argc ; i++) 124 { 125 strcat(pszCommandLine,PARAMS_SEPARATOR); 126 strcat(pszCommandLine,argv[i]); 127 } 128 129 PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite,pszCommandLine); 130 131 132 // Close pipe handles (do not continue to modify the parent). 133 // You need to make sure that no handles to the write end of the 134 // output pipe are maintained in this process or else the pipe will 135 // not close when the child process exits and the ReadFile will hang. 136 if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle"); 137 if (!CloseHandle(hInputRead )) DisplayError("CloseHandle"); 138 if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle"); 139 140 141 // Launch the thread that gets the input and sends it to the child. 142 hThread = CreateThread(NULL,0,GetAndSendInputThread, 143 (LPVOID)hInputWrite,0,&ThreadId); 144 if (hThread == NULL) DisplayError("CreateThread"); 145 146 147 // Read the child's output. 148 ReadAndHandleOutput(hOutputRead); 149 // Redirection is complete 150 151 152 // Force the read on the input to return by closing the stdin handle. 153 if (!CloseHandle(hStdIn)) DisplayError("CloseHandle"); 154 155 156 // Tell the thread to exit and wait for thread to die. 157 bRunThread = FALSE; 158 159 if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED) 160 DisplayError("WaitForSingleObject"); 161 162 if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle"); 163 if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle"); 164 165 return 0; 166 } 167 168 /////////////////////////////////////////////////////////////////////// 169 // PrepAndLaunchRedirectedChild 170 // Sets up STARTUPINFO structure, and launches redirected child. 171 /////////////////////////////////////////////////////////////////////// 172 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut, 173 HANDLE hChildStdIn, 174 HANDLE hChildStdErr, 175 char *pchCommandLine) 176 { 177 PROCESS_INFORMATION pi; 178 STARTUPINFO si; 179 static CHAR Title[] = "debugged program console"; 180 181 // Set up the start up info struct. 182 ZeroMemory(&si,sizeof(STARTUPINFO)); 183 si.cb = sizeof(STARTUPINFO); 184 si.dwFlags = STARTF_USESTDHANDLES; 185 si.hStdOutput = hChildStdOut; 186 si.hStdInput = hChildStdIn; 187 si.hStdError = hChildStdErr; 188 si.lpTitle = Title; 189 // Use this if you want to hide the child: 190 // si.wShowWindow = SW_HIDE; 191 // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to 192 // use the wShowWindow flags. 193 194 195 // Launch the process that you want to redirect (in this case, 196 // Child.exe). Make sure Child.exe is in the same directory as 197 // redirect.c launch redirect from a command line to prevent location 198 // confusion. 199 if (!CreateProcess(NULL,pchCommandLine,NULL,NULL,TRUE, 200 CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) 201 DisplayError("CreateProcess"); 202 203 204 // Set global child process handle to cause threads to exit. 205 hChildProcess = pi.hProcess; 206 207 208 // Close any unnecessary handles. 209 if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle"); 210 } 211 212 /////////////////////////////////////////////////////////////////////// 213 // ReadAndHandleOutput 214 // Monitors handle for input. Exits when child exits or pipe breaks. 215 /////////////////////////////////////////////////////////////////////// 216 void ReadAndHandleOutput(HANDLE hPipeRead) 217 { 218 CHAR lpBuffer[256]; 219 DWORD nBytesRead; 220 DWORD nCharsWritten; 221 222 while(TRUE) 223 { 224 if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer), 225 &nBytesRead,NULL) || !nBytesRead) 226 { 227 if (GetLastError() == ERROR_BROKEN_PIPE) 228 break; // pipe done - normal exit path. 229 else 230 DisplayError("ReadFile"); // Something bad happened. 231 } 232 233 // Display the character read on the screen. 234 if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer, 235 nBytesRead,&nCharsWritten,NULL)) 236 DisplayError("WriteConsole"); 237 } 238 } 239 240 241 /////////////////////////////////////////////////////////////////////// 242 // GetAndSendInputThread 243 // Thread procedure that monitors the console for input and sends input 244 // to the child process through the input pipe. 245 // This thread ends when the child application exits. 246 /////////////////////////////////////////////////////////////////////// 247 DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam) 248 { 249 CHAR read_buff[256]; 250 DWORD nBytesRead,nBytesWrote; 251 HANDLE hPipeWrite = (HANDLE)lpvThreadParam; 252 253 // Get input from our console and send it to child through the pipe. 254 while (bRunThread) 255 { 256 if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL)) 257 DisplayError("ReadConsole"); 258 259 read_buff[nBytesRead] = '\0'; // Follow input with a NULL. 260 261 if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL)) 262 { 263 if (GetLastError() == ERROR_NO_DATA) 264 break; // Pipe was closed (normal exit path). 265 else 266 DisplayError("WriteFile"); 267 } 268 } 269 270 return 1; 271 } 272 273 /////////////////////////////////////////////////////////////////////// 274 // DisplayError 275 // Displays the error number and corresponding message. 276 /////////////////////////////////////////////////////////////////////// 277 void DisplayError(const char *pszAPI) 278 { 279 LPVOID lpvMessageBuffer; 280 CHAR szPrintBuffer[512]; 281 DWORD nCharsWritten; 282 283 FormatMessage( 284 FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 285 NULL, GetLastError(), 286 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 287 (LPTSTR)&lpvMessageBuffer, 0, NULL); 288 289 wsprintf(szPrintBuffer, 290 "ERROR: API = %s.\n error code = %d.\n message = %s.\n", 291 pszAPI, GetLastError(), (char *)lpvMessageBuffer); 292 293 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer, 294 lstrlen(szPrintBuffer),&nCharsWritten,NULL); 295 296 LocalFree(lpvMessageBuffer); 297 ExitProcess(GetLastError()); 298 } 299