1/************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24#define UNICODE 25#define WIN32_LEAN_AND_MEAN 26#ifdef _MSC_VER 27#pragma warning(push,1) // disable warnings within system headers 28#endif 29#include <windows.h> 30#ifdef _MSC_VER 31#pragma warning(pop) 32#endif 33 34#define _UNICODE 35#include <tchar.h> 36 37#include <string.h> 38#include <stdlib.h> 39#include <systools/win32/uwinapi.h> 40 41#include <stdio.h> 42 43#ifdef UNOPKG 44 45DWORD passOutputToConsole(HANDLE readPipe, HANDLE console) 46{ 47 BYTE aBuffer[1024]; 48 DWORD dwRead = 0; 49 HANDLE hReadPipe = readPipe; 50 BOOL fSuccess; 51 DWORD dwWritten; 52 53 //Indicates that we read an odd number of bytes. That is, we only read half of the last 54 //wchar_t 55 bool bIncompleteWchar = false; 56 //fprintf, fwprintf will both send char data without the terminating zero. 57 //fwprintf converts the unicode string first. 58 //We expect here to receive unicode without the terminating zero. 59 //unopkg and the extension manager code MUST 60 //use dp_misc::writeConsole instead of using fprintf, etc. 61 62 DWORD dwToRead = sizeof(aBuffer); 63 BYTE * pBuffer = aBuffer; 64 while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) ) 65 { 66 //If the previous ReadFile call read an odd number of bytes, then the last one was 67 //put at the front of the buffer. We increase the number of read bytes by one to reflect 68 //that one byte. 69 if (bIncompleteWchar) 70 dwRead++; 71 //We must make sure that only complete wchar_t|s are written. WriteConsolse takes 72 //the number of wchar_t|s as argument. ReadFile, however, reads bytes. 73 bIncompleteWchar = dwRead % 2 ? true : false; 74 if (bIncompleteWchar) 75 { 76 //To test this case, give aBuffer a small odd size, e.g. aBuffer[3] 77 //The last byte, which is the incomplete wchar_t (half of it), will not be written. 78 fSuccess = WriteConsoleW( console, aBuffer, 79 (dwRead - 1) / 2, &dwWritten, NULL ); 80 81 //Move the last byte to the front of the buffer, so that it is the start of the 82 //next string 83 aBuffer[0] = aBuffer[dwRead - 1]; 84 85 //Make sure that ReadFile does not overwrite the first byte the next time 86 dwToRead = sizeof(aBuffer) - 1; 87 pBuffer = aBuffer + 1; 88 89 } 90 else 91 { //We have read an even number of bytes. Therefore, we do not put the last incomplete 92 //wchar_t at the front of the buffer. We will use the complete buffer the next time 93 //when ReadFile is called. 94 dwToRead = sizeof(aBuffer); 95 pBuffer = aBuffer; 96 fSuccess = WriteConsoleW( console, 97 aBuffer, dwRead / 2, &dwWritten, NULL ); 98 } 99 } 100 101 return 0; 102} 103 104#endif 105 106#ifdef UNOPKG 107DWORD WINAPI OutputThread( LPVOID pParam ) 108{ 109 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE )); 110} 111 112#else 113DWORD WINAPI OutputThread( LPVOID pParam ) 114{ 115 BYTE aBuffer[256]; 116 DWORD dwRead = 0; 117 HANDLE hReadPipe = (HANDLE)pParam; 118 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 119 { 120 BOOL fSuccess; 121 DWORD dwWritten; 122 123 fSuccess = WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL ); 124 } 125 126 return 0; 127} 128#endif 129//--------------------------------------------------------------------------- 130// Thread that reads from child process standard error pipe 131//--------------------------------------------------------------------------- 132 133#ifdef UNOPKG 134DWORD WINAPI ErrorThread( LPVOID pParam ) 135{ 136 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE )); 137} 138 139#else 140DWORD WINAPI ErrorThread( LPVOID pParam ) 141{ 142 BYTE aBuffer[256]; 143 DWORD dwRead = 0; 144 HANDLE hReadPipe = (HANDLE)pParam; 145 146 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 147 { 148 BOOL fSuccess; 149 DWORD dwWritten; 150 151 fSuccess = WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL ); 152 } 153 154 return 0; 155} 156#endif 157//--------------------------------------------------------------------------- 158// Thread that writes to child process standard input pipe 159//--------------------------------------------------------------------------- 160#ifdef UNOPKG 161 162DWORD WINAPI InputThread( LPVOID pParam ) 163{ 164 DWORD dwRead = 0; 165 HANDLE hWritePipe = (HANDLE)pParam; 166 167 //We need to read in the complete input until we encounter a new line before 168 //converting to Unicode. This is necessary because the input string can use 169 //characters of one, two, and more bytes. If the last character is not 170 //complete, then it will not be converted properly. 171 172 //Find out how a new line (0xd 0xa) looks like with the used code page. 173 //Characters may have one or multiple bytes and different byte ordering 174 //can be used (little and big endian); 175 int cNewLine = WideCharToMultiByte( 176 GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL); 177 char * mbBuff = new char[cNewLine]; 178 WideCharToMultiByte( 179 GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL); 180 181 const size_t dwBufferSize = 256; 182 char* readBuf = (char*) malloc(dwBufferSize); 183 int readAll = 0; 184 size_t curBufSize = dwBufferSize; 185 186 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), 187 readBuf + readAll, 188 curBufSize - readAll, &dwRead, NULL ) ) 189 { 190 readAll += dwRead; 191 int lastBufSize = curBufSize; 192 //Grow the buffer if necessary 193 if (readAll > curBufSize * 0.7) 194 { 195 curBufSize *= 2; 196 readBuf = (char *) realloc(readBuf, curBufSize); 197 } 198 199 //If the buffer was filled completely then 200 //there could be more input coming. But if we read from the console 201 //and the console input fits exactly in the buffer, then the next 202 //ReadFile would block until the users presses return, etc. 203 //Therefor we check if last character is a new line. 204 //To test this, set dwBufferSize to 4 and enter "no". This should produce 205 //4 bytes with most code pages. 206 if ( readAll == lastBufSize 207 && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0) 208 { 209 //The buffer was completely filled and the last byte(s) are no 210 //new line, so there is more to come. 211 continue; 212 } 213 //Obtain the size of the buffer for the converted string. 214 int sizeWBuf = MultiByteToWideChar( 215 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0); 216 217 wchar_t * wideBuf = new wchar_t[sizeWBuf]; 218 219 //Do the conversion. 220 MultiByteToWideChar( 221 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf); 222 223 BOOL fSuccess; 224 DWORD dwWritten; 225 fSuccess = WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL ); 226 delete[] wideBuf; 227 readAll = 0; 228 } 229 delete[] mbBuff; 230 free(readBuf); 231 return 0; 232} 233#else 234DWORD WINAPI InputThread( LPVOID pParam ) 235{ 236 BYTE aBuffer[256]; 237 DWORD dwRead = 0; 238 HANDLE hWritePipe = (HANDLE)pParam; 239 240 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 241 { 242 BOOL fSuccess; 243 DWORD dwWritten; 244 245 fSuccess = WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL ); 246 } 247 248 return 0; 249} 250#endif 251 252//--------------------------------------------------------------------------- 253// Thread that waits until child process reached input idle 254//--------------------------------------------------------------------------- 255 256DWORD WINAPI WaitForUIThread( LPVOID pParam ) 257{ 258 HANDLE hProcess = (HANDLE)pParam; 259 260#ifndef UNOPKG 261 if ( !_tgetenv( TEXT("UNOPKG") ) ) 262 WaitForInputIdle( hProcess, INFINITE ); 263#endif 264 265 return 0; 266} 267 268 269//--------------------------------------------------------------------------- 270// Ctrl-Break handler that terminates the child process if Ctrl-C was pressed 271//--------------------------------------------------------------------------- 272 273HANDLE hTargetProcess = INVALID_HANDLE_VALUE; 274 275BOOL WINAPI CtrlBreakHandler( 276 DWORD // control signal type 277) 278{ 279 TerminateProcess( hTargetProcess, 255 ); 280 return TRUE; 281} 282 283 284//--------------------------------------------------------------------------- 285 286//--------------------------------------------------------------------------- 287 288#ifdef __MINGW32__ 289int main( int, char ** ) 290#else 291int _tmain( int, _TCHAR ** ) 292#endif 293{ 294 TCHAR szTargetFileName[MAX_PATH] = TEXT(""); 295 STARTUPINFO aStartupInfo; 296 PROCESS_INFORMATION aProcessInfo; 297 298 ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) ); 299 aStartupInfo.cb = sizeof(aStartupInfo); 300 aStartupInfo.dwFlags = STARTF_USESTDHANDLES; 301 302 // Create an output pipe where the write end is inheritable 303 304 HANDLE hOutputRead, hOutputWrite; 305 306 if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) ) 307 { 308 HANDLE hTemp; 309 310 DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); 311 CloseHandle( hOutputWrite ); 312 hOutputWrite = hTemp; 313 314 aStartupInfo.hStdOutput = hOutputWrite; 315 } 316 317 // Create an error pipe where the write end is inheritable 318 319 HANDLE hErrorRead, hErrorWrite; 320 321 if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) ) 322 { 323 HANDLE hTemp; 324 325 DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); 326 CloseHandle( hErrorWrite ); 327 hErrorWrite = hTemp; 328 329 aStartupInfo.hStdError = hErrorWrite; 330 } 331 332 // Create an input pipe where the read end is inheritable 333 334 HANDLE hInputRead, hInputWrite; 335 336 if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) ) 337 { 338 HANDLE hTemp; 339 340 DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); 341 CloseHandle( hInputRead ); 342 hInputRead = hTemp; 343 344 aStartupInfo.hStdInput = hInputRead; 345 } 346 347 // Get image path with same name but with .exe extension 348 349 TCHAR szModuleFileName[MAX_PATH]; 350 351 GetModuleFileName( NULL, szModuleFileName, MAX_PATH ); 352 _TCHAR *lpLastDot = _tcsrchr( szModuleFileName, '.' ); 353 if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) ) 354 { 355 size_t len = lpLastDot - szModuleFileName; 356 _tcsncpy( szTargetFileName, szModuleFileName, len ); 357 _tcsncpy( szTargetFileName + len, _T(".EXE"), sizeof(szTargetFileName)/sizeof(szTargetFileName[0]) - len ); 358 } 359 360 // Create process with same command line, environment and stdio handles which 361 // are directed to the created pipes 362 363 BOOL fSuccess = CreateProcess( 364 szTargetFileName, 365 GetCommandLine(), 366 NULL, 367 NULL, 368 TRUE, 369 0, 370 NULL, 371 NULL, 372 &aStartupInfo, 373 &aProcessInfo ); 374 375 if ( fSuccess ) 376 { 377 // These pipe ends are inherited by the child process and no longer used 378 CloseHandle( hOutputWrite ); 379 CloseHandle( hErrorWrite ); 380 CloseHandle( hInputRead ); 381 382 // Set the Ctrl-Break handler 383 hTargetProcess = aProcessInfo.hProcess; 384 SetConsoleCtrlHandler( CtrlBreakHandler, TRUE ); 385 386 // Create threads that redirect remote pipe io to current process's console stdio 387 388 DWORD dwOutputThreadId, dwErrorThreadId, dwInputThreadId; 389 390 HANDLE hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId ); 391 HANDLE hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId ); 392 HANDLE hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId ); 393 394 // Create thread that wait until child process entered input idle 395 396 DWORD dwWaitForUIThreadId; 397 HANDLE hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId ); 398 399 DWORD dwWaitResult; 400 HANDLE hObjects[] = 401 { 402 hTargetProcess, 403 hWaitForUIThread, 404 hOutputThread, 405 hErrorThread 406 }; 407 408 #ifdef UNOPKG 409 dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, TRUE, INFINITE ); 410 #else 411 bool bDetach = false; 412 int nOpenPipes = 2; 413 do 414 { 415 dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, FALSE, INFINITE ); 416 417 switch ( dwWaitResult ) 418 { 419 case WAIT_OBJECT_0: // The child process has terminated 420 case WAIT_OBJECT_0 + 1: // The child process entered input idle 421 bDetach = true; 422 break; 423 case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed 424 case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed 425 bDetach = --nOpenPipes <= 0; 426 break; 427 default: // Something went wrong 428 bDetach = true; 429 break; 430 } 431 } while( !bDetach ); 432 433#endif 434 435 CloseHandle( hOutputThread ); 436 CloseHandle( hErrorThread ); 437 CloseHandle( hInputThread ); 438 CloseHandle( hWaitForUIThread ); 439 440 DWORD dwExitCode = 0; 441 GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode ); 442 CloseHandle( aProcessInfo.hProcess ); 443 CloseHandle( aProcessInfo.hThread ); 444 445 return dwExitCode; 446 } 447 448 return -1; 449} 450