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