mirror of https://github.com/lianthony/NT4.0
5965 lines
167 KiB
5965 lines
167 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dlltask.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the OS/2 V2.0 Tasking API Calls
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 20-Sep-1989 (Adapted from URTL\alloc.c)
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_TASKING
|
|
#define INCL_OS2V20_FILESYS
|
|
#define INCL_OS2V20_ERRORS
|
|
#include "os2dll.h"
|
|
#include "os2dll16.h"
|
|
#include "os2win.h"
|
|
#include "conrqust.h"
|
|
#include <mi.h>
|
|
#include <ldrxport.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#if PMNT
|
|
#define INCL_32BIT
|
|
#include "pmnt.h"
|
|
#endif
|
|
|
|
#ifdef DBCS
|
|
// MSKK Apr.19.1993 V-AkihiS
|
|
//
|
|
// OS/2 internal multibyte string function.
|
|
//
|
|
#include "dlldbcs.h"
|
|
#define strstr Od2MultiByteStrstr
|
|
#endif
|
|
|
|
extern PSZ Od216ApiTable[1];
|
|
extern ULONG Od2SessionNumber;
|
|
extern ULONG timing;
|
|
extern ULONG Od2DosExitIsDone;
|
|
extern ULONG Od2Saved16Stack;
|
|
extern HANDLE Od2SyncSem;
|
|
extern HANDLE Od2GarbageCollectSem;
|
|
|
|
BOOLEAN Od2NeedToResumeThreads = FALSE;
|
|
HANDLE Od2CritSecEvent;
|
|
HANDLE Od2NewThreadSync;
|
|
BOOLEAN Od2NewThreadDisabled = FALSE;
|
|
|
|
VOID
|
|
Od2JumpTo16ExitRoutine(
|
|
PFNEXITLIST ExitRoutine,
|
|
ULONG ExitReason);
|
|
|
|
VOID
|
|
Od2CloseSrvHandle(
|
|
IN ULONG HandleNumber,
|
|
IN HANDLE Handle1,
|
|
IN HANDLE Handle2,
|
|
IN HANDLE Handle3
|
|
);
|
|
|
|
extern VOID __cdecl RestoreTebForThread1();
|
|
extern VOID __cdecl MoveInfoSegintoTeb();
|
|
extern VOID __cdecl RestoreTeb();
|
|
|
|
extern PVOID __cdecl Od2JumpTo16SignalDispatch(ULONG address, ULONG regs,
|
|
ULONG usFlagNum, ULONG usFlagArg);
|
|
|
|
VOID
|
|
Od2JumpTo16SignalDispatchBorder(VOID);
|
|
|
|
VOID
|
|
Od2JumpTo16SignalDispatchEnd(VOID);
|
|
|
|
VOID
|
|
ExitFlatAreaBegin(VOID);
|
|
|
|
VOID
|
|
ExitFlatAreaEnd(VOID);
|
|
|
|
VOID
|
|
Od2ContinueStartBorder(VOID);
|
|
|
|
VOID
|
|
Od2ContinueEndBorder(VOID);
|
|
|
|
VOID
|
|
Od2SetSegmentRegisters(VOID);
|
|
|
|
VOID
|
|
Od2SetSegmentRegistersEnd(VOID);
|
|
|
|
VOID
|
|
SaveFSSeg(
|
|
LOCALINFOSEG* pTebBlock
|
|
);
|
|
|
|
VOID
|
|
RestoreFSSeg(
|
|
LOCALINFOSEG* pTebBlock
|
|
);
|
|
|
|
VOID
|
|
Od2Continue(PVOID pContext);
|
|
|
|
APIRET
|
|
SearchForPath(
|
|
IN ULONG SearchFlags,
|
|
IN PSZ PathName,
|
|
OUT PBYTE Buffer,
|
|
IN ULONG Length
|
|
);
|
|
|
|
VOID
|
|
Od2CloseAllTimers( VOID );
|
|
|
|
|
|
VOID
|
|
Od2CloseAllRAMSharedSemaphores( VOID );
|
|
|
|
VOID
|
|
Od2ExitListDispatcher( VOID );
|
|
|
|
VOID
|
|
Od2InfiniteSleep( VOID );
|
|
|
|
VOID
|
|
Ow2Exit(
|
|
IN ULONG StringCode,
|
|
IN PCHAR ErrorText,
|
|
IN ULONG ExitCode
|
|
);
|
|
|
|
VOID
|
|
Od2FinalProcessCleanup( VOID );
|
|
|
|
BOOLEAN
|
|
ldtCreateSelBitmap();
|
|
|
|
NTSTATUS
|
|
Od2AlertableWaitForSingleObject(
|
|
IN HANDLE handle
|
|
);
|
|
|
|
NTSTATUS
|
|
Od2AcquireMutant(
|
|
IN HANDLE handle
|
|
);
|
|
|
|
NTSTATUS
|
|
Od2ReleaseMutant(
|
|
IN HANDLE handle
|
|
);
|
|
|
|
VOID
|
|
Od2WaitOnCritSectOrSuspend(VOID);
|
|
|
|
static CHAR ConfigSysString[] = "CONFIG.SYS";
|
|
|
|
#define BLANK(ch) ((ch) == ' ' || (ch) == '\t')
|
|
#define SEPARATOR(ch) (BLANK(ch) || (ch) == '"' || (ch) == '(' || (ch) == ')' || \
|
|
(ch) == '>' || (ch) == '<' || (ch) == '|' || (ch) == '&' || (ch) == '\0')
|
|
|
|
extern ULONG Od2Start16Stack;
|
|
extern ULONG Od2Start16DS;
|
|
|
|
extern PSZ Od2ExecPgmErrorText;
|
|
extern ULONG Od2ExecPgmErrorTextLength;
|
|
|
|
extern HANDLE CtrlDataSemaphore;
|
|
extern HANDLE hOs2Srv;
|
|
|
|
extern HANDLE Od2SyncSem;
|
|
extern HANDLE Od2GarbageCollectSem;
|
|
|
|
#define NUMOF32ASYNCPROC 50
|
|
|
|
struct
|
|
{
|
|
HANDLE hProcess;
|
|
ULONG dwProcessId;
|
|
BOOLEAN CreateSync;
|
|
} Od2AsyncProc[NUMOF32ASYNCPROC];
|
|
|
|
ULONG Od2CritSectCounter = 0;
|
|
ULONG Od2CritSectOwner = 0;
|
|
BOOLEAN Od2SigHandAlreadyInProgress = FALSE;
|
|
|
|
#pragma pack (1)
|
|
typedef struct _PHARLAP_CONFIG {
|
|
UCHAR uchCopyRight[0x32];
|
|
USHORT usType;
|
|
USHORT usRsv1;
|
|
USHORT usRsv2;
|
|
USHORT usSign;
|
|
} CONFIGPHARLAP, *PCONFIGPHARLAP;
|
|
#pragma pack ()
|
|
|
|
NTSTATUS
|
|
AcquireTaskLock( VOID )
|
|
{
|
|
if ((!Od2SigHandlingInProgress) || ((Od2CurrentThreadId()) != 1))
|
|
return Od2AcquireMutant(Od2Process->TaskLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ReleaseTaskLock( VOID )
|
|
{
|
|
if ((!Od2SigHandlingInProgress) || ((Od2CurrentThreadId()) != 1))
|
|
return Od2ReleaseMutant(Od2Process->TaskLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
Od2InitializeTask( VOID )
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Od2Process = RtlAllocateHeap( Od2Heap, 0, sizeof( *Od2Process ) );
|
|
ASSERT( Od2Process != NULL );
|
|
if (Od2Process == NULL) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
RtlZeroMemory( Od2Process, sizeof( *Od2Process ) );
|
|
|
|
Status = NtCreateMutant(
|
|
&Od2Process->TaskLock,
|
|
MUTANT_ALL_ACCESS,
|
|
NULL,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
IF_OD2_DEBUG ( SEMAPHORES ) {
|
|
DbgPrint("Od2InitSem: error at NtopenSemaphore, Status %x\n", Status);
|
|
}
|
|
#endif
|
|
}
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return(Status);
|
|
}
|
|
|
|
RtlInitializeResource( &Od2Process->FileLock );
|
|
|
|
InitializeListHead( &Od2Process->ThreadList );
|
|
InitializeListHead( &Od2Process->ExitList );
|
|
InitializeListHead( &Od2Process->MsgFileList );
|
|
|
|
Od2Thread1 = RtlAllocateHeap( Od2Heap, 0, sizeof( *Od2Thread1 ) );
|
|
ASSERT( Od2Thread1 != NULL );
|
|
if (Od2Thread1 == NULL) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
RtlZeroMemory( Od2Thread1, sizeof( *Od2Thread1 ) );
|
|
//
|
|
// Create event that will be used by DosSuspendThread and
|
|
// DosEnterCritSect APIs.
|
|
//
|
|
Status = NtCreateEvent(
|
|
&(Od2Thread1->Event),
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
NotificationEvent,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Od2InitializeTask fail to create thread event, status=%x\n",
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
RtlFreeHeap(Od2Heap, 0, Od2Thread1);
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
InsertTailList( &Od2Process->ThreadList, &Od2Thread1->Link );
|
|
//
|
|
// Duplicate our own thread handle for later suspension in critsec
|
|
//
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
GetCurrentThread(),
|
|
GetCurrentProcess(),
|
|
&Od2Thread1->ThreadHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)){
|
|
#if DBG
|
|
KdPrint(( "Od2InitializeTask: fail to duplicate thread %d\n",GetLastError()));
|
|
#endif
|
|
return( STATUS_ACCESS_DENIED);
|
|
}
|
|
|
|
if (!ldtCreateSelBitmap()) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
//
|
|
// Create an Nt event to be used for
|
|
// syncronization between a thread calling DosExitCritSec and
|
|
// threads that were in 32 bit api code when DosEnterCritSec was called
|
|
//
|
|
Status = NtCreateEvent(
|
|
&Od2CritSecEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
IF_OD2_DEBUG ( SEMAPHORES ) {
|
|
DbgPrint("Od2InitSem: error at NtCreateSemaphore, Status %x\n", Status);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Mutant that will be used for new thread sincronzation with process
|
|
// termination.
|
|
|
|
Status = NtCreateMutant(
|
|
&Od2NewThreadSync,
|
|
MUTANT_ALL_ACCESS,
|
|
NULL,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Od2InitializeTask: Fail to create mutant, Status=%x\n",
|
|
Status);
|
|
#endif // DBG
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
// Terminate thread if something goes wrong. Used during sturtup only from
|
|
// Od2RegisterThread.
|
|
|
|
VOID
|
|
Od2AbortThread(
|
|
IN POD2_THREAD Thread
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtReleaseMutant(Od2NewThreadSync, NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("Od2AbortThread: Release NewThreadSync, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif //DBG
|
|
NtClose(Thread->ThreadHandle);
|
|
RtlFreeHeap(Od2Heap, 0, Thread);
|
|
ExitThread(0);
|
|
}
|
|
|
|
// Call subsystem to register the new thread. But don't do anything in the case
|
|
// that the process is going to terminate. For this reasone use the mutant
|
|
// Od2NewThreadSync to syncronize with exit list processing. Before exit list
|
|
// processing the flag Od2NewThreadDisabled will be set indicating that new thread
|
|
// can't be created.
|
|
|
|
NTSTATUS
|
|
Od2RegisterThread(
|
|
IN POD2_THREAD Thread
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSCREATETHREAD_MSG a = &m.u.DosCreateThread;
|
|
THREAD_BASIC_INFORMATION ThreadInfo;
|
|
NTSTATUS Status;
|
|
|
|
Status = Od2AlertableWaitForSingleObject(Od2NewThreadSync);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("Od2RegisterThread: Wait for NewThreadSync, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
|
|
// If process is going to terminate, don't do anything, terminate yourself.
|
|
if (Od2NewThreadDisabled) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("Od2RegisterThread: No new threads\n");
|
|
}
|
|
#endif //DBG
|
|
Od2AbortThread(Thread);
|
|
}
|
|
|
|
a->Flags = Thread->Flags;
|
|
a->ClientOs2Tib = &Thread->Os2Tib;
|
|
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
GetCurrentThread(),
|
|
hOs2Srv,
|
|
&a->ThreadHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)){
|
|
ASSERT(FALSE);
|
|
Thread->rc = ERROR_ACCESS_DENIED;
|
|
Od2AbortThread(Thread);
|
|
}
|
|
|
|
Status = NtQueryInformationThread(GetCurrentThread(),
|
|
ThreadBasicInformation,
|
|
(PVOID)(&ThreadInfo),
|
|
sizeof(ThreadInfo),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
KdPrint(( "Od2RegisterThread: fail to Query Information %lx\n",Status));
|
|
#endif
|
|
Thread->rc = Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
|
|
Od2CloseSrvHandle(1, a->ThreadHandle, NULL, NULL);
|
|
Od2AbortThread(Thread);
|
|
}
|
|
|
|
a->ClientId = ThreadInfo.ClientId;
|
|
|
|
Od2CallSubsystem( &m, NULL, Os2CreateThread, sizeof( *a ) );
|
|
|
|
if( Thread->rc = m.ReturnedErrorValue ) {
|
|
// If the return value is ERROR_INVALID_FUNCTION, thats means that the
|
|
// server perform exit processing for this process. It will know to close
|
|
// the handle.
|
|
if (Thread->rc != ERROR_INVALID_FUNCTION) {
|
|
#if DBG
|
|
KdPrint(( "Od2RegisterThread: OS2Srv error return value %ld\n",m.ReturnedErrorValue));
|
|
#endif // DBG
|
|
Od2CloseSrvHandle(1, a->ThreadHandle, NULL, NULL);
|
|
}
|
|
Od2AbortThread(Thread);
|
|
}
|
|
|
|
Status = NtReleaseMutant(Od2NewThreadSync, NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("Od2RegisterThread: Release NewThreadSync, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif //DBG
|
|
|
|
// The thread is known by the server.
|
|
|
|
AcquireTaskLock();
|
|
InsertTailList( &Od2Process->ThreadList, &Thread->Link );
|
|
ReleaseTaskLock();
|
|
|
|
#if PMNT
|
|
// Force all PM threads to run on processor#1. Otherwise, PM Desktop
|
|
// locks-up
|
|
if (ProcessIsPMProcess())
|
|
{
|
|
DWORD Ret;
|
|
|
|
Ret = SetThreadAffinityMask(
|
|
GetCurrentThread(),
|
|
0x1);
|
|
#if DBG
|
|
if (Ret == 0)
|
|
{
|
|
DbgPrint("Od2RegisterThread: failed to SetThreadAffinityMask\n");
|
|
}
|
|
else if (Ret != 1)
|
|
{
|
|
DbgPrint("Od2RegisterThread: SetThreadAffinityMask returned %x (now set to 1)\n",
|
|
Ret);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
#endif //PMNT
|
|
|
|
// Tell the thread that created us, that we are all right. Thread that called
|
|
// DosCreateThread will continue execution.
|
|
|
|
Status = NtSetEvent(Thread->Event, NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("Od2RegisterThread: fail to set startup event, Status=%x\n",
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
|
|
if ((Thread->Flags & DCT_SUSPENDED) != 0) {
|
|
// Suspend myself.
|
|
DosSuspendThread((TID)(Thread->Os2Tib.ThreadId));
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
Od2InitializeThread(
|
|
IN POD2_THREAD Thread
|
|
)
|
|
{
|
|
PTEB Teb;
|
|
LINFOSEG *pLocalInfo;
|
|
ldrrei_t *pexecinfo;
|
|
NT_TIB *NtTib;
|
|
|
|
Teb = NtCurrentTeb();
|
|
Teb->EnvironmentPointer = (PVOID)Thread;
|
|
NtTib = &Teb->NtTib;
|
|
|
|
//
|
|
// Set Local Information Fields
|
|
//
|
|
pLocalInfo = (LINFOSEG *) &(Thread->Os2Tib.LInfoSeg),
|
|
pLocalInfo->pidCurrent = (USHORT)(Od2Process->Pib.ProcessId);
|
|
pLocalInfo->pidParent = (USHORT)(Od2Process->Pib.ParentProcessId);
|
|
pLocalInfo->prtyCurrent = (USHORT)(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.Priority);
|
|
#ifndef PMNT
|
|
pLocalInfo->sgCurrent = (USHORT)Od2SessionNumber; // What's a more appropriate value?
|
|
#endif
|
|
pLocalInfo->rfProcStatus = 0; // What's a more appropriate value?
|
|
pLocalInfo->fForeground = TRUE;
|
|
#ifndef PMNT
|
|
pLocalInfo->typeProcess = 0; // BUGBUG - BRIEF3.1 for Beta 1 YS. PT_WINDOWABLEVIO; // meaning windowed, protected mode
|
|
#endif
|
|
|
|
if (Thread->Os2Tib.ThreadId != 1) {
|
|
#ifdef PMNT
|
|
// we do not know whether a process is a PM Process
|
|
// until loading terminates
|
|
// 1st thread fields are determined in Od2UpdateLocalInfoAtStart()
|
|
// Note: PMSHELL has typeProcess & sgCurrent of a PM Process under PM\NT,
|
|
// but not under OS2 native
|
|
if (ProcessIsPMProcess()) {
|
|
pLocalInfo->sgCurrent = (USHORT)(32 + Od2SessionNumber); //PQ - see PMWIN\wininit1.c, this is SGID_PM
|
|
// and all PM processes have a session number
|
|
// above or equal to this number
|
|
pLocalInfo->typeProcess = 3; // Indicates a PM process
|
|
}
|
|
else {
|
|
pLocalInfo->sgCurrent = (USHORT)Od2SessionNumber; // What's a more appropriate value?
|
|
pLocalInfo->typeProcess = 0; // BUGBUG - BRIEF3.1 for Beta 1 YS. PT_WINDOWABLEVIO; // meaning windowed, protected mode
|
|
}
|
|
#endif
|
|
pexecinfo = (PVOID) LDRExecInfo;
|
|
pLocalInfo->selEnvironment = pexecinfo->ei_envsel;
|
|
pLocalInfo->offCmdLine = pexecinfo->ei_comoff;
|
|
pLocalInfo->cbDataSegment = pexecinfo->ei_dgroupsize;
|
|
pLocalInfo->cbStack = pexecinfo->ei_stacksize;
|
|
pLocalInfo->cbHeap = pexecinfo->ei_heapsize;
|
|
pLocalInfo->hmod = pexecinfo->ei_hmod;
|
|
pLocalInfo->selDS = pexecinfo->ei_ds;
|
|
pLocalInfo->tidCurrent = (USHORT)(Od2CurrentThreadId());
|
|
Thread->Os2Tib.LInfoSeg.IsRealTEB = 0;
|
|
}
|
|
|
|
Thread->Os2Tib.LInfoSeg.tebptr = (ULONG)&(Thread->Os2Tib.TebBackupIn16Bit);
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2UserThreadStartup(
|
|
IN POD2_THREAD Thread
|
|
)
|
|
{
|
|
try {
|
|
Od2RegisterThread(Thread);
|
|
Od2InitializeThread( Thread );
|
|
DosExit( EXIT_THREAD, (*Thread->StartAddress)( Thread->Parameter ) );
|
|
}
|
|
//
|
|
// if Os2Debug is on, and ntsd is attached, it will get the second chance
|
|
//
|
|
#if DBG
|
|
except( (Os2Debug ? Ow2FaultFilter(EXCEPTION_CONTINUE_SEARCH, GetExceptionInformation()):
|
|
|
|
Ow2FaultFilter(EXCEPTION_EXECUTE_HANDLER, GetExceptionInformation())) ) {
|
|
#else
|
|
except( Ow2FaultFilter(EXCEPTION_EXECUTE_HANDLER, GetExceptionInformation()) ) {
|
|
#endif
|
|
#if DBG
|
|
KdPrint(("OS2: Internal error - Exception occured in 32bit os2ss code\n"));
|
|
#endif
|
|
DosEnterCritSec();
|
|
Ow2DisplayExceptionInfo();
|
|
|
|
DosExit(EXIT_PROCESS, 13);
|
|
}
|
|
//
|
|
// If DosExit fails, then force an exception.
|
|
//
|
|
|
|
#if DBG
|
|
KdPrint(( "OS2DLL: DosExit failed, about to generate a fault\n" ));
|
|
DbgBreakPoint();
|
|
#endif
|
|
}
|
|
|
|
POD2_THREAD
|
|
Od2FindOd2Thread(
|
|
POD2_THREAD refThread
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POD2_THREAD Thread;
|
|
|
|
ListHead = &Od2Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OD2_THREAD, Link );
|
|
if (Thread == refThread)
|
|
break;
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
if (Thread != refThread) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d] Od2FindOd2Thread(#%x) - invalid thread\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
refThread);
|
|
}
|
|
#endif // DBG
|
|
return NULL;
|
|
}
|
|
return Thread;
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosCreateThread(
|
|
OUT PTID ThreadId,
|
|
IN PFNTHREAD StartAddress,
|
|
IN ULONG Parameter,
|
|
IN ULONG Flags,
|
|
IN ULONG StackSize
|
|
)
|
|
{
|
|
POD2_THREAD Thread;
|
|
ULONG Tid;
|
|
NTSTATUS Status;
|
|
APIRET rc;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
KdPrint(("entering DosCreateThread\n"));
|
|
}
|
|
#endif
|
|
StackSize = ROUND_UP_TO_PAGES( StackSize );
|
|
#ifdef PMNT
|
|
if (StackSize == 0 || Flags & ~(DCT_SUSPENDED | DCT_RUNABLE_HIDDEN)) {
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
#else
|
|
if (StackSize == 0 || Flags & ~DCT_SUSPENDED) {
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// probe parms
|
|
//
|
|
|
|
try {
|
|
Od2ProbeForWrite( (PVOID)ThreadId, sizeof( PTID ), 1 );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
if (Od2Process->Pib.Status & PS_EXITLIST) {
|
|
return( ERROR_INVALID_FUNCTION );
|
|
}
|
|
|
|
Thread = RtlAllocateHeap( Od2Heap, 0, sizeof( *Thread ) );
|
|
if (!Thread) {
|
|
#if DBG
|
|
KdPrint(("OS2: DosCreateThread out of heap memory, fail\n"));
|
|
#endif
|
|
ASSERT( FALSE );
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// Create event that will notify to DosCreateThread that the thread was created.
|
|
// After it will be used by DosSuspendThread. The handle to this event will be
|
|
// closed upon DosExit with EXIT_THREAD parameter.
|
|
Status = NtCreateEvent(
|
|
&(Thread->Event),
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
NotificationEvent,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("DosCreateThread fail to create thread event, status=%x\n",
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
RtlFreeHeap(Od2Heap, 0, Thread);
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
Thread->StartAddress = StartAddress;
|
|
Thread->Parameter = Parameter;
|
|
Thread->Os2Tib.MustCompleteForceFlag = 0;
|
|
Thread->Flags = Flags;
|
|
|
|
Thread->ThreadHandle = CreateThread( NULL,
|
|
StackSize,
|
|
(PFNTHREAD)Od2UserThreadStartup,
|
|
(PVOID)Thread,
|
|
0, // Create thread not suspended
|
|
&Tid);
|
|
if (!(Thread->ThreadHandle)){
|
|
#if DBG
|
|
KdPrint(("DosCreateThread - fail at win32 CreateThread, %d\n",GetLastError()));
|
|
#endif
|
|
RtlFreeHeap(Od2Heap, 0, Thread);
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
// Wait for notification from the thread that it has been initialized
|
|
Status = Od2AlertableWaitForSingleObject(Thread->Event);
|
|
#if DBG
|
|
if (Status != STATUS_SUCCESS) {
|
|
DbgPrint("DosCreateThread wait on startup event, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
|
|
//
|
|
// The thread is now running, need to acquire the lock so we
|
|
// can safely return the results
|
|
//
|
|
AcquireTaskLock();
|
|
if (Od2FindOd2Thread(Thread)) {
|
|
|
|
rc = Thread->rc;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
KdPrint(("leaving DosCreateThread with rc %ld\n",rc));
|
|
}
|
|
#endif
|
|
|
|
if (rc == ERROR_INVALID_FUNCTION) {
|
|
// The process is going to die. Wait for death.
|
|
ReleaseTaskLock();
|
|
Od2InfiniteSleep();
|
|
}
|
|
|
|
//
|
|
// This is the application address, so write only 16 bit
|
|
//
|
|
if (rc == NO_ERROR) {
|
|
*(PUSHORT)ThreadId = (USHORT) Thread->Os2Tib.ThreadId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The thread exited already, can't use the thread structure:
|
|
// set status to OK
|
|
//
|
|
rc = NO_ERROR;
|
|
|
|
// PatrickQ 1-9-96:
|
|
// Set ThreadID anyhow - _beginthread() uses thread ID for its
|
|
// return value and some applications might fail if they get an invalid
|
|
// thread ID (example: CBA application)
|
|
|
|
*(PUSHORT)ThreadId = 1; // dummy value but valid thread ID
|
|
|
|
#if DBG
|
|
// Add debug message at this exit-point as well
|
|
IF_OD2_DEBUG( TASKING )
|
|
{
|
|
KdPrint(("leaving DosCreateThread with rc %ld (thread already exited)\n",rc));
|
|
}
|
|
#endif
|
|
}
|
|
ReleaseTaskLock();
|
|
return(rc);
|
|
}
|
|
|
|
APIRET
|
|
Od2AttachWinThreadToOs2(VOID)
|
|
{
|
|
//
|
|
// This routine is called by a thread created by Win32, to enable it to use OS/2 APIs
|
|
// The current usage is for Async netBios (client\dllnet16.c)
|
|
//
|
|
// It:
|
|
// creates OD2_THREAD structure
|
|
// Initialize it with the Od2Process list
|
|
// Call os2srv so it will treat is as an Os/2 created thread
|
|
//
|
|
OS2_API_MSG m;
|
|
POS2_DOSCREATETHREAD_MSG a = &m.u.DosCreateThread;
|
|
POD2_THREAD Thread;
|
|
THREAD_BASIC_INFORMATION ThreadInfo;
|
|
NTSTATUS Status;
|
|
|
|
Thread = RtlAllocateHeap( Od2Heap, 0, sizeof( *Thread ) );
|
|
if (Thread == NULL) {
|
|
#if DBG
|
|
KdPrint(( "OS2DLL: Od2AttachWinThreadToOs2 can't allocate from heap\n" ));
|
|
ASSERT(FALSE);
|
|
#endif
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
a->Flags = DCT_RUNABLE | DCT_ATTACHED;
|
|
a->ClientOs2Tib = &Thread->Os2Tib;
|
|
|
|
//
|
|
// Duplicate our own thread handle for os2srv
|
|
//
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
GetCurrentThread(),
|
|
hOs2Srv,
|
|
&a->ThreadHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)){
|
|
#if DBG
|
|
KdPrint(( "Od2AttachWinThreadtoOs2: fail to duplicate thread %d\n",GetLastError()));
|
|
#endif
|
|
RtlFreeHeap(Od2Heap, 0, Thread);
|
|
return( ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
Status = NtQueryInformationThread(GetCurrentThread(),
|
|
ThreadBasicInformation,
|
|
(PVOID)(&ThreadInfo),
|
|
sizeof(ThreadInfo),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
KdPrint(( "Od2AttachWinThreadtoOs2: fail to Query Information %lx\n",Status));
|
|
#endif
|
|
RtlFreeHeap(Od2Heap, 0, Thread);
|
|
Od2CloseSrvHandle(1, a->ThreadHandle, NULL, NULL);
|
|
return(Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED));
|
|
}
|
|
|
|
a->ClientId = ThreadInfo.ClientId;
|
|
|
|
Od2CallSubsystem( &m, NULL, Os2CreateThread, sizeof( *a ) );
|
|
|
|
if( m.ReturnedErrorValue ) {
|
|
#if DBG
|
|
KdPrint(( "Od2AttachWinThreadtoOs2: OS2Srv error return value %ld\n",m.ReturnedErrorValue));
|
|
#endif
|
|
RtlFreeHeap(Od2Heap, 0, Thread);
|
|
Od2CloseSrvHandle(1, a->ThreadHandle, NULL, NULL);
|
|
return(m.ReturnedErrorValue);
|
|
}
|
|
Od2InitializeThread( Thread );
|
|
AcquireTaskLock();
|
|
InsertTailList( &Od2Process->ThreadList, &Thread->Link );
|
|
ReleaseTaskLock();
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosGetThreadInfo(
|
|
OUT PNT_TIB *ThreadInfo,
|
|
OUT PPIB *ProcessInfo
|
|
)
|
|
{
|
|
PTEB Teb;
|
|
|
|
Teb = NtCurrentTeb();
|
|
try {
|
|
*ThreadInfo = &Teb->NtTib;
|
|
*ProcessInfo = &Od2Process->Pib;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
return( NO_ERROR );
|
|
}
|
|
|
|
VOID
|
|
Od2DosExit(
|
|
ULONG ExitAction,
|
|
ULONG ExitResult,
|
|
ULONG ExitReason
|
|
)
|
|
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSEXIT_MSG a = &m.u.DosExit;
|
|
ULONG CurrentTid = Od2CurrentThreadId();
|
|
NTSTATUS Status;
|
|
|
|
if (timing)
|
|
{
|
|
printf("Os2 time at start of Od2DosExit is %d\n", (GetTickCount()) - timing);
|
|
}
|
|
|
|
//
|
|
// An ExitReason of 0xF0000000L is a special flag that
|
|
// tells us this is a win32 process calling to detach
|
|
// itself from the os2ss. The server already knows this
|
|
// is a win32 attached process, so it'll detach it.
|
|
//
|
|
|
|
a->ExitAction = ExitAction;
|
|
a->ExitResult = ExitResult;
|
|
Od2Process->ResultCodes.ExitReason = ((ExitReason != 0xF0000000L) ? ExitReason : TC_EXIT);
|
|
Od2Process->ResultCodes.ExitResult = ExitResult;
|
|
//
|
|
// If a thread that owns a critsect exits, clean it up first
|
|
//
|
|
while (Od2CritSectCounter && Od2CritSectOwner == CurrentTid) {
|
|
DosExitCritSec();
|
|
}
|
|
|
|
if (ExitAction == EXIT_THREAD && CurrentTid != 1) {
|
|
AcquireTaskLock();
|
|
Status = NtClose(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Event);
|
|
#if DBG
|
|
if (Status != STATUS_SUCCESS) {
|
|
DbgPrint("Od2DosExit fail to close thread event, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
RemoveEntryList( &((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Link );
|
|
NtClose(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->ThreadHandle);
|
|
RtlFreeHeap(Od2Heap, 0, NtCurrentTeb()->EnvironmentPointer);
|
|
ReleaseTaskLock();
|
|
}
|
|
else {
|
|
if (ExitReason == EXIT_THREAD || ExitReason == EXIT_PROCESS) {
|
|
//
|
|
// Note that the server is going to remove this process
|
|
// state, for later use (os2ses\os2.c)
|
|
//
|
|
Od2DosExitIsDone = 1;
|
|
}
|
|
}
|
|
|
|
Od2CallSubsystem( &m, NULL, Os2Exit, sizeof( *a ) );
|
|
|
|
if (ExitReason == 0xF0000000L) {
|
|
|
|
//
|
|
// Special value that tells us we're detaching a win32 thread
|
|
//
|
|
|
|
NtCurrentTeb()->EnvironmentPointer = NULL;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// For a thread exiting itself, win32 does not clean properly
|
|
// unless you call ExitThread()
|
|
//
|
|
if (ExitAction == EXIT_THREAD && CurrentTid != 1) {
|
|
ExitThread(0);
|
|
}
|
|
|
|
Od2InfiniteSleep();
|
|
}
|
|
|
|
VOID
|
|
Od2ExitGP()
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSGP_MSG a = &m.u.DosExitGP;
|
|
ULONG ApiIndex, Length;
|
|
|
|
ApiIndex = ((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->ApiIndex/sizeof(PSZ);
|
|
|
|
Length = strlen(Od216ApiTable[ApiIndex]);
|
|
if (Length >= MAX_API_NAME_FOR_GP)
|
|
{
|
|
Length = MAX_API_NAME_FOR_GP - 1;
|
|
}
|
|
RtlMoveMemory(&a->ApiName[0], Od216ApiTable[ApiIndex], Length);
|
|
a->ApiName[Length] = '\0';
|
|
Od2Process->ResultCodes.ExitReason = TC_TRAP;
|
|
Od2Process->ResultCodes.ExitResult = 13;
|
|
Od2CallSubsystem( &m, NULL, Os2ExitGP, sizeof( *a ) );
|
|
|
|
Od2InfiniteSleep();
|
|
|
|
#if DBG
|
|
KdPrint(("done with DosExitGP, still alive, commit suicide now\n"));
|
|
#endif
|
|
NtTerminateThread(NtCurrentThread(), STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
VOID
|
|
DosExit(
|
|
IN ULONG ExitAction,
|
|
IN ULONG ExitResult
|
|
)
|
|
{
|
|
if (Od2Process->Pib.Status & PS_EXITLIST) {
|
|
DosExitList( EXLST_EXIT, NULL );
|
|
}
|
|
|
|
Od2DosExit(ExitAction,ExitResult,TC_EXIT);
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosWaitChild(
|
|
IN ULONG WaitTarget,
|
|
IN ULONG WaitOption,
|
|
OUT PRESULTCODES ResultCodes,
|
|
OUT PPID ResultProcessId,
|
|
IN PID ProcessId
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSWAITCHILD_MSG a = &m.u.DosWaitChild;
|
|
ULONG i;
|
|
ULONG ExitCode;
|
|
NTSTATUS Status;
|
|
HANDLE hProcess = (HANDLE)ProcessId;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
KdPrint(("entering DosWaitChild\n"));
|
|
}
|
|
#endif
|
|
|
|
if (WaitTarget > DCWA_PROCESSTREE) {
|
|
return( ERROR_INVALID_DATA );
|
|
}
|
|
|
|
if (WaitOption > DCWW_NOWAIT) {
|
|
return( ERROR_INVALID_DATA );
|
|
}
|
|
|
|
if (ProcessId != 0){
|
|
//
|
|
// see if this is a win32 process
|
|
//
|
|
|
|
for (i = 0;i<NUMOF32ASYNCPROC;i++){
|
|
if ((Od2AsyncProc[i].hProcess == hProcess) && !Od2AsyncProc[i].CreateSync){
|
|
Od2AsyncProc[i].hProcess = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < NUMOF32ASYNCPROC){
|
|
//
|
|
// it is a win32 process we are waiting on -
|
|
// don't involve os2srv
|
|
//
|
|
|
|
//
|
|
// Wait for Completion and set Os/2 app parameters
|
|
//
|
|
Status = Od2AlertableWaitForSingleObject(hProcess);
|
|
if (!NT_SUCCESS(Status)){
|
|
|
|
#if DBG
|
|
KdPrint(( "DosWaitChild: fail at WaitForSingleObject %lx\n",Status));
|
|
#endif
|
|
NtClose(hProcess);
|
|
//BUGBUG: do we need to call Od2RemoveWin32ChildProcess();
|
|
return(Or2MapNtStatusToOs2Error(
|
|
Status,ERROR_ACCESS_DENIED));
|
|
}
|
|
GetExitCodeProcess(hProcess, &ExitCode);
|
|
|
|
#if DBG
|
|
if (ExitCode != NO_ERROR){
|
|
KdPrint(("DosWaitChild: result of win32 process is %d\n",
|
|
ExitCode));
|
|
}
|
|
#endif
|
|
Ow2WinExitCode2ResultCode(
|
|
ExitCode,
|
|
&ResultCodes->ExitResult,
|
|
&ResultCodes->ExitReason);
|
|
|
|
*ResultProcessId = ProcessId;
|
|
Od2RemoveWin32ChildProcess();
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
}
|
|
|
|
a->WaitTarget = WaitTarget;
|
|
a->WaitOption = WaitOption;
|
|
a->ProcessId = ProcessId;
|
|
a->ResultCodes.ExitReason = 0;
|
|
a->ResultCodes.ExitResult = 0;
|
|
a->ResultProcessId = 0;
|
|
|
|
Od2CallSubsystem( &m, NULL, Os2WaitChild, sizeof( *a ) );
|
|
|
|
try {
|
|
*ResultCodes = a->ResultCodes;
|
|
*ResultProcessId = a->ResultProcessId;
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
KdPrint(("leaving DosWaitChild with rc %ld\n",m.ReturnedErrorValue));
|
|
}
|
|
#endif
|
|
return( m.ReturnedErrorValue );
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosWaitThread(
|
|
IN OUT PTID ThreadId,
|
|
IN ULONG WaitOption
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSWAITTHREAD_MSG a = &m.u.DosWaitThread;
|
|
|
|
if (WaitOption > DCWW_NOWAIT) {
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
try {
|
|
Od2ProbeForWrite( (PVOID)ThreadId, sizeof( PTID ), 1 );
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
a->ThreadId = *ThreadId;
|
|
a->WaitOption = WaitOption;
|
|
|
|
Od2CallSubsystem( &m, NULL, Os2WaitThread, sizeof( *a ) );
|
|
|
|
*ThreadId = a->ThreadId;
|
|
return( m.ReturnedErrorValue );
|
|
}
|
|
|
|
APIRET
|
|
Od2SuspendAllThreads( VOID )
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POD2_THREAD Thread;
|
|
ULONG CurrentTid = Od2CurrentThreadId();
|
|
ULONG SuspendCount;
|
|
APIRET rc = NO_ERROR;
|
|
|
|
AcquireTaskLock();
|
|
//
|
|
// Walk through the list of threads and suspend all but not the current
|
|
//
|
|
ListHead = &Od2Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OD2_THREAD, Link );
|
|
if (Thread->Os2Tib.ThreadId != CurrentTid) {
|
|
|
|
Status = NtSuspendThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]Od2SuspendAllThreads Suspend Thread #%d, SuspendCount %d\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount);
|
|
}
|
|
#endif // DBG
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_THREAD_IS_TERMINATING) {
|
|
#if DBG
|
|
DbgPrint("[%d,%d]Od2SuspendAllThreads: Fail to suspend shread #%d\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId
|
|
);
|
|
#endif // DBG
|
|
rc = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
ReleaseTaskLock();
|
|
|
|
return(rc);
|
|
}
|
|
|
|
APIRET
|
|
Od2ResumeAllThreads( VOID )
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POD2_THREAD Thread;
|
|
ULONG CurrentTid = Od2CurrentThreadId();
|
|
ULONG SuspendCount;
|
|
APIRET rc = NO_ERROR;
|
|
|
|
AcquireTaskLock();
|
|
//
|
|
// Walk through the list of threads and resume all but not the current
|
|
//
|
|
ListHead = &Od2Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OD2_THREAD, Link );
|
|
if (Thread->Os2Tib.ThreadId != CurrentTid) {
|
|
|
|
Status = NtResumeThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]Od2ResumeAllThreads Suspend Thread #%d, SuspendCount %d\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount);
|
|
}
|
|
#endif // DBG
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_THREAD_IS_TERMINATING) {
|
|
#if DBG
|
|
DbgPrint("[%d,%d]Od2ResumeAllThreads: Fail to resume shread #%d\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId
|
|
);
|
|
#endif // DBG
|
|
rc = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
ReleaseTaskLock();
|
|
|
|
return(rc);
|
|
}
|
|
|
|
POD2_THREAD
|
|
Od2FindThreadById(
|
|
ULONG ThreadId
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POD2_THREAD Thread;
|
|
|
|
ListHead = &Od2Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OD2_THREAD, Link );
|
|
if (Thread->Os2Tib.ThreadId == ThreadId)
|
|
break;
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
if (Thread->Os2Tib.ThreadId != ThreadId) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d] Od2FindThreadById(#%d) - invalid thread id\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
ThreadId);
|
|
}
|
|
#endif // DBG
|
|
return NULL;
|
|
}
|
|
return Thread;
|
|
}
|
|
|
|
APIRET
|
|
DosSuspendThread(
|
|
IN TID ThreadId
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
POD2_THREAD Thread;
|
|
ULONG SuspendCount;
|
|
BOOLEAN Myself;
|
|
|
|
AcquireTaskLock();
|
|
|
|
// If the thread that must be suspended is the owner of
|
|
// critical section, wait until it will exit from critical section
|
|
// If the current thread is suspended, don't suspend another thread.
|
|
// Wait until the current thread will be resumed.
|
|
|
|
while (
|
|
(Od2CritSectOwner != 0 && Od2CritSectOwner == (ULONG)ThreadId) ||
|
|
(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.
|
|
MustCompleteForceFlag & MCF_SUSPENDED)
|
|
)
|
|
{
|
|
ReleaseTaskLock();
|
|
Od2WaitOnCritSectOrSuspend();
|
|
AcquireTaskLock();
|
|
}
|
|
|
|
Thread = Od2FindThreadById((ULONG)ThreadId);
|
|
if (Thread == NULL) {
|
|
ReleaseTaskLock();
|
|
return ERROR_INVALID_THREADID;
|
|
}
|
|
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_SUSPENDED) {
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_FROZEN) {
|
|
Thread->Os2Tib.MustCompleteForceFlag |= MCF_SUSPENDED_AND_FROZEN;
|
|
}
|
|
// Already suspended
|
|
ReleaseTaskLock();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
Status = NtResetEvent(Thread->Event, NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d,%d]DosSuspenThread(#%d): fail to reset event, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
ThreadId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
|
|
// Thread is considered suspended
|
|
Thread->Os2Tib.MustCompleteForceFlag |= MCF_SUSPENDED;
|
|
|
|
//
|
|
// Don't suspend the current thread
|
|
//
|
|
if (!(Myself = (Thread->Os2Tib.ThreadId == Od2CurrentThreadId()))) {
|
|
|
|
Status = NtSuspendThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosSuspendThread(#%d), SuspendCount %x, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount,
|
|
Status);
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
#if DBG
|
|
else {
|
|
// The thread is going to suspend itself
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosSuspendThread - myself\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId());
|
|
}
|
|
}
|
|
#endif //DBG
|
|
|
|
//
|
|
// If the thread is in 32 bit don't suspend it immediately. It will wait
|
|
// for resuming upon return to 16 bit. If thread executes signal handler
|
|
// it will wait upon signal handler return to 16 bit.
|
|
//
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_IN32BIT ||
|
|
(Od2Process->Pib.SigHandInProgress && ((ULONG)ThreadId == 1L))) {
|
|
//
|
|
// The thread is in 32 bit or during signal processing
|
|
//
|
|
// If API was called for self-suspend, the thread must not be resumed
|
|
// (it wasn't suspended, actually)
|
|
//
|
|
if (!Myself) {
|
|
Status = NtResumeThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosSuspendThread(#%d) Resuming Thread in 32bit, SuspendCount %x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount);
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d,%d]DosSuspendThread(#%d) failed at NtResumeThread, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
}
|
|
ReleaseTaskLock();
|
|
|
|
if (Myself && !(Thread->Os2Tib.MustCompleteForceFlag & MCF_IN32BIT)) {
|
|
//
|
|
// This is possible
|
|
// - in the case that the thread is suspended during thread startup.
|
|
// - if the curent thread is frozen by debuggre process after
|
|
// breakpoint.
|
|
//
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosSuspendThread: Frozen after breakpoint or in startup\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId());
|
|
}
|
|
#endif
|
|
NtSuspendThread (NtCurrentThread(), NULL);
|
|
}
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
APIRET
|
|
DosResumeThread(
|
|
IN TID ThreadId
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
POD2_THREAD Thread;
|
|
ULONG SuspendCount;
|
|
|
|
AcquireTaskLock();
|
|
|
|
Thread = Od2FindThreadById((ULONG)ThreadId);
|
|
if (Thread == NULL) {
|
|
ReleaseTaskLock();
|
|
return ERROR_INVALID_THREADID;
|
|
}
|
|
|
|
if (!(Thread->Os2Tib.MustCompleteForceFlag & MCF_SUSPENDED)) {
|
|
// Already resumed
|
|
ReleaseTaskLock();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_FROZEN) {
|
|
Thread->Os2Tib.MustCompleteForceFlag &= ~MCF_SUSPENDED_AND_FROZEN;
|
|
ReleaseTaskLock();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Thread is considered resumed
|
|
Thread->Os2Tib.MustCompleteForceFlag &= ~MCF_SUSPENDED;
|
|
//
|
|
// If the thread is resumed being in 32 bit, nothing must be done
|
|
//
|
|
if (!(Thread->Os2Tib.MustCompleteForceFlag & MCF_IN32BIT) &&
|
|
!(Od2Process->Pib.SigHandInProgress && ((ULONG)ThreadId == 1L))) {
|
|
//
|
|
// The thread was suspended in 16 bit. Just resume it.
|
|
//
|
|
Status = NtResumeThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosResumeThread(#%d), SuspendCount %x, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount,
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
Status = NtSetEvent(Thread->Event, NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d,%d]DosResumeThread(#%d): fail to set event, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
ThreadId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
|
|
ReleaseTaskLock();
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosEnterCritSec( VOID )
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POD2_THREAD Thread;
|
|
ULONG CurrentTid = Od2CurrentThreadId();
|
|
ULONG SuspendCount;
|
|
|
|
|
|
AcquireTaskLock();
|
|
// If other thread is the owner of critical section wait until it will release
|
|
// the critical section
|
|
// If the current thread was suspened, wait until it will be released
|
|
while (
|
|
(Od2CritSectOwner != 0 && Od2CritSectOwner != CurrentTid) ||
|
|
(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.
|
|
MustCompleteForceFlag & MCF_SUSPENDED)
|
|
)
|
|
{
|
|
ReleaseTaskLock();
|
|
Od2WaitOnCritSectOrSuspend();
|
|
AcquireTaskLock();
|
|
}
|
|
// The current thread isn't suspended due to other thread critical section and
|
|
// it isn't suspended by DosSuspendThread.
|
|
Od2CritSectCounter++;
|
|
// Only if there is the 1st call to DosEnterCritSec proceed. If it is not,
|
|
// just increment the counter.
|
|
if (Od2CritSectCounter == 1) {
|
|
Od2CritSectOwner = CurrentTid;
|
|
//
|
|
// set flag for ExitCritSect
|
|
//
|
|
Od2NeedToResumeThreads = TRUE;
|
|
Status = NtResetEvent (
|
|
Od2CritSecEvent,
|
|
NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d,%d]DosEnterCritSec: failed at NtResetEvent, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Status);
|
|
}
|
|
#endif
|
|
//
|
|
// Walk through the list of threads and suspend all but not the current
|
|
//
|
|
ListHead = &Od2Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OD2_THREAD, Link );
|
|
if (Thread->Os2Tib.ThreadId != CurrentTid) {
|
|
|
|
Status = NtSuspendThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosEnterCritSect Suspend Thread #%x, SuspendCount %x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount);
|
|
}
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_THREAD_IS_TERMINATING) {
|
|
KdPrint(("DosEnterCritSect error: Status %lx Suspending Thread #%x, SuspendCount %x\n",
|
|
Status, Thread->Os2Tib.ThreadId, SuspendCount));
|
|
}
|
|
#endif // DBG
|
|
//
|
|
// If the thread is in 32 bit don't suspend it immediately. It will wait
|
|
// for resuming upon return to 16 bit. If thread executes signal handler
|
|
// it will wait upon signal handler return to 16 bit.
|
|
//
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_IN32BIT ||
|
|
(Od2Process->Pib.SigHandInProgress &&
|
|
(Thread->Os2Tib.ThreadId == 1L))) {
|
|
//
|
|
// The thread in 32 bit or during the signal processing
|
|
//
|
|
Status = NtResumeThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosEnterCritSect Resuming Thread in 32bit #%x in 32bit code, SuspendCount %x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount);
|
|
}
|
|
#endif
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
KdPrint(("DosEnterCritSect error: Status %lx Resuming Thread #%x, SuspendCount %x\n",
|
|
Status, Thread->Os2Tib.ThreadId, SuspendCount));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
ReleaseTaskLock();
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosExitCritSec( VOID )
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG SuspendCount;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POD2_THREAD Thread;
|
|
ULONG CurrentTid = Od2CurrentThreadId();
|
|
|
|
if (Od2CritSectCounter == 0) {
|
|
#if DBG
|
|
KdPrint(("DosExitCritSec - critsect underflow\n"));
|
|
#endif
|
|
return(ERROR_CRITSEC_UNDERFLOW);
|
|
}
|
|
|
|
AcquireTaskLock();
|
|
Od2CritSectCounter--;
|
|
if (Od2CritSectCounter == 0 && Od2NeedToResumeThreads) {
|
|
// The critical section is over
|
|
Od2CritSectOwner = 0;
|
|
//
|
|
// Walk through the list of threads and resume all threads that were
|
|
// in 16 bit while DosEnterCritSec was called.
|
|
// Threads that were in 32 bit, i.e. in an API, are
|
|
// released from the sync event, if they are waiting on it
|
|
//
|
|
ListHead = &Od2Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OD2_THREAD, Link );
|
|
if (Thread->Os2Tib.ThreadId != CurrentTid) {
|
|
|
|
if (!(Thread->Os2Tib.MustCompleteForceFlag & MCF_IN32BIT) &&
|
|
!(Od2Process->Pib.SigHandInProgress &&
|
|
((ULONG)Thread->Os2Tib.ThreadId == 1L))) {
|
|
//
|
|
// This thread was in 16 bit when frozen by DosEnterCritSec
|
|
//
|
|
Status = NtResumeThread (Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DosExitCritSect Resuming Thread #%x, SuspendCount %x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Thread->Os2Tib.ThreadId,
|
|
SuspendCount);
|
|
}
|
|
#endif
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
KdPrint(("DosExitCritSect error: Status %lx Resuming Thread #%x, SuspendCount %x\n",
|
|
Status, Thread->Os2Tib.ThreadId, SuspendCount));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
Status = NtSetEvent (
|
|
Od2CritSecEvent,
|
|
NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d,%d]DosExitCritSec: failed at NtSetEvent, Status=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Status);
|
|
}
|
|
#endif
|
|
Od2NeedToResumeThreads = FALSE;
|
|
}
|
|
|
|
ReleaseTaskLock();
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
VOID
|
|
Od2WaitOnCritSectOrSuspend(VOID)
|
|
{
|
|
if (Od2NeedToResumeThreads && Od2CritSectOwner != Od2CurrentThreadId()) {
|
|
//
|
|
// Another thread called DosEnterCritSect
|
|
//
|
|
Od2AlertableWaitForSingleObject(Od2CritSecEvent);
|
|
}
|
|
//
|
|
// Check if the thread must be suspended
|
|
//
|
|
if (((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.
|
|
MustCompleteForceFlag & MCF_SUSPENDED) {
|
|
//
|
|
// DosSuspendThread was called for the current thread
|
|
//
|
|
Od2AlertableWaitForSingleObject(
|
|
((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Event);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The following routine is being called on the verge of going 32bit
|
|
// to 16 bit. If EnterCritSect is in power, and other conditions are
|
|
// met, the thread will wait on a semaphore, for DosExitCritSec to release
|
|
// it.
|
|
//
|
|
|
|
VOID
|
|
Od2CheckForCritSectOrSuspend(VOID)
|
|
{
|
|
//
|
|
// During signal handling do nothing
|
|
//
|
|
if (!(Od2SigHandAlreadyInProgress && Od2CurrentThreadId() == 1)) {
|
|
// The thread is considered to be in 16bit now. If any other thread enter
|
|
// to critical section or calls DosSuspendThread for the current thread it
|
|
// will symply suspend it.
|
|
((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.
|
|
MustCompleteForceFlag &= ~MCF_IN32BIT;
|
|
//
|
|
// We are not thread1 in signal handling, so wait if critical section or suspend
|
|
// events are in non-signaled state.
|
|
//
|
|
Od2WaitOnCritSectOrSuspend();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2FreezeThread(
|
|
IN ULONG rEax,
|
|
IN PCONTEXT pContext
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
POD2_THREAD Thread;
|
|
LOCALINFOSEG Linfoseg;
|
|
|
|
//
|
|
// Save 40 bytes of area fs:0.
|
|
// In 16 bit this area is the local info segment, and in 32bit
|
|
// we restore the TEB.
|
|
//
|
|
SaveFSSeg(&Linfoseg);
|
|
RestoreTeb();
|
|
|
|
//
|
|
// Clear the flag of alert in kernel.
|
|
//
|
|
Status = NtTestAlert();
|
|
|
|
//
|
|
// We need to update the value of register Eax, for interrupted context,
|
|
// because it may be changed after the NtGetContextThread().
|
|
// This can happen if the thread was in kernel mode.
|
|
//
|
|
pContext->Eax = rEax;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]Od2FreezeThread\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId());
|
|
}
|
|
#endif
|
|
|
|
AcquireTaskLock();
|
|
Thread = Od2FindThreadById(Od2CurrentThreadId());
|
|
ASSERT(Thread != NULL);
|
|
|
|
Thread->Os2Tib.MustCompleteForceFlag |= MCF_FROZEN;
|
|
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_SUSPENDED) {
|
|
//
|
|
// The thread is already suspended. Just update its state.
|
|
//
|
|
Thread->Os2Tib.MustCompleteForceFlag |= MCF_SUSPENDED_AND_FROZEN;
|
|
ReleaseTaskLock();
|
|
}
|
|
else {
|
|
ReleaseTaskLock();
|
|
DosSuspendThread((TID)Od2CurrentThreadId());
|
|
}
|
|
|
|
RestoreFSSeg(&Linfoseg);
|
|
|
|
//
|
|
// Continue with the interrupted context.
|
|
//
|
|
NtContinue(pContext, FALSE);
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
VOID
|
|
Od2UnfreezeThread(
|
|
IN ULONG rEax,
|
|
IN PCONTEXT pContext
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
POD2_THREAD Thread;
|
|
LOCALINFOSEG Linfoseg;
|
|
|
|
//
|
|
// Save 40 bytes of area fs:0.
|
|
// In 16 bit this area is the local info segment, and in 32bit
|
|
// we restore the TEB.
|
|
//
|
|
SaveFSSeg(&Linfoseg);
|
|
RestoreTeb();
|
|
|
|
//
|
|
// Clear the flag of alert in kernel.
|
|
//
|
|
Status = NtTestAlert();
|
|
|
|
//
|
|
// We need to update the value of register Eax, for interrupted context,
|
|
// because it may be changed after the NtGetContextThread().
|
|
// This can happen if the thread was in kernel mode.
|
|
//
|
|
pContext->Eax = rEax;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]Od2UnfreezeThread\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId());
|
|
}
|
|
#endif
|
|
|
|
AcquireTaskLock();
|
|
Thread = Od2FindThreadById(Od2CurrentThreadId());
|
|
ASSERT(Thread != NULL);
|
|
|
|
Thread->Os2Tib.MustCompleteForceFlag &= ~MCF_FROZEN;
|
|
|
|
if (Thread->Os2Tib.MustCompleteForceFlag & MCF_SUSPENDED_AND_FROZEN) {
|
|
//
|
|
// The thread is suspended and frozen, so we should not resume it.
|
|
// Just update its state.
|
|
//
|
|
Thread->Os2Tib.MustCompleteForceFlag &= ~MCF_SUSPENDED_AND_FROZEN;
|
|
ReleaseTaskLock();
|
|
}
|
|
else {
|
|
ReleaseTaskLock();
|
|
DosResumeThread((TID)Od2CurrentThreadId());
|
|
}
|
|
|
|
RestoreFSSeg(&Linfoseg);
|
|
|
|
//
|
|
// Continue with the interrupted context.
|
|
//
|
|
NtContinue(pContext, FALSE);
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// IMPORTANT !!!
|
|
// Changing the code below (Od2GetSegmentRegisters and Od2PrepareEnterToSignalHandler)
|
|
// check the follows:
|
|
// - Od2GetSegmentRegisters (dlltask.c)
|
|
// - Entry flat and Exit flat (doscalls.asm)
|
|
// - Od2Continue and Od2SetSegmentRegisters (dll16.asm)
|
|
// - Os2SignalGetThreadContext (srvxcpt.c)
|
|
//
|
|
|
|
//
|
|
// Function NtGetContextThread is not accurate with values of segment registers ES and DS
|
|
// in the case that the code segment is 1b (flat). We need to find the proper values for
|
|
// these registers.
|
|
// The context of interrupted thread can be on of the follows:
|
|
// - flat (CS==1b, SS==23)
|
|
// - 16bit (CS!=1b, SS!=23)
|
|
// - Entry flat (CS==1b, SS!=23)
|
|
// - Exit flat (CS==1b, SS!=23)
|
|
// - Od2JumpTo16SignalDispatch (CS==1b, SS!=23)
|
|
// - Od2Continue (CS==1b, SS!=23)
|
|
// For each one of this cases we know where to find the segment registers.
|
|
// We may need these registers to execute the signal handler or to return to interrupted
|
|
// context. The values we will use are not the same in this cases. For example, in the
|
|
// case of Entry flat, we will use 16bit values of ES and DS for signal handler, but
|
|
// we will return with 0x23 value for both registers.
|
|
//
|
|
|
|
VOID
|
|
Od2GetSegmentRegisters(
|
|
PUSHORT pEsHandler,
|
|
PUSHORT pDsHandler,
|
|
PUSHORT pEsReturn,
|
|
PUSHORT pDsReturn,
|
|
PCONTEXT pContext
|
|
)
|
|
{
|
|
//
|
|
// By default assume that the registers should be 0x23 -- flat
|
|
//
|
|
*pEsReturn = *pDsReturn = 0x23;
|
|
|
|
if (pContext->SegSs == 0x23) {
|
|
//
|
|
// Flat mode
|
|
//
|
|
*pEsHandler = *(PUSHORT)(Od2Saved16Stack+16);
|
|
*pDsHandler = *(PUSHORT)(Od2Saved16Stack+18);
|
|
#if DBG
|
|
DbgPrint("Od2GetSegmentRegisters: This branch is implemented, but it mustn't be in use\n");
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
else if (pContext->SegCs == 0x1b) {
|
|
//
|
|
// Not a flat mode. May be 16bit or some kind of thunk
|
|
//
|
|
if (
|
|
//
|
|
// Exit flat thunk. The 16 bit segments were stored in the most significant parts
|
|
// of ECX and EDX. We need only the least significant word of registers upon
|
|
// returning to 16bit.
|
|
//
|
|
(pContext->Eip > (ULONG) ExitFlatAreaBegin &&
|
|
pContext->Eip < (ULONG) ExitFlatAreaEnd) ||
|
|
//
|
|
// Od2JumpTo16SignalDispatch thunk.
|
|
//
|
|
((pContext->Eip > (ULONG) Od2JumpTo16SignalDispatch &&
|
|
pContext->Eip < (ULONG) Od2JumpTo16SignalDispatchEnd) ||
|
|
pContext->Eip == pContext->Ebp) ||
|
|
|
|
(pContext->Eip > (ULONG) Od2ContinueStartBorder &&
|
|
pContext->Eip < (ULONG) Od2ContinueEndBorder) ||
|
|
|
|
(pContext->Eip >= (ULONG) Od2SetSegmentRegisters &&
|
|
pContext->Eip < (ULONG) Od2SetSegmentRegistersEnd)
|
|
)
|
|
{
|
|
//
|
|
// Take ES and DS from the most significant parts of ECX and EDX
|
|
//
|
|
*pEsHandler = *(PUSHORT) ((PBYTE)&(pContext->Ecx) + 2);
|
|
*pDsHandler = *(PUSHORT) ((PBYTE)&(pContext->Edx) + 2);
|
|
|
|
//
|
|
// Check if we are still using flat data access. In this case return from
|
|
// signal with default values of segment registers.
|
|
//
|
|
if (pContext->Eip > (ULONG) Od2JumpTo16SignalDispatch &&
|
|
pContext->Eip < (ULONG) Od2JumpTo16SignalDispatchBorder) {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Entry flat thunk. EBX point to 16bit stack frame. We will return back with
|
|
// 0x23 (flat) for both segment registers.
|
|
// It may be also Od2Continue thunk that return to Od2JumpTo16SignalDispatch
|
|
// before Od2JumpTo16SignalDispatchBorder.
|
|
//
|
|
*pEsHandler = *(PUSHORT) (pContext->Ebx + 16);
|
|
*pDsHandler = *(PUSHORT) (pContext->Ebx + 18);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// 16bit
|
|
//
|
|
*pEsHandler = (USHORT) pContext->SegEs;
|
|
*pDsHandler = (USHORT) pContext->SegDs;
|
|
}
|
|
|
|
//
|
|
// Return with registers as they must to be in 16bit
|
|
//
|
|
*pEsReturn = *pEsHandler;
|
|
*pDsReturn = *pDsHandler;
|
|
}
|
|
|
|
//
|
|
// This funtion must be called in the signal handler.
|
|
//
|
|
|
|
VOID
|
|
Od2PrepareEnterToSignalHandler(
|
|
PCONTEXT pContext,
|
|
POD2_CONTEXT_SAVE_AREA pSaveArea
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
SaveFSSeg(&(pSaveArea->TebBlock)); // Save 40 bytes in TEB.
|
|
RestoreTebForThread1(); // Use TEB that it must be for thread1
|
|
Status = NtTestAlert(); // Clear alert flag of the thread
|
|
#if DBG
|
|
IF_OD2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Od2PrepareEnterToSignalHandler: NtTestAlert() = %x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Status);
|
|
}
|
|
// There is thread1
|
|
ASSERT(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.ThreadId == 1L);
|
|
#endif // DBG
|
|
|
|
pSaveArea->SignalHandlingInProgress = Od2SigHandAlreadyInProgress;
|
|
//
|
|
// Thread1 executes signal handler, thus it will not be actually suspended
|
|
// neither by DosSuspendThread no by DosEnterCritSect. If needed the thread will
|
|
// be suspended after return to normal context.
|
|
//
|
|
Od2SigHandAlreadyInProgress = TRUE;
|
|
|
|
if (pContext->SegSs != 0x23) {
|
|
//
|
|
// If the thread was using 16bit stack while interrupted, make new stack frame in
|
|
// it's free area.
|
|
//
|
|
USHORT newSP =
|
|
*(PUSHORT) &(pContext->Esp)
|
|
- 20 // -20: the length of the new stack frame. We need it in the case that
|
|
// other signal will arive while the current signal handler is
|
|
// being executed in 32bit.
|
|
- 12; // -12: the area beyond the stack that is used by Od2Continue
|
|
USHORT SegEs, SegDs;
|
|
|
|
Od2GetSegmentRegisters(
|
|
&SegEs,
|
|
&SegDs,
|
|
&(pSaveArea->SegEs),
|
|
&(pSaveArea->SegDs),
|
|
pContext
|
|
);
|
|
|
|
Od2Saved16Stack =
|
|
// Flat address of the new current stack top
|
|
(ULONG) SELTOFLAT(pContext->SegSs) + newSP;
|
|
|
|
//
|
|
// Make new stack frame
|
|
//
|
|
*(PUSHORT)(Od2Saved16Stack) = newSP;
|
|
*(PUSHORT)(Od2Saved16Stack+2) = *(PUSHORT)&(pContext->SegSs);
|
|
*(PUSHORT)(Od2Saved16Stack+6) = *(PUSHORT)&(pContext->Edx);
|
|
*(PUSHORT)(Od2Saved16Stack+8) = *(PUSHORT)&(pContext->Ebx);
|
|
*(PUSHORT)(Od2Saved16Stack+10) = *(PUSHORT)&(pContext->Ecx);
|
|
*(PUSHORT)(Od2Saved16Stack+12) = *(PUSHORT)&(pContext->Esi);
|
|
*(PUSHORT)(Od2Saved16Stack+14) = *(PUSHORT)&(pContext->Edi);
|
|
*(PUSHORT)(Od2Saved16Stack+16) = SegEs;
|
|
*(PUSHORT)(Od2Saved16Stack+18) = SegDs;
|
|
}
|
|
|
|
//
|
|
// Od2Saved16Stack is set in each entry flat. It will be changed during 16bit signal
|
|
// handler processing. If the interrupted context was flat it will be restored to
|
|
// the original value.
|
|
//
|
|
pSaveArea->Saved16Stack = Od2Saved16Stack;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Od2PrepareEnterToSignalHandler: from %x:%x stack %x:%x ebp=%x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
pContext->SegCs,
|
|
pContext->Eip,
|
|
pContext->SegSs,
|
|
pContext->Esp,
|
|
pContext->Ebp);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
VOID
|
|
Od2MakeSignalHandlerContext(
|
|
POS2_REGISTER16_SIGNAL pContext16
|
|
)
|
|
{
|
|
//
|
|
// Copy only registers from the stack frame. It is the context for executing 16bit
|
|
// signal handler. IP and CS aren't copied (we know the address of the 16bit handler)
|
|
//
|
|
RtlMoveMemory((PBYTE)pContext16, (PBYTE) Od2Saved16Stack, 20);
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Make signal handler context:\nbx=%04x cx=%04x dx=%04x si=%04x di=%04x sp=%04x Ss=%04x Ds=%04x Es=%04x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
pContext16->regBX,
|
|
pContext16->regCX,
|
|
pContext16->regDX,
|
|
pContext16->regSI,
|
|
pContext16->regDI,
|
|
pContext16->regSP,
|
|
pContext16->regSS,
|
|
pContext16->regDS,
|
|
pContext16->regES
|
|
);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
VOID
|
|
Od2ExitFromSignalHandler(
|
|
PCONTEXT pContext,
|
|
POD2_CONTEXT_SAVE_AREA pSaveArea
|
|
)
|
|
{
|
|
|
|
if (!(pSaveArea->SignalHandlingInProgress))
|
|
{
|
|
//
|
|
// The interruped context wasn't executing signal handler. We consider signal handler
|
|
// over.
|
|
//
|
|
Od2SigHandAlreadyInProgress = FALSE;
|
|
Od2Process->Pib.SigHandInProgress = FALSE;
|
|
//
|
|
// At this point the thread can be suspended by DosSuspendThread or DosEnterCritSec
|
|
// APIs (if the interrupted thread was permitted to be directly suspended).
|
|
// In the case that in the interrupted context thread was permitted to be suspended
|
|
// check if it must wait for release (or exit critical section). If it wasn't
|
|
// permitted it will check it upon exit from 32bit.
|
|
//
|
|
if(!(((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.
|
|
MustCompleteForceFlag & MCF_IN32BIT)) {
|
|
//
|
|
// The thread will wait here only in the case that critical section event or
|
|
// suspend event of the current thread are in the non-signaled state (that
|
|
// means that the thread must be suspended or other thread own critical
|
|
// section).
|
|
//
|
|
Od2WaitOnCritSectOrSuspend();
|
|
}
|
|
}
|
|
|
|
//
|
|
// The values for segment registers were fetched upon entry to signal handler
|
|
//
|
|
pContext->SegEs = pSaveArea->SegEs;
|
|
pContext->SegDs = pSaveArea->SegDs;
|
|
Od2Saved16Stack = pSaveArea->Saved16Stack;
|
|
|
|
//
|
|
// Restore 40 byte of TEB as they were in the interrupted context: TEB, Local infoseg or
|
|
// mix.
|
|
//
|
|
RestoreFSSeg(&(pSaveArea->TebBlock));
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Exiting from signal handler\nEax=%08x Ebx=%08x Ecx=%08x Edx=%08x Esi=%08x Edi=%08x\nEip=%08x Esp=%08x Ebp=%08x Cs=%04x Ss=%04x Ds=%04x Es=%04x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
pContext->Eax,
|
|
pContext->Ebx,
|
|
pContext->Ecx,
|
|
pContext->Edx,
|
|
pContext->Esi,
|
|
pContext->Edi,
|
|
pContext->Eip,
|
|
pContext->Esp,
|
|
pContext->Ebp,
|
|
pContext->SegCs,
|
|
pContext->SegSs,
|
|
pContext->SegDs,
|
|
pContext->SegEs
|
|
);
|
|
}
|
|
#endif // DBG
|
|
|
|
//
|
|
// Continue to execute interrupted context. The argument is the pointer to part of
|
|
// the context structure starting from ES:
|
|
// ES, DS, EDI, ESI, EBX, EDX, ECX, EAX, EBP, EIP, CS, EFLAGS, ESP, SS.
|
|
// This function is used as substitute to NtContinue. NtContinue hasn't mutual
|
|
// exclusion for using context with NtGetContextThread and NtSetContextThread till
|
|
// build 717.
|
|
// This way seems to be safe enough.
|
|
//
|
|
Od2Continue(&(pContext->SegEs));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2TranslateConfigSysInCommandLine(
|
|
IN OUT PSZ *ArgumentsBuffer
|
|
)
|
|
//
|
|
// This routine scans the command line for a WIN32 program looking for a reference to the
|
|
// config.sys file. if it finds one, it reallocates the command line, replacing the name
|
|
// by the name of os2conf.nt. os2conf.nt is created using Od2FileIsConfigSys();
|
|
// It first attempts to create the file for writing, and if that fails, for reading.
|
|
//
|
|
|
|
{
|
|
PSZ Args = *ArgumentsBuffer;
|
|
BOOLEAN quoting1, quoting2;
|
|
CHAR ch, ch2;
|
|
PSZ CSPtr, LastSeparator, p, q;
|
|
STRING CanonicalString;
|
|
APIRET RetCode;
|
|
ULONG ParseFlags, FileType;
|
|
NTSTATUS Status;
|
|
|
|
if (Args == NULL) {
|
|
return;
|
|
}
|
|
|
|
CSPtr = ConfigSysString;
|
|
LastSeparator = Args - 1;
|
|
quoting1 = quoting2 = FALSE;
|
|
|
|
for (; (ch = *Args) != '\0'; Args++) {
|
|
|
|
if (!quoting1) {
|
|
|
|
if (ch == '^') {
|
|
if (!quoting2 || Args[1] == '"') {
|
|
quoting1 = TRUE;
|
|
continue;
|
|
}
|
|
|
|
} else if (ch == '"') {
|
|
quoting2 = !quoting2;
|
|
CSPtr = ConfigSysString;
|
|
LastSeparator = Args;
|
|
continue;
|
|
|
|
} else if (quoting2) {
|
|
continue;
|
|
|
|
} else if (SEPARATOR(ch)) {
|
|
CSPtr = ConfigSysString;
|
|
LastSeparator = Args;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
quoting1 = FALSE;
|
|
|
|
ch = toupper((UCHAR)ch);
|
|
|
|
if (*CSPtr != ch) {
|
|
if (CSPtr == ConfigSysString) {
|
|
continue;
|
|
}
|
|
|
|
CSPtr = ConfigSysString;
|
|
}
|
|
|
|
if (*CSPtr == ch) {
|
|
|
|
CSPtr++;
|
|
|
|
if (*CSPtr == '\0') {
|
|
|
|
CSPtr = ConfigSysString;
|
|
|
|
ch2 = Args[1];
|
|
|
|
if (!SEPARATOR(ch2)) {
|
|
continue;
|
|
}
|
|
|
|
Args[1] = '\0';
|
|
|
|
RetCode = Od2Canonicalize(LastSeparator + 1,
|
|
CANONICALIZE_FILE_DEV_OR_PIPE,
|
|
&CanonicalString,
|
|
NULL,
|
|
&ParseFlags, // should return 0
|
|
&FileType // should return FILE_TYPE_FILE
|
|
);
|
|
|
|
Args[1] = ch2;
|
|
|
|
if (RetCode != NO_ERROR || ParseFlags != 0 || FileType != FILE_TYPE_FILE) {
|
|
#if DBG
|
|
IF_OD2_DEBUG(MISC) {
|
|
KdPrint(("Od2TranslateConfigSysInCommandLine: Failed to canonicalize config.sys name, rc = %x, ParseFlags = %lx, FileType = %lx\n",
|
|
RetCode, ParseFlags, FileType));
|
|
}
|
|
#endif
|
|
if (CanonicalString.Buffer != NULL) {
|
|
RtlFreeHeap(Od2Heap, 0, CanonicalString.Buffer);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!Od2FileIsConfigSys(&CanonicalString, OPEN_ACCESS_READWRITE, &Status)) {
|
|
RtlFreeHeap(Od2Heap, 0, CanonicalString.Buffer);
|
|
continue;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
IF_OD2_DEBUG(MISC) {
|
|
KdPrint(("Od2TranslateConfigSysInCommandLine: Failed to create config.sys for READWRITE, Status = %lx\n",
|
|
Status));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Try opening for just reading
|
|
//
|
|
|
|
Od2FileIsConfigSys(&CanonicalString, OPEN_ACCESS_READONLY, &Status);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
IF_OD2_DEBUG(MISC) {
|
|
KdPrint(("Od2TranslateConfigSysInCommandLine: Failed to create config.sys for READONLY, Status = %lx\n",
|
|
Status));
|
|
}
|
|
#endif
|
|
|
|
// note -- if Od2FileIsConfigSys failed to generate os2conf.nt for some reason, we
|
|
// leave the file name as config.sys so the caller has something.
|
|
// Is this correct?
|
|
|
|
RtlFreeHeap(Od2Heap, 0, CanonicalString.Buffer);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// now change the name from config.sys to os2conf.nt
|
|
//
|
|
|
|
p = RtlAllocateHeap(Od2Heap, 0, strlen(*ArgumentsBuffer) + CanonicalString.Length);
|
|
|
|
if (p == NULL) {
|
|
#if DBG
|
|
IF_OD2_DEBUG(MISC) {
|
|
KdPrint(("Od2TranslateConfigSysInCommandLine: Failed to alloc space for new cmdline\n"));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We can't expand the name, drop it
|
|
//
|
|
|
|
RtlFreeHeap(Od2Heap, 0, CanonicalString.Buffer);
|
|
continue;
|
|
}
|
|
|
|
q = p; // save the start address
|
|
|
|
|
|
RtlMoveMemory(p, *ArgumentsBuffer, LastSeparator - (*ArgumentsBuffer) + 1);
|
|
p += LastSeparator - (*ArgumentsBuffer) + 1;
|
|
|
|
LastSeparator = p - 1;
|
|
|
|
RtlMoveMemory(p,
|
|
CanonicalString.Buffer + FILE_PREFIX_LENGTH,
|
|
CanonicalString.Length - FILE_PREFIX_LENGTH
|
|
);
|
|
p += CanonicalString.Length - FILE_PREFIX_LENGTH;
|
|
|
|
RtlMoveMemory(p,
|
|
Args + 1,
|
|
strlen(Args + 1) + 1
|
|
);
|
|
|
|
Args = p - 1; // readjust args
|
|
|
|
RtlFreeHeap(Od2Heap, 0, *ArgumentsBuffer);
|
|
*ArgumentsBuffer = q; // and ArgumentsBuffer
|
|
|
|
RtlFreeHeap(Od2Heap, 0, CanonicalString.Buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HANDLE
|
|
Od2DupHandleForRedirection(
|
|
HANDLE Handle
|
|
)
|
|
{
|
|
HANDLE h;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtDuplicateObject(
|
|
NtCurrentProcess(),
|
|
Handle,
|
|
NtCurrentProcess(),
|
|
&h,
|
|
(ACCESS_MASK) 0,
|
|
OBJ_INHERIT,
|
|
DUPLICATE_SAME_ACCESS);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
KdPrint(("Od2DupHandleForRedirection: failed to dup handle, Status = %lx\n", Status));
|
|
#endif
|
|
return(NULL);
|
|
}
|
|
|
|
return(h);
|
|
}
|
|
|
|
IO_VECTOR_TYPE
|
|
Od2GetVectorTypeForRedirection(
|
|
IN PFILE_HANDLE pHand
|
|
)
|
|
{
|
|
if (pHand->IoVectorType == ConVectorType) {
|
|
if ((pHand->Flags & ACCESS_FLAGS) == OPEN_ACCESS_READONLY) {
|
|
return KbdVectorType;
|
|
}
|
|
if ((pHand->Flags & ACCESS_FLAGS) == OPEN_ACCESS_WRITEONLY) {
|
|
return ScreenVectorType;
|
|
}
|
|
#if DBG
|
|
DbgPrint("[%d] Wrong access flags (%x) for ConVectorType handle\n",
|
|
Od2Process->Pib.ProcessId,
|
|
pHand->Flags & ACCESS_FLAGS
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
}
|
|
return pHand->IoVectorType;
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2PrepareStdHandleRedirection(
|
|
IN ULONG EnableFlags,
|
|
OUT POS2_STDHANDLES StdStruc
|
|
)
|
|
|
|
//
|
|
// This routine must be called with AcquireFileLockShared() in effect
|
|
//
|
|
|
|
{
|
|
PFILE_HANDLE pHand;
|
|
HANDLE hTmp;
|
|
IO_VECTOR_TYPE IoVectorType;
|
|
|
|
StdStruc->Flags = 0;
|
|
|
|
if (EnableFlags & STDFLAG_IN) {
|
|
|
|
pHand = &HandleTable[0];
|
|
IoVectorType = Od2GetVectorTypeForRedirection(pHand);
|
|
|
|
switch (IoVectorType) {
|
|
|
|
case RemoteVectorType:
|
|
if (pHand->NtHandle != SesGrp->StdIn) {
|
|
StdStruc->StdIn = pHand->NtHandle;
|
|
StdStruc->Flags |= STDFLAG_IN;
|
|
}
|
|
break;
|
|
|
|
case FileVectorType:
|
|
case PipeVectorType:
|
|
case DeviceVectorType:
|
|
case ComVectorType:
|
|
if ((hTmp = Od2DupHandleForRedirection(pHand->NtHandle)) != NULL) {
|
|
StdStruc->StdIn = hTmp;
|
|
StdStruc->Flags |= STDFLAG_IN | STDFLAG_CLOSEIN;
|
|
}
|
|
break;
|
|
|
|
case KbdVectorType:
|
|
if (SesGrp->hConsoleInput != SesGrp->StdIn) {
|
|
if ((hTmp = Od2DupHandleForRedirection(SesGrp->hConsoleInput)) != NULL) {
|
|
StdStruc->StdIn = hTmp;
|
|
StdStruc->Flags |= STDFLAG_IN | STDFLAG_CLOSEIN;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ScreenVectorType:
|
|
if (SesGrp->hConsoleOutput != SesGrp->StdOut) {
|
|
if ((hTmp = Od2DupHandleForRedirection(SesGrp->hConsoleOutput)) != NULL) {
|
|
StdStruc->StdIn = hTmp;
|
|
StdStruc->Flags |= STDFLAG_IN | STDFLAG_CLOSEIN;
|
|
}
|
|
} else {
|
|
StdStruc->StdIn = SesGrp->hConsoleOutput;
|
|
StdStruc->Flags |= STDFLAG_IN;
|
|
}
|
|
break;
|
|
|
|
case NulVectorType:
|
|
case LptVectorType:
|
|
case MouseVectorType:
|
|
case ClockVectorType:
|
|
case PointerVectorType:
|
|
case MonitorVectorType:
|
|
if ((hTmp = Ow2GetNulDeviceHandle()) != NULL) {
|
|
StdStruc->StdIn = hTmp;
|
|
StdStruc->Flags |= STDFLAG_IN;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (EnableFlags & STDFLAG_OUT) {
|
|
|
|
pHand = &HandleTable[1];
|
|
IoVectorType = Od2GetVectorTypeForRedirection(pHand);
|
|
|
|
switch (IoVectorType) {
|
|
|
|
case RemoteVectorType:
|
|
if (pHand->NtHandle != SesGrp->StdOut) {
|
|
StdStruc->StdOut = pHand->NtHandle;
|
|
StdStruc->Flags |= STDFLAG_OUT;
|
|
}
|
|
break;
|
|
|
|
case FileVectorType:
|
|
case PipeVectorType:
|
|
case DeviceVectorType:
|
|
case ComVectorType:
|
|
if ((hTmp = Od2DupHandleForRedirection(pHand->NtHandle)) != NULL) {
|
|
StdStruc->StdOut = hTmp;
|
|
StdStruc->Flags |= STDFLAG_OUT | STDFLAG_CLOSEOUT;
|
|
}
|
|
break;
|
|
|
|
case ScreenVectorType:
|
|
if (SesGrp->hConsoleOutput != SesGrp->StdOut) {
|
|
if ((hTmp = Od2DupHandleForRedirection(SesGrp->hConsoleOutput)) != NULL) {
|
|
StdStruc->StdOut = hTmp;
|
|
StdStruc->Flags |= STDFLAG_OUT | STDFLAG_CLOSEOUT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KbdVectorType:
|
|
if (SesGrp->hConsoleInput != SesGrp->StdIn) {
|
|
if ((hTmp = Od2DupHandleForRedirection(SesGrp->hConsoleInput)) != NULL) {
|
|
StdStruc->StdOut = hTmp;
|
|
StdStruc->Flags |= STDFLAG_OUT | STDFLAG_CLOSEOUT;
|
|
}
|
|
} else {
|
|
StdStruc->StdOut = SesGrp->hConsoleInput;
|
|
StdStruc->Flags |= STDFLAG_OUT;
|
|
}
|
|
break;
|
|
|
|
case NulVectorType:
|
|
case LptVectorType:
|
|
case MouseVectorType:
|
|
case ClockVectorType:
|
|
case PointerVectorType:
|
|
case MonitorVectorType:
|
|
if ((hTmp = Ow2GetNulDeviceHandle()) != NULL) {
|
|
StdStruc->StdOut = hTmp;
|
|
StdStruc->Flags |= STDFLAG_OUT;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (EnableFlags & STDFLAG_ERR) {
|
|
|
|
pHand = &HandleTable[2];
|
|
IoVectorType = Od2GetVectorTypeForRedirection(pHand);
|
|
|
|
switch (IoVectorType) {
|
|
|
|
case RemoteVectorType:
|
|
if (pHand->NtHandle != SesGrp->StdErr) {
|
|
StdStruc->StdErr = pHand->NtHandle;
|
|
StdStruc->Flags |= STDFLAG_ERR;
|
|
}
|
|
break;
|
|
|
|
case FileVectorType:
|
|
case PipeVectorType:
|
|
case DeviceVectorType:
|
|
case ComVectorType:
|
|
if ((hTmp = Od2DupHandleForRedirection(pHand->NtHandle)) != NULL) {
|
|
StdStruc->StdErr = hTmp;
|
|
StdStruc->Flags |= STDFLAG_ERR | STDFLAG_CLOSEERR;
|
|
}
|
|
break;
|
|
|
|
case ScreenVectorType:
|
|
if (SesGrp->hConsoleOutput != SesGrp->StdOut) {
|
|
if ((hTmp = Od2DupHandleForRedirection(SesGrp->hConsoleOutput)) != NULL) {
|
|
StdStruc->StdErr = hTmp;
|
|
StdStruc->Flags |= STDFLAG_ERR | STDFLAG_CLOSEERR;
|
|
}
|
|
} else {
|
|
StdStruc->StdErr = SesGrp->hConsoleOutput;
|
|
StdStruc->Flags |= STDFLAG_ERR;
|
|
}
|
|
break;
|
|
|
|
case KbdVectorType:
|
|
if (SesGrp->hConsoleInput != SesGrp->StdIn) {
|
|
if ((hTmp = Od2DupHandleForRedirection(SesGrp->hConsoleInput)) != NULL) {
|
|
StdStruc->StdErr = hTmp;
|
|
StdStruc->Flags |= STDFLAG_ERR | STDFLAG_CLOSEERR;
|
|
}
|
|
} else {
|
|
StdStruc->StdErr = SesGrp->hConsoleInput;
|
|
StdStruc->Flags |= STDFLAG_ERR;
|
|
}
|
|
break;
|
|
|
|
case NulVectorType:
|
|
case LptVectorType:
|
|
case MouseVectorType:
|
|
case ClockVectorType:
|
|
case PointerVectorType:
|
|
case MonitorVectorType:
|
|
if ((hTmp = Ow2GetNulDeviceHandle()) != NULL) {
|
|
StdStruc->StdErr = hTmp;
|
|
StdStruc->Flags |= STDFLAG_ERR;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2CleanupStdHandleRedirection(
|
|
IN POS2_STDHANDLES StdStruc
|
|
)
|
|
{
|
|
if (StdStruc->Flags & STDFLAG_CLOSEIN) {
|
|
NtClose(StdStruc->StdIn);
|
|
}
|
|
|
|
if (StdStruc->Flags & STDFLAG_CLOSEOUT) {
|
|
NtClose(StdStruc->StdOut);
|
|
}
|
|
|
|
if (StdStruc->Flags & STDFLAG_CLOSEERR) {
|
|
NtClose(StdStruc->StdErr);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
Od2IsFileConsoleType(
|
|
PSTRING NtImagePathName
|
|
#if PMNT
|
|
,
|
|
PULONG IsPMApp
|
|
#endif // PMNT
|
|
)
|
|
|
|
{
|
|
HANDLE FileHandle;
|
|
UNICODE_STRING Unicode;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PIO_STATUS_BLOCK pIoStatusBlock = &IoStatusBlock;
|
|
LARGE_INTEGER ByteOffset;
|
|
PVOID MemoryAddress;
|
|
ULONG RegionSize;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PIMAGE_DOS_HEADER pHeader;
|
|
PIMAGE_NT_HEADERS pPeHeader;
|
|
PCONFIGPHARLAP PharLapConfigured;
|
|
CONFIGPHARLAP PharLapBlockBuffer;
|
|
UCHAR String16stub[6];
|
|
UCHAR StringRational[36];
|
|
PUCHAR pb;
|
|
NTSTATUS Status;
|
|
|
|
#if PMNT
|
|
*IsPMApp = 0;
|
|
#endif // PMNT
|
|
Status = Or2MBStringToUnicodeString(&Unicode,
|
|
(PANSI_STRING)NtImagePathName,
|
|
(BOOLEAN)TRUE);
|
|
if (!NT_SUCCESS( Status )) {
|
|
goto thirdreturn;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&Unicode,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile(&FileHandle,
|
|
GENERIC_READ | FILE_READ_DATA | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ,
|
|
FILE_NON_DIRECTORY_FILE |
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto thirdreturn;
|
|
}
|
|
|
|
MemoryAddress = 0;
|
|
RegionSize = 4*1024;
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&MemoryAddress,
|
|
0,
|
|
&RegionSize,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto thirdreturn;
|
|
}
|
|
|
|
|
|
ByteOffset.LowPart = 0;
|
|
ByteOffset.HighPart = 0;
|
|
Status = NtReadFile(FileHandle,
|
|
0,
|
|
0,
|
|
0,
|
|
&IoStatusBlock,
|
|
MemoryAddress,
|
|
4*1024,
|
|
&ByteOffset,
|
|
0);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto secondreturn;;
|
|
}
|
|
|
|
pHeader = (PIMAGE_DOS_HEADER) MemoryAddress;
|
|
|
|
//
|
|
// The following EXE recognition code is adapted from ntos\mm\creasect.c
|
|
// Last updated on May 5th, 1993
|
|
//
|
|
|
|
if (pHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
Status = STATUS_INVALID_IMAGE_NOT_MZ;
|
|
goto firstreturn;
|
|
}
|
|
|
|
if (pHeader->e_lfarlc != (USHORT)0x40) {
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto firstreturn;
|
|
}
|
|
|
|
//
|
|
// Save the 6 bytes at Dosheader + 0x200 for a check later
|
|
//
|
|
RtlMoveMemory(&String16stub, (PUCHAR)pHeader + 0x200, 6);
|
|
|
|
//
|
|
// Save the 32 bytes at Dosheader + e_cparhdr << 4
|
|
//
|
|
if ((pb = (PUCHAR)((ULONG)pHeader + ((ULONG)pHeader->e_cparhdr << 4))) <
|
|
(PUCHAR)((ULONG)pHeader + 4*1024 - 0x30 - sizeof(USHORT)) ) {
|
|
pb += *(PUSHORT)(pb + 0x30);
|
|
if ((ULONG)pb < (ULONG)pHeader + 4*1024 - 36) {
|
|
RtlMoveMemory(&StringRational, pb, 36);
|
|
}
|
|
}
|
|
|
|
PharLapConfigured = (PCONFIGPHARLAP) ((ULONG)pHeader +
|
|
((ULONG)pHeader->e_cparhdr << 4));
|
|
|
|
if ((ULONG)pHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) > 4*1024) {
|
|
|
|
if ((ULONG)PharLapConfigured <
|
|
(ULONG)pHeader + 0x1000 - sizeof(CONFIGPHARLAP)) {
|
|
RtlMoveMemory(&PharLapBlockBuffer, PharLapConfigured, sizeof(CONFIGPHARLAP));
|
|
PharLapConfigured = &PharLapBlockBuffer;
|
|
} else {
|
|
PharLapConfigured = NULL;
|
|
}
|
|
|
|
ByteOffset.LowPart = (ULONG)pHeader->e_lfanew;
|
|
ByteOffset.HighPart = 0;
|
|
Status = NtReadFile(FileHandle,
|
|
0,
|
|
0,
|
|
0,
|
|
&IoStatusBlock,
|
|
MemoryAddress,
|
|
4*1024,
|
|
&ByteOffset,
|
|
0);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto firstreturn;
|
|
}
|
|
pPeHeader = (PIMAGE_NT_HEADERS) MemoryAddress;
|
|
|
|
}
|
|
else {
|
|
|
|
if ((ULONG)PharLapConfigured >=
|
|
(ULONG)pHeader + 0x1000 - sizeof(CONFIGPHARLAP)) {
|
|
PharLapConfigured = NULL;
|
|
}
|
|
|
|
pPeHeader = (PIMAGE_NT_HEADERS) ((ULONG) MemoryAddress +
|
|
(ULONG) pHeader->e_lfanew);
|
|
}
|
|
|
|
if (pPeHeader->Signature != IMAGE_NT_SIGNATURE) {
|
|
|
|
if ((USHORT)pPeHeader->Signature == (USHORT)IMAGE_OS2_SIGNATURE) {
|
|
|
|
if ((((PIMAGE_OS2_HEADER) pPeHeader)->ne_exetyp == 2) ||
|
|
((((PIMAGE_OS2_HEADER) pPeHeader)->ne_exetyp == 0) &&
|
|
(((((PIMAGE_OS2_HEADER) pPeHeader)->ne_expver & 0xff00) ==
|
|
0x200) ||
|
|
((((PIMAGE_OS2_HEADER)pPeHeader)->ne_expver & 0xff00) ==
|
|
0x300)))) {
|
|
|
|
Status = STATUS_INVALID_IMAGE_WIN_16;
|
|
goto firstreturn;
|
|
}
|
|
|
|
// This exetype is not documented anywhere, but exetype
|
|
// utility knows about it and tells us that exetype==5 means
|
|
// binary is for DOS4.0.....!!
|
|
// and an empty entry table also means DOS4.0
|
|
// so we give it to NTDOS.
|
|
|
|
if (((PIMAGE_OS2_HEADER)pPeHeader)->ne_exetyp == 5 ||
|
|
((PIMAGE_OS2_HEADER)pPeHeader)->ne_enttab ==
|
|
((PIMAGE_OS2_HEADER)pPeHeader)->ne_imptab )
|
|
{
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto firstreturn;
|
|
}
|
|
|
|
//
|
|
// Borland Dosx types: exe type 1
|
|
//
|
|
// - "new" Borland Dosx BP7.0
|
|
// exe type == 1
|
|
// DosHeader + 0x200 contains the string "16STUB"
|
|
// 0x200 happens to be e_parhdr*16
|
|
//
|
|
|
|
if (((PIMAGE_OS2_HEADER)pPeHeader)->ne_exetyp == 1 &&
|
|
RtlCompareMemory(String16stub, "16STUB", 6) == 6) {
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto firstreturn;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Check for PharLap extended header which we run as a dos app.
|
|
// The PharLap config block is pointed to by the SizeofHeader
|
|
// field in the DosHdr.
|
|
// The following algorithm for detecting a pharlap exe
|
|
// was recommended by PharLap Software Inc.
|
|
//
|
|
|
|
if (PharLapConfigured != NULL) {
|
|
|
|
if (RtlCompareMemory(&PharLapConfigured->uchCopyRight[0x18],
|
|
"Phar Lap Software, Inc.", 24) == 24 &&
|
|
(PharLapConfigured->usSign == 0x4b50 || // stub loader type 2
|
|
PharLapConfigured->usSign == 0x4f50 || // bindable 286|DosExtender
|
|
PharLapConfigured->usSign == 0x5650 )) // bindable 286|DosExtender (Adv)
|
|
{
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto firstreturn;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for Rational extended header which we run as a dos app.
|
|
// We look for the rational copyright at:
|
|
// wCopyRight = *(DosHeader->e_cparhdr*16 + 30h)
|
|
// pCopyRight = wCopyRight + DosHeader->e_cparhdr*16
|
|
// "Copyright (C) Rational Systems, Inc."
|
|
//
|
|
|
|
if (RtlCompareMemory(StringRational,
|
|
"Copyright (C) Rational Systems, Inc.",
|
|
36) == 36 ) {
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto firstreturn;
|
|
}
|
|
|
|
#if PMNT
|
|
if ((((PIMAGE_OS2_HEADER)pPeHeader)->ne_flags & 0x0300) == 0x0300) {
|
|
//
|
|
// Presentation manager application
|
|
//
|
|
*IsPMApp = 1;
|
|
}
|
|
#endif //PMNT
|
|
|
|
Status = STATUS_INVALID_IMAGE_NE_FORMAT;
|
|
goto firstreturn;
|
|
}
|
|
|
|
if ((USHORT)pPeHeader->Signature == (USHORT)IMAGE_OS2_SIGNATURE_LE) {
|
|
Status = STATUS_INVALID_IMAGE_LE_FORMAT;
|
|
goto firstreturn;
|
|
}
|
|
} else {
|
|
if (pPeHeader->Signature == IMAGE_NT_SIGNATURE &&
|
|
pPeHeader->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_OS2_CUI) {
|
|
Status = 0xdeadbeef;
|
|
} else {
|
|
Status = 0;
|
|
}
|
|
}
|
|
|
|
firstreturn:
|
|
NtClose(FileHandle);
|
|
|
|
secondreturn:
|
|
NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&MemoryAddress,
|
|
&RegionSize,
|
|
MEM_RELEASE);
|
|
|
|
thirdreturn:
|
|
RtlFreeUnicodeString(&Unicode);
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
VOID
|
|
Od2FillErrorTextBuffer(
|
|
OUT PSZ ErrorText OPTIONAL,
|
|
IN LONG MaximumErrorTextLength,
|
|
IN PSZ Contents OPTIONAL
|
|
)
|
|
{
|
|
if (ARGUMENT_PRESENT( ErrorText ) && MaximumErrorTextLength--){
|
|
if (ARGUMENT_PRESENT( Contents )) {
|
|
while (MaximumErrorTextLength--) {
|
|
if (*ErrorText = *Contents++) {
|
|
ErrorText++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*ErrorText = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
APIRET
|
|
Od2FormatPgmName(
|
|
OUT PSZ ErrorText OPTIONAL,
|
|
IN LONG MaximumErrorTextLength,
|
|
IN PCHAR *ImageFileName,
|
|
OUT PCHAR ImageFileBuffer
|
|
)
|
|
{
|
|
APIRET rc = NO_ERROR;
|
|
|
|
if (!Od2IsAbsolutePath( *ImageFileName )) {
|
|
rc = DosSearchPath( SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT |
|
|
SEARCH_IGNORENETERRS,
|
|
"PATH",
|
|
*ImageFileName,
|
|
ImageFileBuffer,
|
|
CCHMAXPATH
|
|
);
|
|
if (rc != NO_ERROR) {
|
|
if (rc == ERROR_ENVVAR_NOT_FOUND) {
|
|
rc = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
Od2FillErrorTextBuffer( ErrorText,
|
|
MaximumErrorTextLength,
|
|
*ImageFileName
|
|
);
|
|
}
|
|
else {
|
|
*ImageFileName = ImageFileBuffer;
|
|
}
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
#define SEARCH_FLAGS (SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS)
|
|
|
|
APIRET
|
|
DosAddExtensionAndSearchPath(
|
|
// IN ULONG SearchFlags,
|
|
// IN PSZ PathOrVariableName,
|
|
IN PSZ FileName,
|
|
OUT PBYTE Buffer
|
|
// IN ULONG Length
|
|
)
|
|
{
|
|
APIRET rc;
|
|
PSZ SearchPath;
|
|
PSZ PathName;
|
|
PSZ Path;
|
|
ULONG FileNameLen, PathLen;
|
|
CHAR FileBuf[4][ CCHMAXPATH+1 ];
|
|
int i;
|
|
|
|
for ( i = 0 ; i < 4 ; i++ )
|
|
{
|
|
strcpy(FileBuf[i], FileName);
|
|
}
|
|
|
|
strcat(FileBuf[0], ".com");
|
|
strcat(FileBuf[1], ".bat");
|
|
strcat(FileBuf[2], ".cmd");
|
|
strcat(FileBuf[3], ".exe");
|
|
|
|
//
|
|
// Validate the parameters
|
|
//
|
|
|
|
// if (Length == 0) {
|
|
// return( ERROR_BUFFER_OVERFLOW );
|
|
// }
|
|
//
|
|
// if (SearchFlags & ~(SEARCH_CUR_DIRECTORY |
|
|
// SEARCH_ENVIRONMENT |
|
|
// SEARCH_IGNORENETERRS
|
|
// )
|
|
// ) {
|
|
// return( ERROR_INVALID_PARAMETER );
|
|
// }
|
|
//
|
|
// if (FileName == NULL) {
|
|
// //
|
|
// // Note that OS/2 does not check for this case until after the
|
|
// // SEARCH_CUR_DIRECTORY logic, so OS/2 would GP fault if passed a null
|
|
// // file name pointer and the SEARCH_CUR_DIRECTORY flag. Seems like a
|
|
// // bug so we will check before we use the FileName pointer.
|
|
// //
|
|
//
|
|
// return( ERROR_FILE_NOT_FOUND );
|
|
// }
|
|
|
|
//
|
|
// Check the current directory first if requested.
|
|
// we pass Canonicalize the requested name.
|
|
//
|
|
|
|
// if (SearchFlags & SEARCH_CUR_DIRECTORY) {
|
|
// rc = SearchForPath( SearchFlags,
|
|
// FileName,
|
|
// Buffer,
|
|
// Length
|
|
// );
|
|
//
|
|
// if (rc != ERROR_SS_RETRY) {
|
|
// return( rc );
|
|
// }
|
|
// }
|
|
|
|
for ( i = 0 ; i < 4 ; i++ )
|
|
{
|
|
rc = SearchForPath( SEARCH_FLAGS,
|
|
FileBuf[i],
|
|
Buffer,
|
|
CCHMAXPATH
|
|
);
|
|
|
|
if (rc != ERROR_SS_RETRY)
|
|
{
|
|
if (i == 3) // EXE only
|
|
{
|
|
return (rc);
|
|
} else
|
|
return( 1 );
|
|
}
|
|
}
|
|
|
|
if (Od2IsAbsolutePath( FileBuf[0] ))
|
|
{
|
|
return( 1 );
|
|
}
|
|
|
|
//
|
|
// If pass search path is an environment variable name, then get its
|
|
// value from the OS/2 Environment Block.
|
|
//
|
|
|
|
// if (SearchFlags & SEARCH_ENVIRONMENT) {
|
|
// rc = DosScanEnv( PathOrVariableName, &SearchPath );
|
|
// if (rc != NO_ERROR) {
|
|
// try {
|
|
// *Buffer = '\0';
|
|
// }
|
|
// except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
// Od2ExitGP();
|
|
// }
|
|
// return( rc );
|
|
// }
|
|
// }
|
|
// else {
|
|
// SearchPath = PathOrVariableName;
|
|
// }
|
|
|
|
rc = DosScanEnv( "PATH", &SearchPath );
|
|
if (rc != NO_ERROR)
|
|
{
|
|
return( rc );
|
|
}
|
|
|
|
//
|
|
// probe filename and path. figure out maximum length of combined
|
|
// filename and path
|
|
//
|
|
|
|
try {
|
|
FileNameLen = strlen( FileName ) + 4; // 4 for the extenstion (.exe)
|
|
PathLen = strlen( SearchPath );
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
//
|
|
// + 2 is for terminating nul and possible slash
|
|
//
|
|
|
|
PathName = RtlAllocateHeap( Od2Heap, 0, PathLen + FileNameLen + 2 );
|
|
if (!PathName) {
|
|
#if DBG
|
|
KdPrint(("OS2: DosAddExtensionAndSearchPath out of heap memory, fail\n"));
|
|
#endif
|
|
ASSERT( FALSE );
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// for each element in search path, append filename and call FindFile.
|
|
//
|
|
|
|
while (*SearchPath) {
|
|
|
|
#ifdef DBCS
|
|
// MSKK Apr.09.1993 V-AkihiS
|
|
//
|
|
// find end of path element
|
|
//
|
|
Path = SearchPath;
|
|
while (*SearchPath && *SearchPath != ';') {
|
|
if (Ow2NlsIsDBCSLeadByte(*SearchPath, SesGrp->DosCP)) SearchPath++;
|
|
if (*SearchPath) SearchPath++;
|
|
}
|
|
#else
|
|
// BUGBUG fix for DBCS
|
|
//
|
|
// find end of path element
|
|
//
|
|
|
|
for (Path = SearchPath;*SearchPath && *SearchPath != ';';SearchPath++) {
|
|
;
|
|
}
|
|
#endif
|
|
PathLen = SearchPath - Path;
|
|
// if (PathLen != 0) {
|
|
// RtlMoveMemory( PathName, Path, PathLen );
|
|
// if (!ISSLASH( PathName[ PathLen-1 ] )) {
|
|
// PathName[ PathLen ] = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
//
|
|
// //
|
|
// // +1 is for terminating NUL
|
|
// //
|
|
//
|
|
// RtlMoveMemory( PathName+PathLen+1, FileName, FileNameLen+1 );
|
|
// }
|
|
// else {
|
|
//
|
|
// //
|
|
// // +1 is for terminating NUL
|
|
// //
|
|
//
|
|
// RtlMoveMemory( PathName+PathLen, FileName, FileNameLen+1 );
|
|
// }
|
|
//
|
|
// rc = SearchForPath( SearchFlags,
|
|
// PathName,
|
|
// Buffer,
|
|
// Length
|
|
// );
|
|
//
|
|
// if (rc != ERROR_SS_RETRY) {
|
|
// return( rc );
|
|
// }
|
|
// }
|
|
|
|
if (PathLen != 0)
|
|
{
|
|
RtlMoveMemory( PathName, Path, PathLen );
|
|
#ifdef DBCS
|
|
// MSKK Apr.09.1993 V-AkihiS
|
|
//
|
|
// Determine whether the last char of PathName
|
|
// is slash or not.
|
|
//
|
|
{
|
|
ULONG i = 0;
|
|
BOOLEAN SlashFlag = FALSE;
|
|
|
|
while (i < PathLen) {
|
|
if (Ow2NlsIsDBCSLeadByte(PathName[i], SesGrp->DosCP)) {
|
|
SlashFlag = FALSE;
|
|
i++;
|
|
if (i < PathLen) {
|
|
i++;
|
|
}
|
|
} else {
|
|
if (ISSLASH(PathName[i])) {
|
|
SlashFlag = TRUE;
|
|
} else {
|
|
SlashFlag = FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
if (!SlashFlag)
|
|
{
|
|
PathName[ PathLen++ ] = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
}
|
|
}
|
|
#else
|
|
if (!ISSLASH( PathName[ PathLen-1 ] ))
|
|
{
|
|
PathName[ PathLen++ ] = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
}
|
|
#endif
|
|
|
|
for ( i = 0 ; i < 4 ; i++ )
|
|
{
|
|
//
|
|
// +1 is for terminating NUL
|
|
//
|
|
|
|
RtlMoveMemory( PathName+PathLen, FileBuf[i], FileNameLen+1 );
|
|
|
|
rc = SearchForPath( SEARCH_FLAGS,
|
|
PathName,
|
|
Buffer,
|
|
CCHMAXPATH
|
|
);
|
|
|
|
if (rc != ERROR_SS_RETRY)
|
|
{
|
|
RtlFreeHeap( Od2Heap, 0, PathName );
|
|
if (i == 3) // EXE only
|
|
{
|
|
return (rc);
|
|
} else
|
|
return( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*SearchPath) { // point past ;
|
|
SearchPath++;
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap( Od2Heap, 0, PathName );
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
|
|
CHAR CmdStrUp[] = {"/C " };
|
|
CHAR CmdStrLo[] = {"/c " };
|
|
|
|
|
|
BOOLEAN
|
|
Od2IsPgmCmd(
|
|
IN PSZ *ImageFileName,
|
|
IN PSZ Arguments OPTIONAL,
|
|
OUT PCHAR ImageFileBuffer,
|
|
OUT PSZ *CmdArguments,
|
|
OUT PHANDLE hRedirFile,
|
|
OUT PULONG RedirFileType,
|
|
OUT PULONG pRedirectionFlag,
|
|
IN ULONG SessionFlag,
|
|
OUT PSZ *CmdLineTruncationPoint
|
|
)
|
|
{
|
|
CHAR CmdFileBuf[ CCHMAXPATH+1 ], *CmdFileName;
|
|
PSZ FileName = *ImageFileName;
|
|
CHAR c, *p, *p1, *NulPlace = NULL, *RedirFileName;
|
|
int i, ArgNum, CharNum;
|
|
BOOLEAN FindFullName;
|
|
ULONG RedirectionFlag = NO_REDIR, FileFlags;
|
|
APIRET rc;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING )
|
|
{
|
|
KdPrint(("Od2IsPgmCmd: enter with File %s, Arg %s\n",
|
|
FileName, Arguments));
|
|
}
|
|
#endif
|
|
|
|
*pRedirectionFlag = NO_REDIR;
|
|
|
|
/*
|
|
* 1. if no Arg - ignore
|
|
*/
|
|
|
|
if (Arguments == NULL)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
/*
|
|
* 2. if no FileName (NULL) - assume cmd (for StartSession)
|
|
* else
|
|
* ignore path and check if "cmd" or "cmd.exe"
|
|
*/
|
|
|
|
if (FileName)
|
|
{
|
|
p = FileName;
|
|
|
|
while (c = *FileName++)
|
|
{
|
|
if (c == '/' || c == (CHAR)OBJ_NAME_PATH_SEPARATOR || c == ':')
|
|
{
|
|
p = FileName;
|
|
}
|
|
}
|
|
|
|
if(strnicmp(p, "cmd", 3) ||
|
|
(p[3] && (p[3] != ' ') && strnicmp(p+3, ".exe", 4)))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 3. check Arg: ignore 1st arg and check the 2nd to be "/c"
|
|
*/
|
|
|
|
for ( p = Arguments, ArgNum = 0, CharNum = 0, c = *(p++); ; c = *(p++) )
|
|
{
|
|
if (!c && !p[0])
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if (ArgNum == 1)
|
|
{
|
|
if ((c == CmdStrUp[CharNum]) || (c == CmdStrLo[CharNum]))
|
|
{
|
|
CharNum++;
|
|
if (CharNum == 3)
|
|
{
|
|
break;
|
|
}
|
|
} else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
} else
|
|
{
|
|
if (!c || (c == ' ') || (c == '\t'))
|
|
{
|
|
ArgNum++ ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 4. Arg: ignore more space's
|
|
* BUGBUG if Arg contains '"', '|', '&', '>', '<' or '^' - ignore
|
|
*/
|
|
|
|
for ( c = *p ; (c == ' ') || (c == '\t') ; c = *(++p) );
|
|
|
|
if (c == '\0')
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
*CmdArguments = p ;
|
|
|
|
for ( ; c ; c = *(++p) )
|
|
{
|
|
if (( c == '|' ) || ( c == '&' ) ||
|
|
( c == '<' ) || ( c == '^' ) || ( c == '"' ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( c == '>' )
|
|
{
|
|
p1 = p;
|
|
|
|
/*
|
|
* skip over space's
|
|
*/
|
|
|
|
for ( c = *(++p1) ; (c == ' ') || (c == '\t') ; c = *(++p1) );
|
|
|
|
if (!stricmp(p1, "NUL" ) && (p[-2] != '2'))
|
|
{
|
|
// found output redirected to NUL ( "> NUL" )
|
|
|
|
if(NulPlace != NULL)
|
|
{
|
|
// found one before
|
|
|
|
return ( FALSE );
|
|
}
|
|
|
|
NulPlace = p;
|
|
|
|
p1 += 3;
|
|
|
|
/*
|
|
* skip over space's
|
|
*/
|
|
|
|
for ( c = *p1 ; (c == ' ') || (c == '\t') ; c = *(++p1) );
|
|
|
|
if (!c)
|
|
{
|
|
RedirectionFlag |= REDIR_NUL;
|
|
break;
|
|
} else
|
|
{
|
|
return ( FALSE );
|
|
}
|
|
} else
|
|
{
|
|
if(NulPlace != NULL)
|
|
{
|
|
return ( FALSE );
|
|
}
|
|
|
|
NulPlace = p;
|
|
RedirFileName = p1;
|
|
for ( c = *p1 ; c ; c = *(p1++) )
|
|
{
|
|
if (( c == '|' ) || ( c == '&' ) || ( c == '"' ) ||
|
|
( c == '<' ) || ( c == '^' ) || ( c == '>' ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if (( c == ' ' ) || ( c == '\t' ))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( c == '\0' )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
for ( c = *p1 ; (c == ' ') || (c == ' ') ; c = *(++p1) );
|
|
|
|
if ( c != '\0')
|
|
{
|
|
return ( FALSE );
|
|
}
|
|
|
|
RedirectionFlag |= REDIR_FILE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 5. Arg: copy "new" filename to buffer
|
|
*/
|
|
|
|
FindFullName = FALSE;
|
|
|
|
p = *CmdArguments;
|
|
|
|
// // if the program name is quoted, remove them
|
|
//
|
|
// if (*p == '"')
|
|
// {
|
|
// p++ ;
|
|
// }
|
|
|
|
for ( CharNum = 0, c = *p ;
|
|
(CharNum < CCHMAXPATH ) && (c != ' ') && (c != '\0') && (c != '\t') ;
|
|
CmdFileBuf[CharNum] = c, c = p[++CharNum] );
|
|
|
|
if (CharNum == CCHMAXPATH )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// if (CmdFileBuf[CharNum - 1] == '"')
|
|
// {
|
|
// // if the program name is quoted, remove them
|
|
//
|
|
// CharNum-- ;
|
|
// }
|
|
|
|
CmdFileBuf[CharNum] = '\0';
|
|
|
|
for ( i = CharNum ; i && (i > (CharNum - 4)) ; --i )
|
|
{
|
|
c = CmdFileBuf[i - 1];
|
|
|
|
if (c == '.')
|
|
{
|
|
FindFullName = TRUE;
|
|
break;
|
|
} else if (c == '/' || c == (CHAR)OBJ_NAME_PATH_SEPARATOR || c == ':')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
CmdFileName = CmdFileBuf;
|
|
|
|
if ( !FindFullName )
|
|
{
|
|
// CmdFileBuf[CharNum++] = '.';
|
|
// CmdFileBuf[CharNum++] = 'e';
|
|
// CmdFileBuf[CharNum++] = 'x';
|
|
// CmdFileBuf[CharNum++] = 'e';
|
|
// CmdFileBuf[CharNum] = '\0';
|
|
|
|
if (DosAddExtensionAndSearchPath(
|
|
CmdFileName,
|
|
CmdFileBuf
|
|
))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
} else
|
|
{
|
|
|
|
if(Od2FormatPgmName(
|
|
NULL,
|
|
0,
|
|
&CmdFileName,
|
|
CmdFileBuf))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
UNICODE_STRING NameString_U;
|
|
STRING NameString;
|
|
NTSTATUS Status;
|
|
|
|
rc = Od2Canonicalize(RedirFileName,
|
|
CANONICALIZE_FILE_DEV_OR_PIPE,
|
|
&NameString,
|
|
NULL,
|
|
&FileFlags,
|
|
RedirFileType
|
|
);
|
|
|
|
if ((rc != NO_ERROR)|| (FileFlags & CANONICALIZE_META_CHARS_FOUND))
|
|
{
|
|
if (rc == NO_ERROR && (FileFlags & CANONICALIZE_META_CHARS_FOUND))
|
|
{
|
|
RtlFreeHeap(Od2Heap, 0, NameString.Buffer);
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// UNICODE conversion -
|
|
//
|
|
|
|
Status = Or2MBStringToUnicodeString(
|
|
&NameString_U,
|
|
&NameString,
|
|
(BOOLEAN)TRUE);
|
|
|
|
RtlFreeHeap(Od2Heap, 0, NameString.Buffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
InitializeObjectAttributes(&Obja,
|
|
&NameString_U,
|
|
OBJ_CASE_INSENSITIVE|OBJ_INHERIT,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateFile(hRedirFile,
|
|
FILE_GENERIC_WRITE,
|
|
&Obja,
|
|
&IoStatus,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
|
|
NULL,
|
|
0L
|
|
);
|
|
|
|
RtlFreeUnicodeString (&NameString_U);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
strcpy(ImageFileBuffer, CmdFileName);
|
|
*ImageFileName = ImageFileBuffer;
|
|
|
|
if (RedirectionFlag != NO_REDIR)
|
|
{
|
|
//
|
|
// March 4 93 -- We used to truncate the the command line inside the user's
|
|
// buffer. This was wrong. Now, we record the truncation point instead,
|
|
// and truncate it later inside our own buffer.
|
|
//
|
|
|
|
// *NulPlace++ = '\0';
|
|
// *NulPlace = '\0';
|
|
*CmdLineTruncationPoint = NulPlace;
|
|
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING )
|
|
{
|
|
KdPrint(("IsPgmCmd: found redirected file name %s\n",
|
|
RedirFileName));
|
|
}
|
|
#endif
|
|
} else
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING )
|
|
{
|
|
KdPrint(("IsPgmCmd: found redirectd to NULL\n"));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING )
|
|
{
|
|
KdPrint(("Od2IsPgmCmd: exit with File %s, Arg %s\n",
|
|
CmdFileName, *CmdArguments));
|
|
}
|
|
#endif
|
|
|
|
*pRedirectionFlag = RedirectionFlag;
|
|
return( TRUE );
|
|
}
|
|
|
|
APIRET
|
|
Od2FormatExecPgmMessage(
|
|
OUT POS2_DOSEXECPGM_MSG a,
|
|
OUT POS2_CAPTURE_HEADER *CaptureBuffer,
|
|
OUT PNTSTATUS Od2IsConsoleTypeReturnStatus,
|
|
#if PMNT
|
|
OUT PULONG IsPMApp,
|
|
#endif // PMNT
|
|
OUT PSZ ErrorText OPTIONAL,
|
|
IN LONG MaximumErrorTextLength,
|
|
IN ULONG Flags,
|
|
IN OUT PSZ *VariablesBuffer,
|
|
IN OUT PSZ *ArgumentsBuffer,
|
|
IN PSZ *ImageFileName
|
|
)
|
|
{
|
|
ULONG ArgumentsLength, VariablesLength, ImageNameLength;
|
|
ULONG MessageBufferPointers = 0;
|
|
ULONG AppVariablesLength;
|
|
ULONG ImageFileFlags, ImageFileType, RedirectedFileType;
|
|
ULONG RedirectionFlag = NO_REDIR, Win32CurDirsLength = 0;
|
|
ULONG Length, i, SessionFlag = Flags & 0x80000000;
|
|
PSZ s, stemp, CmdArguments, Win32CurDirs, Win32CurDirsStart;
|
|
PSZ CmdLineTruncationPoint = NULL;
|
|
STRING ImageFileString;
|
|
APIRET rc;
|
|
CHAR ImageFileBuffer[ CCHMAXPATH+1 ];
|
|
POS2_CAPTURE_HEADER LocalCaptureBuffer;
|
|
HANDLE hRedirFile = NULL;
|
|
BOOLEAN AddOs2LibPath;
|
|
|
|
Flags &= ~0x80000000;
|
|
|
|
//
|
|
// Call DosSearchPath if NOT absolute path spec.
|
|
//
|
|
|
|
ImageFileString.Length = 0;
|
|
|
|
rc = NO_ERROR;
|
|
|
|
try {
|
|
if(!(rc = Od2FormatPgmName(
|
|
ErrorText,
|
|
MaximumErrorTextLength,
|
|
ImageFileName,
|
|
ImageFileBuffer)))
|
|
{
|
|
if (Od2IsPgmCmd( ImageFileName,
|
|
*ArgumentsBuffer,
|
|
ImageFileBuffer,
|
|
&CmdArguments,
|
|
&hRedirFile,
|
|
&RedirectedFileType,
|
|
&RedirectionFlag,
|
|
SessionFlag,
|
|
&CmdLineTruncationPoint
|
|
))
|
|
{
|
|
*ArgumentsBuffer = CmdArguments;
|
|
RedirectionFlag |= CMD_SHORTCUT;
|
|
} else
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
if (rc != NO_ERROR) {
|
|
return( rc );
|
|
}
|
|
|
|
//
|
|
// Canonicalize image file name
|
|
//
|
|
|
|
rc = Od2Canonicalize( *ImageFileName,
|
|
CANONICALIZE_FILE_OR_DEV,
|
|
&ImageFileString,
|
|
NULL,
|
|
&ImageFileFlags,
|
|
&ImageFileType
|
|
);
|
|
if (rc != NO_ERROR) {
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
NtClose(hRedirFile);
|
|
}
|
|
return( rc );
|
|
}
|
|
|
|
if (ImageFileType != FILE_TYPE_FILE && ImageFileType != FILE_TYPE_UNC) {
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
NtClose(hRedirFile);
|
|
}
|
|
rc = ERROR_ACCESS_DENIED;
|
|
}
|
|
else
|
|
if (ImageFileFlags != 0) {
|
|
rc = ERROR_PATH_NOT_FOUND ;
|
|
}
|
|
else
|
|
if (Flags > EXEC_TRACETREE) {
|
|
rc = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
if (rc != NO_ERROR) {
|
|
RtlFreeHeap( Od2Heap, 0, ImageFileString.Buffer );
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
NtClose(hRedirFile);
|
|
}
|
|
return( rc );
|
|
}
|
|
|
|
RtlZeroMemory( a, sizeof( *a ) );
|
|
|
|
Length = (ULONG)ImageFileString.Length;
|
|
#ifdef DBCS
|
|
// MSKK Apr.12.1993 V-AkihiS
|
|
{
|
|
PSZ LastDelimiter;
|
|
|
|
stemp = LastDelimiter = &ImageFileString.Buffer[0];
|
|
i = 0;
|
|
while (i < Length) {
|
|
if (Ow2NlsIsDBCSLeadByte(*(stemp+i), SesGrp->DosCP)) {
|
|
i++;
|
|
if (i < Length) {
|
|
i++;
|
|
}
|
|
} else {
|
|
if ((*(stemp+i) == '\\') || (*(stemp+i) == '/') || (*(stemp+i) == ':')) {
|
|
LastDelimiter = stemp+i;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
stemp = LastDelimiter;
|
|
if ((*stemp == '\\') || (*stemp == '/') || (*stemp == ':'))
|
|
{
|
|
stemp++;
|
|
}
|
|
}
|
|
#else
|
|
stemp = &ImageFileString.Buffer[Length - 1];
|
|
for ( i = 0 ; i < Length ; i++, stemp-- )
|
|
{
|
|
if ((*stemp == '\\') || (*stemp == '/') || (*stemp == ':'))
|
|
{
|
|
stemp++;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (i >= OS2_MAX_APPL_NAME)
|
|
{
|
|
i = OS2_MAX_APPL_NAME - 1;
|
|
}
|
|
|
|
strncpy(a->ApplName,
|
|
stemp,
|
|
i);
|
|
|
|
a->ApplName[i] = '\0';
|
|
a->ApplNameLength = i + 1;
|
|
|
|
a->CodePage = Od2ProcessCodePage;
|
|
a->Flags = Flags;
|
|
a->CurrentDrive = Od2CurrentDisk;
|
|
a->CmdLineFlag = RedirectionFlag;
|
|
if ( *VariablesBuffer == NULL ) {
|
|
*VariablesBuffer = Od2Environment;
|
|
}
|
|
|
|
Win32CurDirs = s = (PSZ)GetEnvironmentStrings();
|
|
|
|
//
|
|
// 1st measure the size of commitment needed, and commit it
|
|
//
|
|
|
|
//
|
|
// watch for the current directories (appear in environment
|
|
// in the form "=C:=C:\foo")
|
|
//
|
|
|
|
|
|
Win32CurDirsStart = NULL;
|
|
while (*s) {
|
|
if (*s == '=') {
|
|
Win32CurDirsStart = s;
|
|
break;
|
|
}
|
|
s++;
|
|
while (*s++) {
|
|
}
|
|
}
|
|
|
|
if (Win32CurDirsStart != NULL) {
|
|
while (*s) {
|
|
if (*s != '='){
|
|
Win32CurDirsLength = s - Win32CurDirsStart;
|
|
break;
|
|
}
|
|
s++;
|
|
while (*s++) {
|
|
}
|
|
}
|
|
if (Win32CurDirsLength == 0) {
|
|
Win32CurDirsLength = s - Win32CurDirsStart;
|
|
}
|
|
}
|
|
|
|
if (ErrorText == NULL || MaximumErrorTextLength == 0)
|
|
{
|
|
ErrorText = NULL;
|
|
MaximumErrorTextLength = 0;
|
|
} else
|
|
{
|
|
MessageBufferPointers += 1;
|
|
try {
|
|
RtlZeroMemory( ErrorText, MaximumErrorTextLength );
|
|
} except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
RtlFreeHeap( Od2Heap,0,ImageFileString.Buffer );
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
NtClose(hRedirFile);
|
|
}
|
|
Od2ExitGP();
|
|
}
|
|
}
|
|
|
|
try {
|
|
s = *ArgumentsBuffer;
|
|
|
|
if (s != NULL) {
|
|
if (CmdLineTruncationPoint == NULL) {
|
|
if (SessionFlag)
|
|
{
|
|
// In session, copy till first NULL
|
|
|
|
while (*s) {
|
|
s++;
|
|
}
|
|
s++;
|
|
} else {
|
|
if (!*s){
|
|
s++;
|
|
}
|
|
while (*s) {
|
|
while (*s) {
|
|
s++;
|
|
}
|
|
|
|
s++;
|
|
}
|
|
s++;
|
|
}
|
|
} else {
|
|
s = CmdLineTruncationPoint + 2;
|
|
}
|
|
}
|
|
ArgumentsLength = s - *ArgumentsBuffer;
|
|
|
|
AddOs2LibPath = TRUE;
|
|
|
|
s = *VariablesBuffer;
|
|
if (s != NULL) {
|
|
while (*s) {
|
|
|
|
if (strnicmp(s, "OS2LIBPATH=", 11) == 0) {
|
|
AddOs2LibPath = FALSE;
|
|
}
|
|
|
|
while (*s) {
|
|
s++;
|
|
}
|
|
s++;
|
|
}
|
|
s++;
|
|
}
|
|
//
|
|
// add space for Os2LibPath=string0, and for Win32 CurrentDirectories
|
|
// e.g. =C:=C:\foo
|
|
//
|
|
AppVariablesLength = s - *VariablesBuffer;
|
|
VariablesLength = AppVariablesLength + Win32CurDirsLength;
|
|
|
|
if (AddOs2LibPath) {
|
|
VariablesLength += Od2LibPathLength + 11 + 1;
|
|
}
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
if (RedirectionFlag & REDIR_FILE)
|
|
{
|
|
NtClose(hRedirFile);
|
|
}
|
|
RtlFreeHeap(Od2Heap, 0,ImageFileString.Buffer);
|
|
Od2ExitGP();
|
|
}
|
|
|
|
LocalCaptureBuffer = Od2AllocateCaptureBuffer( MessageBufferPointers,
|
|
0,
|
|
MaximumErrorTextLength
|
|
);
|
|
if (LocalCaptureBuffer == NULL) {
|
|
RtlFreeHeap( Od2Heap, 0, ImageFileString.Buffer );
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Win32CurDirs);
|
|
if (RedirectionFlag & REDIR_FILE) {
|
|
NtClose(hRedirFile);
|
|
}
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
//
|
|
// prepare the FileName
|
|
//
|
|
ImageNameLength = ImageFileString.Length + 1;
|
|
stemp = ImageFileString.Buffer;
|
|
if ((*ImageFileName = RtlAllocateHeap(Od2Heap, 0, ImageNameLength)) == NULL)
|
|
{
|
|
Od2FreeCaptureBuffer( LocalCaptureBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ImageFileString.Buffer );
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Win32CurDirs);
|
|
if (RedirectionFlag & REDIR_FILE) {
|
|
NtClose(hRedirFile);
|
|
}
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
if (ImageFileType == FILE_TYPE_UNC) {
|
|
|
|
stemp = ImageFileString.Buffer + 10; // skip over \OS2SS\UNC
|
|
ImageNameLength -= 10; // adjust length
|
|
|
|
**ImageFileName = '\\';
|
|
|
|
RtlMoveMemory((*ImageFileName) + 1,
|
|
stemp,
|
|
ImageNameLength
|
|
);
|
|
} else {
|
|
if ((s = strstr(ImageFileString.Buffer, ":")) != NULL)
|
|
{
|
|
s-- ;
|
|
ImageNameLength -= (s - ImageFileString.Buffer);
|
|
stemp = s;
|
|
}
|
|
RtlMoveMemory(*ImageFileName,
|
|
stemp,
|
|
ImageNameLength
|
|
);
|
|
}
|
|
|
|
*Od2IsConsoleTypeReturnStatus = Od2IsFileConsoleType(&ImageFileString
|
|
#if PMNT
|
|
, IsPMApp
|
|
#endif // PMNT
|
|
);
|
|
|
|
RtlFreeHeap( Od2Heap, 0, ImageFileString.Buffer );
|
|
|
|
//
|
|
// prepare the Arguments
|
|
//
|
|
if (ArgumentsLength)
|
|
{
|
|
if ((s = RtlAllocateHeap(Od2Heap, 0, ArgumentsLength)) == NULL)
|
|
{
|
|
Od2FreeCaptureBuffer( LocalCaptureBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, *ImageFileName );
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Win32CurDirs);
|
|
if (RedirectionFlag & REDIR_FILE) {
|
|
NtClose(hRedirFile);
|
|
}
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
RtlMoveMemory(
|
|
s,
|
|
*ArgumentsBuffer,
|
|
ArgumentsLength
|
|
);
|
|
|
|
if (CmdLineTruncationPoint != NULL) {
|
|
s[ArgumentsLength - 2] = '\0';
|
|
s[ArgumentsLength - 1] = '\0';
|
|
}
|
|
|
|
*ArgumentsBuffer = s;
|
|
|
|
//
|
|
// replace zeros in arguments with blanks, to be win32 like
|
|
// (if in session - then it's already blank, don't replace)
|
|
|
|
//
|
|
// changed on 6/12/92 -- truncates the command line at the second null.
|
|
// This was done because some programs (such as os/2 slick) don't
|
|
// put a double null at the end and leave some garbage there.
|
|
//
|
|
|
|
//
|
|
// look for '\0' (not at the end of the command line)
|
|
// and replace with space
|
|
//
|
|
if (!SessionFlag) {
|
|
while (*s) {
|
|
s++;
|
|
}
|
|
|
|
if (s[1] != '\0')
|
|
{
|
|
*s = ' ';
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
*ArgumentsBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// prepare the Variables
|
|
//
|
|
|
|
if ((s = RtlAllocateHeap(Od2Heap, 0, VariablesLength)) == NULL)
|
|
{
|
|
RtlFreeHeap( Od2Heap, 0, *ArgumentsBuffer );
|
|
Od2FreeCaptureBuffer( LocalCaptureBuffer );
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Win32CurDirs);
|
|
if (RedirectionFlag & REDIR_FILE) {
|
|
NtClose(hRedirFile);
|
|
}
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
stemp = *VariablesBuffer;
|
|
*VariablesBuffer = s;
|
|
|
|
//
|
|
// Start with Win32 CurrentDirectories (e.g. =C:=C:\foo)
|
|
//
|
|
|
|
if (Win32CurDirsStart != NULL) {
|
|
RtlMoveMemory(
|
|
s,
|
|
Win32CurDirsStart,
|
|
Win32CurDirsLength
|
|
);
|
|
s += Win32CurDirsLength;
|
|
}
|
|
|
|
//
|
|
// append app Variables
|
|
//
|
|
|
|
RtlMoveMemory(
|
|
s,
|
|
stemp,
|
|
AppVariablesLength
|
|
);
|
|
|
|
s += (AppVariablesLength - 1); // step on the last 0 of the variables
|
|
|
|
//
|
|
// append Os2LibPath=string0
|
|
//
|
|
|
|
if (AddOs2LibPath) {
|
|
|
|
//
|
|
// append os2libpath= to Variables
|
|
//
|
|
RtlMoveMemory(
|
|
s,
|
|
"Os2LibPath=",
|
|
11);
|
|
s += 11;
|
|
|
|
//
|
|
// append the value of od2libpath
|
|
//
|
|
RtlMoveMemory(
|
|
s,
|
|
Od2LibPath,
|
|
Od2LibPathLength);
|
|
s+=Od2LibPathLength;
|
|
*s++=0;
|
|
*s=0;
|
|
}
|
|
|
|
if (ErrorText != NULL) {
|
|
Od2CaptureMessageString( LocalCaptureBuffer,
|
|
NULL,
|
|
0,
|
|
MaximumErrorTextLength,
|
|
&a->ErrorText
|
|
);
|
|
}
|
|
|
|
if (RedirectionFlag & REDIR_FILE) {
|
|
a->hRedirectedFile = hRedirFile;
|
|
}
|
|
|
|
*CaptureBuffer = LocalCaptureBuffer;
|
|
|
|
switch (*Od2IsConsoleTypeReturnStatus) {
|
|
case STATUS_INVALID_IMAGE_NE_FORMAT:
|
|
case STATUS_INVALID_IMAGE_FORMAT:
|
|
case STATUS_OBJECT_NAME_NOT_FOUND:
|
|
case STATUS_OBJECT_PATH_NOT_FOUND:
|
|
break;
|
|
|
|
default:
|
|
|
|
// Executable is a Win32 program. We now look for config.sys on the command line and
|
|
// translate it to os2conf.nt if necessary
|
|
|
|
Od2TranslateConfigSysInCommandLine(ArgumentsBuffer);
|
|
}
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Win32CurDirs);
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosExecPgm(
|
|
OUT PSZ ErrorText OPTIONAL,
|
|
IN LONG MaximumErrorTextLength,
|
|
IN ULONG Flags,
|
|
IN PSZ Arguments OPTIONAL,
|
|
IN PSZ Variables OPTIONAL,
|
|
OUT PRESULTCODES ResultCodes,
|
|
IN PSZ ImageFileName
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSEXECPGM_MSG a = &m.u.DosExecPgm;
|
|
POS2_CAPTURE_HEADER CaptureBuffer;
|
|
THREAD_BASIC_INFORMATION ThreadInfo;
|
|
OS2_STDHANDLES StdStruc;
|
|
APIRET rc;
|
|
CHAR AlternateFileName[CCHMAXPATH];
|
|
HANDLE hThread, hProcess;
|
|
PSZ VariablesBuffer = Variables;
|
|
PSZ ArgumentsBuffer = Arguments;
|
|
PSZ ExecFileName = ImageFileName;
|
|
ULONG ExitCode;
|
|
ULONG dwProcessId;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
#if PMNT
|
|
ULONG IsPMApp;
|
|
#endif // PMNT
|
|
|
|
#if DBG
|
|
PSZ RoutineName;
|
|
RoutineName = "DosExecPgm";
|
|
|
|
IF_OD2_DEBUG( TASKING )
|
|
{
|
|
KdPrint(("%s: enter with File %s, Arg %s, Flag %lx\n",
|
|
RoutineName, ImageFileName, Arguments, Flags));
|
|
}
|
|
#endif
|
|
|
|
if (SesGrp->PopUpFlag)
|
|
{
|
|
#if DBG
|
|
KdPrint(("%s: illegal during POPUP\n", RoutineName));
|
|
#endif
|
|
return( ERROR_VIO_ILLEGAL_DURING_POPUP );
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!stricmp(ImageFileName, "C:\\OS2\\CMD.EXE"))
|
|
{
|
|
sprintf( AlternateFileName, "%s\\cmd.exe", Od2SystemRoot );
|
|
#if DBG
|
|
KdPrint(("OS2SS: DosExecPgm convert c:\\os2\\cmd.exe to %s\n",
|
|
AlternateFileName));
|
|
#endif
|
|
ImageFileName = AlternateFileName;
|
|
}
|
|
} except( EXCEPTION_EXECUTE_HANDLER ){
|
|
Od2ExitGP();
|
|
}
|
|
|
|
if (Od2Process->Pib.Status & PS_EXITLIST) {
|
|
return( ERROR_INVALID_FUNCTION );
|
|
}
|
|
|
|
try {
|
|
Od2ProbeForWrite( (PVOID)ResultCodes, sizeof( *ResultCodes ), 1 );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ){
|
|
Od2ExitGP();
|
|
}
|
|
|
|
#if PMNT
|
|
if (ProcessIsPMShell() || ProcessIsPMShellChild())
|
|
{
|
|
// Make all sub-processes of PMSHELL run with no access to the
|
|
// Console. This way, they will not have the annoying "Wait",
|
|
// "End Task", "Cancel" pop-up
|
|
Flags = EXEC_BACKGROUND;
|
|
}
|
|
#endif // PMNT
|
|
|
|
rc = Od2FormatExecPgmMessage( a,
|
|
&CaptureBuffer,
|
|
&Status,
|
|
#if PMNT
|
|
&IsPMApp,
|
|
#endif // PMNT
|
|
ErrorText,
|
|
MaximumErrorTextLength,
|
|
Flags,
|
|
&VariablesBuffer,
|
|
&ArgumentsBuffer,
|
|
&ExecFileName
|
|
);
|
|
|
|
if (rc != NO_ERROR) {
|
|
return( rc );
|
|
}
|
|
|
|
// IMPORTANT -- don't modify Status between here and the next if
|
|
|
|
//
|
|
// we don't want to make a copy of the file handle table so we don't
|
|
// have to lock it during the call to the server because the file handles
|
|
// could go away while we're trying to dup them.
|
|
//
|
|
|
|
AcquireFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
a->FileSystemParameters.ParentHandleTable = HandleTable;
|
|
a->FileSystemParameters.ParentTableLength = HandleTableLength;
|
|
a->FileSystemParameters.CurrentDrive = Od2CurrentDisk;
|
|
|
|
//
|
|
// Create the Process, and wait to os2srv to get the results
|
|
// back to us by calling DosExecPgm
|
|
//
|
|
|
|
// Status comes from the call to Od2IsFileConsoleType (returned by Od2FormatExecPgmMessage)
|
|
|
|
if ((Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
|
|
(Status == STATUS_INVALID_IMAGE_FORMAT)) {
|
|
//
|
|
// 16 bit OS/2 program - Ow2execpgm creates it,
|
|
// Then we call os2srv to complete the job
|
|
//
|
|
rc = Ow2ExecPgm(
|
|
Flags,
|
|
ArgumentsBuffer,
|
|
VariablesBuffer,
|
|
ExecFileName,
|
|
#if PMNT
|
|
IsPMApp,
|
|
#endif // PMNT
|
|
NULL,
|
|
NULL,
|
|
&hProcess,
|
|
&hThread,
|
|
&dwProcessId
|
|
);
|
|
|
|
RtlFreeHeap( Od2Heap, 0, ArgumentsBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, VariablesBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ExecFileName );
|
|
|
|
if (rc != NO_ERROR){
|
|
#if DBG
|
|
KdPrint(("DosExecPgm: error returned from Ow2ExecPgm %d\n", rc));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(a->hRedirectedFile);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
return(rc);
|
|
}
|
|
|
|
//
|
|
// duplicate process and thread handles for os2srv
|
|
//
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
hProcess,
|
|
hOs2Srv,
|
|
&(a->hProcess),
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)){
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail to duplicate process %d\n",GetLastError()));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
NtClose(a->hRedirectedFile);
|
|
return(ERROR_ACCESS_DENIED);
|
|
}
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
hThread,
|
|
hOs2Srv,
|
|
&(a->hThread),
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)){
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail to duplicate Thread %d\n",GetLastError()));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
Od2CloseSrvHandle(1, a->hProcess, NULL, NULL);
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
NtClose(a->hRedirectedFile);
|
|
return(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
Status = NtQueryInformationThread(hThread,
|
|
ThreadBasicInformation,
|
|
(PVOID)(&ThreadInfo),
|
|
sizeof(ThreadInfo),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail to Query Information %lx\n",Status));
|
|
#endif
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
Od2CloseSrvHandle(2, a->hProcess, a->hThread, NULL);
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
NtClose(a->hRedirectedFile);
|
|
return(Or2MapNtStatusToOs2Error(
|
|
Status,ERROR_ACCESS_DENIED));
|
|
}
|
|
|
|
a->ClientId = ThreadInfo.ClientId;
|
|
|
|
if (a->hRedirectedFile)
|
|
{
|
|
HANDLE hRedirectedFile = a->hRedirectedFile;
|
|
|
|
if(!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
hRedirectedFile,
|
|
hProcess,
|
|
&a->hRedirectedFile,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
)){
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail to duplicate Redirecd File %d\n",GetLastError()));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
Od2CloseSrvHandle(2, a->hProcess, a->hThread, NULL);
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
NtClose(hRedirectedFile);
|
|
return(ERROR_ACCESS_DENIED);
|
|
}
|
|
NtClose(hRedirectedFile);
|
|
}
|
|
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
|
|
a->Flags = Flags;
|
|
Od2CallSubsystem( &m, CaptureBuffer, Os2ExecPgm, sizeof( *a ) );
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
|
|
if (m.ReturnedErrorValue == NO_ERROR){
|
|
*ResultCodes = a->ResultCodes;
|
|
}
|
|
|
|
if (a->ErrorText.Length != 0) {
|
|
if ((LONG)(a->ErrorText.Length) < MaximumErrorTextLength) {
|
|
MaximumErrorTextLength = a->ErrorText.Length;
|
|
}
|
|
else {
|
|
MaximumErrorTextLength -= 1;
|
|
}
|
|
|
|
RtlMoveMemory( ErrorText, a->ErrorText.Buffer, MaximumErrorTextLength );
|
|
ErrorText[ MaximumErrorTextLength ] = '\0';
|
|
}
|
|
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
|
|
return( m.ReturnedErrorValue );
|
|
}
|
|
else {
|
|
//
|
|
// Executable is NOT an os/2 16 bit program
|
|
//
|
|
if ((Status != STATUS_OBJECT_NAME_NOT_FOUND) &&
|
|
(Status != STATUS_OBJECT_PATH_NOT_FOUND)) {
|
|
|
|
#if DBG
|
|
KdPrint(( "OS2: Loading a Win 32-bit program - %s\n", ExecFileName));
|
|
#endif
|
|
switch( Flags ){
|
|
case EXEC_ASYNCRESULT:
|
|
// break;
|
|
|
|
case EXEC_BACKGROUND:
|
|
// break;
|
|
|
|
case EXEC_ASYNC:
|
|
// break;
|
|
|
|
case EXEC_SYNC:
|
|
|
|
//
|
|
// Let the win32 program have input
|
|
//
|
|
if (Flags == EXEC_SYNC){
|
|
#if PMNT
|
|
if (! ProcessIsPMProcess()) {
|
|
#endif
|
|
rc = Od2RemoveConsoleThread();
|
|
if (rc != NO_ERROR){
|
|
#if DBG
|
|
KdPrint(("DosExecPgm: error returned from Od2RemoveConsoleThread %d\n", rc));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(a->hRedirectedFile);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ArgumentsBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, VariablesBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ExecFileName );
|
|
return(rc);
|
|
}
|
|
#if PMNT
|
|
} // endif !ProcessIsPMProcess()
|
|
#endif
|
|
} else if (Flags == EXEC_ASYNCRESULT){
|
|
|
|
// add the info on the async Win32 child process
|
|
|
|
rc = Od2AddWin32ChildProcess();
|
|
if (rc != NO_ERROR){
|
|
#if DBG
|
|
KdPrint(("DosExecPgm: error returned from Od2AddWin32ChildProcess %d\n", rc));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(a->hRedirectedFile);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ArgumentsBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, VariablesBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ExecFileName );
|
|
return(rc);
|
|
}
|
|
}
|
|
|
|
if (a->hRedirectedFile) {
|
|
Od2PrepareStdHandleRedirection(STDFLAG_IN | STDFLAG_ERR, &StdStruc);
|
|
StdStruc.StdOut = a->hRedirectedFile;
|
|
StdStruc.Flags |= STDFLAG_OUT | STDFLAG_CLOSEOUT;
|
|
} else if (a->CmdLineFlag & REDIR_NUL) {
|
|
Od2PrepareStdHandleRedirection(STDFLAG_IN | STDFLAG_ERR, &StdStruc);
|
|
if ((StdStruc.StdOut = Ow2GetNulDeviceHandle()) != NULL) {
|
|
StdStruc.Flags |= STDFLAG_OUT;
|
|
}
|
|
} else {
|
|
Od2PrepareStdHandleRedirection(STDFLAG_ALL, &StdStruc);
|
|
}
|
|
|
|
rc = Ow2ExecPgm(
|
|
Flags | EXEC_WINDOW_PROGRAM, // This bit set the CREATE_NEW_PROCESS_GROUP and does redirection
|
|
ArgumentsBuffer,
|
|
VariablesBuffer,
|
|
ExecFileName,
|
|
#if PMNT
|
|
0, // Not PM app
|
|
#endif // PMNT
|
|
NULL,
|
|
&StdStruc,
|
|
&hProcess,
|
|
&hThread,
|
|
&dwProcessId
|
|
);
|
|
|
|
//
|
|
// clean up redir stuff (will close a->hRedirectedFile if needed)
|
|
//
|
|
|
|
if (StdStruc.Flags & STDFLAG_CLOSEALL) {
|
|
Od2CleanupStdHandleRedirection(&StdStruc);
|
|
}
|
|
|
|
RtlFreeHeap( Od2Heap, 0, ArgumentsBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, VariablesBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ExecFileName );
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
|
|
if (rc != NO_ERROR){
|
|
#if DBG
|
|
KdPrint(("DosExecPgm: error returned from Ow2ExecPgm %d\n", rc));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return(rc);
|
|
}
|
|
if (hThread)
|
|
{
|
|
if (ResumeThread( hThread) == (ULONG)-1)
|
|
{
|
|
rc = GetLastError();
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail to Resume New Thread %l\n", rc));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
return(rc);
|
|
}
|
|
}
|
|
|
|
if (Flags != EXEC_SYNC){
|
|
//
|
|
// We need to return the PID to the caller at this point
|
|
//
|
|
ResultCodes->ExitReason = (ULONG)hProcess;
|
|
|
|
if (Flags == EXEC_ASYNCRESULT){
|
|
//
|
|
// remember the hprocess for DosWaitChild
|
|
//
|
|
for (i = 0;i<NUMOF32ASYNCPROC;i++){
|
|
if (Od2AsyncProc[i].hProcess == 0){
|
|
Od2AsyncProc[i].hProcess = hProcess;
|
|
Od2AsyncProc[i].dwProcessId = dwProcessId;
|
|
Od2AsyncProc[i].CreateSync = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (i == NUMOF32ASYNCPROC ){
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: Too many Async win32 processes\n"));
|
|
ASSERT(FALSE);
|
|
#endif
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else rc = NO_ERROR;
|
|
}
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(hThread);
|
|
return(rc);
|
|
}
|
|
|
|
for (i = 0;i<NUMOF32ASYNCPROC;i++)
|
|
{
|
|
if (Od2AsyncProc[i].hProcess == 0)
|
|
{
|
|
Od2AsyncProc[i].hProcess = hProcess;
|
|
Od2AsyncProc[i].dwProcessId = dwProcessId;
|
|
Od2AsyncProc[i].CreateSync = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (i == NUMOF32ASYNCPROC ){
|
|
#if DBG
|
|
ASSERT(FALSE);
|
|
KdPrint(( "DosExecPgm(Sync): Too many Async win32 processes\n"));
|
|
#endif
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
//
|
|
// Wait for Completion and set Os/2 app parameters
|
|
//
|
|
do {
|
|
Status = NtWaitForSingleObject (
|
|
hProcess,
|
|
TRUE, // Alertable
|
|
NULL // Forever
|
|
);
|
|
#if DBG
|
|
if (Status == STATUS_USER_APC) {
|
|
DbgPrint("[%d,%d] WARNING !!! DosExecPgm wait was broken by APC\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId()
|
|
);
|
|
}
|
|
#endif
|
|
} while (Status == STATUS_USER_APC);
|
|
Od2AsyncProc[i].hProcess = 0;
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail at WaitForSingleObject %lx\n",Status));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
return(Or2MapNtStatusToOs2Error(
|
|
Status,ERROR_ACCESS_DENIED));
|
|
}
|
|
GetExitCodeProcess(hProcess, &ExitCode);
|
|
Ow2WinExitCode2ResultCode(
|
|
ExitCode,
|
|
&ResultCodes->ExitResult,
|
|
&ResultCodes->ExitReason);
|
|
|
|
//
|
|
// Free the handles for the exec'ed process
|
|
//
|
|
NtClose(hProcess);
|
|
NtClose(hThread);
|
|
|
|
#if DBG
|
|
if (ResultCodes->ExitResult != NO_ERROR){
|
|
KdPrint(("DosExecPgm: win32 process ExitCode %d => Result %d, Reason %d\n",
|
|
ExitCode, ResultCodes->ExitResult, ResultCodes->ExitReason));
|
|
}
|
|
#endif
|
|
#if PMNT
|
|
if (! ProcessIsPMProcess()) {
|
|
#endif
|
|
//
|
|
// Restore session console mode
|
|
// A handle parameter is not needed
|
|
//
|
|
Status = Od2RestartConsoleThread();
|
|
if (!NT_SUCCESS(Status)){
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
#if DBG
|
|
KdPrint(( "DosExecPgm: fail to RestartConsoleThread %lx\n",Status));
|
|
#endif
|
|
return(Or2MapNtStatusToOs2Error(Status,ERROR_ACCESS_DENIED));
|
|
}
|
|
#if PMNT
|
|
}
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
|
|
return(NO_ERROR);
|
|
break;
|
|
|
|
case EXEC_TRACETREE:
|
|
case EXEC_TRACE:
|
|
case EXEC_FROZEN:
|
|
default:
|
|
|
|
#if DBG
|
|
ASSERT(FALSE);
|
|
KdPrint(("DosExecPgm with %s (%d) not supported\n",
|
|
(Flags == EXEC_TRACETREE) ? "EXEC_TRACETREE" :
|
|
(Flags == EXEC_TRACE) ? "EXEC_TRACE" :
|
|
(Flags == EXEC_FROZEN) ? "EXEC_FROZEN" :
|
|
"Unknown flag", Flags ));
|
|
#endif
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(a->hRedirectedFile);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ArgumentsBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, VariablesBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ExecFileName );
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
}
|
|
else {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
NtClose(a->hRedirectedFile);
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ArgumentsBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, VariablesBuffer );
|
|
RtlFreeHeap( Od2Heap, 0, ExecFileName );
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
#if DBG
|
|
KdPrint(( "OS2: EXE file not found - %s\n", ExecFileName));
|
|
#endif
|
|
return(ERROR_FILE_NOT_FOUND);
|
|
} else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
|
|
#if DBG
|
|
KdPrint(( "OS2: Path to EXE file not found - %s\n", ExecFileName));
|
|
#endif
|
|
return(ERROR_PATH_NOT_FOUND);
|
|
} else {
|
|
#if DBG
|
|
KdPrint(( "OS2SRV: Program can not be executed %s\n",
|
|
ExecFileName));
|
|
#endif
|
|
return(Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_PARAMETER));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Od2DisableNewThread(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = Od2AlertableWaitForSingleObject(Od2NewThreadSync);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("ExitList: Wait for NewThreadSync, Status=%x\n",
|
|
Status);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
Od2NewThreadDisabled = TRUE;
|
|
Status = NtReleaseMutant(
|
|
Od2NewThreadSync,
|
|
NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("ExitList: Wait for NewThreadSync, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif //DBG
|
|
}
|
|
|
|
//
|
|
// Find address of TEB backup for thread1. We use it to restore TEB for
|
|
// exit list dispatcher.
|
|
//
|
|
|
|
PVOID
|
|
Od2GetThread1TebBackup(VOID)
|
|
{
|
|
POD2_THREAD Thread;
|
|
|
|
Thread = Od2FindThreadById(1L);
|
|
if (Thread == NULL) {
|
|
#if DBG
|
|
DbgPrint("Od2GetThread1Teb: Can't find thread1 in client\n");
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return NULL;
|
|
}
|
|
return (PVOID) &(Thread->Os2Tib.TebBackupIn16Bit);
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2ExitListDispatcher( VOID )
|
|
{
|
|
ULONG i;
|
|
LARGE_INTEGER timeout;
|
|
NTSTATUS Status;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( CLEANUP ) {
|
|
KdPrint(("ExitListDispatcher\n"));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// See if we need to restore the real TEB (thread1 was caught in 16 bit
|
|
// when Exit has to happen). This check is part of RestoreTebForThread1 (fs:36).
|
|
// We can't use regulare RestoreTeb function, because it uses pointer to
|
|
// TEB backup that might be invalid already. We use that we know that we are
|
|
// in thread1, so we know the proper address of TEB backup.
|
|
//
|
|
|
|
RestoreTebForThread1();
|
|
|
|
((POD2_THREAD)(NtCurrentTeb()->EnvironmentPointer))->Os2Tib.LInfoSeg.rfProcStatus |= PS_EXITLIST;
|
|
|
|
if (!(Od2Process->Pib.Status & PS_EXITLIST)) {
|
|
Od2Process->Pib.Status |= PS_EXITLIST;
|
|
}
|
|
else {
|
|
//
|
|
// An exception happened while processing exitlist, cleanup
|
|
// and die
|
|
//
|
|
Od2FinalProcessCleanup();
|
|
Ow2Exit(306, NULL, 1); // 306 is IDS_INTERNAL_OS2_ERROR
|
|
}
|
|
|
|
//
|
|
// free locks so 16bit exit routines can
|
|
// call APIs that acquire the locks, in case this thread
|
|
// was holding the lock
|
|
//
|
|
|
|
// This var was used to tell sign that there is exit list processing
|
|
Od2SigHandlingInProgress = TRUE;
|
|
// This is the new var that really means that there is signal handle
|
|
// processing. DosSuspendThread and DosEnterCritSect will be actually
|
|
// disabled during exitlist processing.
|
|
Od2SigHandAlreadyInProgress = TRUE;
|
|
Od2Process->Pib.SigHandInProgress = TRUE;
|
|
|
|
//
|
|
// In we got a signal while having the garbage collection semaphore, free it
|
|
// so exitlist routines can call sem apis
|
|
//
|
|
(VOID)NtReleaseSemaphore (
|
|
Od2GarbageCollectSem,
|
|
1,
|
|
NULL);
|
|
|
|
// Call to NtTestAlert to avoid ALERT on any wait that follows
|
|
|
|
Status = NtTestAlert();
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d] Od2ExitListDispatcher: NtTestAlert() = %x\n",
|
|
Od2Process->Pib.ProcessId,
|
|
Od2CurrentThreadId(),
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
|
|
Od2DisableNewThread();
|
|
|
|
if (SesGrp->InTermination & 2)
|
|
{
|
|
for ( i = 0 ; i < NUMOF32ASYNCPROC ; i++ )
|
|
{
|
|
if (Od2AsyncProc[i].hProcess != 0)
|
|
{
|
|
GenerateConsoleCtrlEvent(
|
|
CTRL_BREAK_EVENT,
|
|
Od2AsyncProc[i].dwProcessId
|
|
);
|
|
|
|
NtTerminateProcess( Od2AsyncProc[i].hProcess, STATUS_SUCCESS );
|
|
timeout.LowPart = 0xf4240; // wait max 0.1 second for process to die
|
|
timeout.HighPart = 0;
|
|
NtWaitForSingleObject( Od2AsyncProc[i].hProcess, (BOOLEAN)TRUE, &timeout );
|
|
NtClose(Od2AsyncProc[i].hProcess);
|
|
Od2AsyncProc[i].hProcess = 0;
|
|
|
|
// BUGBUG: do we need to call Od2RemoveWin32ChildProcess for each one ??
|
|
}
|
|
}
|
|
}
|
|
|
|
DosExitList( EXLST_EXIT, NULL );
|
|
}
|
|
|
|
VOID
|
|
Od2FinalProcessCleanup( VOID )
|
|
{
|
|
if (timing)
|
|
{
|
|
printf("Os2 time at start of Od2FinalProcessCleanup is %d\n", (GetTickCount()) - timing);
|
|
}
|
|
|
|
//
|
|
// Stop all timer threads
|
|
//
|
|
Od2CloseAllTimers();
|
|
|
|
//
|
|
// Stop the Netbios component
|
|
//
|
|
|
|
Od2NetbiosDelete();
|
|
|
|
//
|
|
// Stop the Disk IoCtl component.
|
|
//
|
|
|
|
Od2DiskIOTerminate();
|
|
|
|
|
|
//
|
|
// Okay to release this as we must be the only client thread left
|
|
//
|
|
|
|
ReleaseTaskLock();
|
|
|
|
//
|
|
// Cleanup 16 bit RAM semaphores in shared memory
|
|
//
|
|
|
|
Od2CloseAllRAMSharedSemaphores();
|
|
|
|
//
|
|
// Close any open semaphpre handles
|
|
//
|
|
|
|
Od2CloseAllSemaphores();
|
|
|
|
//
|
|
// Close any open queue handles
|
|
//
|
|
|
|
Od2CloseAllQueues();
|
|
|
|
|
|
//
|
|
// Delete all the critical sections.
|
|
//
|
|
|
|
NtClose( Od2Process->TaskLock );
|
|
RtlDeleteResource( &Od2Process->FileLock );
|
|
RtlDestroyHeap( Od2Heap );
|
|
RtlDestroyHeap( Od2PortHeap );
|
|
|
|
if (timing)
|
|
{
|
|
printf("Os2 time at end of Od2FinalProcessCleanup is %d\n", (GetTickCount()) - timing);
|
|
}
|
|
}
|
|
|
|
|
|
APIRET
|
|
Od2ExitList(
|
|
ULONG OrderCode,
|
|
PFNEXITLIST ExitRoutine,
|
|
BOOLEAN flag /* TRUE for 32-bit exit list routine */
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POS2_EXITLISTENTRY ExitListEntry;
|
|
POS2_EXITLISTENTRY NewExitListEntry;
|
|
APIRET rc;
|
|
ULONG Order;
|
|
ULONG Action;
|
|
OS2_API_MSG m;
|
|
POS2_TERMINATEPROCESS_MSG a = &m.u.TerminateProcess;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( CLEANUP ) {
|
|
KdPrint(("Od2ExitList\n"));
|
|
}
|
|
#endif
|
|
//
|
|
// Format of the OrderCode is as follows:
|
|
//
|
|
// 33222222222211111111110000000000
|
|
// 10987654321098765432109876543210
|
|
//
|
|
// RRRRRRRRRRRRRRRRoooooooopRRRRRaa
|
|
//
|
|
// R = Reserver bit, must be zero
|
|
//
|
|
// aa = Action
|
|
// 0 - illegal
|
|
// 1 - add address to termination list
|
|
// 2 - remove address from termination list
|
|
// 3 - transfer to next address on termination list
|
|
//
|
|
// p = Position
|
|
// 0 - first // In native OS2 it is strongly the opposite.
|
|
// 1 - last // So :
|
|
// // 0 - last
|
|
// // 1 - first
|
|
// // [yosefd] 12/27/93
|
|
//
|
|
// oooooooo - Order within position
|
|
//
|
|
|
|
//
|
|
// Error if any of the reserved bits are set
|
|
//
|
|
|
|
//BUGBUG - Why disallow p bit set ??? if (OrderCode & 0xFFFF00FC)
|
|
if (OrderCode & 0xFFFF007C)
|
|
{
|
|
return( ERROR_INVALID_DATA );
|
|
}
|
|
|
|
//
|
|
// Isolate Action and Order components. Translate Position bit by
|
|
// adding x0100 to the 8 bit order code.
|
|
//
|
|
|
|
Action = OrderCode & 0x007F;
|
|
Order = (OrderCode & 0xFF00) >> 8;
|
|
if (Action == EXLST_ADD) {
|
|
if (!(OrderCode & 0x0080))
|
|
Order += 0x0100;
|
|
}
|
|
//
|
|
// Error if Order and/or Position specified and not adding an exit list
|
|
// routine.
|
|
//
|
|
else if (Order != 0) return( ERROR_INVALID_DATA );
|
|
|
|
//
|
|
// Now lock the process while we grovel the list of installed exit list
|
|
// handlers.
|
|
//
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( CLEANUP ) {
|
|
KdPrint(("Od2ExitList before task lock\n"));
|
|
}
|
|
#endif
|
|
AcquireTaskLock();
|
|
#if DBG
|
|
IF_OD2_DEBUG( CLEANUP ) {
|
|
KdPrint(("Od2ExitList after task lock\n"));
|
|
}
|
|
#endif
|
|
|
|
ListHead = &Od2Process->ExitList;
|
|
ListNext = ListHead->Flink;
|
|
rc = NO_ERROR;
|
|
switch( Action ) {
|
|
case EXLST_ADD:
|
|
//
|
|
// Adding an exit list routine. Search the list of installed
|
|
// handlers to find out where to insert the new one. Return
|
|
// an error if the handler is already installed.
|
|
//
|
|
|
|
while (ListNext != ListHead) {
|
|
ExitListEntry = CONTAINING_RECORD( ListNext, OD2_EXITLISTENTRY, Link );
|
|
if (Order <= ExitListEntry->Order) {
|
|
if (ExitListEntry->Order == Order &&
|
|
ExitListEntry->ExitRoutine == ExitRoutine
|
|
|
|
) {
|
|
rc = ERROR_ALREADY_EXISTS;
|
|
break;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
//
|
|
// If not a duplicate entry, then allocate the memory for a new
|
|
// entry and link it into the list.
|
|
//
|
|
|
|
if (rc == NO_ERROR) {
|
|
NewExitListEntry = RtlAllocateHeap( Od2Heap, 0,
|
|
sizeof( OD2_EXITLISTENTRY )
|
|
);
|
|
if (NewExitListEntry == NULL) {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else {
|
|
NewExitListEntry->Order = Order;
|
|
NewExitListEntry->ExitRoutine = ExitRoutine;
|
|
NewExitListEntry->flag = flag;
|
|
InsertTailList( ListNext, &NewExitListEntry->Link );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case EXLST_REMOVE:
|
|
//
|
|
// Deleting an exit list routine. Search the list for the
|
|
// routine. If found remove it from the list. Otherwise
|
|
// return an error.
|
|
//
|
|
|
|
while (ListNext != ListHead) {
|
|
ExitListEntry = CONTAINING_RECORD( ListNext, OD2_EXITLISTENTRY, Link );
|
|
if (ExitListEntry->ExitRoutine == ExitRoutine) {
|
|
RemoveEntryList( &ExitListEntry->Link );
|
|
ListNext = NULL;
|
|
break;
|
|
}
|
|
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (ListNext != NULL) {
|
|
rc = ERROR_PROC_NOT_FOUND;
|
|
}
|
|
break;
|
|
|
|
case EXLST_EXIT:
|
|
//
|
|
// Calling the next entry in the exit list. Remove the head of
|
|
// the list, free the memory for the entry and then modify our
|
|
// return context so we return to the next exitlist routine
|
|
// Error if this function is called and the current process is
|
|
// NOT exiting.
|
|
//
|
|
|
|
if (Od2Process->Pib.Status & PS_EXITLIST) {
|
|
#if PMNT
|
|
// Can the exit list routines in the case of PMSHELL. They hang
|
|
// and they probably aren't meant to be executed anyway under
|
|
// OS/2 since PMSHELL is not supposed to actually exit.
|
|
if ((ListNext == ListHead) ||
|
|
ProcessIsPMShell()) {
|
|
#else
|
|
if (ListNext == ListHead) {
|
|
#endif // not PMNT
|
|
|
|
Really_lets_exit:
|
|
a->ExitResult = Od2Process->ResultCodes.ExitResult;
|
|
a->ExitReason = Od2Process->ResultCodes.ExitReason;
|
|
|
|
//
|
|
// All done, okay to exit. Need to do final process
|
|
// cleanup and call the server to have it nuke us.
|
|
//
|
|
Od2FinalProcessCleanup();
|
|
|
|
if (Od2ExecPgmErrorTextLength != 0) {
|
|
strncpy((PSZ) &a->ErrorText,
|
|
Od2ExecPgmErrorText,
|
|
Od2ExecPgmErrorTextLength);
|
|
}
|
|
a->ErrorText[Od2ExecPgmErrorTextLength] = '\0';
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( CLEANUP ) {
|
|
KdPrint(("Od2ExitList, calling InternalTerminateProcess\n"));
|
|
}
|
|
#endif
|
|
// free process and thread
|
|
Od2CallSubsystem( &m,
|
|
NULL,
|
|
Oi2TerminateProcess,
|
|
sizeof( *a ) );
|
|
|
|
Od2InfiniteSleep();
|
|
}
|
|
else {
|
|
ListNext = RemoveHeadList( &Od2Process->ExitList );
|
|
ExitListEntry = CONTAINING_RECORD( ListNext, OD2_EXITLISTENTRY, Link );
|
|
ReleaseTaskLock();
|
|
#if DBG
|
|
IF_OD2_DEBUG( CLEANUP ) {
|
|
KdPrint(("Od2ExitList, Going to jump to 16 bit exit list\n"));
|
|
}
|
|
#endif
|
|
if (Od2Process->Pib.Killed) {
|
|
Od2Process->ResultCodes.ExitReason = TC_KILLPROCESS;
|
|
}
|
|
|
|
if (ExitListEntry->flag) {
|
|
Od2JumpToExitRoutine( ExitListEntry->ExitRoutine,
|
|
Od2Process->ResultCodes.ExitReason
|
|
);
|
|
}
|
|
else {
|
|
if (Od2Start16Stack && Od2Start16DS) {
|
|
Od2JumpTo16ExitRoutine(ExitListEntry->ExitRoutine,
|
|
Od2Process->ResultCodes.ExitReason
|
|
);
|
|
}
|
|
else {
|
|
//
|
|
// Loader failed, but C startup registered
|
|
// an ExitList rouine. Need to realy quit
|
|
//
|
|
goto Really_lets_exit;
|
|
ASSERT(FALSE); // Should never get here
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
rc = ERROR_INVALID_FUNCTION;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rc = ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Unlock the process and return the return code. Note that the code
|
|
// for the EXTLST_EXIT case will have modified the return address
|
|
// to point to the next exit list handler or to a stub that will
|
|
// really terminate the process.
|
|
//
|
|
|
|
ReleaseTaskLock();
|
|
return( rc );
|
|
}
|
|
|
|
APIRET
|
|
DosExitList(
|
|
ULONG OrderCode,
|
|
PFNEXITLIST ExitRoutine
|
|
)
|
|
{
|
|
return( Od2ExitList( OrderCode, ExitRoutine, (BOOLEAN)TRUE ) );
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosKillProcess(
|
|
IN ULONG KillTarget,
|
|
IN PID ProcessId
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSKILLPROCESS_MSG a = &m.u.DosKillProcess;
|
|
ULONG i;
|
|
HANDLE hProcess = (HANDLE)ProcessId;
|
|
LARGE_INTEGER timeout;
|
|
PLARGE_INTEGER ptimeout = &timeout;
|
|
|
|
if (KillTarget > DKP_PROCESS) {
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if (ProcessId == 0) {
|
|
return( ERROR_INVALID_PROCID );
|
|
}
|
|
//
|
|
// see if this is a win32 process
|
|
//
|
|
|
|
for (i = 0;i<NUMOF32ASYNCPROC;i++){
|
|
//if ((Od2AsyncProc[i].hProcess == hProcess) && !Od2AsyncProc[i].CreateSync){
|
|
if (Od2AsyncProc[i].hProcess == hProcess){
|
|
Od2AsyncProc[i].hProcess = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < NUMOF32ASYNCPROC){
|
|
//
|
|
// it is a win32 process -
|
|
// don't involve os2srv , just kill it
|
|
//
|
|
|
|
if ( KillTarget == DKP_PROCESSTREE )
|
|
{
|
|
// kill process tree - try to do it by issue GenerateConsoleCtrlEvent
|
|
|
|
GenerateConsoleCtrlEvent(
|
|
CTRL_BREAK_EVENT,
|
|
Od2AsyncProc[i].dwProcessId
|
|
);
|
|
}
|
|
|
|
NtTerminateProcess( hProcess, STATUS_SUCCESS );
|
|
timeout.LowPart = 0x989680; // wait max one second for process to die
|
|
timeout.HighPart = 0;
|
|
NtWaitForSingleObject( hProcess, (BOOLEAN)TRUE, ptimeout );
|
|
NtClose(hProcess);
|
|
Od2RemoveWin32ChildProcess();
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
a->KillTarget = KillTarget;
|
|
a->ProcessId = ProcessId;
|
|
|
|
return( Od2CallSubsystem( &m, NULL, Os2KillProcess, sizeof( *a ) ) );
|
|
}
|
|
|
|
|
|
|
|
APIRET
|
|
DosSetPriority(
|
|
IN ULONG Scope,
|
|
IN ULONG Class,
|
|
IN LONG Delta,
|
|
IN ULONG TargetId
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSSETPRIORITY_MSG a = &m.u.DosSetPriority;
|
|
|
|
if (Scope > PRTYS_THREAD) {
|
|
return( ERROR_INVALID_SCOPE );
|
|
}
|
|
|
|
if (Class > PRTYC_FOREGROUNDSERVER) {
|
|
return( ERROR_INVALID_PCLASS );
|
|
}
|
|
|
|
if (Delta < PRTYD_MINIMUM || Delta > PRTYD_MAXIMUM) {
|
|
return( ERROR_INVALID_PDELTA );
|
|
}
|
|
|
|
a->Scope = Scope;
|
|
a->Class = Class;
|
|
a->Delta = Delta;
|
|
a->TargetId = TargetId;
|
|
|
|
return( Od2CallSubsystem( &m, NULL, Os2SetPriority, sizeof( *a ) ) );
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosGetPriority(
|
|
IN ULONG Scope,
|
|
OUT PUSHORT Priority,
|
|
IN ULONG TargetId
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSGETPRIORITY_MSG a = &m.u.DosGetPriority;
|
|
APIRET rc;
|
|
|
|
if (Scope > PRTYS_THREAD) {
|
|
return( ERROR_INVALID_SCOPE );
|
|
}
|
|
|
|
try {
|
|
*Priority = 0;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
a->Scope = Scope;
|
|
a->TargetId = TargetId;
|
|
|
|
rc = Od2CallSubsystem( &m, NULL, Os2GetPriority, sizeof( *a ) );
|
|
|
|
if (rc == NO_ERROR) {
|
|
*Priority = (USHORT) a->Priority;
|
|
}
|
|
return(rc);
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosScanEnv(
|
|
IN PSZ VariableName,
|
|
OUT PSZ *VariableValue
|
|
)
|
|
{
|
|
PCH s;
|
|
STRING SearchName;
|
|
STRING CurrentName;
|
|
|
|
//
|
|
// Get the address of the OS/2 Environment block from the OS/2
|
|
//
|
|
s = Od2Environment;
|
|
//
|
|
// Construct a counted string that describes the environment variable
|
|
// name we are looking for.
|
|
//
|
|
|
|
try {
|
|
RtlInitString( &SearchName, VariableName );
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
//
|
|
// Loop over all of the null terminated strings in the environment
|
|
// block. A null character marks the end of the environment strings.
|
|
//
|
|
while (*s) {
|
|
//
|
|
// For each null terminated environment string, construct a counted
|
|
// string that describes the variable name portion (the part before
|
|
// the equal sign).
|
|
//
|
|
|
|
CurrentName.Buffer = s;
|
|
while (*s) {
|
|
if (*s == '=') {
|
|
CurrentName.Length = (USHORT) (s - CurrentName.Buffer);
|
|
CurrentName.MaximumLength = CurrentName.Length;
|
|
|
|
//
|
|
// Compare this variable name with what we are looking for,
|
|
// sensitive to case. If found, then return a pointer to
|
|
// value portion of the string which is the first character
|
|
// after the equal sign.
|
|
//
|
|
|
|
if (RtlEqualString( &CurrentName, &SearchName, (BOOLEAN)FALSE )) {
|
|
try {
|
|
*VariableValue = s + 1;
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
return( NO_ERROR );
|
|
}
|
|
|
|
//
|
|
// Move to the null character that terminates the variable
|
|
// value string. Then break out to the outer loop to find
|
|
// the next variable name.
|
|
//
|
|
|
|
while (*s) {
|
|
s++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else {
|
|
s++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip over the terminating null character for this string.
|
|
//
|
|
|
|
s++;
|
|
}
|
|
|
|
//
|
|
// If we get here, then we did not find the variable name we were looking
|
|
// for. Return the appropriate error code.
|
|
//
|
|
|
|
return( ERROR_ENVVAR_NOT_FOUND );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CheckIfTerminalNetError(
|
|
IN APIRET ErrorCode,
|
|
IN ULONG SearchFlags
|
|
)
|
|
{
|
|
if (ErrorCode == ERROR_FILE_NOT_FOUND ||
|
|
ErrorCode == ERROR_PATH_NOT_FOUND ||
|
|
ErrorCode == ERROR_NO_MORE_FILES ||
|
|
ErrorCode == ERROR_FILENAME_EXCED_RANGE
|
|
) {
|
|
return( FALSE );
|
|
}
|
|
|
|
if (!(SearchFlags & SEARCH_IGNORENETERRS)) {
|
|
return( TRUE );
|
|
}
|
|
|
|
if (ErrorCode == ERROR_INVALID_ACCESS ||
|
|
#if 0
|
|
ErrorCode == ERROR_NETWORK_BUSY ||
|
|
ErrorCode == ERROR_TOO_MANY_CMDS ||
|
|
ErrorCode == ERROR_ADAP_HDW_ERR ||
|
|
ErrorCode == ERROR_BAD_NET_RESP ||
|
|
ErrorCode == ERROR_UNEXP_NET_ERR ||
|
|
ErrorCode == ERROR_BAD_REM_ADAP ||
|
|
ErrorCode == ERROR_NETNAME_DELETED ||
|
|
ErrorCode == ERROR_BAD_DEV_TYPE ||
|
|
ErrorCode == ERROR_TOO_MANY_SESS ||
|
|
ErrorCode == ERROR_REQ_NOT_ACCEP ||
|
|
#endif
|
|
ErrorCode == ERROR_INVALID_PASSWORD ||
|
|
ErrorCode == ERROR_VC_DISCONNECTED
|
|
) {
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
APIRET
|
|
FindFile(
|
|
IN PSZ FileName
|
|
)
|
|
{
|
|
APIRET rc;
|
|
HDIR SearchHandle;
|
|
FILEFINDBUF3 FindBuffer;
|
|
ULONG Entries;
|
|
|
|
Entries = 1;
|
|
SearchHandle = (HDIR) HDIR_CREATE; // allocate a new handle
|
|
rc = DosFindFirst( FileName,
|
|
&SearchHandle,
|
|
ATTR_ALL,
|
|
&FindBuffer,
|
|
sizeof(FindBuffer),
|
|
&Entries,
|
|
FIL_STANDARD
|
|
);
|
|
if (rc == NO_ERROR) {
|
|
DosFindClose(SearchHandle);
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
APIRET
|
|
SearchForPath(
|
|
IN ULONG SearchFlags,
|
|
IN PSZ PathName,
|
|
OUT PBYTE Buffer,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
APIRET rc;
|
|
STRING PathBuffer;
|
|
ULONG PathType;
|
|
ULONG PathFlags;
|
|
PCH src;
|
|
ULONG len;
|
|
|
|
rc = FindFile( PathName );
|
|
if (rc != NO_ERROR) {
|
|
if (CheckIfTerminalNetError( rc, SearchFlags )) {
|
|
try {
|
|
*Buffer = '\0';
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
return( rc );
|
|
}
|
|
else {
|
|
rc = ERROR_SS_RETRY;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Canonicalize( filename)
|
|
//
|
|
|
|
rc = Od2Canonicalize( PathName,
|
|
CANONICALIZE_FILE_OR_DEV,
|
|
&PathBuffer,
|
|
NULL,
|
|
&PathFlags,
|
|
&PathType
|
|
);
|
|
if (rc == NO_ERROR) {
|
|
if (PathBuffer.Length >= (USHORT) Length) {
|
|
rc = ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
else {
|
|
src = PathBuffer.Buffer;
|
|
len = PathBuffer.Length;
|
|
if (!strnicmp( src, "\\OS2SS\\DRIVES\\", 14 )) {
|
|
src += 14;
|
|
len -= 14;
|
|
}
|
|
|
|
try {
|
|
RtlMoveMemory( Buffer, src, len+1 );
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap(Od2Heap, 0, PathBuffer.Buffer);
|
|
}
|
|
else
|
|
if (rc == ERROR_PATH_NOT_FOUND) {
|
|
rc = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
APIRET
|
|
DosSearchPath(
|
|
IN ULONG SearchFlags,
|
|
IN PSZ PathOrVariableName,
|
|
IN PSZ FileName,
|
|
OUT PBYTE Buffer,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
APIRET rc;
|
|
PSZ SearchPath;
|
|
PSZ PathName;
|
|
PSZ Path;
|
|
ULONG FileNameLen, PathLen;
|
|
|
|
//
|
|
// Validate the parameters
|
|
//
|
|
|
|
if (Length == 0) {
|
|
return( ERROR_BUFFER_OVERFLOW );
|
|
}
|
|
|
|
if (SearchFlags & ~(SEARCH_CUR_DIRECTORY |
|
|
SEARCH_ENVIRONMENT |
|
|
SEARCH_IGNORENETERRS
|
|
)
|
|
) {
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if (FileName == NULL) {
|
|
//
|
|
// Note that OS/2 does not check for this case until after the
|
|
// SEARCH_CUR_DIRECTORY logic, so OS/2 would GP fault if passed a null
|
|
// file name pointer and the SEARCH_CUR_DIRECTORY flag. Seems like a
|
|
// bug so we will check before we use the FileName pointer.
|
|
//
|
|
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
|
|
//
|
|
// Check the current directory first if requested.
|
|
// we pass Canonicalize the requested name.
|
|
//
|
|
|
|
if (SearchFlags & SEARCH_CUR_DIRECTORY) {
|
|
rc = SearchForPath( SearchFlags,
|
|
FileName,
|
|
Buffer,
|
|
Length
|
|
);
|
|
|
|
if (rc != ERROR_SS_RETRY) {
|
|
return( rc );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If pass search path is an environment variable name, then get its
|
|
// value from the OS/2 Environment Block.
|
|
//
|
|
|
|
if (SearchFlags & SEARCH_ENVIRONMENT) {
|
|
rc = DosScanEnv( PathOrVariableName, &SearchPath );
|
|
if (rc != NO_ERROR) {
|
|
try {
|
|
*Buffer = '\0';
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
return( rc );
|
|
}
|
|
}
|
|
else {
|
|
SearchPath = PathOrVariableName;
|
|
}
|
|
|
|
//
|
|
// probe filename and path. figure out maximum length of combined
|
|
// filename and path
|
|
//
|
|
|
|
try {
|
|
FileNameLen = strlen( FileName );
|
|
PathLen = strlen( SearchPath );
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
//
|
|
// + 2 is for terminating nul and possible slash
|
|
//
|
|
|
|
PathName = RtlAllocateHeap( Od2Heap, 0, PathLen + FileNameLen + 2 );
|
|
if (!PathName) {
|
|
#if DBG
|
|
KdPrint(("OS2: DosSearchPath out of heap memory, fail\n"));
|
|
#endif
|
|
ASSERT( FALSE );
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
|
|
//
|
|
// for each element in search path, append filename and call FindFile.
|
|
//
|
|
|
|
while (*SearchPath) {
|
|
|
|
#ifdef DBCS
|
|
// MSKK Apr.09.1993 V-AkihiS
|
|
//
|
|
// find end of path element
|
|
//
|
|
Path = SearchPath;
|
|
while (*SearchPath && *SearchPath != ';') {
|
|
if (Ow2NlsIsDBCSLeadByte(*SearchPath, SesGrp->DosCP)) SearchPath++;
|
|
if (*SearchPath) SearchPath++;
|
|
}
|
|
#else
|
|
// BUGBUG fix for DBCS
|
|
//
|
|
// find end of path element
|
|
//
|
|
|
|
for (Path = SearchPath;*SearchPath && *SearchPath != ';';SearchPath++) {
|
|
;
|
|
}
|
|
#endif
|
|
PathLen = SearchPath - Path;
|
|
if (PathLen != 0) {
|
|
RtlMoveMemory( PathName, Path, PathLen );
|
|
#ifdef DBCS
|
|
// MSKK Apr.09.1993 V-AkihiS
|
|
{
|
|
ULONG i = 0;
|
|
BOOLEAN SlashFlag = FALSE;
|
|
|
|
while (i < PathLen) {
|
|
if (Ow2NlsIsDBCSLeadByte(PathName[i], SesGrp->DosCP)) {
|
|
SlashFlag = FALSE;
|
|
i++;
|
|
if (i <PathLen) {
|
|
i++;
|
|
}
|
|
} else {
|
|
if (ISSLASH(PathName[i])) {
|
|
SlashFlag = TRUE;
|
|
} else {
|
|
SlashFlag = FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (!SlashFlag &&
|
|
!((PathLen == 2) && (PathName[PathLen-1] == ':'))
|
|
) {
|
|
PathName[ PathLen ] = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
|
|
//
|
|
// +1 is for terminating NUL
|
|
//
|
|
|
|
RtlMoveMemory( PathName+PathLen+1, FileName, FileNameLen+1 );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// +1 is for terminating NUL
|
|
//
|
|
|
|
RtlMoveMemory( PathName+PathLen, FileName, FileNameLen+1 );
|
|
}
|
|
}
|
|
#else
|
|
if (!ISSLASH( PathName[ PathLen-1 ] ) &&
|
|
!((PathLen == 2) && (PathName[ PathLen-1] == ':')) // Path is X:
|
|
) {
|
|
PathName[ PathLen ] = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
|
|
//
|
|
// +1 is for terminating NUL
|
|
//
|
|
|
|
RtlMoveMemory( PathName+PathLen+1, FileName, FileNameLen+1 );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// +1 is for terminating NUL
|
|
//
|
|
|
|
RtlMoveMemory( PathName+PathLen, FileName, FileNameLen+1 );
|
|
}
|
|
#endif
|
|
|
|
rc = SearchForPath( SearchFlags,
|
|
PathName,
|
|
Buffer,
|
|
Length
|
|
);
|
|
|
|
if (rc != ERROR_SS_RETRY) {
|
|
RtlFreeHeap( Od2Heap, 0, PathName );
|
|
return( rc );
|
|
}
|
|
}
|
|
|
|
if (*SearchPath) { // point past ;
|
|
SearchPath++;
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap( Od2Heap, 0, PathName );
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
|
|
VOID
|
|
Od2CloseSrvHandle(
|
|
IN ULONG HandleNumber,
|
|
IN HANDLE Handle1,
|
|
IN HANDLE Handle2,
|
|
IN HANDLE Handle3
|
|
)
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_DOSCLOSE_HANDLE_MSG a = &m.u.DosCloseHandle;
|
|
|
|
a->HandleNumber = HandleNumber;
|
|
a->HandleTable[0] = Handle1;
|
|
a->HandleTable[1] = Handle2;
|
|
a->HandleTable[2] = Handle3;
|
|
|
|
Od2CallSubsystem( &m, NULL, Os2CloseHandle, sizeof( *a ));
|
|
return;
|
|
}
|
|
|