diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/dos/command/cmdredir.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/mvdm/dos/command/cmdredir.c')
-rw-r--r-- | private/mvdm/dos/command/cmdredir.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/private/mvdm/dos/command/cmdredir.c b/private/mvdm/dos/command/cmdredir.c new file mode 100644 index 000000000..5cf700c63 --- /dev/null +++ b/private/mvdm/dos/command/cmdredir.c @@ -0,0 +1,664 @@ +/* cmdredir.c - SCS routines for redirection + * + * + * Modification History: + * + * Sudeepb 22-Apr-1992 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> + +#define CMDREDIR_DEBUG 1 + +PPIPE_INPUT cmdPipeList = NULL; + +BOOL cmdCheckCopyForRedirection (pRdrInfo) +PREDIRCOMPLETE_INFO pRdrInfo; +{ +PPIPE_INPUT pPipe, pPipePrev; +PPIPE_OUTPUT pPipeOut; + + if (pRdrInfo == NULL) + return TRUE; + if (pRdrInfo->ri_pPipeStdIn != NULL) { + + //Piping and Pipe list is empty? + ASSERT(cmdPipeList != NULL); + + // in most cases, we have only one pipe for stdin + if (pRdrInfo->ri_pPipeStdIn == cmdPipeList){ + pPipe = pRdrInfo->ri_pPipeStdIn; + cmdPipeList = pPipe->Next; + } + // multiple piping + // search for the right one + else { + pPipe = pPipePrev = cmdPipeList; + while (pPipe != NULL && pPipe != pRdrInfo->ri_pPipeStdIn){ + pPipePrev = pPipe; + pPipe = pPipe->Next; + } + if (pPipe != NULL) + // remove it from the list + pPipePrev->Next = pPipe->Next; + } + if (pPipe != NULL) { + // grab the critical section. As soon as we have a + // a hold on the critical section, it is safe to kill + // the piping thread because it is in dormant unless + // it has terminated which is also safe for us. + EnterCriticalSection(&pPipe->CriticalSection); + // if the thread is till running, kill it + if (WaitForSingleObject(pPipe->hThread, 0)) { + TerminateThread(pPipe->hThread, 0); + WaitForSingleObject(pPipe->hThread, INFINITE); + } + LeaveCriticalSection(&pPipe->CriticalSection); + CloseHandle(pPipe->hFileWrite); + CloseHandle(pPipe->hPipe); + CloseHandle(pPipe->hDataEvent); + CloseHandle(pPipe->hThread); + DeleteCriticalSection(&pPipe->CriticalSection); + DeleteFile(pPipe->pFileName); + free(pPipe->pFileName); + free (pPipe); + } + } + // the application is terminating, let the output thread knows + // about it so it can exit appropriately. + // the output thread is responsible for clean up + if (pRdrInfo->ri_pPipeStdOut) { + // The output thread must wait for the event before + // it can exit. + SetEvent((pRdrInfo->ri_pPipeStdOut)->hExitEvent); + // wait 1 seconds for the thread to go away. + // this is done because our parent process may put up + // its prompt before our sibling process has a chance to + // completely display data on its display surface. + // note that we can not wait forever here because + // the sibling process could be the other dos application and + // we will be deadlock if it is the case + WaitForSingleObject(pRdrInfo->ri_hStdOutThread, 1000); + CloseHandle(pRdrInfo->ri_hStdOutThread); + } + if (pRdrInfo->ri_pPipeStdErr) { + SetEvent((pRdrInfo->ri_pPipeStdErr)->hExitEvent); + WaitForSingleObject(pRdrInfo->ri_hStdErrThread, 1000); + CloseHandle(pRdrInfo->ri_hStdErrThread); + } + free (pRdrInfo); + + return TRUE; +} + +BOOL cmdCreateTempFile (phTempFile,ppszTempFile) +PHANDLE phTempFile; +PCHAR *ppszTempFile; +{ + +PCHAR pszTempPath = NULL; +DWORD TempPathSize; +PCHAR pszTempFileName; +HANDLE hTempFile; +SECURITY_ATTRIBUTES sa; + + pszTempPath = malloc(MAX_PATH + 12); + + if (pszTempPath == NULL) + return FALSE; + + if ((TempPathSize = GetTempPath ( + MAX_PATH, + pszTempPath)) == 0){ + free (pszTempPath); + return FALSE; + } + + if (TempPathSize >= MAX_PATH) { + free (pszTempPath); + return FALSE; + } + + // CMDCONF.C depends on the size of this buffer + if ((pszTempFileName = malloc (MAX_PATH + 13)) == NULL){ + free (pszTempPath); + return FALSE; + } + + // if this fails it probably means we have a bad path + if (!GetTempFileName(pszTempPath, "scs", 0, pszTempFileName)) + { + // lets get something else, which should succeed + TempPathSize = GetWindowsDirectory(pszTempPath, MAX_PATH); + if (!TempPathSize || TempPathSize >= MAX_PATH) + strcpy(pszTempPath, "\\"); + + // try again and hope for the best + GetTempFileName(pszTempPath, "scs", 0, pszTempFileName); + } + + + // must have a security descriptor so that the child process + // can inherit this file handle. This is done because when we + // shell out with piping the 32 bits application must have inherited + // the temp filewe created, see cmdGetStdHandle + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + if ((hTempFile = CreateFile (pszTempFileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + OPEN_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY, + NULL)) == (HANDLE)-1){ + free (pszTempFileName); + free (pszTempPath); + return FALSE; + } + + *phTempFile = hTempFile; + *ppszTempFile = pszTempFileName; + free (pszTempPath); + return TRUE; +} + +/* cmdCheckStandardHandles - Check if we have to do anything to support + * standard io redirection, if so save away + * pertaining information. + * + * Entry - pVDMInfo - VDMInfo Structure + * pbStdHandle - pointer to bit array for std handles + * + * EXIT - return NULL if no redirection involved + * return pointer to REDIRECTION_INFO + */ + +PREDIRCOMPLETE_INFO cmdCheckStandardHandles ( + PVDMINFO pVDMInfo, + USHORT UNALIGNED *pbStdHandle + ) +{ +USHORT bTemp = 0; +PREDIRCOMPLETE_INFO pRdrInfo; + + if (pVDMInfo->StdIn) + bTemp |= MASK_STDIN; + + if (pVDMInfo->StdOut) + bTemp |= MASK_STDOUT; + + if (pVDMInfo->StdErr) + bTemp |= MASK_STDERR; + + if(bTemp){ + + if ((pRdrInfo = malloc (sizeof (REDIRCOMPLETE_INFO))) == NULL) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + } + + RtlZeroMemory ((PVOID)pRdrInfo, sizeof(REDIRCOMPLETE_INFO)); + pRdrInfo->ri_hStdErr = pVDMInfo->StdErr; + pRdrInfo->ri_hStdOut = pVDMInfo->StdOut; + pRdrInfo->ri_hStdIn = pVDMInfo->StdIn; + + nt_std_handle_notification(TRUE); + fSoftpcRedirection = TRUE; + } + else{ + pRdrInfo = NULL; + nt_std_handle_notification(FALSE); + fSoftpcRedirection = FALSE; + } + + *pbStdHandle = bTemp; + return pRdrInfo; +} + +/* cmdGetStdHandle - Get the 32 bit NT standard handle for the VDM + * + * + * Entry - Client (CX) - 0,1 or 2 (stdin stdout stderr) + * Client (AX:BX) - redirinfo pointer + * + * EXIT - Client (BX:CX) - 32 bit handle + * Client (DX:AX) - file size + */ + +VOID cmdGetStdHandle (VOID) +{ +USHORT iStdHandle; +PREDIRCOMPLETE_INFO pRdrInfo; + + iStdHandle = getCX(); + pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getAX() << 16) + (ULONG)getBX()); + + switch (iStdHandle) { + + case HANDLE_STDIN: + + if (GetFileType(pRdrInfo->ri_hStdIn) == FILE_TYPE_PIPE) { + if (!cmdHandleStdinWithPipe (pRdrInfo)) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + setCF(1); + return; + } + setCX ((USHORT)pRdrInfo->ri_hStdInFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdInFile >> 16)); + } + else { + setCX ((USHORT)pRdrInfo->ri_hStdIn); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdIn >> 16)); + } + break; + + case HANDLE_STDOUT: + if (GetFileType (pRdrInfo->ri_hStdOut) == FILE_TYPE_PIPE){ + if (!cmdHandleStdOutErrWithPipe(pRdrInfo, HANDLE_STDOUT)) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + setCF(1); + return; + } + setCX ((USHORT)pRdrInfo->ri_hStdOutFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdOutFile >> 16)); + + } + else { + // sudeepb 16-Mar-1992; This will be a compatibilty problem. + // If the user gives the command "dosls > lpt1" we will + // inherit the 32 bit handle of lpt1, so the ouput will + // directly go to the LPT1 and a DOS TSR/APP hooking int17 + // wont see this printing. Is this a big deal??? + setCX ((USHORT)pRdrInfo->ri_hStdOut); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdOut >> 16)); + } + break; + + case HANDLE_STDERR: + + if (pRdrInfo->ri_hStdErr == pRdrInfo->ri_hStdOut + && pRdrInfo->ri_hStdOutFile != 0) { + setCX ((USHORT)pRdrInfo->ri_hStdOutFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdOutFile >> 16)); + pRdrInfo->ri_hStdErrFile = pRdrInfo->ri_hStdOutFile; + break; + } + + if (GetFileType (pRdrInfo->ri_hStdErr) == FILE_TYPE_PIPE){ + if(!cmdHandleStdOutErrWithPipe(pRdrInfo, HANDLE_STDERR)) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + setCF(1); + return; + } + setCX ((USHORT)pRdrInfo->ri_hStdErrFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdErrFile >> 16)); + } + else { + setCX ((USHORT)pRdrInfo->ri_hStdErr); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdErr >> 16)); + } + break; + } + setAX(0); + setDX(0); + setCF(0); + return; +} + +BOOL cmdHandleStdOutErrWithPipe( + PREDIRCOMPLETE_INFO pRdrInfo, + USHORT HandleType + ) +{ + + HANDLE hFile; + PCHAR pFileName; + PPIPE_OUTPUT pPipe; + BYTE *Buffer; + DWORD ThreadId; + HANDLE hEvent; + HANDLE hFileWrite; + HANDLE hThread; + + if(!cmdCreateTempFile(&hFile,&pFileName)) + return FALSE; + // must have a different handle so that writter(dos app) and reader(us) + // wont use the same handle object(especially, file position) + hFileWrite = CreateFile(pFileName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_TEMPORARY, + NULL + ); + if (hFileWrite == INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + DeleteFile(pFileName); + return FALSE; + } + Buffer = malloc(sizeof(PIPE_OUTPUT) + PIPE_OUTPUT_BUFFER_SIZE); + if (Buffer == NULL) { + CloseHandle(hFile); + CloseHandle(hFileWrite); + DeleteFile(pFileName); + return FALSE; + } + pPipe = (PPIPE_OUTPUT)Buffer; + pPipe->Buffer = Buffer + sizeof(PIPE_OUTPUT); + pPipe->BufferSize = PIPE_OUTPUT_BUFFER_SIZE; + pPipe->hFile = hFileWrite; + pPipe->pFileName = pFileName; + pPipe->hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pPipe->hExitEvent == NULL) { + CloseHandle(hFile); + CloseHandle(hFileWrite); + DeleteFile(pFileName); + free(pPipe); + return FALSE; + } + + if (HandleType == HANDLE_STDOUT) { + pPipe->hPipe = pRdrInfo->ri_hStdOut; + pRdrInfo->ri_pPipeStdOut = pPipe; + pRdrInfo->ri_hStdOutFile = hFile; + + } + else { + pPipe->hPipe = pRdrInfo->ri_hStdErr; + pRdrInfo->ri_pPipeStdErr = pPipe; + pRdrInfo->ri_hStdErrFile = hFile; + + } + hThread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL, + (DWORD)0, + (LPTHREAD_START_ROUTINE)cmdPipeOutThread, + (LPVOID)pPipe, + 0, + &ThreadId + ); + if (hThread == NULL) { + CloseHandle(pPipe->hExitEvent); + CloseHandle(hFileWrite); + CloseHandle(hFile); + DeleteFile(pFileName); + free(Buffer); + return FALSE; + } + if (HandleType == HANDLE_STDOUT) + pRdrInfo->ri_hStdOutThread = hThread; + else + pRdrInfo->ri_hStdErrThread = hThread; + return TRUE; +} + +/* independent thread to read application stdout(file) to NTVDM stdout(PIPE). + The CPU thread would notify us through hExitEvent when the application + is terminating(thus, we can detect EOF and exit + */ + +VOID cmdPipeOutThread(LPVOID lpParam) +{ + PPIPE_OUTPUT pPipe; + DWORD BytesRead; + DWORD BytesWritten; + BOOL ExitPending; + + pPipe = (PPIPE_OUTPUT)lpParam; + + ExitPending = FALSE; + + while(ReadFile(pPipe->hFile, pPipe->Buffer, pPipe->BufferSize, &BytesRead, NULL) ) { + // go nothing doesn't mean it hits EOF!!!!!! + // we can not just exit now, instead, we have to wait and poll + // until the application is terminated. + // + if (BytesRead == 0) { + // if read nothing and the application is gone, we can quit now + if (ExitPending) + break; + if (!WaitForSingleObject(pPipe->hExitEvent, PIPE_OUTPUT_TIMEOUT)) + ExitPending = TRUE; + } + else { + if (!WriteFile(pPipe->hPipe, pPipe->Buffer, BytesRead, &BytesWritten, NULL) || + BytesWritten != BytesRead) + break; + } + } + // if we were out of loop because of errors, wait for the cpu thread. + if (!ExitPending) + WaitForSingleObject(pPipe->hExitEvent, INFINITE); + + CloseHandle(pPipe->hFile); + CloseHandle(pPipe->hPipe); + CloseHandle(pPipe->hExitEvent); + DeleteFile(pPipe->pFileName); + free(pPipe->pFileName); + free(pPipe); + ExitThread(0); +} + +BOOL cmdHandleStdinWithPipe ( + PREDIRCOMPLETE_INFO pRdrInfo + ) +{ + + HANDLE hStdinFile; + PCHAR pStdinFileName; + PPIPE_INPUT pPipe; + BYTE *Buffer; + DWORD ThreadId; + HANDLE hEvent; + HANDLE hFileWrite; + + if(!cmdCreateTempFile(&hStdinFile,&pStdinFileName)) + return FALSE; + + + // must have a different handle so that reader(dos app) and writter(us) + // wont use the same handle object(especially, file position) + hFileWrite = CreateFile(pStdinFileName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_TEMPORARY, + NULL + ); + if (hFileWrite == INVALID_HANDLE_VALUE) { + CloseHandle(hStdinFile); + DeleteFile(pStdinFileName); + return FALSE; + } + Buffer = malloc(sizeof(PIPE_INPUT) + PIPE_INPUT_BUFFER_SIZE); + if (Buffer == NULL) { + CloseHandle(hStdinFile); + CloseHandle(hFileWrite); + DeleteFile(pStdinFileName); + return FALSE; + } + hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (hEvent == NULL) { + CloseHandle(hStdinFile); + CloseHandle(hFileWrite); + DeleteFile(pStdinFileName); + free(Buffer); + return FALSE; + } + pPipe = (PPIPE_INPUT)Buffer; + pPipe->Buffer = Buffer + sizeof(PIPE_INPUT); + pPipe->BufferSize = PIPE_INPUT_BUFFER_SIZE; + pPipe->fEOF = FALSE; + pPipe->hFileWrite = hFileWrite; + pPipe->hFileRead = hStdinFile; + pPipe->hDataEvent = hEvent; + pPipe->hPipe = pRdrInfo->ri_hStdIn; + pPipe->pFileName = pStdinFileName; + InitializeCriticalSection(&pPipe->CriticalSection); + pPipe->hThread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL, + (DWORD)0, + (LPTHREAD_START_ROUTINE)cmdPipeInThread, + (LPVOID)pPipe, + 0, + &ThreadId + ); + if (pPipe->hThread == NULL) { + CloseHandle(hFileWrite); + CloseHandle(pPipe->hDataEvent); + CloseHandle(hStdinFile); + DeleteFile(pStdinFileName); + free(Buffer); + return FALSE; + } + // always have the new node in the head of the list because + // it is the node used by the top command.com running in the process. + // We may have multiple command.com instances running in the same + // ntvdm proecess and each command.com has a private PREDIRCOMPLETE_INFO + // associated with it if its stdin is redirected to a pipe. + pPipe->Next = cmdPipeList; + cmdPipeList = pPipe; + pRdrInfo->ri_hStdInFile = hStdinFile; + pRdrInfo->ri_pPipeStdIn = pPipe; + return TRUE; +} + +/* Independent thread to read from pipe(NTVDM STDIN) and write to + file(DOS application STDIN) until either the pipe is broken or + there are some errors. + This thread may never terminate itself because it can block + in the ReadFile call to the pipe forever. If this is the case, + we have to rely on the CPU thread to kill it. To allow the CPU + thread safely launching the killing, this thread yields the + critical section when it is safe to be killed and the CPU thread + would claim the critical section first before going for kill. + */ + +VOID cmdPipeInThread(LPVOID lpParam) +{ + PPIPE_INPUT pPipe; + DWORD BytesRead, BytesWritten; + BOOL ReadStatus, WriteStatus; + BOOL ApplicationTerminated, fEOF; + + pPipe = (PPIPE_INPUT)lpParam; + while (TRUE) { + + // this read can take forever without getting back anything + ReadStatus = ReadFile(pPipe->hPipe, pPipe->Buffer, + pPipe->BufferSize, &BytesRead, NULL); + + // claim the critical section so we won't get killed + // by the CPU thread + EnterCriticalSection(&pPipe->CriticalSection); + if (ReadStatus) { + if (BytesRead != 0) { + WriteStatus = WriteFile(pPipe->hFileWrite, + pPipe->Buffer, + BytesRead, + &BytesWritten, + NULL + ); + if (pPipe->WaitData && WriteStatus && BytesWritten != 0) + SetEvent(pPipe->hDataEvent); + } + } + else { + if (GetLastError() == ERROR_BROKEN_PIPE) { + + // pipe is broken and more data to read? + ASSERT(BytesRead == 0); + pPipe->fEOF = TRUE; + LeaveCriticalSection(&pPipe->CriticalSection); + break; + } + } + // as soon as we leave the critical seciton, the CPU thread may + // step in and kill us + LeaveCriticalSection(&pPipe->CriticalSection); + } + ExitThread(0); +} + +/* cmdPipeFileDataEOF - Check for new data or EOF + * + * + * Entry - hFile, DOS application STDIN file handle(file) + * &fEOF, to return if the pipe is broken + * EXIT - TRUE if either there are new data or EOF is true + * *fEOF == TRUE if EOF + */ + +BOOL cmdPipeFileDataEOF(HANDLE hFile, BOOL *fEOF) +{ + PPIPE_INPUT pPipe; + BOOL NewData; + DWORD WaitStatus; + + pPipe = cmdPipeList; + while (pPipe != NULL && pPipe->hFileRead != hFile) + pPipe = pPipe->Next; + + NewData = TRUE; + *fEOF = TRUE; + + if (pPipe != NULL) { + EnterCriticalSection(&pPipe->CriticalSection); + *fEOF = pPipe->fEOF; + if (!(*fEOF)) { + pPipe->WaitData = TRUE; + LeaveCriticalSection(&pPipe->CriticalSection); + WaitStatus = WaitForSingleObject(pPipe->hDataEvent, PIPE_INPUT_TIMEOUT); + EnterCriticalSection(&pPipe->CriticalSection); + *fEOF = pPipe->fEOF; + pPipe->WaitData = FALSE; + NewData = WaitStatus == 0 ? TRUE : FALSE; + } + LeaveCriticalSection(&pPipe->CriticalSection); + } + return(NewData || *fEOF); +} + +/* cmdPipeFileEOF - Check if the pipe is broken + * + * + * Entry - hFile, DOS application STDIN file handle(file) + * + * EXIT - TRUE if the write end of the pipe is closed + */ + + +BOOL cmdPipeFileEOF(HANDLE hFile) +{ + PPIPE_INPUT pPipe; + BOOL fEOF; + + pPipe = cmdPipeList; + while (pPipe != NULL && pPipe->hFileRead != hFile) + pPipe = pPipe->Next; + + fEOF = TRUE; + + if (pPipe != NULL) { + EnterCriticalSection(&pPipe->CriticalSection); + fEOF = pPipe->fEOF; + LeaveCriticalSection(&pPipe->CriticalSection); + } + if (!fEOF) { + Sleep(PIPE_INPUT_TIMEOUT); + EnterCriticalSection(&pPipe->CriticalSection); + fEOF = pPipe->fEOF; + LeaveCriticalSection(&pPipe->CriticalSection); + } + return (fEOF); +} |