mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4982 lines
148 KiB
4982 lines
148 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvtask.c
|
|
|
|
Abstract:
|
|
|
|
Tasking API
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 11-Oct-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_TASKING
|
|
#define INCL_OS2V20_ERRORS
|
|
#define INCL_OS2V20_EXCEPTIONS
|
|
#include "os2srv.h"
|
|
#include "os2tile.h"
|
|
#define NTOS2_ONLY
|
|
#include "sesport.h"
|
|
#include "os2win.h"
|
|
#include <stdio.h>
|
|
#if PMNT
|
|
#define INCL_32BIT
|
|
#include "pmnt.h"
|
|
extern PID PMNTPMShellPid;
|
|
#endif // PMNT
|
|
|
|
extern HANDLE Os2SyncSem;
|
|
|
|
#define TRC_C_ReadMem_I 1
|
|
#define TRC_C_ReadMem_D 2
|
|
#define TRC_C_ReadReg 3
|
|
#define TRC_C_WriteMem_I 4
|
|
#define TRC_C_WriteMem_D 5
|
|
#define TRC_C_WriteReg 6
|
|
#define TRC_C_Go 7
|
|
#define TRC_C_Term 8
|
|
#define TRC_C_SStep 9
|
|
#define TRC_C_Stop 10
|
|
#define TRC_C_Freeze 11
|
|
#define TRC_C_Resume 12
|
|
#define TRC_C_NumToSel 13
|
|
#define TRC_C_GetFPRegs 14
|
|
#define TRC_C_SetFPRegs 15
|
|
#define TRC_C_GetLibName 16
|
|
#define TRC_C_ThrdStat 17
|
|
#define TRC_C_SUC_ret 0
|
|
#define TRC_C_ERR_ret -1
|
|
#define TRC_C_SIG_ret -2
|
|
#define TRC_C_TBT_ret -3
|
|
#define TRC_C_BPT_ret -4
|
|
#define TRC_C_NMI_ret -5
|
|
#define TRC_C_KIL_ret -6
|
|
#define TRC_C_GPF_ret -7
|
|
#define TRC_C_LIB_ret -8
|
|
#define TRC_C_FPE_ret -9
|
|
#define TRC_C_THD_ret -10
|
|
#define TRC_C_STP_ret -11
|
|
#define TRC_C_NEW_ret -12
|
|
#define TRC_C_AFR_ret -13
|
|
#define TRC_C_Thawed 0
|
|
#define TRC_C_Frozen 1
|
|
#define TRC_C_Runnable 0
|
|
#define TRC_C_Suspended 1
|
|
#define TRC_C_Blocked 2
|
|
#define TRC_C_CritSec 3
|
|
|
|
#define TRC_MustBeFrozen 1
|
|
#define TRC_Frozen 2
|
|
|
|
typedef struct _DbgThreadStatus {
|
|
UCHAR DebugState;
|
|
UCHAR ThreadState;
|
|
USHORT Priority;
|
|
} DBGTHREADSTATUS;
|
|
|
|
typedef struct _Os2ExitParam {
|
|
POS2_THREAD t;
|
|
POS2_API_MSG m;
|
|
} OS2_EXIT_PARAM, POS2_EXIT_PARAM;
|
|
|
|
HANDLE Os2hWritePipe;
|
|
|
|
BOOLEAN Os2CLI = FALSE;
|
|
POS2_THREAD Os2CLIThread;
|
|
|
|
HANDLE FirstOs2ProcessHandle = (HANDLE)0;
|
|
CLIENT_ID FirstOs2ProcessClientId;
|
|
|
|
PVOID
|
|
ldrFindMTEForHandle(
|
|
USHORT mte_handle);
|
|
|
|
VOID
|
|
Os2SigKillProcess(
|
|
POS2_PROCESS Process);
|
|
|
|
VOID
|
|
Os2SigKillProcessTree(
|
|
IN POS2_PROCESS RootProcess,
|
|
IN BOOLEAN IncludeRoot
|
|
);
|
|
|
|
VOID
|
|
Os2PrepareCmdSignals(
|
|
POS2_PROCESS Process);
|
|
|
|
NTSTATUS
|
|
Os2SendTmReleaseThreadOnLPC(IN POS2_SESSION Session, IN POS2_PROCESS Process);
|
|
|
|
|
|
USHORT ldrFindSegForHandleandNum(
|
|
USHORT mte,
|
|
USHORT handle,
|
|
USHORT segnum);
|
|
|
|
VOID
|
|
ldrRestoreEntryPoint(
|
|
IN POS2_PROCESS Process
|
|
);
|
|
|
|
UCHAR
|
|
ldrGetEntryPoint(
|
|
IN POS2_PROCESS Process
|
|
);
|
|
|
|
BOOLEAN
|
|
ldrGetModName(
|
|
PVOID mte,
|
|
USHORT hmod,
|
|
PCHAR buf,
|
|
USHORT bc
|
|
);
|
|
|
|
VOID
|
|
ldrReturnProgramAndLibMTE(
|
|
IN POS2_PROCESS Process,
|
|
OUT USHORT *ProgramMTE,
|
|
OUT USHORT *LibMTE,
|
|
OUT USHORT *Cmd
|
|
);
|
|
|
|
#if DBG
|
|
PSZ DbgpKmApiName[ DbgKmMaxApiNumber+1 ] = {
|
|
"DbgKmException",
|
|
"DbgKmCreateThread",
|
|
"DbgKmCreateProcess",
|
|
"DbgKmExitThread",
|
|
"DbgKmExitProcess",
|
|
"DbgKmLoadDll",
|
|
"DbgKmUnloadDll",
|
|
"Unknown DbgKm Api Number"
|
|
};
|
|
#endif
|
|
|
|
#if PMNT
|
|
|
|
HANDLE hPMNTDevice = NULL;
|
|
|
|
APIRET
|
|
InitPMNTDevice()
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
STRING NameString;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
RtlInitString( &NameString, PMNTDD_DEVICE_NAME );
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&UnicodeString,
|
|
&NameString,
|
|
TRUE );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&UnicodeString,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile( &hPMNTDevice,
|
|
SYNCHRONIZE, // | FILE_READ_DATA | FILE_WRITE_DATA,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
0,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
RtlFreeUnicodeString( &UnicodeString );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
#if DBG
|
|
KdPrint(("InitPMNTDevice: NtOpenFile failed, ret=%x\n", Status));
|
|
#endif
|
|
return (Status);
|
|
}
|
|
|
|
return( NO_ERROR );
|
|
}
|
|
|
|
APIRET
|
|
PMNTDDIOMap(
|
|
HANDLE ThreadHandle
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
if (hPMNTDevice == NULL)
|
|
{
|
|
if (InitPMNTDevice() != NO_ERROR)
|
|
{
|
|
#if DBG
|
|
DbgPrint("PMNT_IOCTL: failed to open PMNTDD\n");
|
|
#endif
|
|
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
Status = NtDeviceIoControlFile( hPMNTDevice,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
IOCTL_PMNTDD_IO_MAP,
|
|
(PVOID)&ThreadHandle,
|
|
sizeof(ThreadHandle),
|
|
NULL, // output buffer
|
|
0 // output buffer length
|
|
);
|
|
|
|
if NT_SUCCESS(Status)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("PMNTDDIOMap: Error, failed to call PMNTDD.SYS, Status=%x\n",
|
|
Status);
|
|
return (Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED));
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#endif // PMNT
|
|
|
|
|
|
BOOLEAN
|
|
PMHandledInOut(
|
|
POS2_THREAD Thread)
|
|
{
|
|
#if PMNT
|
|
#if DBG
|
|
DbgPrint("WARNING: IN/OUT exception at [%x,%x], Handle=%x!!!\n",
|
|
Thread->Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Thread->Process->ProcessHandle);
|
|
#endif // DBG
|
|
|
|
if (PMNTDDIOMap(Thread->Process->ProcessHandle) == NO_ERROR)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
DbgPrint("PMHandledInOut: returning FALSE !\n");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
#else
|
|
return(FALSE);
|
|
#endif // not PMNT
|
|
}
|
|
|
|
|
|
VOID
|
|
Os2SuspendProcess(
|
|
IN POS2_PROCESS Process
|
|
)
|
|
|
|
/*+++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to suspend an entire Os/2 client process
|
|
|
|
Arguments:
|
|
|
|
Process - Process to suspend
|
|
|
|
--*/
|
|
{
|
|
POS2_THREAD Thread;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
Thread = NULL;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD(ListNext,
|
|
OS2_THREAD,
|
|
Link );
|
|
if (Thread == NULL)
|
|
break;
|
|
NtSuspendThread( Thread->ThreadHandle, NULL );
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Os2ResumeProcess(
|
|
IN POS2_PROCESS Process
|
|
)
|
|
|
|
/*+++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to resume an entire Os/2 client process
|
|
|
|
Arguments:
|
|
|
|
Process - Process to suspend
|
|
|
|
--*/
|
|
{
|
|
POS2_THREAD Thread;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
Thread = NULL;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD(ListNext,
|
|
OS2_THREAD,
|
|
Link );
|
|
if (Thread == NULL)
|
|
break;
|
|
NtResumeThread( Thread->ThreadHandle, NULL );
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
Os2CompleteResumeThread(
|
|
IN POS2_THREAD Thread
|
|
)
|
|
{
|
|
ULONG SuspendCount;
|
|
NTSTATUS Status;
|
|
|
|
do {
|
|
Status = NtResumeThread (Thread->ThreadHandle, &SuspendCount);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("OS2SRV: [%d,%d] NtResumeThread Status=%x\n",
|
|
Thread->Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
break;
|
|
}
|
|
} while (SuspendCount > 1);
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
Os2SetNewContext(
|
|
IN POS2_THREAD Thread,
|
|
PVOID CallSite)
|
|
{
|
|
CONTEXT Context;
|
|
NTSTATUS Status;
|
|
POS2_PROCESS Process = Thread->Process;
|
|
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
#if DBG
|
|
do {
|
|
#endif // DBG
|
|
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("OS2SRV: [%d,%d] Fail to get context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Hack that avoid exit list (or infinite sleep) handler execution if the thread is
|
|
// using INT 3 instruction. Relevant for checked build only.
|
|
//
|
|
if (Context.SegCs == 0x1b && Context.SegSs == 0x23) {
|
|
BYTE opcode;
|
|
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
(PVOID)(Context.Eip - 1), // previous byte
|
|
&opcode,
|
|
1,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2SetNewContext: Fail to read instruction, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
if (opcode == 0xcc) { // INT 3
|
|
|
|
DbgPrint("[%d]Os2SetNewContext: after INT 3\n",
|
|
Process->ProcessId);
|
|
|
|
Os2CompleteResumeThread(Thread);
|
|
Sleep(100L); // 0.1 sec
|
|
NtSuspendThread(Thread->ThreadHandle, NULL);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
} while (TRUE);
|
|
#endif // DBG
|
|
|
|
Context.SegSs = Context.SegDs = Context.SegEs = 0x23;
|
|
Context.SegCs = 0x1b;
|
|
Context.Eip = (ULONG) CallSite;
|
|
Context.Esp = Thread->InitialStack;
|
|
Context.EFlags &= 0xfffffbff; // Clear direction flag. By default run-time
|
|
// library assume that direction flag is cleared.
|
|
// RtlMoveMemory, for example, don't clear this
|
|
// flag on entry, but assume it 0.
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
DbgPrint("Os2SetNewContext [%d]: Stack=%x\n",
|
|
Process->ProcessId,
|
|
Context.Esp);
|
|
}
|
|
#endif //DBG
|
|
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("OS2SRV: [%d,%d] Fail to set context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Os2ForceProcessToSleep(
|
|
IN POS2_PROCESS Process
|
|
)
|
|
{
|
|
POS2_THREAD Thread;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
Thread = NULL;
|
|
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD(ListNext,
|
|
OS2_THREAD,
|
|
Link );
|
|
if (Thread == NULL)
|
|
break;
|
|
|
|
if (!Thread->MustComplete) {
|
|
|
|
Os2SetNewContext(Thread, Process->InfiniteSleep);
|
|
|
|
//
|
|
// Resume thread. It will wait in the infinite alertable wait.
|
|
//
|
|
|
|
Os2CompleteResumeThread(Thread);
|
|
}
|
|
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
|
|
VOID SetDebuggeeContextInMsg(
|
|
IN PCONTEXT pContext,
|
|
pPTRACEBUF pptracebuf)
|
|
{
|
|
//
|
|
// set ptracbuf value from context
|
|
//
|
|
pptracebuf->rAX = (USHORT) pContext->Eax;
|
|
pptracebuf->rBX = (USHORT) pContext->Ebx;
|
|
pptracebuf->rCX = (USHORT) pContext->Ecx;
|
|
pptracebuf->rDX = (USHORT) pContext->Edx;
|
|
pptracebuf->rSI = (USHORT) pContext->Esi;
|
|
pptracebuf->rDI = (USHORT) pContext->Edi;
|
|
pptracebuf->rBP = (USHORT) pContext->Ebp;
|
|
pptracebuf->rDS = (USHORT) pContext->SegDs;
|
|
pptracebuf->rES = (USHORT) pContext->SegEs;
|
|
pptracebuf->rIP = (USHORT) pContext->Eip;
|
|
pptracebuf->rCS = (USHORT) pContext->SegCs;
|
|
pptracebuf->rF = (USHORT) pContext->EFlags; // BUGBUG ???
|
|
pptracebuf->rSP = (USHORT) pContext->Esp;
|
|
pptracebuf->rSS = (USHORT) pContext->SegSs;
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2CreateExitThread(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m,
|
|
PFNTHREAD pTerminationThread
|
|
)
|
|
{
|
|
//
|
|
// Create a thread and pass it the parameters. The message
|
|
// is created internally, so we copy it to a new structure.
|
|
//
|
|
ULONG Tid;
|
|
HANDLE ExitThreadHandle;
|
|
POS2_DOSEXIT_MSG b;
|
|
POS2_API_MSG m1 = RtlAllocateHeap(Os2Heap, 0, sizeof(OS2_API_MSG));
|
|
POS2_DOSEXIT_MSG a = &m->u.DosExit;
|
|
|
|
m->ApiNumber = Os2Exit;
|
|
|
|
if (!m1)
|
|
return FALSE;
|
|
|
|
PORT_MSG_DATA_LENGTH(*m1) = PORT_MSG_DATA_LENGTH(*m);
|
|
PORT_MSG_TOTAL_LENGTH(*m1) = PORT_MSG_TOTAL_LENGTH(*m);
|
|
PORT_MSG_ZERO_INIT(*m1) = 0L;
|
|
b = &m1->u.DosExit;
|
|
b->ExitAction = a->ExitAction;
|
|
b->ExitResult = a->ExitResult;
|
|
m1->ApiNumber = Os2Exit;
|
|
m1->h.ClientId = t->ClientId;
|
|
|
|
ExitThreadHandle = CreateThread( NULL,
|
|
0,
|
|
pTerminationThread,
|
|
m1,
|
|
0,
|
|
&Tid);
|
|
if (!ExitThreadHandle){
|
|
#if DBG
|
|
DbgPrint("OS2SRV: - fail with error %d at win32 CreateThread - Extend your non-paged pool\n",
|
|
GetLastError());
|
|
#endif
|
|
ASSERT(FALSE);
|
|
RtlFreeHeap(Os2Heap, 0, m1);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Suspend the process, so when we return it does not
|
|
// do anything until Os2DosExitThread finishes it
|
|
//
|
|
Os2SuspendProcess(t->Process);
|
|
NtClose(ExitThreadHandle);
|
|
|
|
//
|
|
// return to the internal server thread
|
|
//
|
|
return(TRUE);
|
|
}
|
|
|
|
VOID
|
|
Os2DosExitThread(
|
|
IN PVOID Parameter
|
|
)
|
|
{
|
|
//
|
|
// This thread is created in Os2DosExit, to free up internal server
|
|
// threads so
|
|
// they don't lock when trying to terminate threads/processes
|
|
//
|
|
|
|
POS2_API_MSG m = (POS2_API_MSG)Parameter;
|
|
POS2_THREAD t;
|
|
|
|
Os2AcquireStructureLock();
|
|
t = Os2LocateThreadByClientId( NULL /*Process*/, &m->h.ClientId );
|
|
if (t != NULL) {
|
|
Os2DosExit( t,m );
|
|
}
|
|
Os2ReleaseStructureLock();
|
|
RtlFreeHeap( Os2Heap, 0, m );
|
|
ExitThread(0);
|
|
}
|
|
|
|
VOID
|
|
Os2WaitSyncAndExitThread(
|
|
IN PVOID Parameter
|
|
)
|
|
{
|
|
POS2_API_MSG m = (POS2_API_MSG)Parameter;
|
|
POS2_THREAD t;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtWaitForSingleObject(
|
|
Os2SyncSem,
|
|
TRUE,
|
|
NULL);
|
|
#if DBG
|
|
if (Status != STATUS_SUCCESS) {
|
|
DbgPrint("OS2SRV: Wait to Sync Sem, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
Os2AcquireStructureLock();
|
|
t = Os2LocateThreadByClientId( NULL /*Process*/, &m->h.ClientId );
|
|
if (t != NULL) {
|
|
Os2SuspendProcess(t->Process);
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
DbgPrint("OS2SRV: Can't find the thread that cause process termination\n");
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
}
|
|
Status = NtReleaseMutant(Os2SyncSem, NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)){
|
|
DbgPrint("OS2SRV: Fail to release Sync Sem, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
if (t != NULL) {
|
|
Os2DosExit( t,m );
|
|
}
|
|
Os2ReleaseStructureLock();
|
|
RtlFreeHeap( Os2Heap, 0, m );
|
|
ExitThread(0);
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosExit(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosExit API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
Note:
|
|
the flow of control for terminating a process is
|
|
DosExit
|
|
Call Os2InternalTerminateThread for each thread
|
|
when only thread 1 is left, exit list processing is done
|
|
when exit list processing is done, it calls Oi2TerminateProcess
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSEXIT_MSG a = &m->u.DosExit;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POS2_PROCESS Process = t->Process;
|
|
POS2_THREAD Thread, Thread1;
|
|
ULONG SyncOwner;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Allow for asynchronous execution of Os2DosExit - in the case
|
|
// it is called by an internal server thread, not by the OS/2 application
|
|
// the flag for this is that m->ApiNumber = Os2MaxApiNumber; This would
|
|
// not pass the test in server\apireqst.c
|
|
//
|
|
|
|
if (m->ApiNumber == Os2MaxApiNumber) {
|
|
if ((t->Flags & OS2_THREAD_THREAD1) &&
|
|
(Process->ExitStatus & OS2_EXIT_IN_PROGRESS)) {
|
|
return FALSE;
|
|
}
|
|
if (Os2CreateExitThread(
|
|
t,
|
|
m,
|
|
(PFNTHREAD)Os2DosExitThread)) {
|
|
return FALSE;
|
|
}
|
|
// Try to perform the Os2DosExit synchronsously
|
|
}
|
|
|
|
m->ReturnedErrorValue = a->ExitResult;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Entering Os2DosExit - PID %d, TID %d\n", t->Process->ProcessId, t->ThreadId));
|
|
}
|
|
#endif
|
|
|
|
if (a->ExitAction == EXIT_THREAD && (t->Flags & OS2_THREAD_THREAD1)) {
|
|
a->ExitAction = EXIT_PROCESS;
|
|
}
|
|
|
|
//
|
|
// Guard against races between abrupt exit (signal) and programmatic
|
|
// exit
|
|
//
|
|
if (!(Process->ExitStatus & OS2_EXIT_WAIT_FOR_SYNC)) {
|
|
if (Process->ExitStatus & OS2_EXIT_IN_PROGRESS){
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Os2DosExit, Exit already in progress, quit\n"));
|
|
}
|
|
#endif
|
|
return(FALSE);
|
|
}
|
|
else {
|
|
//
|
|
// if we start to exit a process, mark it appropriately
|
|
//
|
|
if(a->ExitAction == EXIT_PROCESS) {
|
|
Process->ExitStatus |= OS2_EXIT_IN_PROGRESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (a->ExitAction == EXIT_PROCESS) {
|
|
|
|
Os2SuspendProcess(Process);
|
|
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
&Process->ClientPib->SyncOwner,
|
|
&SyncOwner,
|
|
sizeof( Process->ClientPib->SyncOwner ),
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (SyncOwner != 0) {
|
|
|
|
// There is the thread that owns SyncSem. We must wait until it
|
|
// will free it. For this reason create new thread that will
|
|
// perform actual termination.
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
DbgPrint("OS2SRV: Termination from [%d,%d] find that SYNC was owned by %d\n",
|
|
Process->ProcessId,
|
|
t->ThreadId,
|
|
SyncOwner);
|
|
}
|
|
#endif // DBG
|
|
#if DBG
|
|
if (Process->ExitStatus & OS2_EXIT_WAIT_FOR_SYNC) {
|
|
DbgPrint("OS2SRV: Process own Sync Sem once more\n");
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
ListNext = ListNext->Flink;
|
|
if ((ULONG) Thread->ThreadId == SyncOwner) {
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
#if DBG
|
|
if ((ULONG) Thread->ThreadId != SyncOwner) {
|
|
DbgPrint("OS2SRV: Can't find thread owner SyncSem\n");
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
if (Os2CreateExitThread(
|
|
t,
|
|
m,
|
|
(PFNTHREAD)Os2WaitSyncAndExitThread)) {
|
|
|
|
Process->ExitStatus |= OS2_EXIT_WAIT_FOR_SYNC;
|
|
|
|
Os2CompleteResumeThread(Thread);
|
|
|
|
return FALSE; // Don't reply
|
|
}
|
|
// Try to continue termination without waiting to Sync Sem
|
|
#if DBG
|
|
DbgPrint("OS2SRV: WARNING!!! Process owner Sync Sem will be terminated\n");
|
|
#endif // DBG
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
DbgPrint("OS2SRV: Fail to read PIB of the client, Status=%x\n",
|
|
Status);
|
|
#endif // DBG
|
|
}
|
|
|
|
//
|
|
// Change context of each thread to infinite sleep routine. The threads
|
|
// will be resumed so the kernel process lock will be free. This will
|
|
// permit to perform actions on the process such as threads termination.
|
|
// On other hand all threads will be in the wait stat and they can't harm.
|
|
//
|
|
Os2ForceProcessToSleep(Process);
|
|
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
ListNext = ListNext->Flink;
|
|
if (Thread->Dying || (Thread->Flags & OS2_THREAD_THREAD1)) {
|
|
Thread1 = Thread;
|
|
continue;
|
|
}
|
|
if (!Thread->MustComplete) {
|
|
Thread->CurrentSignals |= SIGNAL_TO_FLAG(SIGAPTERM);
|
|
if (Thread->WaitBlock != NULL) {
|
|
Thread->WaitBlock->WaitReplyMessage.ReturnedErrorValue = ERROR_INTERRUPT;
|
|
Os2NotifyWaitBlock(Thread->WaitBlock,WaitInterrupt,NULL,NULL);
|
|
}
|
|
Os2InternalTerminateThread(Thread, m);
|
|
}
|
|
else {
|
|
#if DBG
|
|
KdPrint(( "Os2DosExit, ThreadMustComplete %d\n", Thread->ThreadId));
|
|
#endif
|
|
Thread->PendingSignals |= SIGNAL_TO_FLAG(SIGAPTERM);
|
|
}
|
|
}
|
|
//
|
|
// Terminate Thread1 last
|
|
//
|
|
Thread = Thread1;
|
|
if (!Thread->MustComplete) {
|
|
Thread->CurrentSignals |= SIGNAL_TO_FLAG(SIGAPTERM);
|
|
if (Thread->WaitBlock != NULL) {
|
|
Thread->WaitBlock->WaitReplyMessage.ReturnedErrorValue = ERROR_INTERRUPT;
|
|
Os2NotifyWaitBlock(Thread->WaitBlock,WaitInterrupt,NULL,NULL);
|
|
}
|
|
Os2InternalTerminateThread(Thread, m);
|
|
}
|
|
}
|
|
else if (a->ExitAction == EXIT_THREAD) {
|
|
|
|
Thread = t;
|
|
if (Thread->Dying) {
|
|
}
|
|
else {
|
|
if (!Thread->MustComplete) {
|
|
|
|
Thread->CurrentSignals |= SIGNAL_TO_FLAG(SIGAPTERM);
|
|
if (Thread->WaitBlock != NULL) {
|
|
Thread->WaitBlock->WaitReplyMessage.ReturnedErrorValue = ERROR_INTERRUPT;
|
|
Os2NotifyWaitBlock(Thread->WaitBlock,WaitInterrupt,NULL,NULL);
|
|
}
|
|
|
|
Os2InternalTerminateThread(Thread, m);
|
|
}
|
|
else {
|
|
Thread->PendingSignals |= SIGNAL_TO_FLAG(SIGAPTERM);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
m->ReturnedErrorValue = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Leaving Os2DosExit. rc is %ld\n",m->ReturnedErrorValue));
|
|
}
|
|
#endif
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
VOID
|
|
Os2ForceClientCleanup(
|
|
IN POS2_THREAD t
|
|
)
|
|
{
|
|
//
|
|
// This routine is used in the case where a 16b ExitList Routine
|
|
// encounters a GP while we close an app. We want the exitlistdispatcher
|
|
// to cleanup the client state
|
|
//
|
|
Os2SetNewContext(t, t->Process->ExitListDispatcher);
|
|
|
|
//
|
|
// Now resume and alert the thread (came in suspended)
|
|
// get the chance now to execute the ExitList Dispatcher
|
|
//
|
|
Os2CompleteResumeThread(t);
|
|
}
|
|
|
|
VOID
|
|
Os2ApiGPPopupThread(
|
|
IN PVOID Parameter
|
|
)
|
|
{
|
|
POS2_THREAD t;
|
|
POS2_API_MSG m = (POS2_API_MSG)Parameter;
|
|
UCHAR ApplName[OS2_PROCESS_MAX_APPL_NAME];
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Os2ApiGPPopupThread\n"));
|
|
}
|
|
#endif
|
|
|
|
__try {
|
|
|
|
Os2AcquireStructureLock();
|
|
t = Os2LocateThreadByClientId( NULL /*Process*/, &m->h.ClientId );
|
|
|
|
if (t == NULL){
|
|
//
|
|
// Another event caused this thread to be removed before we got the lock
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: ApiGPPopupThread occurred while executing API %s, thread terminated, cancel popup\n", &m->u.DosExitGP.ApiName[0]);
|
|
#endif
|
|
Os2ReleaseStructureLock();
|
|
__leave;
|
|
}
|
|
|
|
RtlCopyMemory(ApplName, t->Process->ApplName, OS2_PROCESS_MAX_APPL_NAME);
|
|
#if PMNT
|
|
Os2ReleaseStructureLock();
|
|
#endif // PMNT
|
|
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred while executing API %s\n", &m->u.DosExitGP.ApiName[0]);
|
|
#endif
|
|
|
|
Os2ApiGPPopup(
|
|
ApplName,
|
|
&m->u.DosExitGP.ApiName[0]
|
|
);
|
|
|
|
#if PMNT
|
|
Os2AcquireStructureLock();
|
|
t = Os2LocateThreadByClientId( NULL /*Process*/, &m->h.ClientId );
|
|
|
|
if (t == NULL){
|
|
//
|
|
// Another event caused this thread to be removed before we got the lock
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: Os2ApiGPPopupThread: thread terminated already\n");
|
|
#endif
|
|
Os2ReleaseStructureLock();
|
|
__leave;
|
|
}
|
|
#endif // PMNT
|
|
|
|
if ((t->Flags & OS2_THREAD_THREAD1) &&
|
|
(t->Process->ExitStatus & OS2_EXIT_IN_PROGRESS)){
|
|
//
|
|
// An Exception happened while processing exit list - terminate the process
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred while executing exitlist routine\n");
|
|
#endif
|
|
Os2ForceClientCleanup(t);
|
|
Os2ReleaseStructureLock();
|
|
__leave;
|
|
}
|
|
|
|
{
|
|
POS2_DOSEXIT_MSG b = &m->u.DosExit;
|
|
b->ExitAction = EXIT_PROCESS;
|
|
b->ExitResult = 13;
|
|
ASSERT(m->h.ClientId.UniqueProcess == t->ClientId.UniqueProcess);
|
|
ASSERT(m->h.ClientId.UniqueThread == t->ClientId.UniqueThread);
|
|
Os2DosExit(t, m);
|
|
}
|
|
|
|
Os2ReleaseStructureLock();
|
|
}
|
|
__finally {
|
|
RtlFreeHeap(Os2Heap, 0, m);
|
|
ExitThread(0);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosExitGP(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
Note:
|
|
After Popup the message, this routine calls Os2DosExit.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Entering Os2DosExitGP\n"));
|
|
}
|
|
#endif
|
|
|
|
if (t->Process->ErrorAction & OS2_ENABLE_ACCESS_VIO_POPUP) {
|
|
|
|
//
|
|
// Create a separate thread to do the popup and kill the process
|
|
//
|
|
ULONG Tid;
|
|
POS2_API_MSG pm1;
|
|
|
|
pm1 = RtlAllocateHeap(Os2Heap, 0, sizeof(OS2_API_MSG));
|
|
|
|
if (pm1) {
|
|
HANDLE GPThreadHandle;
|
|
|
|
RtlCopyMemory(pm1, m, sizeof(OS2_API_MSG));
|
|
GPThreadHandle = CreateThread( NULL,
|
|
0,
|
|
(PFNTHREAD)Os2ApiGPPopupThread,
|
|
pm1,
|
|
0,
|
|
&Tid);
|
|
if (!GPThreadHandle){
|
|
#if DBG
|
|
KdPrint(("Os2ExitGP - fail at win32 CreateThread, %d\n",GetLastError()));
|
|
#endif
|
|
RtlFreeHeap(Os2Heap, 0, pm1);
|
|
}
|
|
else {
|
|
Os2SuspendProcess(t->Process);
|
|
NtClose(GPThreadHandle);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else {
|
|
#if DBG
|
|
KdPrint(("Os2ExitGP - fail at RtlAllocateHeap\n"));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if ((t->Flags & OS2_THREAD_THREAD1) &&
|
|
(t->Process->ExitStatus & OS2_EXIT_IN_PROGRESS)){
|
|
//
|
|
// An Exception happened while processing exit list - terminate the process
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred while executing exitlist routine\n");
|
|
#endif
|
|
Os2ForceClientCleanup(t);
|
|
return(FALSE);
|
|
}
|
|
|
|
{
|
|
OS2_API_MSG m1;
|
|
POS2_DOSEXIT_MSG b = &m1.u.DosExit;
|
|
m1.h.ClientId = t->ClientId;
|
|
b->ExitAction = EXIT_PROCESS;
|
|
b->ExitResult = 13;
|
|
return Os2DosExit(t, &m1);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2InternalTerminateProcess(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine terminates a process. It is called after exit list processing
|
|
has completed. It frees thread one and the process and alerts anyone
|
|
waiting on the thread or process.
|
|
|
|
Arguments:
|
|
|
|
t - thread one of process to terminate.
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
FALSE - do not return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_TERMINATEPROCESS_MSG a = &m->u.TerminateProcess;
|
|
POS2_PROCESS Process = t->Process;
|
|
POS2_PROCESS RootProcessInSession;
|
|
OS2_TERMCMD Os2TermCmd;
|
|
ULONG nNumberOfBytesWritten;
|
|
BOOLEAN LoadingFailed = FALSE;
|
|
BOOLEAN CheckForBackgound = FALSE;
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Entering Os2InternalTerminateProcess\n"));
|
|
}
|
|
#endif
|
|
|
|
#if PMNT
|
|
//
|
|
// Check for termination of PMShell
|
|
//
|
|
if (PMNTPMShellPid == t->Process->ProcessId)
|
|
{
|
|
UNICODE_STRING EventString_U;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS Status;
|
|
HANDLE Od2PMShellEvent;
|
|
|
|
PMNTPMShellPid = 0;
|
|
|
|
// Now reset the PMShell event to the not-signaled state
|
|
|
|
RtlInitUnicodeString( &EventString_U, OS2_SS_PMSHELL_EVENT);
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&EventString_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Open the global subsystem synchronization Nt semaphore
|
|
//
|
|
Status = NtOpenEvent(&Od2PMShellEvent,
|
|
EVENT_ALL_ACCESS,
|
|
&Obja);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
#if DBG
|
|
DbgPrint("OS2SRV: Od2DosExit(), failed to open PMShellEvent, Status %x\n", Status);
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
Status = NtResetEvent (
|
|
Od2PMShellEvent,
|
|
NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("OS2SRV: Od2DosExit(), failed to NtResetEvent PMShellEvent, Status %x\n", Status);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
}
|
|
#endif // PMNT
|
|
|
|
// printf("Os2 time at start of Os2InternalTerminateProcess is %d\n", (GetTickCount()));
|
|
NtClose(Process->ClientPort);
|
|
if (Process->ResultCodes.ExitReason != TC_TRAP) {
|
|
Process->ResultCodes.ExitReason = a->ExitReason;
|
|
Process->ResultCodes.ExitResult = ~0x80000000 & a->ExitResult;
|
|
if ((a->ExitResult & 0x80000000) != 0) {
|
|
LoadingFailed = TRUE;
|
|
}
|
|
}
|
|
#if DBG
|
|
if (!(t->Flags & OS2_THREAD_THREAD1))
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess - OS2_THREAD_THREAD1=0 \n"));
|
|
#endif
|
|
Os2RemoveThread( Process, t );
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess - 1 \n"));
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess - 2 \n"));
|
|
}
|
|
#endif
|
|
|
|
RootProcessInSession = Process->Session->Process;
|
|
|
|
if (Process->Parent != Os2RootProcess || RootProcessInSession != Process) {
|
|
//
|
|
// Terminate the thread only in the case of
|
|
// a process that was created as a result of an OS/2 API.
|
|
// Otherwise the client itself issues ExitProcess
|
|
// as a result of ServeTmRequest.
|
|
//
|
|
Os2TermCmd.Param1 = 0;
|
|
Os2TermCmd.op = Os2TerminateThread;
|
|
Os2TermCmd.Handle = t->ThreadHandle;
|
|
if (!WriteFile(
|
|
Os2hWritePipe,
|
|
(VOID *)&Os2TermCmd,
|
|
sizeof(Os2TermCmd),
|
|
&nNumberOfBytesWritten,
|
|
NULL)) {
|
|
//
|
|
// The Pipe of Os2TerminationThread is full
|
|
//
|
|
ASSERT(FALSE);
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess: can't post thread termination command, status %d, ignore\n",
|
|
GetLastError()));
|
|
#endif
|
|
|
|
}
|
|
}
|
|
else {
|
|
NtClose( t->ThreadHandle );
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess - 3 \n"));
|
|
}
|
|
#endif
|
|
if (Process->Flags & OS2_PROCESS_TRACE){
|
|
Os2NotifyWait( WaitProcess, Process, (PVOID) a);
|
|
}
|
|
|
|
Os2DeallocateThread( t );
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess - 4 \n"));
|
|
}
|
|
#endif
|
|
if (Process->Session->ReferenceCount > 1){
|
|
CheckForBackgound = TRUE;
|
|
}
|
|
if(Os2DereferenceSession(Process->Session, a, (BOOLEAN)FALSE)==NULL){
|
|
Process->Session=NULL;
|
|
}
|
|
|
|
if (Process->Parent == Os2RootProcess && RootProcessInSession == Process) {
|
|
//
|
|
// Root Process of a Session
|
|
//
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POS2_PROCESS Process1;
|
|
BOOLEAN FoundForeground = FALSE;
|
|
|
|
NtClose( Process->ProcessHandle );
|
|
//
|
|
// check if all of the remaining processes in the session are detached
|
|
// if so - let the root process of the session go
|
|
//
|
|
if (CheckForBackgound){
|
|
for (
|
|
ListHead = &Os2RootProcess->ListLink,
|
|
ListNext = ListHead->Flink;
|
|
ListNext != ListHead ;
|
|
ListNext = ListNext->Flink
|
|
) {
|
|
Process1 = CONTAINING_RECORD( ListNext, OS2_PROCESS, ListLink );
|
|
if ( Process1->Session == Process->Session && Process1 != Process) {
|
|
//
|
|
// check if detach
|
|
//
|
|
if (!(Process1->Flags & OS2_PROCESS_BACKGROUND)) {
|
|
FoundForeground = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
if (!FoundForeground){
|
|
//
|
|
// Scanned the whole list - no foreground child - allow
|
|
// root process to quit
|
|
//
|
|
Os2TerminateConSession ( Process->Session,
|
|
a);
|
|
//
|
|
// No more calls to sesssion->consoleport
|
|
//
|
|
NtClose(Process->Session->ConsolePort);
|
|
Process->Session->ConsolePort = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
Os2RemoveProcess( Process );
|
|
if (!LoadingFailed) {
|
|
LDRUnloadExe(Process);
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess - 5 \n"));
|
|
}
|
|
#endif
|
|
//
|
|
// Now do config.sys cleanup processing
|
|
//
|
|
if (Process->ConfigSysUsageFlag) {
|
|
Os2UpdateRegistryFromConfigSys();
|
|
Process->ConfigSysUsageFlag = FALSE;
|
|
}
|
|
|
|
if (Process->Parent == Os2RootProcess && RootProcessInSession == Process) {
|
|
//
|
|
// This process was created from win32 - can safely free structure
|
|
// at this point
|
|
//
|
|
Os2DeallocateProcess( Process );
|
|
}
|
|
else {
|
|
//
|
|
// This is a process that was created as a result of an OS/2 API.
|
|
// We have to terminate it. The RootProcess of each session is quiting
|
|
// by itself as a result of the last Os2DereferenceSession
|
|
// The termination thread will call Os2NotifyDeathOfProcess which will
|
|
// deallocate the process
|
|
//
|
|
Os2TermCmd.op = Os2TerminateProcess;
|
|
Os2TermCmd.Handle = Process->ProcessHandle;
|
|
Os2TermCmd.Param1 = m;
|
|
Os2TermCmd.Param2 = Process;
|
|
|
|
if (!WriteFile(
|
|
Os2hWritePipe,
|
|
(VOID *)&Os2TermCmd,
|
|
sizeof(Os2TermCmd),
|
|
&nNumberOfBytesWritten,
|
|
NULL)){
|
|
|
|
//
|
|
// The Pipe of Os2TerminationThread is full
|
|
//
|
|
ASSERT(FALSE);
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Os2InternalTerminateProcess: can't post process termination command, status %d, ignore\n",
|
|
GetLastError()));
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Leaving Os2InternalTerminateProcess\n"));
|
|
}
|
|
#endif
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
VOID
|
|
Os2NotifyDeathOfProcess(
|
|
IN PVOID m,
|
|
IN PVOID Proc)
|
|
{
|
|
|
|
POS2_PROCESS Process = (POS2_PROCESS)Proc;
|
|
POS2_TERMINATEPROCESS_MSG a = &((POS2_API_MSG)m)->u.TerminateProcess;
|
|
|
|
//
|
|
// Now notify processes waiting on this process termination
|
|
//
|
|
Os2NotifyWait( WaitProcess, Process, (PVOID) a);
|
|
Os2DeallocateProcess( Process );
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2WaitDeadThreadSatisfy(
|
|
IN OS2_WAIT_REASON WaitReason,
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m,
|
|
IN PVOID WaitParameter,
|
|
IN PVOID SatisfyParameter1,
|
|
IN PVOID SatisfyParameter2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used by thread one when waiting for all other threads
|
|
in the process to die.
|
|
|
|
Arguments:
|
|
|
|
N/A
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(WaitReason);
|
|
UNREFERENCED_PARAMETER(t);
|
|
UNREFERENCED_PARAMETER(m);
|
|
UNREFERENCED_PARAMETER(WaitParameter);
|
|
UNREFERENCED_PARAMETER(SatisfyParameter1);
|
|
UNREFERENCED_PARAMETER(SatisfyParameter2);
|
|
return TRUE;
|
|
}
|
|
|
|
// This routine will be called from TerminationThread to set the context
|
|
// of thread1 to exit list dispatcher.
|
|
|
|
VOID
|
|
Os2SwitchContextToExitListDispatcher(
|
|
IN PVOID Thread
|
|
)
|
|
{
|
|
NtSuspendThread(((POS2_THREAD)Thread)->ThreadHandle, NULL);
|
|
Os2SetNewContext((POS2_THREAD)Thread,
|
|
((POS2_THREAD)Thread)->Process->ExitListDispatcher);
|
|
|
|
//
|
|
// Now "alert" the thread - in case it is blocked, it'll
|
|
// get the chance now to execute the ExitList Dispatcher
|
|
//
|
|
NtAlertThread(((POS2_THREAD)Thread)->ThreadHandle);
|
|
|
|
if (!(((POS2_THREAD)Thread)->Process->Flags & OS2_PROCESS_TRACE) ||
|
|
(((POS2_THREAD)Thread)->Process->Flags & OS2_PROCESS_TERMINATE)) {
|
|
Os2CompleteResumeThread((POS2_THREAD)Thread);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2InternalTerminateThread(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine terminates a thread. If it is thread one, it dispatches
|
|
exit list processing. Otherwise, it frees the thread and alerts
|
|
anyone waiting on the thread.
|
|
|
|
Arguments:
|
|
|
|
t - thread to terminate
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSEXIT_MSG a = &m->u.DosExit;
|
|
POS2_THREAD Thread1;
|
|
POS2_PROCESS Process;
|
|
POS2_WAIT_BLOCK WaitBlock;
|
|
BOOLEAN ReturnValue;
|
|
|
|
OS2_TERMCMD Os2TermCmd;
|
|
ULONG nNumberOfBytesWritten;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Entering Os2InternalTerminateThread - TID %d\n", t->ThreadId));
|
|
}
|
|
#endif
|
|
|
|
Process = t->Process;
|
|
|
|
//
|
|
// if we're thread 1, dispatch exit list processing.
|
|
//
|
|
// note: thread 1 must notify any waits before it waits for other
|
|
// threads to complete. otherwise, the other threads will never finish.
|
|
//
|
|
|
|
if (t->Flags & OS2_THREAD_THREAD1) {
|
|
Os2NotifyWait( WaitThread, t, NULL );
|
|
if (t->Dying) {
|
|
#if DBG
|
|
KdPrint(("InternalTerminateThread - Thread1 died already\n"));
|
|
#endif
|
|
}
|
|
t->Dying = TRUE;
|
|
//
|
|
// dispatch exitlist processing
|
|
//
|
|
|
|
Process->Flags |= OS2_PROCESS_EXIT;
|
|
|
|
Os2SendTmReleaseThreadOnLPC(Process->Session, Process);
|
|
if (t->Link.Flink == t->Link.Blink) { // no other threads exist
|
|
ReturnValue = TRUE;
|
|
}
|
|
else {
|
|
if (!Os2InitializeWait((OS2_WAIT_ROUTINE) Os2WaitDeadThreadSatisfy,
|
|
t,
|
|
m,
|
|
0,
|
|
&WaitBlock)) {
|
|
ASSERT (FALSE);
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Os2InternalTerminateThread: Thread1 handled, Wait Initialized\n"));
|
|
}
|
|
#endif
|
|
ReturnValue = TRUE;
|
|
}
|
|
Os2TermCmd.Param1 = (PVOID)1; // mean resume this thread for exitlist processing
|
|
Os2TermCmd.Param2 = (PVOID)t; // used to set new context for the thread.
|
|
Os2TermCmd.op = Os2TerminateThread;
|
|
Os2TermCmd.Handle = t->ThreadHandle;
|
|
if (!WriteFile(
|
|
Os2hWritePipe,
|
|
(VOID *)&Os2TermCmd,
|
|
sizeof(Os2TermCmd),
|
|
&nNumberOfBytesWritten,
|
|
NULL)){
|
|
//
|
|
// The Pipe of Os2TerminationThread is full
|
|
//
|
|
ASSERT(FALSE);
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Os2InternalTerminateThread: can't post thread termination command, status %d, ignore\n",
|
|
GetLastError()));
|
|
#endif
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Leaving Os2InternalTerminateThread\n"));
|
|
}
|
|
#endif
|
|
return( ReturnValue );
|
|
}
|
|
else {
|
|
//
|
|
// Find Thread1
|
|
//
|
|
POS2_THREAD ThreadTmp;
|
|
PLIST_ENTRY ListHead2, ListNext2;
|
|
|
|
ListHead2 = &Process->ThreadList;
|
|
ListNext2 = ListHead2->Flink;
|
|
Thread1 = NULL;
|
|
while (ListNext2 != ListHead2) {
|
|
ThreadTmp = CONTAINING_RECORD(ListNext2,
|
|
OS2_THREAD,
|
|
Link );
|
|
if (ThreadTmp->Flags & OS2_THREAD_THREAD1) {
|
|
Thread1 = ThreadTmp;
|
|
break;
|
|
}
|
|
else
|
|
ListNext2 = ListNext2->Flink;
|
|
}
|
|
Os2RemoveThread( Process, t );
|
|
Os2NotifyWait( WaitThread, t, NULL );
|
|
|
|
if (t->Flags & OS2_THREAD_ATTACHED) {
|
|
|
|
//
|
|
// Don't actually terminate an attached
|
|
// thread, only detach it from the os/2ss
|
|
//
|
|
|
|
CloseHandle(t->ThreadHandle);
|
|
|
|
} else if (a->ExitAction == EXIT_THREAD) {
|
|
|
|
//
|
|
// We are terminating a single thread as result of DosExit
|
|
// with EXIT_THREAD semantics, The thread will kill itself,
|
|
// all we need to do is close the handle.
|
|
//
|
|
CloseHandle(t->ThreadHandle);
|
|
|
|
} else {
|
|
//
|
|
// We are killing a thread that did not call DosExit and
|
|
// is not thread 1
|
|
//
|
|
|
|
Os2TermCmd.Param1 = 0;
|
|
Os2TermCmd.op = Os2TerminateThread;
|
|
Os2TermCmd.Handle = t->ThreadHandle;
|
|
if (!WriteFile(
|
|
Os2hWritePipe,
|
|
(VOID *)&Os2TermCmd,
|
|
sizeof(Os2TermCmd),
|
|
&nNumberOfBytesWritten,
|
|
NULL)){
|
|
//
|
|
// The Pipe of Os2TerminationThread is full
|
|
//
|
|
ASSERT(FALSE);
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Os2InternalTerminateThread: can't post thread termination command, status %d, ignore\n",
|
|
GetLastError()));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
Os2DeallocateThread( t );
|
|
|
|
if (Process->ThreadList.Flink == Process->ThreadList.Blink && // only thread 1 is left and
|
|
Thread1->Dying == TRUE) { // it has already called this
|
|
// routine.
|
|
//
|
|
// dispatch exitlist processing
|
|
//
|
|
|
|
Os2NotifyWaitBlock(Thread1->WaitBlock,0,NULL,NULL);
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Leaving Os2InternalTerminateThread\n"));
|
|
}
|
|
#endif
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Os2ExceptionTerminateProcess(
|
|
POS2_THREAD Thread,
|
|
PDBGKM_APIMSG ReceiveMsg
|
|
)
|
|
{
|
|
// The TC_TRAP value which is put in the process
|
|
// ResultCodes structure prevents from further setting
|
|
// this structure and is returned to the parent process.
|
|
|
|
Thread->Process->ResultCodes.ExitReason = TC_TRAP;
|
|
switch (ReceiveMsg->u.Exception.ExceptionRecord.ExceptionCode) {
|
|
case STATUS_ACCESS_VIOLATION:
|
|
Thread->Process->ResultCodes.ExitResult = 13;
|
|
break;
|
|
|
|
case STATUS_ILLEGAL_FLOAT_CONTEXT:
|
|
Thread->Process->ResultCodes.ExitResult = 7;
|
|
break;
|
|
|
|
default:
|
|
Thread->Process->ResultCodes.ExitResult = 0;
|
|
}
|
|
|
|
if ((Thread->Flags & OS2_THREAD_THREAD1) &&
|
|
(Thread->Process->ExitStatus & OS2_EXIT_IN_PROGRESS)){
|
|
//
|
|
// An Exception happened while processing exit list - terminate the process
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred while executing exitlist routine\n");
|
|
#endif
|
|
Os2ForceClientCleanup(Thread);
|
|
}
|
|
else {
|
|
Os2SigKillProcess(Thread->Process);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Os2AccessGPPopupThread(
|
|
IN PVOID Parameter
|
|
)
|
|
{
|
|
POS2_THREAD Thread;
|
|
PDBGKM_APIMSG ReceiveMsg = (PDBGKM_APIMSG)Parameter;
|
|
CONTEXT Context;
|
|
NTSTATUS Status;
|
|
UCHAR ApplName[OS2_PROCESS_MAX_APPL_NAME];
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Os2AccessGPPopupThread\n"));
|
|
}
|
|
#endif
|
|
|
|
__try {
|
|
|
|
Os2AcquireStructureLock();
|
|
Thread = Os2LocateThreadByClientId( NULL /*Process*/, &ReceiveMsg->h.ClientId );
|
|
|
|
if (Thread == NULL){
|
|
//
|
|
// Another event caused this thread to be removed before we got the lock
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: AccessGPPopupThread: thread terminated, cancel popup\n");
|
|
#endif
|
|
Os2ReleaseStructureLock();
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Get the context record for the target thread.
|
|
//
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred at CS=%x, IP=%x\n", Context.SegCs, Context.Eip & 0xffff);
|
|
#endif
|
|
RtlCopyMemory(ApplName, Thread->Process->ApplName, OS2_PROCESS_MAX_APPL_NAME);
|
|
#if PMNT
|
|
Os2ReleaseStructureLock();
|
|
#endif // PMNT
|
|
Os2AccessGPPopup(
|
|
Context.SegCs,
|
|
Context.Eip & 0xffff,
|
|
Context.Eax & 0xffff,
|
|
Context.Ebx & 0xffff,
|
|
Context.Ecx & 0xffff,
|
|
Context.Edx & 0xffff,
|
|
Context.Esi & 0xffff,
|
|
Context.Edi & 0xffff,
|
|
Context.Ebp & 0xffff,
|
|
Context.Esp & 0xffff,
|
|
Context.SegSs & 0xffff,
|
|
Context.SegDs & 0xffff,
|
|
Context.SegEs & 0xffff,
|
|
ApplName
|
|
);
|
|
|
|
#if PMNT
|
|
Os2AcquireStructureLock();
|
|
Thread = Os2LocateThreadByClientId( NULL /*Process*/, &ReceiveMsg->h.ClientId );
|
|
|
|
if (Thread == NULL){
|
|
//
|
|
// Another event caused this thread to be removed before we got the lock
|
|
//
|
|
#if DBG
|
|
DbgPrint("OS2SRV: AccessGPPopupThread: thread terminated already\n");
|
|
#endif
|
|
Os2ReleaseStructureLock();
|
|
__leave;
|
|
}
|
|
#endif // PMNT
|
|
}
|
|
else {
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred at 16bit. Fail to get context, Status=0x%X", Status);
|
|
#endif
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
Os2ExceptionTerminateProcess(Thread, ReceiveMsg);
|
|
Os2ReleaseStructureLock();
|
|
}
|
|
__finally {
|
|
RtlFreeHeap(Os2Heap, 0, ReceiveMsg);
|
|
ExitThread(0);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Os2HandleException(
|
|
IN POS2_PROCESS Process,
|
|
IN PDBGKM_APIMSG ReceiveMsg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when an exception occurs and there is no stack
|
|
frame to handle it. It terminates the thread and the process.
|
|
|
|
Arguments:
|
|
|
|
Process - process in which exception occurred
|
|
|
|
ReceiveMsg - exception message passed by debugger
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
CONTEXT Context;
|
|
NTSTATUS Status;
|
|
POS2_THREAD Thread;
|
|
|
|
Thread = Os2LocateThreadByClientId( Process, &(ReceiveMsg->h.ClientId) );
|
|
|
|
if (Thread == NULL) {
|
|
#if DBG
|
|
IF_OS2_DEBUG(MISC) {
|
|
KdPrint(("Os2srv: Os2HandleException Null Thread handle\n"));
|
|
}
|
|
#endif
|
|
ReceiveMsg->ReturnedStatus = DBG_CONTINUE;
|
|
NtReplyPort(Os2DebugPort, (PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the context record for the target thread.
|
|
//
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
|
|
#if DBG
|
|
KdPrint(("Os2srv: Exception %x at cs:ip = %x:%x , program terminated\n",
|
|
ReceiveMsg->u.Exception.ExceptionRecord.ExceptionCode,
|
|
Context.SegCs,
|
|
Context.Eip));
|
|
#endif
|
|
|
|
if ( Context.SegCs != 0x1B) {
|
|
if (Thread->Process->ErrorAction & OS2_ENABLE_ACCESS_VIO_POPUP) {
|
|
//
|
|
// Create a separate thread to do the popup and kill the process
|
|
//
|
|
ULONG Tid;
|
|
PDBGKM_APIMSG ReceiveMsg1;
|
|
|
|
ReceiveMsg1 = RtlAllocateHeap(Os2Heap, 0, sizeof(DBGKM_APIMSG));
|
|
|
|
if (ReceiveMsg1) {
|
|
HANDLE GPThreadHandle;
|
|
|
|
RtlCopyMemory(ReceiveMsg1, ReceiveMsg, sizeof(DBGKM_APIMSG));
|
|
|
|
GPThreadHandle = CreateThread( NULL,
|
|
0,
|
|
(PFNTHREAD)Os2AccessGPPopupThread,
|
|
ReceiveMsg1,
|
|
0,
|
|
&Tid);
|
|
if (GPThreadHandle){
|
|
//
|
|
// Success, the thread will do the popup
|
|
//
|
|
NtClose(GPThreadHandle);
|
|
//
|
|
// Suspend the falted process and let debugger go
|
|
//
|
|
Os2SuspendProcess(Thread->Process);
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort, (PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
else {
|
|
RtlFreeHeap(Os2Heap, 0, ReceiveMsg1);
|
|
#if DBG
|
|
KdPrint(("Os2HandleException - fail at win32 CreateThread, %d\n",GetLastError()));
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
#if DBG
|
|
KdPrint(("Os2HandleException - fail at RtlAllocateHeap\n"));
|
|
#endif
|
|
}
|
|
// fall thru, handle the exception with no popup
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
DbgPrint("OS2SRV: GP occurred at CS=%x, IP=%x\n", Context.SegCs, Context.Eip & 0xffff);
|
|
#endif
|
|
|
|
Os2ExceptionTerminateProcess(Thread, ReceiveMsg);
|
|
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort, (PPORT_MESSAGE) ReceiveMsg);
|
|
}
|
|
|
|
#if DBG
|
|
// DbgPrint already defined
|
|
#else
|
|
// DbgPrint is used in free build to handle DbgPrint exception
|
|
#undef DbgPrint
|
|
ULONG
|
|
DbgPrint(
|
|
PCH Format,
|
|
...
|
|
);
|
|
#endif
|
|
|
|
VOID
|
|
Os2HandleDebugEvent(
|
|
IN POS2_PROCESS Process,
|
|
IN PDBGKM_APIMSG ReceiveMsg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when an debug event occurs.
|
|
|
|
Arguments:
|
|
|
|
Process - process in which exception occurred
|
|
|
|
ReceiveMsg - exception message passed by debugger
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
OS2_API_MSG m;
|
|
POS2_TERMINATETHREAD_MSG a = &m.u.TerminateThread;
|
|
POS2_THREAD Thread;
|
|
CONTEXT Context;
|
|
NTSTATUS Status, ExceptionCode;
|
|
POS2_SES_GROUP_PARMS SesGrp;
|
|
ULONG FlatCliAddress;
|
|
UCHAR ucInst;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG(MISC) {
|
|
KdPrint(("OS2SRV: Received message on debug port - %s \n",
|
|
DbgpKmApiName[ReceiveMsg->ApiNumber]));
|
|
KdPrint(("OS2SRV: Os2DebugUserClientId.UniqueProcess = %x\n",
|
|
Os2DebugUserClientId.UniqueProcess));
|
|
KdPrint(("OS2SRV: ClientId.UniqueProcess = %x\n",
|
|
ReceiveMsg->h.ClientId.UniqueProcess));
|
|
KdPrint(("OS2SRV: ClientId.UniqueThread = %x\n",
|
|
ReceiveMsg->h.ClientId.UniqueThread));
|
|
KdPrint(("OS2SRV: HandleDebugEvent: ExceptionCode = %x\n",
|
|
ReceiveMsg->u.Exception.ExceptionRecord.ExceptionCode));
|
|
|
|
}
|
|
#endif
|
|
|
|
if (ReceiveMsg->ApiNumber == DbgKmExceptionApi) {
|
|
ExceptionCode = ReceiveMsg->u.Exception.ExceptionRecord.ExceptionCode;
|
|
|
|
if (ExceptionCode == DBG_PRINTEXCEPTION_C) {
|
|
PEXCEPTION_RECORD pExceptionRecord = &(ReceiveMsg->u.Exception.ExceptionRecord);
|
|
|
|
if (pExceptionRecord->NumberParameters == 2) {
|
|
|
|
#define DBG_PRINTEXCEPTION_BUFFER_LEN 512
|
|
UCHAR buffer[DBG_PRINTEXCEPTION_BUFFER_LEN];
|
|
LONG length = pExceptionRecord->ExceptionInformation[0] > DBG_PRINTEXCEPTION_BUFFER_LEN ?
|
|
DBG_PRINTEXCEPTION_BUFFER_LEN :
|
|
pExceptionRecord->ExceptionInformation[0];
|
|
|
|
Process = Os2LocateProcessByClientId(&(ReceiveMsg->h.ClientId));
|
|
|
|
if (Process) {
|
|
Status = NtReadVirtualMemory(
|
|
Process->ProcessHandle,
|
|
(PVOID) pExceptionRecord->ExceptionInformation[1],
|
|
buffer,
|
|
length,
|
|
&length);
|
|
|
|
if (NT_SUCCESS(Status) && length > 1) {
|
|
buffer[length - 1] = '\0';
|
|
DbgPrint(buffer);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Process already died. Ignore this DbgPrint
|
|
//
|
|
DbgPrint("OS2SRV: DbgPrint called during process termination\n");
|
|
}
|
|
#if DBG
|
|
// DbgPrint remain defined
|
|
#else
|
|
#undef DbgPrint
|
|
#define DbgPrint(_x_) Or2DbgPrintFunctionNeverExisted(_x_)
|
|
#endif
|
|
}
|
|
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort, (PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
|
|
Thread = Os2LocateThreadByClientId(Process,
|
|
&(ReceiveMsg->h.ClientId));
|
|
|
|
if (Thread == NULL) {
|
|
//
|
|
// Look for A Ctrl C message was sent from the console
|
|
//
|
|
if (ExceptionCode == DBG_CONTROL_C ||
|
|
ExceptionCode == DBG_CONTROL_BREAK ) {
|
|
OS2SESREQUESTMSG CtrlMsg;
|
|
POS2_PROCESS CtrlProcess;
|
|
CtrlProcess = Os2LocateProcessByClientId(&(ReceiveMsg->h.ClientId));
|
|
|
|
//
|
|
// We got CtrlC/CtrlBrk BUGBUG - don't send it when in RAW mode
|
|
//
|
|
if (CtrlProcess != NULL){
|
|
if (CtrlProcess->Session != NULL &&
|
|
!CtrlProcess->Session->InTermination &&
|
|
!Os2CLI) {
|
|
|
|
CtrlMsg.Session = CtrlProcess->Session;
|
|
SesGrp = (POS2_SES_GROUP_PARMS)CtrlProcess->Session->SesGrpAddress;
|
|
if ((SesGrp->hConsolePopUp == NULL ) && // PopUp - ignore
|
|
!SesGrp->WinProcessNumberInSession && // Win child - ignore
|
|
(((ExceptionCode == DBG_CONTROL_C) &&
|
|
!(SesGrp->ModeFlag & 1 )) || // ^C in binary (RAW) mode - ignore
|
|
#if PMNT
|
|
// Don't propagate CTRL-BREAK in case of a PM process !
|
|
((ExceptionCode == DBG_CONTROL_BREAK) &&
|
|
!Os2srvProcessIsPMProcess(CtrlProcess))))
|
|
#else
|
|
(ExceptionCode == DBG_CONTROL_BREAK)))
|
|
#endif
|
|
{
|
|
if (ExceptionCode == DBG_CONTROL_C){
|
|
#if DBG
|
|
IF_OS2_DEBUG(SIG) {
|
|
KdPrint(("Ctrl C Received, Process Id %d\n", CtrlProcess->ProcessId));
|
|
}
|
|
#endif
|
|
CtrlMsg.d.Signal.Type = XCPT_SIGNAL_INTR;
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG(SIG) {
|
|
KdPrint(("Ctrl Break Received, Process Id %d\n", CtrlProcess->ProcessId));
|
|
}
|
|
#endif
|
|
CtrlMsg.d.Signal.Type = XCPT_SIGNAL_BREAK;
|
|
}
|
|
Status = Os2CtrlSignalHandler(&CtrlMsg, CtrlProcess);
|
|
}
|
|
}
|
|
}
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
else {
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL &&
|
|
(ExceptionCode == STATUS_BREAKPOINT || ExceptionCode == STATUS_SINGLE_STEP)
|
|
){
|
|
//
|
|
// BreakPoint in one of the service threads of
|
|
// OS2.exe, under ntsd
|
|
//
|
|
DbgSsHandleKmApiMsg(ReceiveMsg, NULL);
|
|
return;
|
|
}
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Exception Happened within app termination in win32\n"));
|
|
#endif
|
|
if (!ReceiveMsg->u.Exception.FirstChance) {
|
|
//
|
|
// Second Chance message in 32 bit code - break to debugger
|
|
//
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL) {
|
|
DbgSsHandleKmApiMsg(ReceiveMsg, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the context record for the target thread.
|
|
//
|
|
Context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
|
|
Status = NtGetContextThread(Thread->ThreadHandle,
|
|
&Context);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG(MISC) {
|
|
KdPrint(("OS2SRV, HandleDebugEvent: ExceptionCode = %x\n",
|
|
ExceptionCode));
|
|
KdPrint(("OS2SRV: HandleDebugEvent: Exception occurred at cs:eip = %x:%x\n",
|
|
Context.SegCs,
|
|
Context.Eip));
|
|
}
|
|
#endif
|
|
|
|
switch (ExceptionCode) {
|
|
|
|
case STATUS_BREAKPOINT:
|
|
case STATUS_SINGLE_STEP:
|
|
|
|
Process = Thread->Process;
|
|
|
|
if (Process->Flags & OS2_PROCESS_TRACE &&
|
|
Process->ProcessMTE &&
|
|
Context.SegCs != 0x1B) {
|
|
|
|
//
|
|
// The process is being traced by an OS/2 debugger,
|
|
// it is completed loading, and it is in 16 bit code -
|
|
// need to wake the 16bit debugger, waiting on DosPtrace
|
|
//
|
|
if (!Os2NotifyWait( WaitThread, Thread, (PVOID)ExceptionCode)) {
|
|
if (!Os2NotifyWait( WaitProcess, Thread, (PVOID)ExceptionCode)) {
|
|
#if DBG
|
|
KdPrint(("OS2SRV, HandleDebugEvent: debuggee signaled %lx, fail to notify debugger\n",
|
|
ExceptionCode));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// if running under the ntsd debugger, break in, else
|
|
// GP popup
|
|
//
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL){
|
|
#if DBG
|
|
IF_OS2_DEBUG(MISC) {
|
|
KdPrint(("OS2SRV: Received breakpoint at cs:eip = %x:%x, ignored\n",
|
|
Context.SegCs,
|
|
Context.Eip));
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
else {
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
return;
|
|
}
|
|
|
|
case STATUS_ILLEGAL_FLOAT_CONTEXT:
|
|
if (Os2DispatchVector(ReceiveMsg, Thread, Context)) {
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Floating point instruction executed with no floating point emulator installed*\n"));
|
|
#endif
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
return;
|
|
}
|
|
else {
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
|
case STATUS_INTEGER_OVERFLOW:
|
|
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
|
case STATUS_ILLEGAL_INSTRUCTION:
|
|
case STATUS_FLOAT_DENORMAL_OPERAND:
|
|
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
|
case STATUS_FLOAT_INEXACT_RESULT:
|
|
case STATUS_FLOAT_INVALID_OPERATION:
|
|
case STATUS_FLOAT_OVERFLOW:
|
|
case STATUS_FLOAT_STACK_CHECK:
|
|
case STATUS_FLOAT_UNDERFLOW:
|
|
if (Os2DispatchVector(ReceiveMsg, Thread, Context)) {
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
return;
|
|
}
|
|
else {
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
case STATUS_PRIVILEGED_INSTRUCTION:
|
|
//
|
|
// Check to see if client or server raised the exception
|
|
// if so return as not handled so the stack based handler
|
|
// will handle it.
|
|
//
|
|
if (Context.SegCs == 0x1b) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
KdPrint(( "Os2HandleDebugEvent - STATUS_PRIVELEGED_INSTRUCTION at 32 bit code %X:%X, let stack based handler deal with it\n",
|
|
Context.SegCs,Context.Eip));
|
|
}
|
|
#endif
|
|
if (!ReceiveMsg->u.Exception.FirstChance) {
|
|
//
|
|
// Second Chance message in 32 bit code - break to debugger
|
|
//
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL) {
|
|
DbgSsHandleKmApiMsg(ReceiveMsg, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
|
|
FlatCliAddress = (ULONG)(SELTOFLAT((USHORT)Context.SegCs)) | (ULONG)(Context.Eip);
|
|
Status = NtReadVirtualMemory( Thread->Process->ProcessHandle,
|
|
(PVOID) FlatCliAddress,
|
|
(PVOID) &(ucInst),
|
|
1,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - Read Instruction, failed to read %x:%x Status %lx\n", Context.SegCs,Context.Eip, Status));
|
|
#endif
|
|
}
|
|
|
|
if (ucInst == 0xFA) {
|
|
|
|
POS2_PROCESS TmpProcess = NULL;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - CLI instruction got STATUS_PRIVILEGED_INSTRUCTION\n"));
|
|
#endif
|
|
//
|
|
// for a CLI we freeze all os/2 threads
|
|
//
|
|
|
|
if (!Os2CLI){
|
|
|
|
Os2CLI = TRUE;
|
|
Os2CLIThread = Thread;
|
|
for (
|
|
ListHead = &Os2RootProcess->ListLink,
|
|
ListNext = ListHead->Flink;
|
|
ListNext != ListHead ;
|
|
ListNext = ListNext->Flink
|
|
) {
|
|
TmpProcess = CONTAINING_RECORD( ListNext, OS2_PROCESS, ListLink );
|
|
if ( TmpProcess != Thread->Process ) {
|
|
Os2SuspendProcess(TmpProcess);
|
|
}
|
|
else {
|
|
//
|
|
// for the calling process, freeze other threads
|
|
//
|
|
POS2_THREAD TmpThread = NULL;
|
|
PLIST_ENTRY ThreadListHead, ThreadListNext;
|
|
ThreadListHead = &TmpProcess->ThreadList;
|
|
ThreadListNext = ThreadListHead->Flink;
|
|
while (ThreadListNext != ThreadListHead) {
|
|
TmpThread = CONTAINING_RECORD( ThreadListNext, OS2_THREAD, Link );
|
|
ThreadListNext = ThreadListNext->Flink;
|
|
if (TmpThread != Thread) {
|
|
NtSuspendThread(TmpThread->ThreadHandle, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// A second CLI can come before all threads are suspended
|
|
// we let the latter, if not from the same thread,
|
|
// to get the exception next time, by not incrementing
|
|
// his eip
|
|
//
|
|
if (Os2CLIThread == Thread){
|
|
Context.Eip += 1;
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - Read Instruction, failed to write %x:%x Status %lx\n", Context.SegCs,Context.Eip, Status));
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - Second CLI from another thread\n"));
|
|
#endif
|
|
}
|
|
//
|
|
// let the kernel continue
|
|
//
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
#if PMNT
|
|
else if (ucInst == 0xE4 || ucInst == 0xE5 || ucInst == 0xEC || ucInst == 0xED) {
|
|
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - IN instruction got STATUS_PRIVILEGED_INSTRUCTION\n"));
|
|
#endif
|
|
//
|
|
// See if PM can handle by mapping ports
|
|
//
|
|
if (Os2srvProcessIsPMProcess(Thread->Process) &&
|
|
PMHandledInOut(Thread))
|
|
{
|
|
|
|
//
|
|
// let the kernel continue
|
|
//
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
}
|
|
else {
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
}
|
|
}
|
|
else if (ucInst == 0xE6 || ucInst == 0xE7 || ucInst == 0xEE || ucInst == 0xEF) {
|
|
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - OUT instruction got STATUS_PRIVILEGED_INSTRUCTION\n"));
|
|
#endif
|
|
//
|
|
// See if PM can handle by mapping ports
|
|
//
|
|
if (Os2srvProcessIsPMProcess(Thread->Process) &&
|
|
PMHandledInOut(Thread))
|
|
{
|
|
|
|
//
|
|
// let the kernel continue
|
|
//
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
}
|
|
else {
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
}
|
|
}
|
|
#endif // PMNT
|
|
else {
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
}
|
|
return;
|
|
break;
|
|
|
|
case STATUS_ACCESS_VIOLATION:
|
|
|
|
//
|
|
// Check to see if client or server raised the exception
|
|
// if so return as not handled so the stack based handler
|
|
// will handle it.
|
|
//
|
|
if (Context.SegCs == 0x1b) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
KdPrint(( "Os2HandleDebugEvent - STATUS_ACCESS_VIOLATION at 32 bit code %X:%X, let stack based handler deal with it\n",
|
|
Context.SegCs,Context.Eip));
|
|
}
|
|
#endif
|
|
if (!ReceiveMsg->u.Exception.FirstChance) {
|
|
//
|
|
// Second Chance message in 32 bit code - break to debugger
|
|
//
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL) {
|
|
DbgSsHandleKmApiMsg(ReceiveMsg, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
|
|
FlatCliAddress = (ULONG)(SELTOFLAT((USHORT)Context.SegCs)) | (ULONG)(Context.Eip);
|
|
Status = NtReadVirtualMemory( Thread->Process->ProcessHandle,
|
|
(PVOID) FlatCliAddress,
|
|
(PVOID) &(ucInst),
|
|
1,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - Read Instruction, failed to read %x:%x Status %lx\n", Context.SegCs,Context.Eip, Status));
|
|
#endif
|
|
}
|
|
|
|
if (ucInst == 0xFB) {
|
|
|
|
POS2_PROCESS TmpProcess = NULL;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - STI instruction got STATUS_ACCESS_VIOLATION\n"));
|
|
#endif
|
|
//
|
|
// for STI we resume all os/2 threads
|
|
//
|
|
|
|
if (Os2CLI){
|
|
//
|
|
// Need to resume all threads, otherwise STI is mute
|
|
//
|
|
Os2CLI = FALSE;
|
|
for (
|
|
ListHead = &Os2RootProcess->ListLink,
|
|
ListNext = ListHead->Flink;
|
|
ListNext != ListHead ;
|
|
ListNext = ListNext->Flink
|
|
) {
|
|
TmpProcess = CONTAINING_RECORD( ListNext, OS2_PROCESS, ListLink );
|
|
if ( TmpProcess != Thread->Process ) {
|
|
Os2ResumeProcess(TmpProcess);
|
|
}
|
|
else {
|
|
//
|
|
// for the calling process, freeze other threads
|
|
//
|
|
POS2_THREAD TmpThread = NULL;
|
|
PLIST_ENTRY ThreadListHead, ThreadListNext;
|
|
ThreadListHead = &TmpProcess->ThreadList;
|
|
ThreadListNext = ThreadListHead->Flink;
|
|
while (ThreadListNext != ThreadListHead) {
|
|
TmpThread = CONTAINING_RECORD( ThreadListNext, OS2_THREAD, Link );
|
|
ThreadListNext = ThreadListNext->Flink;
|
|
if (TmpThread != Thread) {
|
|
NtResumeThread(TmpThread->ThreadHandle, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Context.Eip += 1;
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
KdPrint(( "Os2HandleDebugEvent - Read Instruction, failed to write %x:%x Status %lx\n", Context.SegCs,Context.Eip, Status));
|
|
#endif
|
|
}
|
|
//
|
|
// let the kernel continue
|
|
//
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
else {
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
}
|
|
return;
|
|
break;
|
|
default:
|
|
|
|
//
|
|
// Check to see if client or server raised the exception
|
|
// if so return as not handled so the stack based handler
|
|
// will handle it.
|
|
//
|
|
if (Context.SegCs == 0x1b) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
KdPrint(( "Os2HandleDebugEvent - Exception %X at 32 bit code %X:%X, let stack based handler deal with it\n",
|
|
ReceiveMsg->u.Exception.ExceptionRecord.ExceptionCode,
|
|
Context.SegCs,Context.Eip));
|
|
}
|
|
#endif
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
return;
|
|
}
|
|
Os2HandleException(Process,ReceiveMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ReceiveMsg->ApiNumber == DbgKmExitProcessApi) {
|
|
|
|
POS2_PROCESS CtrlProcess;
|
|
CLIENT_ID CtrlProcessCid = ReceiveMsg->h.ClientId;
|
|
|
|
if (ReceiveMsg->h.ClientId.UniqueProcess == FirstOs2ProcessClientId.UniqueProcess) {
|
|
//
|
|
// The os2 client that started os2srv is gone,
|
|
// the logoff/shutdown logic of os2srv needs to know
|
|
// it,mark it.
|
|
//
|
|
FirstOs2ProcessHandle = (HANDLE)-1;
|
|
}
|
|
|
|
//
|
|
// Let the kernel go so other os2srv threads are not blocked
|
|
// on this process.
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL){
|
|
DbgSsHandleKmApiMsg(ReceiveMsg, NULL);
|
|
}
|
|
else {
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
}
|
|
//
|
|
// now acquire the lock so we can handle os2srv structures
|
|
//
|
|
Os2AcquireStructureLock();
|
|
//
|
|
// No living threads - just cleanup after the process if it still exists
|
|
// in our structures
|
|
//
|
|
CtrlProcess = Os2LocateProcessByClientId(&CtrlProcessCid);
|
|
if (CtrlProcess != NULL){
|
|
//
|
|
// Like Os2DosExit, but no xtlremotecall
|
|
//
|
|
OS2_API_MSG m;
|
|
POS2_TERMINATEPROCESS_MSG a = &m.u.TerminateProcess;
|
|
POS2_THREAD Thread, Thread1;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PORT_MSG_DATA_LENGTH(m) = sizeof(m) - sizeof(PORT_MESSAGE);
|
|
PORT_MSG_TOTAL_LENGTH(m) = sizeof(m);
|
|
PORT_MSG_ZERO_INIT(m) = 0L;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
KdPrint(( "Os2HandleDebugEvent - Process Terminated message, but Process structure still exists" ));
|
|
KdPrint(( "\tProcess %X. Application will be terminated\n", CtrlProcess));
|
|
}
|
|
#endif
|
|
a->ExitReason = TC_EXIT;
|
|
a->ExitResult = ERROR_INTERRUPT;
|
|
((POS2_DOSEXIT_MSG)a)->ExitAction = EXIT_PROCESS;
|
|
|
|
ListHead = &CtrlProcess->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
ListNext = ListNext->Flink;
|
|
if (Thread->Dying || (Thread->Flags & OS2_THREAD_THREAD1)) {
|
|
Thread1 = Thread;
|
|
continue;
|
|
}
|
|
Os2InternalTerminateThread(Thread, &m);
|
|
}
|
|
Os2InternalTerminateProcess(Thread1, &m);
|
|
}
|
|
Os2ReleaseStructureLock();
|
|
return;
|
|
}
|
|
|
|
if (Os2DebugUserClientId.UniqueProcess != NULL){
|
|
DbgSsHandleKmApiMsg(ReceiveMsg, NULL);
|
|
}
|
|
else {
|
|
ReceiveMsg->ReturnedStatus = DBG_EXCEPTION_HANDLED;
|
|
NtReplyPort(Os2DebugPort,
|
|
(PPORT_MESSAGE) ReceiveMsg);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosKillProcess(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosKillProcess API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSKILLPROCESS_MSG a = &m->u.DosKillProcess;
|
|
POS2_PROCESS Process;
|
|
BOOLEAN NoHandler = TRUE;
|
|
|
|
|
|
//
|
|
// for (each process to be killed)
|
|
// look up thread 1
|
|
// if (dying)
|
|
// ERROR_INVALID_PROC_ID
|
|
// issues SIGTERM
|
|
//
|
|
|
|
Process = Os2LocateProcessByProcessId( m,
|
|
t->Process,
|
|
(PID)a->ProcessId,
|
|
(BOOLEAN)FALSE
|
|
);
|
|
|
|
if (Process != NULL) {
|
|
POS2_SESSION Session = Process->Session;
|
|
POS2_REGISTER_HANDLER_REC pRec;
|
|
|
|
if ((pRec = Session->RegisterCtrlHandler) != NULL) {
|
|
while (pRec != NULL) {
|
|
if (pRec->Signal == (ULONG)XCPT_SIGNAL_KILLPROC &&
|
|
pRec->fAction != SIGA_IGNORE ) {
|
|
//
|
|
// See if a the registered handler
|
|
// is child of Process
|
|
//
|
|
POS2_PROCESS p = pRec->Process;
|
|
while (p != Process && p != Os2RootProcess){
|
|
p = p->Parent;
|
|
}
|
|
if (p == Process){
|
|
NoHandler = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
pRec = pRec->Link;
|
|
}
|
|
}
|
|
if (a->KillTarget == DKP_PROCESS) {
|
|
if (NoHandler || (pRec->Process != Process)){
|
|
Os2SigKillProcess(Process);
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
}
|
|
else {
|
|
m->ReturnedErrorValue = Os2IssueSignal(Process, XCPT_SIGNAL_KILLPROC);
|
|
}
|
|
}
|
|
else {
|
|
if (NoHandler){
|
|
Os2SigKillProcessTree(Process, TRUE);
|
|
}
|
|
else
|
|
Os2IssueSignalTree(Process, XCPT_SIGNAL_KILLPROC);
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
Os2FillErrorTextBuffer(
|
|
IN OUT PSTRING ErrorText,
|
|
IN PSTRING Contents,
|
|
IN ULONG ContentsIndex
|
|
)
|
|
{
|
|
ULONG n;
|
|
|
|
if (ErrorText->MaximumLength != 0) {
|
|
n = Contents->Length - ContentsIndex;
|
|
if (n > (ULONG)ErrorText->MaximumLength) {
|
|
n = ErrorText->MaximumLength;
|
|
}
|
|
|
|
ErrorText->Length = (USHORT)n;
|
|
RtlMoveMemory( ErrorText->Buffer,
|
|
Contents->Buffer + ContentsIndex,
|
|
n);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Os2FillErrorTextBufferFile(
|
|
IN OUT PSTRING ErrorText,
|
|
IN PUCHAR Contents
|
|
)
|
|
{
|
|
ULONG n;
|
|
|
|
n = strlen(Contents);
|
|
if (n != 0) {
|
|
if (n > (ULONG)ErrorText->MaximumLength) {
|
|
n = ErrorText->MaximumLength;
|
|
}
|
|
|
|
ErrorText->Length = (USHORT)n;
|
|
RtlMoveMemory( ErrorText->Buffer,
|
|
Contents,
|
|
n);
|
|
}
|
|
}
|
|
|
|
|
|
APIRET
|
|
Os2CreateProcess(
|
|
IN PVOID RequestMsg OPTIONAL,
|
|
IN POS2_THREAD t OPTIONAL,
|
|
POS2_DOSEXECPGM_MSG a,
|
|
POS2_SESSION Session OPTIONAL,
|
|
POS2_THREAD *NewThread
|
|
)
|
|
{
|
|
// New Child Child Dos Dos
|
|
// Session Session Process ExecPgm StartSession
|
|
// _______ _______ _______ _______ ____________
|
|
// RequestMsg + + +
|
|
// t + +
|
|
// a + + + + +
|
|
// Session + + +
|
|
// *NewThread NULL + + NULL NULL
|
|
//
|
|
// if RequestMsg -> new/child session/process (from ConCreat.c)
|
|
// if t -> ExecPgm/StartSession (from ApiReqst.c)
|
|
// if !Session -> ExecPgm/child process
|
|
// if *NewThread -> child process/session
|
|
//
|
|
// if Session == NULL and RequestMsg == NULL ->DosExecPgm
|
|
// else if Session == NULL *NewThread != NULL ->ConCreate that is not a session root
|
|
// else if Session->ChildSession ->DosStartSession (receive also t parm) ??
|
|
// else if Session && RequestMsg -> NewSession
|
|
//
|
|
|
|
NTSTATUS Status;
|
|
POS2_PROCESS ParentProcess, Process = NULL;
|
|
HANDLE ParentProcessHandle;
|
|
POS2_THREAD Thread;
|
|
APIRET RetCode;
|
|
CONTEXT Context;
|
|
HANDLE ReplyEvent;
|
|
ULONG Length;
|
|
PROCESS_BASIC_INFORMATION BasicInfo;
|
|
|
|
// uses stuff from the parent process
|
|
if ( ARGUMENT_PRESENT(t) ) // && !ARGUMENT_PRESENT(Session) )
|
|
{
|
|
ParentProcess = t->Process;
|
|
ParentProcessHandle = t->Process->ProcessHandle;
|
|
} else
|
|
{
|
|
ParentProcess = NULL;
|
|
ParentProcessHandle = NULL;
|
|
}
|
|
|
|
if (*NewThread != NULL)
|
|
{
|
|
//
|
|
// This is a child process/session that was already created
|
|
// by DosExecPgm/DosStartSession
|
|
//
|
|
Process = (*NewThread)->Process;
|
|
} else
|
|
{
|
|
|
|
//
|
|
// Initialize the Process and Thread strucutures
|
|
//
|
|
if (ARGUMENT_PRESENT(RequestMsg))
|
|
{
|
|
Process = (POS2_PROCESS)Session->Process;
|
|
} else
|
|
{
|
|
Process = Os2AllocateProcess();
|
|
}
|
|
if (Process == NULL)
|
|
{
|
|
Os2DereferenceSession(Session, 0, (BOOLEAN)TRUE);
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
switch( a->Flags )
|
|
{
|
|
case EXEC_TRACETREE:
|
|
Process->Flags |= OS2_PROCESS_TRACETREE;
|
|
// fall through
|
|
|
|
case EXEC_TRACE:
|
|
Process->Flags |= OS2_PROCESS_TRACE;
|
|
// fall through
|
|
|
|
case EXEC_ASYNCRESULT:
|
|
Process->Flags |= OS2_PROCESS_SAVERESULT;
|
|
// fall through
|
|
|
|
case EXEC_ASYNC:
|
|
break;
|
|
|
|
case EXEC_FROZEN:
|
|
Process->Flags |= OS2_PROCESS_FROZEN;
|
|
break;
|
|
|
|
case EXEC_BACKGROUND:
|
|
Process->Flags |= OS2_PROCESS_BACKGROUND;
|
|
break;
|
|
|
|
case EXEC_SYNC:
|
|
Process->Flags |= OS2_PROCESS_SYNCHRONOUS;
|
|
break;
|
|
}
|
|
|
|
if ((Thread = Os2AllocateThread(
|
|
#ifdef PMNT
|
|
DCT_RUNABLE,
|
|
#endif
|
|
Process )) == NULL)
|
|
{
|
|
Os2DeallocateProcess( Process );
|
|
Os2DereferenceSession(Session, 0, (BOOLEAN)TRUE);
|
|
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
}
|
|
|
|
if (*NewThread != NULL)
|
|
{
|
|
//
|
|
// case of os2.exe calling after a DosExecPgm was executed
|
|
// everything was set by Parent DosExecPgm - return to ConCreate
|
|
//
|
|
Thread = *NewThread;
|
|
} else
|
|
{
|
|
//
|
|
// Setup the Process and Thread structures
|
|
//
|
|
|
|
Length = (ULONG)a->ApplNameLength;
|
|
if (( Length >= 5 ) &&
|
|
(!strnicmp(&a->ApplName[Length - 5], ".exe", 4)))
|
|
{
|
|
Length -= 4; // remove the ".EXE from the appl name
|
|
}
|
|
strncpy(Process->ApplName,
|
|
&a->ApplName[0],
|
|
Length);
|
|
|
|
Process->ApplName[Length - 1] = '\0';
|
|
strupr(Process->ApplName);
|
|
Process->ApplNameLength = Length;
|
|
|
|
if (ARGUMENT_PRESENT(RequestMsg))
|
|
{
|
|
POS2SESREQUESTMSG pReceiveMsg = (POS2SESREQUESTMSG)RequestMsg;
|
|
|
|
//
|
|
// A new session is created
|
|
//
|
|
Process->ClientId = Thread->ClientId = pReceiveMsg->h.ClientId;
|
|
Process->ProcessHandle = pReceiveMsg->d.Create.d.In.hProcess;
|
|
Thread->ThreadHandle = pReceiveMsg->d.Create.d.In.hThread;
|
|
Process->Session = Session;
|
|
} else
|
|
{
|
|
//
|
|
// DosExecPgm or DosStartSession
|
|
//
|
|
Thread->ThreadHandle = a->hThread;
|
|
Thread->ClientId = a->ClientId;
|
|
Process->ProcessHandle = a->hProcess;
|
|
Process->ClientId = a->ClientId;
|
|
if (ARGUMENT_PRESENT(Session))
|
|
{
|
|
Process->Session = Session;
|
|
} else
|
|
{
|
|
Process->Session = ParentProcess->Session;
|
|
Os2ReferenceSession(Process->Session);
|
|
}
|
|
}
|
|
|
|
Thread->Flags = OS2_THREAD_THREAD1;
|
|
Thread->Os2Class = PRTYC_REGULAR;
|
|
Thread->Os2Level = 0;
|
|
|
|
if (Process->Flags & OS2_PROCESS_FROZEN ||
|
|
Process->Flags & OS2_PROCESS_TRACE)
|
|
{
|
|
NtSuspendThread( Thread->ThreadHandle, NULL );
|
|
}
|
|
|
|
if ( !ARGUMENT_PRESENT(Session) )
|
|
{
|
|
// DosExecPgm
|
|
|
|
RetCode = InitializeFileSystemForExec(&(a->FileSystemParameters),
|
|
ParentProcessHandle,
|
|
Process->ProcessHandle,
|
|
ParentProcess,
|
|
Process,
|
|
a
|
|
);
|
|
} else if (ARGUMENT_PRESENT(t) )
|
|
{
|
|
// DosStartSession
|
|
|
|
RetCode = InitializeFileSystemForChildSesMgr(
|
|
&(a->FileSystemParameters),
|
|
ParentProcessHandle,
|
|
Process->ProcessHandle,
|
|
ParentProcess,
|
|
Process,
|
|
a
|
|
);
|
|
} else
|
|
{
|
|
// New Session
|
|
|
|
InitializeFileSystemForSesMgr(Process);
|
|
RetCode = NO_ERROR;
|
|
}
|
|
|
|
if (RetCode)
|
|
{
|
|
try
|
|
{
|
|
NtTerminateProcess( Process->ProcessHandle,
|
|
STATUS_INVALID_IMAGE_FORMAT
|
|
);
|
|
NtWaitForSingleObject( Process->ProcessHandle, (BOOLEAN)TRUE, NULL );
|
|
NtClose( Thread->ThreadHandle );
|
|
NtClose( Process->ProcessHandle );
|
|
|
|
Os2DereferenceSession(Session, 0, (BOOLEAN)TRUE);
|
|
Os2DeallocateThread( Thread );
|
|
Os2DeallocateProcess( Process );
|
|
|
|
Os2FillErrorTextBufferFile( &a->ErrorText,
|
|
&a->ApplName[0]
|
|
);
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
#if DBG
|
|
KdPrint(("OS2SRV: Got Error %d from InitializeFileSystemForExec\n",
|
|
RetCode));
|
|
#endif
|
|
}
|
|
return(RetCode);
|
|
}
|
|
Os2InsertThread( Process, Thread );
|
|
|
|
Os2SetProcessContext( Process,
|
|
Thread,
|
|
(BOOLEAN)(( (ARGUMENT_PRESENT(Session)) && !(ARGUMENT_PRESENT(t)) ) ?
|
|
(BOOLEAN) TRUE :
|
|
(BOOLEAN) FALSE),
|
|
Process->HandleTableLength,
|
|
a->FileSystemParameters.CurrentDrive,
|
|
ARGUMENT_PRESENT(Session) ? 0L : a->CodePage
|
|
);
|
|
|
|
}
|
|
//
|
|
// Attach the process to the os2srv debug port (process created by csr)
|
|
//
|
|
if (ARGUMENT_PRESENT(RequestMsg))
|
|
{
|
|
PSCREQ_CREATE Create = & ((POS2SESREQUESTMSG)RequestMsg)->d.Create;
|
|
|
|
Status = NtCreateEvent(
|
|
&ReplyEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
#if DBG
|
|
KdPrint(( "Os2CreateProcess failed: NtCreateEvent Status == %X\n",
|
|
Status
|
|
));
|
|
#endif // DBG
|
|
return(Status);
|
|
}
|
|
|
|
Status = Os2DebugProcess(
|
|
&(Thread->ClientId),
|
|
Thread,
|
|
ReplyEvent);
|
|
if (!NT_SUCCESS( Status ) )
|
|
{
|
|
if (Status != STATUS_UNSUCCESSFUL){
|
|
#if DBG
|
|
KdPrint(( "Os2CreateProcess, Error at Os2DebugProcess, Status==%X\n", Status));
|
|
#endif
|
|
}
|
|
} else if (ARGUMENT_PRESENT(Session))
|
|
{
|
|
//
|
|
// This is a root process of a session, attach os2srv debug port to
|
|
// the win32 threads of it
|
|
//
|
|
Status = Os2DebugThread(
|
|
Create->d.In.hEventThread,
|
|
ReplyEvent);
|
|
if (!NT_SUCCESS( Status ) )
|
|
{
|
|
#if DBG
|
|
KdPrint(( "Os2CreateProcess, Error attaching to EventThread, Status==%X\n", Status));
|
|
#endif
|
|
}
|
|
|
|
Status = Os2DebugThread(
|
|
Create->d.In.hSessionRequestThread,
|
|
ReplyEvent);
|
|
if (!NT_SUCCESS( Status ) )
|
|
{
|
|
#if DBG
|
|
KdPrint(( "Os2CreateProcess, Error attaching to SessionRequestThread, Status==%X\n", Status));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Session))
|
|
{
|
|
NtClose(Create->d.In.hSessionRequestThread);
|
|
NtClose(Create->d.In.hEventThread);
|
|
}
|
|
NtClose(ReplyEvent);
|
|
}
|
|
|
|
if (*NewThread)
|
|
{
|
|
//
|
|
// At this place, DosExecPgm that came before set it all up,
|
|
// so return
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
Os2InsertProcess( Session ? (POS2_PROCESS)NULL : ParentProcess,
|
|
Process
|
|
);
|
|
|
|
a->ResultProcessId = Process->ProcessId;
|
|
|
|
if (Session)
|
|
{
|
|
Session->ProcessId = (ULONG)Process->ProcessId;
|
|
|
|
//
|
|
// Save the process unique id, and the process parent unique
|
|
// id. We will use it in Os2DereferenceSession, when a win32 process
|
|
// is about to terminate, and we have to terminate its children.
|
|
//
|
|
Status = NtQueryInformationProcess(
|
|
Process->ProcessHandle,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
#if DBG
|
|
DbgPrint( "Os2CreateProcess failed: NtQueryProcessInformation Status == %X\n",
|
|
Status
|
|
);
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
Session->dwProcessId = BasicInfo.UniqueProcessId;
|
|
Session->dwParentProcessId = BasicInfo.InheritedFromUniqueProcessId;
|
|
}
|
|
}
|
|
|
|
if (!(Process->Flags & OS2_PROCESS_SYNCHRONOUS))
|
|
{
|
|
a->ResultCodes.ExitReason = (ULONG)a->ResultProcessId;
|
|
}
|
|
|
|
*NewThread = Thread;
|
|
|
|
//
|
|
// Get the context record for the new thread. Save Esp for Exception
|
|
// Handling
|
|
//
|
|
Context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
//
|
|
// mask FP exceptions, just in case...
|
|
//
|
|
Context.FloatSave.ControlWord |= 0x3f; // mask exceptions
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
//
|
|
// Remember Initial Stack, for Cleanup in case of exception (os2handleexception())
|
|
//
|
|
Thread->InitialStack = Context.Esp;
|
|
|
|
return( NO_ERROR );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosExecPgm(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_THREAD NewThread = NULL;
|
|
NTSTATUS Status;
|
|
ULONG CmdLineFlag;
|
|
|
|
CmdLineFlag = m->u.DosExecPgm.CmdLineFlag;
|
|
m->u.DosExecPgm.CmdLineFlag &= (~CMD_SHORTCUT);
|
|
|
|
m->ReturnedErrorValue = Os2CreateProcess(
|
|
NULL,
|
|
t,
|
|
&m->u.DosExecPgm,
|
|
NULL,
|
|
&NewThread
|
|
);
|
|
|
|
if ( m->ReturnedErrorValue == NO_ERROR ) {
|
|
if (CmdLineFlag){
|
|
Os2PrepareCmdSignals(NewThread->Process);
|
|
}
|
|
if (NewThread->Process->Flags & OS2_PROCESS_SYNCHRONOUS) {
|
|
if (Os2CreateWait( WaitProcess,
|
|
(OS2_WAIT_ROUTINE)Os2WaitChildSatisfy,
|
|
t,
|
|
m,
|
|
NULL,
|
|
NULL )
|
|
) {
|
|
if (!Os2CLI){
|
|
Status = NtResumeThread( NewThread->ThreadHandle, NULL );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
return( FALSE );
|
|
}
|
|
else {
|
|
POS2_PROCESS p = NewThread->Process;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
Os2DeallocateThread( NewThread );
|
|
Os2DereferenceSession(p->Session, 0, (BOOLEAN)TRUE);
|
|
Os2DeallocateProcess( p );
|
|
NtClose(m->u.DosExecPgm.hThread);
|
|
NtClose(m->u.DosExecPgm.hProcess);
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
if (!Os2CLI){
|
|
Status = NtResumeThread( NewThread->ThreadHandle, NULL );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
return( TRUE );
|
|
}
|
|
else {
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosCreateThread(
|
|
IN POS2_THREAD argt,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
NTSTATUS Status = NO_ERROR;
|
|
POS2_DOSCREATETHREAD_MSG a = &m->u.DosCreateThread;
|
|
POS2_THREAD Thread, t;
|
|
CONTEXT Context;
|
|
POS2_PROCESS Process;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Entering Os2DosCreateThread\n"));
|
|
}
|
|
#endif
|
|
|
|
Process = Os2LocateProcessByClientId(&(m->h.ClientId));
|
|
if (Process == NULL) {
|
|
#if DBG
|
|
KdPrint(( "Os2DosCreateThread, Unknown Thread and Process\n"));
|
|
#endif
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
return( TRUE );
|
|
}
|
|
//
|
|
// Take parameters from Thread1 of the Process
|
|
//
|
|
t = CONTAINING_RECORD( Process->ThreadList.Flink, OS2_THREAD, Link );
|
|
|
|
if (Thread = Os2AllocateThread(
|
|
#ifdef PMNT
|
|
a->Flags,
|
|
#endif
|
|
t->Process )) {
|
|
Thread->ClientOs2Tib = a->ClientOs2Tib;
|
|
Thread->Os2Class = t->Os2Class;
|
|
Thread->Os2Level = t->Os2Level;
|
|
|
|
Thread->ThreadHandle = a->ThreadHandle;
|
|
Thread->ClientId = a->ClientId;
|
|
|
|
//
|
|
// If we're attaching a win32 thread, mark
|
|
// it with a special flag.
|
|
//
|
|
|
|
if (a->Flags & DCT_ATTACHED) {
|
|
Thread->Flags |= OS2_THREAD_ATTACHED;
|
|
}
|
|
|
|
}
|
|
else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
a->ThreadId = Thread->ThreadId;
|
|
|
|
Os2SetThreadPriority( Thread, Thread->Os2Class, Thread->Os2Level );
|
|
|
|
Status = NtWriteVirtualMemory( Thread->Process->ProcessHandle,
|
|
&Thread->ClientOs2Tib->ThreadId,
|
|
(PVOID)&Thread->ThreadId,
|
|
sizeof( Thread->ClientOs2Tib->ThreadId ),
|
|
NULL
|
|
);
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
Os2InsertThread( t->Process, Thread );
|
|
Context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
Context.FloatSave.ControlWord |= 0x3f; //mask exceptions, just in case
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
//
|
|
// Remember Initial Stack, for Cleanup in case of exception
|
|
// (os2handleexception())
|
|
//
|
|
Thread->InitialStack = Context.Esp;
|
|
|
|
if (Os2CLI){
|
|
//
|
|
// don't let new threads get in while CLI is in progress
|
|
//
|
|
Status = NtSuspendThread( Thread->ThreadHandle, NULL );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
else {
|
|
if (Thread != NULL) {
|
|
Os2DeallocateThread( Thread );
|
|
}
|
|
|
|
m->ReturnedErrorValue = Or2MapNtStatusToOs2Error(Status, ERROR_NO_PROC_SLOTS);
|
|
}
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Leaving Os2DosCreateThread. rc is %ld\n",m->ReturnedErrorValue));
|
|
}
|
|
#endif
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosSetPriority(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DOSSETPRIORITY_MSG a = &m->u.DosSetPriority;
|
|
POS2_PROCESS Process;
|
|
POS2_THREAD Thread;
|
|
|
|
switch( a->Scope ) {
|
|
case PRTYS_PROCESS:
|
|
Process = Os2LocateProcessByProcessId( m,
|
|
t->Process,
|
|
(PID)a->TargetId,
|
|
(BOOLEAN)TRUE
|
|
);
|
|
if (Process != NULL) {
|
|
Os2SetProcessPriority( Process, a->Class, a->Delta );
|
|
}
|
|
break;
|
|
|
|
case PRTYS_PROCESSTREE:
|
|
Process = Os2LocateProcessByProcessId( m,
|
|
t->Process,
|
|
(PID)a->TargetId,
|
|
(BOOLEAN)TRUE
|
|
);
|
|
if (Process != NULL) {
|
|
Os2SetProcessTreePriority( Process, a->Class, a->Delta );
|
|
}
|
|
break;
|
|
|
|
case PRTYS_THREAD:
|
|
Thread = Os2LocateThreadByThreadId( m, t, (TID)a->TargetId );
|
|
if (Thread != NULL) {
|
|
Os2SetThreadPriority( Thread, a->Class, a->Delta );
|
|
}
|
|
break;
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosGetPriority(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DOSGETPRIORITY_MSG a = &m->u.DosGetPriority;
|
|
POS2_PROCESS Process;
|
|
POS2_THREAD Thread;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
switch( a->Scope ) {
|
|
case PRTYS_PROCESS:
|
|
Process = Os2LocateProcessByProcessId( m,
|
|
t->Process,
|
|
(PID)a->TargetId,
|
|
//
|
|
// We put FALSE for MustBeChild parameter below
|
|
// because on OS/2 you can call DosStartSession
|
|
// and then DosGetPriority on the PID and we don't
|
|
// keep track
|
|
//
|
|
(BOOLEAN)FALSE
|
|
);
|
|
if (Process != NULL) {
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
if (Thread->Dying) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_THREADID;
|
|
}
|
|
a->Priority = (Thread->Os2Class << 8) | (UCHAR)Thread->Os2Level;
|
|
}
|
|
break;
|
|
|
|
case PRTYS_THREAD:
|
|
Thread = Os2LocateThreadByThreadId( m, t, (TID)a->TargetId );
|
|
if (Thread != NULL) {
|
|
a->Priority = (Thread->Os2Class << 8) | (UCHAR)Thread->Os2Level;
|
|
}
|
|
break;
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosGetPPID(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DOSGETPPID_MSG a = &m->u.DosGetPPID;
|
|
POS2_PROCESS Process;
|
|
|
|
Process = Os2LocateProcessByProcessId( m,
|
|
t->Process,
|
|
(PID)a->ChildPid,
|
|
(BOOLEAN)FALSE
|
|
);
|
|
if (Process != NULL) {
|
|
a->ParentPid = Process->Parent->ProcessId;
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosError(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DOSERROR_MSG a = &m->u.DosError;
|
|
|
|
t->Process->ErrorAction = a->ErrorAction;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2WaitChildSatisfy(
|
|
IN OS2_WAIT_REASON WaitReason,
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m,
|
|
IN PVOID WaitParameter,
|
|
IN POS2_PROCESS TerminatingProcess,
|
|
IN POS2_TERMINATEPROCESS_MSG a
|
|
)
|
|
{
|
|
PID ProcessId;
|
|
TID ThreadId;
|
|
ULONG len;
|
|
STRING ErrorString;
|
|
POS2_PROCESS Process;
|
|
|
|
UNREFERENCED_PARAMETER(WaitParameter);
|
|
if (WaitReason == WaitInterrupt) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// this routine is only called as a result of thread termination or
|
|
// debuggee event
|
|
//
|
|
|
|
if (m->ApiNumber == Os2WaitChild) {
|
|
ProcessId = m->u.DosWaitChild.ProcessId;
|
|
if (ProcessId == 0) {
|
|
Process = TerminatingProcess;
|
|
while ((Process = Process->Parent) != NULL) {
|
|
if (Process == t->Process) {
|
|
ProcessId = TerminatingProcess->ProcessId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (m->ApiNumber == Os2ExecPgm) {
|
|
ProcessId = m->u.DosExecPgm.ResultProcessId;
|
|
}
|
|
else
|
|
if (m->ApiNumber == Os2Ptrace) {
|
|
|
|
POS2_THREAD Thread;
|
|
CONTEXT Context;
|
|
NTSTATUS Status;
|
|
|
|
ProcessId = (PID)(m->u.DosPTrace.PtraceBuf.pid);
|
|
ThreadId = (TID)(m->u.DosPTrace.PtraceBuf.tid);
|
|
Thread = (POS2_THREAD)(TerminatingProcess);
|
|
if ((NTSTATUS)(a) != STATUS_SINGLE_STEP && (NTSTATUS)(a) != STATUS_BREAKPOINT) {
|
|
//
|
|
// This is not a single step or breakpoint message then it
|
|
// must be a termination event.
|
|
//
|
|
if (WaitReason == WaitThread) {
|
|
if ((Thread->Process->ProcessId == ProcessId) &&
|
|
(Thread->ThreadId == (TID) 1)) {
|
|
m->u.DosPTrace.PtraceBuf.cmd = (USHORT)TRC_C_THD_ret;
|
|
|
|
//
|
|
// return - debugger will be waked up
|
|
//
|
|
return(TRUE);
|
|
}
|
|
else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else if (TerminatingProcess->ProcessId == ProcessId) {
|
|
m->u.DosPTrace.PtraceBuf.cmd = (USHORT)TRC_C_KIL_ret;
|
|
//
|
|
// return - debugger will be waked up
|
|
//
|
|
return(TRUE);
|
|
}
|
|
else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
Thread->Process->DebugThreadId = Thread->ThreadId;
|
|
if (Thread->Process->ProcessId == ProcessId) {
|
|
|
|
//
|
|
// set values in return message, return TRUE
|
|
//
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2WaitChildSatisfy of debuggee. PID %d, Status/msg %lx\n", ProcessId, a));
|
|
}
|
|
#endif
|
|
|
|
m->u.DosPTrace.PtraceBuf.tid = (USHORT)(Thread->ThreadId);
|
|
//
|
|
// Get the context record for the target thread.
|
|
//
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2WaitChildStatify of Debugee fail at NtGetContextThread %lx\n", Status));
|
|
}
|
|
#endif
|
|
m->u.DosPTrace.PtraceBuf.value = 0x0005; // Child process not traceable
|
|
m->u.DosPTrace.PtraceBuf.cmd = 0xFFFF; // Error
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
return(TRUE);
|
|
}
|
|
if ((NTSTATUS)(a) == STATUS_SINGLE_STEP) {
|
|
m->u.DosPTrace.PtraceBuf.cmd = 0xFFFD; // singlestep
|
|
//
|
|
// reset TF bit in Eflags
|
|
//
|
|
//Context.EFlags &= ~(0x00000100);
|
|
Context.EFlags |= 0x10000; // RF
|
|
}
|
|
else if ((NTSTATUS)(a) == STATUS_BREAKPOINT) {
|
|
m->u.DosPTrace.PtraceBuf.cmd = 0xFFFC; // breakpoint
|
|
//
|
|
// reverse the thread one step back
|
|
//
|
|
Context.Eip--;
|
|
}
|
|
else {
|
|
//
|
|
// Unknown message
|
|
//
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2WaitChildStatify of Debugee - Unknown message %lx\n", a));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Check if it is the first time - where we need to
|
|
// restore original entry point.
|
|
//
|
|
if (ldrGetEntryPoint(Thread->Process) == (UCHAR)0xCC) {
|
|
ldrRestoreEntryPoint(Thread->Process);
|
|
}
|
|
|
|
//
|
|
// Check if there are mtes to transfer to the debugger.
|
|
//
|
|
if (((LinkMTE *)Thread->Process->LinkMte)->NeedToTransfer) {
|
|
//
|
|
// Start the protocol of TRC_C_LIB_ret until TRC_C_SUC_ret.
|
|
//
|
|
ldrReturnProgramAndLibMTE(Thread->Process,
|
|
&(m->u.DosPTrace.PtraceBuf.mte),
|
|
&(m->u.DosPTrace.PtraceBuf.value),
|
|
&(m->u.DosPTrace.PtraceBuf.cmd)
|
|
);
|
|
}
|
|
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2WaitChildStatify of Debugee fail at NtSetContextThread %lx\n", Status));
|
|
}
|
|
#endif
|
|
}
|
|
//
|
|
// Suspend the debuggee. The debugger will then resume it
|
|
//
|
|
Os2SuspendProcess(Thread->Process);
|
|
|
|
if ((NTSTATUS)(a) != STATUS_SINGLE_STEP) {
|
|
SetDebuggeeContextInMsg(&Context, &(m->u.DosPTrace.PtraceBuf));
|
|
}
|
|
|
|
//
|
|
// return - debugger will be waked up
|
|
//
|
|
return(TRUE);
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2WaitChildSatisfy of debuggee. different PID %d, msg %lx\n", ProcessId, a));
|
|
}
|
|
#endif
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
#if DBG
|
|
KdPrint(("Os2srv: Os2WaitChildSatisfy. illegal api number %d\n", m->ApiNumber));
|
|
#endif
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
if ((NTSTATUS)(a) == STATUS_SINGLE_STEP || (NTSTATUS)(a) == STATUS_BREAKPOINT) {
|
|
//
|
|
// This is a single step or breakpoint message, but the waiting
|
|
// message is not Os2DosPtrace, quit
|
|
//
|
|
return(FALSE);
|
|
}
|
|
|
|
if (TerminatingProcess->ProcessId == ProcessId) {
|
|
if (m->ApiNumber == Os2WaitChild) {
|
|
m->u.DosWaitChild.ResultCodes = TerminatingProcess->ResultCodes;
|
|
m->u.DosWaitChild.ResultProcessId = ProcessId;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
if (m->ApiNumber == Os2ExecPgm) {
|
|
m->u.DosExecPgm.ResultCodes = TerminatingProcess->ResultCodes;
|
|
m->u.DosExecPgm.ResultProcessId = 0;
|
|
len = strlen(a->ErrorText);
|
|
if (len != 0) {
|
|
ErrorString.Length = (USHORT) len + (USHORT) 1;
|
|
ErrorString.MaximumLength = (USHORT) len + (USHORT) 2;
|
|
ErrorString.Buffer = a->ErrorText;
|
|
Os2FillErrorTextBuffer(&m->u.DosExecPgm.ErrorText,
|
|
&ErrorString,
|
|
0);
|
|
m->ReturnedErrorValue =
|
|
TerminatingProcess->ResultCodes.ExitResult;
|
|
}
|
|
if (m->CaptureBuffer != NULL) {
|
|
Os2ReleaseCapturedArguments(m);
|
|
}
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosWaitChild(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DOSWAITCHILD_MSG a = &m->u.DosWaitChild;
|
|
POS2_PROCESS Process;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
// // Obsolete code below: was keeping processes around as zombie so that
|
|
// // DosCWait doesn't fail when father calls it after child termination.
|
|
// // However, it turns out that OS/2 doesn't do this.
|
|
// Process = NULL;
|
|
// ListHead = &Os2ZombieList;
|
|
// ListNext = ListHead->Flink;
|
|
// while (ListNext != ListHead) {
|
|
// Process = CONTAINING_RECORD( ListNext, OS2_PROCESS, ListLink );
|
|
// if (a->ProcessId == 0) {
|
|
// if (t->Process->Parent->ProcessId == Process->CommandSubTreeId) {
|
|
// break;
|
|
// }
|
|
// }
|
|
// else
|
|
// if (Process->ProcessId == a->ProcessId) {
|
|
// break;
|
|
// }
|
|
//
|
|
// Process = NULL;
|
|
// ListNext = ListNext->Flink;
|
|
// }
|
|
//
|
|
// if (Process != NULL) {
|
|
// a->ResultCodes = Process->ResultCodes;
|
|
// a->ResultProcessId = Process->ProcessId;
|
|
// RemoveEntryList( &Process->ListLink );
|
|
//
|
|
// Os2DeallocateProcess( Process );
|
|
// return( TRUE );
|
|
// }
|
|
|
|
ListHead = &t->Process->ChildrenList;
|
|
ListNext = ListHead->Flink;
|
|
if (ListNext == ListHead) {
|
|
m->ReturnedErrorValue = ERROR_WAIT_NO_CHILDREN;
|
|
return( TRUE );
|
|
}
|
|
|
|
while (ListNext != ListHead) {
|
|
Process = CONTAINING_RECORD( ListNext, OS2_PROCESS, SiblingLink );
|
|
if (a->ProcessId == 0) {
|
|
break;
|
|
}
|
|
else
|
|
if (Process->ProcessId == a->ProcessId) {
|
|
break;
|
|
}
|
|
|
|
Process = NULL;
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (Process == NULL) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_PROCID;
|
|
return(TRUE);
|
|
}
|
|
|
|
if (a->WaitOption == DCWW_NOWAIT) {
|
|
m->ReturnedErrorValue = ERROR_CHILD_NOT_COMPLETE;
|
|
return( TRUE );
|
|
}
|
|
|
|
if (Os2CreateWait( WaitProcess,
|
|
(OS2_WAIT_ROUTINE)Os2WaitChildSatisfy,
|
|
t,
|
|
m,
|
|
NULL,
|
|
NULL )
|
|
) {
|
|
return( FALSE );
|
|
}
|
|
else {
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2WaitThreadSatisfy(
|
|
IN OS2_WAIT_REASON WaitReason,
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m,
|
|
IN PVOID WaitParameter,
|
|
IN POS2_THREAD TerminatingThread,
|
|
IN PVOID SatisfyParameter2
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(WaitParameter);
|
|
UNREFERENCED_PARAMETER(SatisfyParameter2);
|
|
if (WaitReason == WaitInterrupt) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// the ThreadLock is held on entry to this routine. this
|
|
// routine is only called as a result of thread termination.
|
|
//
|
|
|
|
if (t->Process == TerminatingThread->Process &&
|
|
(m->u.DosWaitThread.ThreadId == 0 ||
|
|
m->u.DosWaitThread.ThreadId == TerminatingThread->ThreadId
|
|
)
|
|
) {
|
|
m->u.DosWaitThread.ThreadId = TerminatingThread->ThreadId;
|
|
return( TRUE );
|
|
}
|
|
else {
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosWaitThread(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DOSWAITTHREAD_MSG a = &m->u.DosWaitThread;
|
|
POS2_PROCESS Process;
|
|
POS2_THREAD Thread;
|
|
|
|
Process = t->Process;
|
|
|
|
if (a->ThreadId == (TID)0) {
|
|
//
|
|
// Waiting for any thread in this process to die.
|
|
//
|
|
|
|
if (t->Link.Flink == t->Link.Blink) {
|
|
//
|
|
// If we are the only thread in the list then waiting for any
|
|
// thread is invalid
|
|
//
|
|
|
|
m->ReturnedErrorValue = ERROR_INVALID_THREADID;
|
|
}
|
|
}
|
|
else
|
|
if (a->ThreadId == (TID)1) {
|
|
//
|
|
// Waiting on thread 1 is not allowed.
|
|
//
|
|
|
|
m->ReturnedErrorValue = ERROR_INVALID_THREADID;
|
|
}
|
|
else {
|
|
//
|
|
// Waiting for a specific thread in this process to die. See if
|
|
// it is a valid thread id.
|
|
//
|
|
|
|
Thread = Os2LocateThreadByThreadId( m, t, a->ThreadId );
|
|
if (Thread == NULL || Thread == t) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_THREADID;
|
|
}
|
|
}
|
|
|
|
if (m->ReturnedErrorValue == NO_ERROR) {
|
|
if (a->WaitOption == DCWW_NOWAIT) {
|
|
m->ReturnedErrorValue = ERROR_THREAD_NOT_TERMINATED;
|
|
}
|
|
else {
|
|
Os2CreateWait( WaitThread,
|
|
(OS2_WAIT_ROUTINE)Os2WaitThreadSatisfy,
|
|
t,
|
|
m,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
return (BOOLEAN)( m->ReturnedErrorValue != NO_ERROR );
|
|
}
|
|
|
|
NTSTATUS
|
|
Os2DispatchFreezeUnfreeze(
|
|
POS2_THREAD Thread,
|
|
PCONTEXT pContext,
|
|
ULONG NewEsp
|
|
)
|
|
{
|
|
POS2_PROCESS Process = Thread->Process;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Save the current context of the thread on the stack.
|
|
//
|
|
Status = NtWriteVirtualMemory(
|
|
Process->ProcessHandle,
|
|
(PVOID)NewEsp,
|
|
pContext,
|
|
sizeof(CONTEXT),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2DispatchFreezeUnfreeze[%d,%d]: Fail write context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return(Status);
|
|
}
|
|
|
|
pContext->Esp = NewEsp - sizeof(ULONG);
|
|
|
|
pContext->SegCs = 0x1b;
|
|
pContext->SegSs = pContext->SegDs = pContext->SegEs = 0x23;
|
|
|
|
//
|
|
// Save the pointer the the context on the stack.
|
|
//
|
|
Status = NtWriteVirtualMemory(
|
|
Process->ProcessHandle,
|
|
(PVOID)pContext->Esp,
|
|
&NewEsp,
|
|
sizeof(ULONG),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2DispatchFreezeUnfreeze[%d,%d]: Fail write context address, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Set the new Eip to point to the currect routine, and set the new
|
|
// context.
|
|
//
|
|
if (Thread->DebugState & TRC_Frozen) {
|
|
pContext->Eip = (ULONG)Process->FreezeThread;
|
|
} else {
|
|
pContext->Eip = (ULONG)Process->UnfreezeThread;
|
|
}
|
|
|
|
Status = NtSetContextThread(
|
|
Thread->ThreadHandle,
|
|
pContext
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2DispatchFreezeUnfreeze[%d,%d]: Fail set context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// If the thread is in wait state, it must be alerted.
|
|
// If it's not in wait state, no harm was done (the dispatch routine
|
|
// call NtTestAlert() to clear the alert kernel flag in this case).
|
|
//
|
|
NtAlertThread(Thread->ThreadHandle);
|
|
Os2CompleteResumeThread(Thread);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
VOID
|
|
Os2FreezeUnfreezeThread(
|
|
IN POS2_THREAD Thread
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
CONTEXT Context;
|
|
POS2_PROCESS Process = Thread->Process;
|
|
ULONG NewEsp;
|
|
|
|
if ((Thread->DebugState & TRC_MustBeFrozen) &&
|
|
! (Thread->DebugState & TRC_Frozen)) {
|
|
//
|
|
// The thread need to be frozen and it's not yet.
|
|
//
|
|
Thread->DebugState |= TRC_Frozen;
|
|
}
|
|
else if (!(Thread->DebugState & TRC_MustBeFrozen) &&
|
|
(Thread->DebugState & TRC_Frozen)) {
|
|
//
|
|
// The thread is frozen and it should not be.
|
|
//
|
|
Thread->DebugState &= ~TRC_Frozen;
|
|
}
|
|
else {
|
|
//
|
|
// Thread state is ok, we need not do enything.
|
|
//
|
|
return;
|
|
}
|
|
|
|
__try
|
|
{
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(
|
|
Thread->ThreadHandle,
|
|
&Context
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2FreezeUnfreezeThread[%d,%d]: Fail get context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
|
|
if (Context.SegCs == 0x1b && Context.SegSs == 0x23) {
|
|
//
|
|
// The thread is in 32bit context.
|
|
// Switch the context of the thread to the client freeze/unfreeze
|
|
// routines.
|
|
//
|
|
NewEsp = Context.Esp - sizeof(CONTEXT);
|
|
Context.SegDs = Context.SegEs = 0x23;
|
|
|
|
Status = Os2DispatchFreezeUnfreeze(
|
|
Thread,
|
|
&Context,
|
|
NewEsp
|
|
);
|
|
return;
|
|
}
|
|
else
|
|
if (Thread->ThreadId == Process->DebugThreadId) {
|
|
//
|
|
// This thread is the one that the breakpoint occured in its
|
|
// context. The context must be in 16bit, not in 32bit context
|
|
// nor the thunk.
|
|
//
|
|
#if DBG
|
|
if (Context.SegCs == 0x1b || Context.SegSs == 0x23) {
|
|
DbgPrint("Os2FreezeUnfreezeThread[%d,%d]: Breakpoint not in 16bit CS=%x, SS=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Context.SegCs,
|
|
Context.SegSs
|
|
);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
if (Thread->ThreadId == (TID) 1) {
|
|
//
|
|
// For thread 1, the 32bit stack pointer is taken from
|
|
// the Process structure. This sp is changing because of
|
|
// signals.
|
|
//
|
|
Status = NtReadVirtualMemory(
|
|
Process->ProcessHandle,
|
|
&(Process->ClientPib->Saved32Esp),
|
|
&NewEsp,
|
|
sizeof(ULONG),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2FreezeUnfreezeThread[%d,%d]: Fail read 32bit stack, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// For thread that is not thread 1, take the sp from the
|
|
// InitialzeStack pointer.
|
|
//
|
|
NewEsp = Thread->InitialStack;
|
|
}
|
|
|
|
//
|
|
// Switch the context of the thread to the client freeze/unfreeze
|
|
// routines.
|
|
//
|
|
NewEsp -= sizeof(Context);
|
|
|
|
Status = Os2DispatchFreezeUnfreeze(
|
|
Thread,
|
|
&Context,
|
|
NewEsp
|
|
);
|
|
return;
|
|
}
|
|
else {
|
|
//
|
|
// The thread is in 16bit context or in the thunk, and it is
|
|
// not the thread that the breakpoint occured in it.
|
|
//
|
|
if (Thread->DebugState & TRC_Frozen) {
|
|
Status = NtSuspendThread(
|
|
Thread->ThreadHandle,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2FreezeUnfreezeThread[%d,%d]: Fail suspend, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
Status = NtResumeThread(
|
|
Thread->ThreadHandle,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("Os2FreezeUnfreezeThread[%d,%d]: Fail resume, Status=%x\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Status
|
|
);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
//
|
|
// We didn't succeed to finish the freeze/unfreeze, so restore
|
|
// the state of the thread.
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
Thread->DebugState ^= (TRC_MustBeFrozen | TRC_Frozen);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Os2ExecuteFreezeUnfreeze(
|
|
POS2_PROCESS Process
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POS2_THREAD Thread;
|
|
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
|
|
//
|
|
// For every thread in the process, check if we need to change
|
|
// from frozen state to unfrozen, or vise versa.
|
|
//
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
Os2FreezeUnfreezeThread(Thread);
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosPTrace(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
PTRACEBUF *a = &(m->u.DosPTrace.PtraceBuf);
|
|
POS2_PROCESS Process;
|
|
CONTEXT Context;
|
|
NTSTATUS Status;
|
|
POS2_THREAD Thread, NextThread, PrevThread;
|
|
PLIST_ENTRY ListHead, ListNext, ListPrev;
|
|
UCHAR NameBuf[256];
|
|
ULONG FlatDebugeeAddress;
|
|
DBGTHREADSTATUS DbgThreadStatus;
|
|
OS2_WAIT_REASON WaitReason;
|
|
USHORT mte = 0;
|
|
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
//
|
|
// Find the debugee process
|
|
//
|
|
ListHead = &Os2RootProcess->ListLink;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Process = CONTAINING_RECORD( ListNext, OS2_PROCESS, ListLink );
|
|
if (Process->ProcessId == (PID)(a->pid)) {
|
|
break;
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (ListNext == ListHead) {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
a->value = 0x0002; // Child process not found
|
|
m->ReturnedErrorValue = ERROR_INVALID_PROCID;
|
|
return(TRUE);
|
|
}
|
|
|
|
if (!(Process->Flags & (OS2_PROCESS_TRACE | OS2_PROCESS_TRACETREE))) {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
a->value = 0x0005; // Child process not traceable
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
if ((a->cmd == TRC_C_ReadReg) || (a->cmd == TRC_C_WriteReg) ||
|
|
(a->cmd == TRC_C_SStep) ||
|
|
((a->tid != 0) && ((a->cmd == TRC_C_Freeze) || (a->cmd == TRC_C_Resume) || (a->cmd == TRC_C_ThrdStat))) ) {
|
|
|
|
//
|
|
// Locate the thread in the debuggee process
|
|
//
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
|
|
//
|
|
// Save NextThread for later use
|
|
//
|
|
NextThread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
if (Thread->ThreadId == (TID)(a->tid)) {
|
|
break;
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (ListNext == ListHead) {
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2DosPTrace invalid thread id %hd for cmd %hd\n", a->tid, a->cmd));
|
|
}
|
|
#endif
|
|
m->ReturnedErrorValue = ERROR_INVALID_TID;
|
|
a->value = 0x0001; // Bad Command
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
return(TRUE);
|
|
}
|
|
//
|
|
// Get the context record for the target thread.
|
|
//
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2DosPTrace fail at NtGetContextThread %lx\n", Status));
|
|
}
|
|
#endif
|
|
a->value = 0x0005; // Child process not traceable
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - command %hd. PID %d, TID %hd\n", a->cmd, Process->ProcessId, a->tid));
|
|
}
|
|
#endif
|
|
switch (a->cmd) {
|
|
case 0x0001: // Read memory I-Space
|
|
//
|
|
// Read Memory from debuggee
|
|
//
|
|
if (a->segv == 0x1b){
|
|
//
|
|
// 32 bit segment - BUGBUG need to skip over
|
|
//
|
|
FlatDebugeeAddress = (ULONG)(a->offv);
|
|
}
|
|
else {
|
|
FlatDebugeeAddress = (ULONG)(SELTOFLAT(a->segv)) | (ULONG)(a->offv);
|
|
}
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
(PVOID) FlatDebugeeAddress,
|
|
(PVOID) &(a->value),
|
|
2,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Read I-Space, failed to read %x:%x Status %lx\n", a->segv, a->offv, Status));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Read I-Space, %x:%x == %hx\n", a->segv, a->offv, a->value));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
break;
|
|
|
|
case 0x0002: // Read memory D-Space
|
|
//
|
|
// Read Memory from debuggee
|
|
//
|
|
if (a->segv == 0x23){
|
|
//
|
|
// 32 bit segment - BUGBUG need to skip over
|
|
//
|
|
FlatDebugeeAddress = (ULONG)(a->offv);
|
|
}
|
|
else {
|
|
FlatDebugeeAddress = (ULONG)(SELTOFLAT(a->segv)) | (ULONG)(a->offv);
|
|
}
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
(PVOID) FlatDebugeeAddress,
|
|
(PVOID) &(a->value),
|
|
2,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Read D-Space, failed to read %x:%x Status %lx\n", a->segv, a->offv, Status));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Read D-Space, %x:%x == %hx\n", a->segv, a->offv, a->value));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
break;
|
|
|
|
case 0x0003: // Read registers
|
|
SetDebuggeeContextInMsg(&Context, a);
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
break;
|
|
|
|
case 0x0004: // Write memory I-Space
|
|
//
|
|
// Write Memory from debuggee
|
|
//
|
|
if (a->segv == 0x1b){
|
|
//
|
|
// 32 bit segment - BUGBUG need to skip over
|
|
//
|
|
FlatDebugeeAddress = (ULONG)(a->offv);
|
|
}
|
|
else {
|
|
FlatDebugeeAddress = (ULONG)(SELTOFLAT(a->segv)) | (ULONG)(a->offv);
|
|
}
|
|
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
(PVOID) FlatDebugeeAddress,
|
|
(PVOID) &(a->value),
|
|
2,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Write I-Space, failed to write %x:%x Status %lx\n", a->segv, a->offv, Status));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Write I-Space, %x:%x == %hx\n", a->segv, a->offv, a->value));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
break;
|
|
|
|
case 0x0005: // Write memory D-Space
|
|
//
|
|
// Write Memory from debuggee
|
|
//
|
|
if (a->segv == 0x23){
|
|
//
|
|
// 32 bit segment - BUGBUG need to skip over
|
|
//
|
|
FlatDebugeeAddress = (ULONG)(a->offv);
|
|
}
|
|
else {
|
|
FlatDebugeeAddress = (ULONG)(SELTOFLAT(a->segv)) | (ULONG)(a->offv);
|
|
}
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
(PVOID) FlatDebugeeAddress,
|
|
(PVOID) &(a->value),
|
|
2,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Write D-Space, failed to write %x:%x Status %lx\n", a->segv, a->offv, Status));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - Write D-Space, %x:%x == %hx\n", a->segv, a->offv, a->value));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
break;
|
|
|
|
case 0x0006: // Write registers
|
|
//
|
|
// set ptracbuf value from context
|
|
//
|
|
Context.Eax = a->rAX;
|
|
Context.Ebx = a->rBX;
|
|
Context.Ecx = a->rCX;
|
|
Context.Edx = a->rDX;
|
|
Context.Esi = a->rSI;
|
|
Context.Edi = a->rDI;
|
|
Context.Ebp = a->rBP;
|
|
Context.SegDs = a->rDS;
|
|
Context.SegEs = a->rES;
|
|
Context.Eip = a->rIP;
|
|
Context.SegCs = a->rCS;
|
|
Context.EFlags |= a->rF;
|
|
Context.Esp = a->rSP;
|
|
Context.SegSs = a->rSS;
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2DosPTrace fail at NtSetContextThread %lx\n", Status));
|
|
}
|
|
#endif
|
|
a->value = 0x0005; // Child process not traceable
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
return(TRUE);
|
|
}
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
break;
|
|
|
|
case 0x0007: // Go (with signal)
|
|
|
|
//
|
|
// If all the debugee threads are frozen, just return error.
|
|
//
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
if (!(Thread->DebugState & TRC_MustBeFrozen)) {
|
|
break;
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (ListNext == ListHead) {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
a->value = 0x0001; // Bad Command
|
|
break;
|
|
}
|
|
|
|
Os2ExecuteFreezeUnfreeze(Process);
|
|
|
|
//
|
|
// Block debugger process for a breakpoint, single step
|
|
// or thread/process termination.
|
|
//
|
|
if ((Thread->Flags & OS2_THREAD_THREAD1) && (Thread->Dying)) {
|
|
WaitReason = WaitProcess;
|
|
}
|
|
else {
|
|
WaitReason = WaitThread;
|
|
}
|
|
if (Os2CreateWait( WaitReason,
|
|
(OS2_WAIT_ROUTINE)Os2WaitChildSatisfy,
|
|
t,
|
|
m,
|
|
NULL,
|
|
NULL )
|
|
) {
|
|
Os2ResumeProcess(Process);
|
|
return(FALSE);
|
|
}
|
|
else {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
}
|
|
break;
|
|
|
|
case 0x0008: // Terminate Child Process
|
|
Process->Flags |= OS2_PROCESS_TERMINATE;
|
|
if (Process->Flags & OS2_EXIT_IN_PROGRESS) {
|
|
Os2ResumeProcess(Process);
|
|
}
|
|
Os2SigKillProcess(Process);
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
break;
|
|
|
|
case 0x0009: // Single Step
|
|
//
|
|
// set TF bit in Eflags
|
|
//
|
|
Context.EFlags |= 0x100;
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
if (!NT_SUCCESS(Status)){
|
|
#if DBG
|
|
IF_OS2_DEBUG(TASKING) {
|
|
KdPrint(("Os2srv: Os2DosPTrace fail at NtSetContextThread %lx\n", Status));
|
|
}
|
|
#endif
|
|
a->value = 0x0005; // Child process not traceable
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Don't allow single step if the current thread is frozen.
|
|
// This is not a problem to code-view, since it unfreezes
|
|
// the thread first, then sstep, and the freezes it again.
|
|
//
|
|
if (Thread->DebugState & TRC_MustBeFrozen) {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
a->value = 0x0001; // Bad Command
|
|
break;
|
|
}
|
|
|
|
Os2ExecuteFreezeUnfreeze(Process);
|
|
|
|
//
|
|
// Block debugger process for a breakpoint, single step
|
|
// or thread/process termination.
|
|
//
|
|
if ((Thread->Flags & OS2_THREAD_THREAD1) && (Thread->Dying)) {
|
|
WaitReason = WaitProcess;
|
|
}
|
|
else {
|
|
WaitReason = WaitThread;
|
|
}
|
|
if (Os2CreateWait( WaitReason,
|
|
(OS2_WAIT_ROUTINE)Os2WaitChildSatisfy,
|
|
t,
|
|
m,
|
|
NULL,
|
|
NULL )
|
|
) {
|
|
Os2ResumeProcess(Process);
|
|
return(FALSE);
|
|
}
|
|
else {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
|
|
case 0x000A: // Stop Child Process
|
|
if (Process->FirstPtrace) {
|
|
//
|
|
// Block debugger process until debuggee gets to it's
|
|
// 16 bit entry point.
|
|
// Os2WaitChildSatisfy will start the protocol of
|
|
// TRC_C_LIB_ret and will restore the 16 bit entry point.
|
|
//
|
|
//
|
|
|
|
// If all the debugee threads are frozen, just return error.
|
|
//
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
if (!(Thread->DebugState & TRC_MustBeFrozen)) {
|
|
break;
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (ListNext == ListHead) {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
a->value = 0x0001; // Bad Command
|
|
break;
|
|
}
|
|
|
|
Os2ExecuteFreezeUnfreeze(Process);
|
|
|
|
Process->FirstPtrace = FALSE;
|
|
|
|
if ((Thread->Flags & OS2_THREAD_THREAD1) && (Thread->Dying)) {
|
|
WaitReason = WaitProcess;
|
|
}
|
|
else {
|
|
WaitReason = WaitThread;
|
|
}
|
|
if (Os2CreateWait( WaitReason,
|
|
(OS2_WAIT_ROUTINE)Os2WaitChildSatisfy,
|
|
t,
|
|
m,
|
|
NULL,
|
|
NULL )
|
|
) {
|
|
Os2ResumeProcess(Process);
|
|
return(FALSE);
|
|
}
|
|
else {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Continue the protocol of TRC_C_LIB_ret until TRC_C_SUC_ret.
|
|
//
|
|
ldrReturnProgramAndLibMTE(Process,
|
|
&(a->mte),
|
|
&(a->value),
|
|
&(a->cmd)
|
|
);
|
|
}
|
|
break;
|
|
|
|
case 0x000B: // Freeze Thread
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
if (a->tid == 0) {
|
|
//
|
|
// Freeze all threads
|
|
//
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD(ListNext,
|
|
OS2_THREAD,
|
|
Link );
|
|
Thread->DebugState |= TRC_MustBeFrozen;
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
else {
|
|
Thread->DebugState |= TRC_MustBeFrozen;
|
|
}
|
|
break;
|
|
|
|
case 0X000C: // Unfreeze Thread
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
if (a->tid == 0) {
|
|
//
|
|
// Unfreeze all threads
|
|
//
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD(ListNext,
|
|
OS2_THREAD,
|
|
Link );
|
|
Thread->DebugState &= ~TRC_MustBeFrozen;
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
else {
|
|
Thread->DebugState &= ~TRC_MustBeFrozen;
|
|
}
|
|
break;
|
|
|
|
case 0x000D: // Convert Seg number to Selector
|
|
|
|
if (a->mte == 0){
|
|
//
|
|
// want the name of the EXE itself
|
|
//
|
|
mte = ((LinkMTE *)Process->LinkMte)->NextMTE->MTE;
|
|
}
|
|
|
|
a->value = ldrFindSegForHandleandNum(mte, a->mte, a->value);
|
|
|
|
if (a->value == 0){
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - convertnumtoseg failed for mte %hx, num %hx\n", a->mte, a->segv));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
}
|
|
else {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - convertnumtoseg mte %hx, num %hx --> seg %hx\n", a->mte, a->segv, a->value));
|
|
}
|
|
#endif
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
break;
|
|
|
|
case 0x000E: // Get FP registers (segv, offv has 94 bytes buf pointer)
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
break;
|
|
|
|
case 0x000F: // Set FP registers (segv, offv has 94 bytes buf pointer)
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
break;
|
|
|
|
case 0x0010: // Get Library module name (value had lib module handle,
|
|
// segv, offv is buffer to receive name
|
|
if (a->value == 0){
|
|
//
|
|
// want the name of the EXE itself
|
|
//
|
|
if (((LinkMTE *)Process->LinkMte)->NextMTE != NULL) {
|
|
mte = ((LinkMTE *)Process->LinkMte)->NextMTE->MTE;
|
|
}
|
|
else {
|
|
//
|
|
// EXE failed to load.
|
|
//
|
|
a->cmd = (USHORT)TRC_C_ERR_ret; // Failed to get module name
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ldrGetModName(
|
|
(a->value ? NULL : ldrFindMTEForHandle(mte)),
|
|
a->value,
|
|
NameBuf,
|
|
256
|
|
)) {
|
|
//
|
|
// Write the name into debugger address space
|
|
//
|
|
FlatDebugeeAddress = (ULONG)(SELTOFLAT(a->segv)) | (ULONG)(a->offv);
|
|
Status = NtWriteVirtualMemory( t->Process->ProcessHandle,
|
|
(PVOID) FlatDebugeeAddress,
|
|
(PVOID) NameBuf,
|
|
(strlen(NameBuf))+1,
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - cmd GetModName, failed to write result Status %lx\n", Status));
|
|
}
|
|
#endif
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
}
|
|
else {
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
|
|
}
|
|
else {
|
|
a->cmd = (USHORT)TRC_C_ERR_ret; // Failed to get module name
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x0011: // Get Thread Status
|
|
if (a->tid == 0) {
|
|
//
|
|
// Locate the 1st thread in the debuggee process
|
|
//
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
NextThread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
a->value = (USHORT)(NextThread->ThreadId);
|
|
}
|
|
else {
|
|
//
|
|
// return the previous id in value (in a circular way).
|
|
// --------
|
|
//
|
|
ListPrev = (&Thread->Link)->Blink;
|
|
PrevThread = CONTAINING_RECORD( ListPrev, OS2_THREAD, Link );
|
|
if (PrevThread == (POS2_THREAD) &Process->ThreadList) {
|
|
ListPrev = (&PrevThread->Link)->Blink;
|
|
PrevThread = CONTAINING_RECORD( ListPrev, OS2_THREAD, Link );
|
|
}
|
|
a->value = (USHORT)(PrevThread->ThreadId);
|
|
}
|
|
|
|
//
|
|
// Write the thread status into debugger memory at address segv:offv
|
|
//
|
|
DbgThreadStatus.DebugState =
|
|
(Thread->DebugState & TRC_MustBeFrozen) ? TRC_C_Frozen : TRC_C_Thawed;
|
|
DbgThreadStatus.ThreadState = 0; // BUGBUG - always runnable
|
|
DbgThreadStatus.Priority = (Thread->Os2Class << 8) | (UCHAR)Thread->Os2Level;
|
|
FlatDebugeeAddress = (ULONG)(SELTOFLAT(a->segv)) | (ULONG)(a->offv);
|
|
Status = NtWriteVirtualMemory( t->Process->ProcessHandle,
|
|
(PVOID) FlatDebugeeAddress,
|
|
(PVOID) &DbgThreadStatus,
|
|
sizeof(DbgThreadStatus),
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - cmd GetThreadStatus, failed to write result Status %lx\n", Status));
|
|
}
|
|
#endif
|
|
m->ReturnedErrorValue = ERROR_ACCESS_DENIED;
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
}
|
|
else {
|
|
a->cmd = (USHORT)TRC_C_SUC_ret;
|
|
}
|
|
break;
|
|
|
|
case 0x0012: // get r/o segment alias - not supported yet
|
|
a->value = 0x0001; // Bad Command
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
|
|
case 0x0013: // get r/w segment alias - not supported yet
|
|
a->value = 0x0001; // Bad Command
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
|
|
case 0x0014: // free alias - not supported yet
|
|
a->value = 0x0001; // Bad Command
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
m->ReturnedErrorValue = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
|
|
default:
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "DosPTrace - invalid command %hd. PID %d, TID %d\n", a->cmd, t->Process->ProcessId, t->ThreadId));
|
|
}
|
|
#endif
|
|
m->ReturnedErrorValue = ERROR_INVALID_PARAMETER;
|
|
a->value = 0x0001; // Bad Command
|
|
a->cmd = (USHORT)TRC_C_ERR_ret;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
Os2SendTmReleaseThreadOnLPC(
|
|
IN POS2_SESSION Session,
|
|
IN POS2_PROCESS Process
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SCREQUESTMSG Request;
|
|
|
|
Request.Request = TaskManRequest;
|
|
Request.d.Tm.Request = TmReleaseLPC;
|
|
Request.d.Tm.ExitResults = (ULONG)Process->ClientId.UniqueProcess;
|
|
|
|
PORT_MSG_TOTAL_LENGTH(Request) = sizeof(SCREQUESTMSG);
|
|
PORT_MSG_DATA_LENGTH(Request) = sizeof(SCREQUESTMSG) - sizeof(PORT_MESSAGE);
|
|
PORT_MSG_ZERO_INIT(Request) = 0L;
|
|
|
|
Status = NtRequestPort(
|
|
Session->ConsolePort,
|
|
(PPORT_MESSAGE) &Request);
|
|
|
|
if ( !NT_SUCCESS( Status ))
|
|
{
|
|
#if DBG
|
|
KdPrint(( "OS2SS: Unable to send release LPC request - Status == %X\n",
|
|
Status));
|
|
#endif
|
|
|
|
return( Status );
|
|
}
|
|
|
|
// ASSERT ( PORT_MSG_TYPE(Request) == LPC_REPLY );
|
|
|
|
return( Request.Status );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosCloseHandle(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes a list of handles (had been opened/duplicated for
|
|
the server by client before an error had occured).
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
Note:
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSCLOSE_HANDLE_MSG b = &m->u.DosCloseHandle;
|
|
ULONG i = b->HandleNumber;
|
|
|
|
UNREFERENCED_PARAMETER(t);
|
|
#if DBG
|
|
IF_OS2_DEBUG( TASKING ) {
|
|
KdPrint(( "Entering Os2CloseHandle\n"));
|
|
}
|
|
#endif
|
|
|
|
for ( ; i ; i-- )
|
|
{
|
|
CloseHandle(b->HandleTable[i - 1]);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
#if PMNT
|
|
BOOLEAN
|
|
PMSetPMshellFlag(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is not in use.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - always
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(t);
|
|
UNREFERENCED_PARAMETER(m);
|
|
return(TRUE);
|
|
}
|
|
#endif
|
|
|