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.
1885 lines
54 KiB
1885 lines
54 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvxcpt.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the OS/2 V2.0 exception handling API calls.
|
|
|
|
Author:
|
|
|
|
Therese Stowell (thereses) 29-June-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_EXCEPTIONS
|
|
#define INCL_OS2V20_ERRORS
|
|
#define SIG_CTRLC 1
|
|
#define SIG_CTRLBREAK 4
|
|
#include "os2srv.h"
|
|
#include "os2tile.h"
|
|
|
|
APIRET
|
|
SendSignalException(
|
|
IN POS2_THREAD Thread,
|
|
IN int Signal
|
|
);
|
|
|
|
NTSTATUS
|
|
Os2CompleteResumeThread(
|
|
IN POS2_THREAD Thread
|
|
);
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosEnterMustComplete(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosEnterMustComplete API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSENTERMUSTCOMPLETE_MSG a = &m->u.DosEnterMustComplete;
|
|
|
|
if (t->MustComplete == MAXIMUM_MUST_COMPLETE) { /* Counter wrapped */
|
|
m->ReturnedErrorValue = ERROR_NESTING_TOO_DEEP;
|
|
}
|
|
else {
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
a->NestingLevel = ++t->MustComplete;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosExitMustComplete(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosExitMustComplete API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSEXITMUSTCOMPLETE_MSG a = &m->u.DosExitMustComplete;
|
|
|
|
if (t->MustComplete == 0) { /* Counter wrapped */
|
|
m->ReturnedErrorValue = ERROR_ALREADY_RESET;
|
|
}
|
|
else {
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
a->NestingLevel = --t->MustComplete;
|
|
if ((t->MustComplete == 0) && (t->PendingSignals != 0)) {
|
|
|
|
//
|
|
// if we are no longer in a MustComplete region, we need to
|
|
// issue any pending signals.
|
|
//
|
|
// possible pending signals are:
|
|
// SIG_APTERM
|
|
// SIG_KILLPROC
|
|
// SIG_INTR
|
|
// SIG_BREAK
|
|
//
|
|
|
|
if (t->PendingSignals & SIGNAL_TO_FLAG(XCPT_SIGNAL_INTR)) {
|
|
t->PendingSignals &= ~SIGNAL_TO_FLAG(XCPT_SIGNAL_INTR);
|
|
SendSignalException(t,XCPT_SIGNAL_INTR);
|
|
}
|
|
if (t->PendingSignals & SIGNAL_TO_FLAG(XCPT_SIGNAL_KILLPROC)) {
|
|
t->PendingSignals &= ~SIGNAL_TO_FLAG(XCPT_SIGNAL_KILLPROC);
|
|
SendSignalException(t,XCPT_SIGNAL_KILLPROC);
|
|
}
|
|
if (t->PendingSignals & SIGNAL_TO_FLAG(XCPT_SIGNAL_BREAK)) {
|
|
t->PendingSignals &= ~SIGNAL_TO_FLAG(XCPT_SIGNAL_BREAK);
|
|
SendSignalException(t,XCPT_SIGNAL_BREAK);
|
|
}
|
|
if (t->PendingSignals & SIGNAL_TO_FLAG(SIGAPTERM)) {
|
|
ASSERT(FALSE);
|
|
t->PendingSignals &= ~SIGNAL_TO_FLAG(SIGAPTERM);
|
|
SendSignalException(t,SIGAPTERM);
|
|
}
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosSetSignalExceptionFocus(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosSetSignalExceptionFocus API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSSETSIGNALEXCEPTIONFOCUS_MSG a = &m->u.DosSetSignalExceptionFocus;
|
|
if (a->Flag == SIG_SETFOCUS) {
|
|
if (t->Process->ExceptionFocus == SIG_MAXSF) {
|
|
m->ReturnedErrorValue = ERROR_NESTING_TOO_DEEP;
|
|
}
|
|
else {
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
a->NestingLevel = ++t->Process->ExceptionFocus;
|
|
}
|
|
}
|
|
else if (a->Flag == SIG_UNSETFOCUS) {
|
|
if (t->Process->ExceptionFocus == 0) {
|
|
m->ReturnedErrorValue = ERROR_ALREADY_RESET;
|
|
}
|
|
else {
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
a->NestingLevel = --t->Process->ExceptionFocus;
|
|
}
|
|
}
|
|
else
|
|
ASSERT (FALSE);
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
FindExceptionFocus(
|
|
IN POS2_PROCESS RootProcess,
|
|
IN ULONG CurrentDepth,
|
|
IN OUT POS2_PROCESS *CurrentFocus,
|
|
IN OUT PULONG CurrentFocusDepth
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the leafmost child of the current process that has
|
|
requested the signal exception focus (DosSetSignalExceptionFocus).
|
|
|
|
Arguments:
|
|
|
|
RootProcess - the process from which to begin the search
|
|
|
|
CurrentDepth - the depth of the rootprocess from the original root process
|
|
|
|
CurrentFocus - the process which is currently the leafmost with the signal focus
|
|
|
|
CurrentFocusDepth - the depth of the current focus from the original root process
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POS2_THREAD Thread1;
|
|
|
|
Thread1 = CONTAINING_RECORD( RootProcess->ThreadList.Flink, OS2_THREAD, Link );
|
|
if (Thread1->Dying == FALSE) {
|
|
|
|
//
|
|
// if the process we're looking at has requested the focus and it's
|
|
// leafward of the current focus, choose it as the current focus.
|
|
//
|
|
|
|
if ((RootProcess->ExceptionFocus != 0) &&
|
|
(*CurrentFocusDepth < CurrentDepth)) { // to pick the last created process, make this a <=
|
|
*CurrentFocus = RootProcess;
|
|
*CurrentFocusDepth = CurrentDepth;
|
|
}
|
|
}
|
|
|
|
ListHead = &RootProcess->ChildrenList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
FindExceptionFocus( CONTAINING_RECORD( ListNext,
|
|
OS2_PROCESS,
|
|
SiblingLink
|
|
),
|
|
CurrentDepth+1,
|
|
CurrentFocus,
|
|
CurrentFocusDepth
|
|
);
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
|
|
|
|
APIRET
|
|
Os2SignalGetThreadContext(
|
|
POS2_THREAD Thread,
|
|
PCONTEXT pContext,
|
|
PULONG pNewSp,
|
|
PULONG pSuspendTimes
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG SuspendCount;
|
|
POS2_PROCESS Process = Thread->Process;
|
|
BOOLEAN SignalWasntDelivered;
|
|
|
|
*pSuspendTimes = 0;
|
|
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
&(Process->ClientPib->SignalWasntDelivered),
|
|
&SignalWasntDelivered,
|
|
sizeof(BOOLEAN),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Fail to read from client, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
#endif // DBG
|
|
ASSERT(FALSE);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
if (SignalWasntDelivered) {
|
|
//
|
|
// Thread1 wan't execute the entry code of signal handler, yet.
|
|
//
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Previous signal wasn't delivered yet\n",
|
|
Process->ProcessId);
|
|
}
|
|
#endif // DBG
|
|
return ERROR_SIGNAL_REFUSED;
|
|
}
|
|
|
|
__try {
|
|
|
|
//
|
|
// Thread1 will be suspended 3 times. There is the possibility that the thread will
|
|
// be resumed by the client process, but only 2 times (in DosSuspend/ResumeThread and
|
|
// DosEnter/ExitCritSect). By suspending for 3 times we insure that the the thread
|
|
// will be suspended.
|
|
// The thread might be resumed by client in the case that signal handler was executed
|
|
// and the flag that indicates that signal handler in progress already cleared. But
|
|
// any subsequent DosSuspendThread or DosEnterCritSect will actually suspend the
|
|
// thread and thier spouses will resume it with the same suspend count.
|
|
//
|
|
|
|
do {
|
|
Status = NtSuspendThread(Thread->ThreadHandle, &SuspendCount);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Fail to suspend thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
#endif // DBG
|
|
ASSERT(FALSE);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: suspend thread, count = %d\n",
|
|
Process->ProcessId,
|
|
SuspendCount);
|
|
}
|
|
#endif // DBG
|
|
(*pSuspendTimes)++;
|
|
|
|
} while (SuspendCount < 2);
|
|
|
|
pContext->ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(Thread->ThreadHandle, pContext);
|
|
if (Status != STATUS_SUCCESS) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Fail to get context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
#endif //DBG
|
|
ASSERT(FALSE);
|
|
return ERROR_SIGNAL_REFUSED;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Hack that avoid signal handler execution if the thread is using INT 3
|
|
// instruction. Relevant for checked build only.
|
|
//
|
|
if (pContext->SegCs == 0x1b && pContext->SegSs == 0x23) {
|
|
BYTE opcode;
|
|
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
(PVOID)(pContext->Eip - 1), // previous byte
|
|
&opcode,
|
|
1,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Fail to read instruction, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
if (opcode == 0xcc) { // INT 3
|
|
//
|
|
// Refuse to signal if the thread in INT 3 handler. It will not use
|
|
// the context to return, so there is no point to set new context and
|
|
// no possibility to perform signal handler. INT 3 is ised by
|
|
// ntdll!DebugService (from DbgPrint).
|
|
//
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: after INT 3\n",
|
|
Process->ProcessId);
|
|
Status = 0xc0000000;
|
|
return ERROR_SIGNAL_REFUSED;
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
if (pContext->SegCs == 0x1b) {
|
|
//
|
|
// The values of DS and ES might be invalid in the context. Kernel don't update
|
|
// them if the thread was in 32bit (CS==1b).
|
|
//
|
|
pContext->SegDs = pContext->SegEs = 0x23;
|
|
}
|
|
|
|
if (pContext->SegSs != 0x23) {
|
|
//
|
|
// The thread was in 16 bit
|
|
//
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
&(Process->ClientPib->Saved32Esp),
|
|
pNewSp,
|
|
sizeof(ULONG),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Fail to read from client, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
#endif // DBG
|
|
ASSERT(FALSE);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// There are 8 bytes beyond the 32bit stack (that was saved) that are used by
|
|
// Od2JumpTo16SignalDispatch to store the jump instruction to 16bit signal
|
|
// handler.
|
|
//
|
|
*pNewSp -= 8;
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2GetThreadContext: Signal on 16bit(%x:%x) -- Stack=%x\n",
|
|
Process->ProcessId,
|
|
pContext->SegCs,
|
|
pContext->Eip,
|
|
*pNewSp);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
else {
|
|
//
|
|
// 8 bytes beyond the stack are used by Od2Continue to return to original
|
|
// context.
|
|
//
|
|
*pNewSp = pContext->Esp - 8;
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2GetThreadContext: Signal on 32bit(%x) -- Stack=%x\n",
|
|
Process->ProcessId,
|
|
pContext->Eip,
|
|
*pNewSp);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
(*pNewSp) &= 0xfffffffc;
|
|
}
|
|
__finally {
|
|
if (!NT_SUCCESS(Status)) {
|
|
while (*pSuspendTimes) {
|
|
Status = NtResumeThread(Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: Fail to resume thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2SignalGetThreadContext: resume thread, count = %d\n",
|
|
Process->ProcessId,
|
|
SuspendCount);
|
|
}
|
|
#endif // DBG
|
|
(*pSuspendTimes)--;
|
|
}
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
APIRET
|
|
Os2SignalSetThreadContext(
|
|
POS2_THREAD Thread,
|
|
PCONTEXT pContext
|
|
)
|
|
{
|
|
BOOLEAN true = TRUE;
|
|
POS2_PROCESS Process = Thread->Process;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
&(Process->ClientPib->SigHandInProgress),
|
|
&true,
|
|
sizeof(BOOLEAN),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalSetThreadContext: Fail to write SigInProgress, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
&(Process->ClientPib->SignalWasntDelivered),
|
|
&true,
|
|
sizeof(BOOLEAN),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalSetThreadContext: Fail to write SigWasntDelivered, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2SetThreadContext: Going to set new context, Eip=%x\n",
|
|
Process->ProcessId,
|
|
pContext->Eip);
|
|
}
|
|
#endif // DBG
|
|
|
|
pContext->ContextFlags = CONTEXT_FULL;
|
|
pContext->EFlags &= 0xfffffbff; // Clear direction flag. By defualt run-time
|
|
// library assume that direction flag is cleared.
|
|
// RtlMoveMemory, for example, don't clear this
|
|
// flag on entry, but assume it 0.
|
|
Status = NtSetContextThread(Thread->ThreadHandle, pContext);
|
|
if (Status != STATUS_SUCCESS) {
|
|
#if DBG
|
|
DbgPrint("[%d]Os2SignalSetThreadContext: Fail to set context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
#endif // DBG
|
|
return ERROR_SIGNAL_REFUSED;
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
DeliverSignal(
|
|
IN POS2_THREAD Thread,
|
|
IN ULONG Signal
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to deliver a signal to a process. It
|
|
can safely assume that the target process is inside the client.
|
|
|
|
Arguments:
|
|
|
|
Thread - Supplies the handle of the thread to be signaled
|
|
|
|
Signal - Supplies the index of the signal to be delivered to the
|
|
process
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Args[2];
|
|
CONTEXT Context;
|
|
ULONG NewSp;
|
|
POS2_PROCESS Process = Thread->Process;
|
|
ULONG SuspendCount;
|
|
APIRET rc;
|
|
ULONG SuspendTimes;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
DbgPrint("Thread 0x%lx delivered signal %ld\n",Thread,Signal);
|
|
}
|
|
#endif
|
|
//
|
|
// Suspend the thread and get it's context
|
|
//
|
|
if ((rc = Os2SignalGetThreadContext(Thread, &Context, &NewSp, &SuspendTimes)) != NO_ERROR) {
|
|
//
|
|
// Continue without context switch. The signal will not be delivered.
|
|
//
|
|
return;
|
|
}
|
|
__try {
|
|
|
|
NewSp -= sizeof( CONTEXT );
|
|
Status = NtWriteVirtualMemory(
|
|
Process->ProcessHandle,
|
|
(PVOID)NewSp,
|
|
&Context,
|
|
sizeof( CONTEXT ),
|
|
NULL);
|
|
if (!NT_SUCCESS( Status )) {
|
|
#if DBG
|
|
DbgPrint("[%d]DeliverSignal: Fail to write old context to client stack, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
#endif // DBG
|
|
ASSERT(FALSE);
|
|
__leave;
|
|
}
|
|
|
|
Args[0] = NewSp; // pass pointer to context
|
|
Args[1] = Signal;
|
|
|
|
NewSp -= 2 * sizeof( ULONG );
|
|
Status = NtWriteVirtualMemory(
|
|
Process->ProcessHandle,
|
|
(PVOID)NewSp,
|
|
Args,
|
|
2 * sizeof( ULONG ),
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
#if DBG
|
|
DbgPrint("[%d]DeliverSignal: Fail to write parameters to client stack, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
#endif // DBG
|
|
ASSERT(FALSE);
|
|
__leave;
|
|
}
|
|
//
|
|
// Set the address of the target code into Eip, the new target stack
|
|
// into Esp, and reload context to make it happen.
|
|
//
|
|
Context.SegSs = Context.SegDs = Context.SegEs = 0x23;
|
|
Context.SegCs = 0x1b;
|
|
Context.Esp = NewSp;
|
|
Context.Eip = (ULONG)Process->SignalDeliverer;
|
|
rc = Os2SignalSetThreadContext(Thread, &Context);
|
|
}
|
|
__finally {
|
|
if (rc != NO_ERROR || !NT_SUCCESS(Status)) {
|
|
while (SuspendTimes) {
|
|
Status = NtResumeThread(Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]DeleverSignal: Fail to resume thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]DeliverSignal: resume thread, count = %d\n",
|
|
Process->ProcessId,
|
|
SuspendCount);
|
|
}
|
|
#endif // DBG
|
|
SuspendTimes--;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Now alert the thread - in case it is blocked it will
|
|
// get the chance now to execute the signal handler
|
|
//
|
|
#if DBG
|
|
IF_OD2_DEBUG( TASKING ) {
|
|
DbgPrint("[%d,%d]DeliverSignal: Going to NtAlertThread(%x.%x)\n",
|
|
Process->ProcessId,
|
|
Thread->ThreadId,
|
|
Thread->ClientId.UniqueProcess,
|
|
Thread->ClientId.UniqueThread);
|
|
}
|
|
#endif
|
|
Status = NtAlertThread(Thread->ThreadHandle);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// We can do nothing but to try to continue execution
|
|
//
|
|
DbgPrint("[%d]DeliverSignal: Fail to alert thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
Os2CompleteResumeThread(Thread);
|
|
}
|
|
}
|
|
}
|
|
|
|
APIRET
|
|
SendSignalException(
|
|
IN POS2_THREAD Thread,
|
|
IN int Signal
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a signal to a particular thread. It will do the
|
|
correct thing based on whether the thread is in a mustcomplete section
|
|
or has signals pending.
|
|
|
|
Arguments:
|
|
|
|
Thread - thread to signal
|
|
|
|
Signal - signal to send
|
|
|
|
Return Value:
|
|
|
|
ERROR_SIGNAL_PENDING - the specified signal is already pending for the
|
|
thread.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG SignalFlag;
|
|
|
|
//
|
|
// convert signal to bitmap value
|
|
//
|
|
|
|
SignalFlag = SIGNAL_TO_FLAG(Signal);
|
|
|
|
//
|
|
// if a signal of this type is already queued, return error.
|
|
//
|
|
|
|
if (Thread->PendingSignals & SignalFlag) {
|
|
return ERROR_SIGNAL_PENDING;
|
|
}
|
|
|
|
//
|
|
// if a signal of this type is currently being processed or
|
|
// the thread is in a mustcomplete region, queue the signal.
|
|
// otherwise, issue it.
|
|
//
|
|
// if the thread is in a wait block, wake it up.
|
|
//
|
|
|
|
if (!(Thread->CurrentSignals & SignalFlag) && !Thread->MustComplete) {
|
|
// Thread->CurrentSignals |= SignalFlag;
|
|
if (Thread->WaitBlock != NULL) {
|
|
Thread->WaitBlock->WaitReplyMessage.ReturnedErrorValue = ERROR_INTERRUPT;
|
|
Os2NotifyWaitBlock(Thread->WaitBlock,WaitInterrupt,NULL,NULL);
|
|
}
|
|
DeliverSignal(Thread,Signal);
|
|
}
|
|
else {
|
|
Thread->PendingSignals |= SignalFlag;
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosSendSignalException(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosSendSignalException API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSSENDSIGNALEXCEPTION_MSG a = &m->u.DosSendSignalException;
|
|
POS2_PROCESS Process;
|
|
POS2_PROCESS FocusProcess;
|
|
POS2_THREAD Thread;
|
|
ULONG Depth;
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
DbgPrint("entering Os2DosSendSignalException\n");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// find the specified process
|
|
//
|
|
|
|
Process = Os2LocateProcessByProcessId( m,
|
|
t->Process,
|
|
(PID)a->ProcessId,
|
|
(BOOLEAN)FALSE
|
|
);
|
|
if (Process == NULL) {
|
|
return( TRUE );
|
|
}
|
|
|
|
//
|
|
// verify that specified process is direct child of caller
|
|
// believe it or not, ERROR_INVALID_FUNCTION is the same error code
|
|
// as is returned by OS/2 v2.0
|
|
//
|
|
|
|
if (Process->Parent != t->Process) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_FUNCTION;
|
|
return( TRUE );
|
|
}
|
|
|
|
//
|
|
// find the leafmost process that has requested the exception focus
|
|
//
|
|
|
|
FocusProcess = NULL;
|
|
Depth = 0;
|
|
FindExceptionFocus(Process,
|
|
(ULONG) 1,
|
|
&FocusProcess,
|
|
&Depth);
|
|
|
|
if (FocusProcess == NULL) {
|
|
m->ReturnedErrorValue = ERROR_NO_SIGNAL_SENT;
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// find thread 1 of the exception focus process
|
|
//
|
|
|
|
Thread = CONTAINING_RECORD( FocusProcess->ThreadList.Flink, OS2_THREAD, Link );
|
|
|
|
if (Thread->Dying) {
|
|
#if DBG
|
|
ASSERT (FALSE);
|
|
DbgPrint( "Os2DosSendSignalException, Thread is dying\n");
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// send the signal. this routine releases the Thread lock.
|
|
//
|
|
|
|
m->ReturnedErrorValue = SendSignalException(Thread,a->Exception);
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
DbgPrint("leaving Os2DosSendSignalException. rc is %ld\n",m->ReturnedErrorValue);
|
|
}
|
|
#endif
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosAcknowledgeSignalException(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the DosAcknowledgeSignalException API.
|
|
|
|
Arguments:
|
|
|
|
t - calling thread
|
|
|
|
m - message
|
|
|
|
Return Value:
|
|
|
|
TRUE - create a return message
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_DOSACKNOWLEDGESIGNALEXCEPTION_MSG a = &m->u.DosAcknowledgeSignalException;
|
|
|
|
|
|
//
|
|
// make sure this signal has been delivered to this thread
|
|
//
|
|
|
|
if (!(t->CurrentSignals & SIGNAL_TO_FLAG(a->SignalNumber))) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_SIGNAL_NUMBER;
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// turn off signal bit
|
|
//
|
|
|
|
t->CurrentSignals &= ~SIGNAL_TO_FLAG(a->SignalNumber);
|
|
|
|
if (t->PendingSignals & SIGNAL_TO_FLAG(a->SignalNumber)) {
|
|
t->PendingSignals &= ~SIGNAL_TO_FLAG(a->SignalNumber);
|
|
//
|
|
// LTS - 4/22/92 : Check for XCPT_SIGNAL_KILLPROC, don't send signal back to
|
|
// client.
|
|
if (a->SignalNumber != XCPT_SIGNAL_KILLPROC) {
|
|
SendSignalException(t,a->SignalNumber);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2GetSigHandlerRec(
|
|
IN POS2_API_MSG m,
|
|
POD2_SIG_HANDLER_REC psighandler,
|
|
POS2_PROCESS *pProcess
|
|
)
|
|
|
|
{
|
|
POS2_DISPATCH16_SIGNAL a = &m->u.Dispatch16Signal;
|
|
POS2_PROCESS Parent;
|
|
POS2_PROCESS prvProcess;
|
|
POS2_PROCESS curProcess;
|
|
POS2_PROCESS Process;
|
|
NTSTATUS Status;
|
|
|
|
Process = Parent = *pProcess;
|
|
|
|
//
|
|
// For ctrl-c and ctrl-break send signal to the last descendant process
|
|
// that has a corresponding signal handler installed
|
|
//
|
|
if (a->usFlagNum == SIG_CTRLC || a->usFlagNum == SIG_CTRLBREAK) {
|
|
//
|
|
// find leafmost process for SIG_CTRLC & SIG_CTRLBREAK which has
|
|
// a signal handler installed
|
|
//
|
|
// Get leaf-most process
|
|
//
|
|
while (!IsListEmpty( &Process->ChildrenList )) {
|
|
prvProcess = Process;
|
|
Process = CONTAINING_RECORD(Process->ChildrenList.Flink,
|
|
OS2_PROCESS,
|
|
SiblingLink );
|
|
}
|
|
}
|
|
|
|
while(TRUE) {
|
|
*pProcess = Process;
|
|
//
|
|
// Read signal handler Record for this process to check if
|
|
// signal handler installed.
|
|
//
|
|
Status = NtReadVirtualMemory( Process->ProcessHandle,
|
|
(PVOID) a->sighandleraddr,
|
|
(PVOID) psighandler,
|
|
sizeof(OD2_SIG_HANDLER_REC),
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
ASSERT(NT_SUCCESS(Status));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check signature of signal handler record for this process
|
|
//
|
|
if (psighandler->signature == 0xdead) {
|
|
|
|
//
|
|
// For all other signals we are done
|
|
//
|
|
if (a->usFlagNum != SIG_CTRLC && a->usFlagNum != SIG_CTRLBREAK) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if default signal handler still installed (Doscalls
|
|
// selector is present)
|
|
//
|
|
if ((psighandler->sighandler[a->usFlagNum - 1] >> 16) !=
|
|
(psighandler->doscallssel >> 16)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
#if DBG
|
|
DbgPrint("Os2GetSigHandlerRec: Hi There, I am not dead\n");
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
if (Process == Parent ||
|
|
(a->usFlagNum != SIG_CTRLC && a->usFlagNum != SIG_CTRLBREAK)) {
|
|
break;
|
|
}
|
|
|
|
Process = Parent;
|
|
while (Process != prvProcess) {
|
|
curProcess = Process;
|
|
Process = CONTAINING_RECORD(Process->ChildrenList.Flink,
|
|
OS2_PROCESS,
|
|
SiblingLink );
|
|
}
|
|
prvProcess = curProcess;
|
|
}
|
|
|
|
m->ReturnedErrorValue = ERROR_SIGNAL_REFUSED;
|
|
ASSERT(NT_SUCCESS(Status));
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
APIRET
|
|
Os2DispatchToHandler(
|
|
IN POS2_THREAD Thread,
|
|
IN POS2_DISPATCH16_SIGNAL a
|
|
)
|
|
{
|
|
APIRET rc;
|
|
NTSTATUS Status;
|
|
CONTEXT Context;
|
|
ULONG NewSp;
|
|
PVOID address = NULL;
|
|
ULONG Length;
|
|
POS2_PROCESS Process = Thread->Process;
|
|
OS2_REGISTER16_SIGNAL b;
|
|
ULONG SuspendCount;
|
|
ULONG SuspendTimes;
|
|
//
|
|
// Suspend the thread and get it's context
|
|
//
|
|
if ((rc = Os2SignalGetThreadContext(Thread, &Context, &NewSp, &SuspendTimes)) !=
|
|
NO_ERROR) {
|
|
//
|
|
// Continue without context switch. The signal will not be delivered.
|
|
//
|
|
return rc;
|
|
}
|
|
|
|
__try {
|
|
//
|
|
// Allocate memory in address space of process and copy context of process
|
|
// who called DosFlagProcess or DosSendSignal and context of thread we are
|
|
// going to run in.
|
|
//
|
|
Length = sizeof(OS2_REGISTER16_SIGNAL) + sizeof(CONTEXT);
|
|
address = 0;
|
|
Status = NtAllocateVirtualMemory(Process->ProcessHandle,
|
|
&address,
|
|
0,
|
|
&Length,
|
|
MEM_RESERVE|MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
#if DBG
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to allocate memory for client, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Write dummy register set of process who called us.
|
|
//
|
|
b.usFlagNum = a->usFlagNum;
|
|
b.usFlagArg = a->usFlagArg;
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
address,
|
|
(PVOID) &b.regSP,
|
|
sizeof(OS2_REGISTER16_SIGNAL),
|
|
NULL);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
#if DBG
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to write 16bit registes block, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
__leave;
|
|
}
|
|
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
(PVOID) ((PCHAR) address +
|
|
sizeof(OS2_REGISTER16_SIGNAL)),
|
|
(PVOID) &Context,
|
|
sizeof(CONTEXT),
|
|
NULL);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
#if DBG
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to write context, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
__leave;
|
|
}
|
|
|
|
Context.SegSs = Context.SegDs = Context.SegEs = 0x23;
|
|
Context.SegCs = 0x1b;
|
|
|
|
NewSp -= sizeof(ULONG);
|
|
Status = NtWriteVirtualMemory( Process->ProcessHandle,
|
|
(PVOID)NewSp,
|
|
&address,
|
|
sizeof(ULONG),
|
|
NULL);
|
|
if (!NT_SUCCESS( Status )) {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
#if DBG
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to write parameter, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
#endif // DBG
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Set the address of the target code into Eip, the new target stack
|
|
// into Esp, and reload context to make it happen.
|
|
//
|
|
Context.Esp = NewSp;
|
|
Context.Eip = a->routine;
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
rc = Os2SignalSetThreadContext(Thread, &Context);
|
|
}
|
|
__finally {
|
|
if (rc != NO_ERROR) {
|
|
if (address) {
|
|
Status = NtFreeVirtualMemory(
|
|
Process->ProcessHandle,
|
|
&address,
|
|
&Length,
|
|
MEM_RESERVE|MEM_COMMIT);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to free memory for client, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
while (SuspendTimes) {
|
|
Status = NtResumeThread(Thread->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to resume thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2DispatchToHandler: resume thread, count = %d\n",
|
|
Process->ProcessId,
|
|
SuspendCount);
|
|
}
|
|
#endif // DBG
|
|
SuspendTimes--;
|
|
}
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2DispatchToHandler: Fail to resume thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
POS2_THREAD
|
|
Os2GetThread1(
|
|
POS2_PROCESS Process
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
POS2_THREAD Thread = NULL;
|
|
|
|
ListHead = &Process->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
Thread = NULL;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, OS2_THREAD, Link );
|
|
if (Thread->Flags & OS2_THREAD_THREAD1)
|
|
return Thread;
|
|
else
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosDispatch16Signal(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
POS2_DISPATCH16_SIGNAL a = &m->u.Dispatch16Signal;
|
|
|
|
POS2_THREAD Thread1 = NULL;
|
|
OD2_SIG_HANDLER_REC sighandler;
|
|
POS2_PROCESS NextProcess;
|
|
POS2_PROCESS Process;
|
|
POS2_PROCESS ParentProcess;
|
|
LARGE_INTEGER timeout;
|
|
PLARGE_INTEGER ptimeout = &timeout;
|
|
NTSTATUS Status;
|
|
OS2_API_MSG mhold;
|
|
ULONG SuspendCount;
|
|
|
|
//
|
|
// Get process which sent message
|
|
//
|
|
Process = Os2LocateProcessByProcessId( NULL,
|
|
t->Process,
|
|
(PID)a->pidProcess,
|
|
(BOOLEAN)FALSE
|
|
);
|
|
|
|
if (Process == NULL) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_PROCID;
|
|
return TRUE;
|
|
}
|
|
|
|
ParentProcess = Process;
|
|
//
|
|
// Scan list of childern see if any exist
|
|
//
|
|
if (!IsListEmpty( &Process->ChildrenList )) {
|
|
NextProcess = CONTAINING_RECORD(Process->ChildrenList.Flink,
|
|
OS2_PROCESS,
|
|
SiblingLink);
|
|
}
|
|
else {
|
|
NextProcess = NULL;
|
|
}
|
|
|
|
doanother:
|
|
//
|
|
// Read Signal handler record from address space of process which is
|
|
// to receive the signal
|
|
//
|
|
|
|
Thread1 = Os2GetThread1(Process);
|
|
|
|
Status = NtSuspendThread(Thread1->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2DosDispatch16Signal: suspend thread, count = %d, Status=%x\n",
|
|
Process->ProcessId,
|
|
SuspendCount,
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
ASSERT(FALSE);
|
|
}
|
|
else {
|
|
|
|
__try {
|
|
|
|
if (Os2GetSigHandlerRec(m, &sighandler, &Process)) {
|
|
__leave;
|
|
}
|
|
//
|
|
// Check to see if this is a Hold Signal message
|
|
//
|
|
if (a->usFlagNum == HOLD_SIGNAL_CLEARED) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d,%d]Os2DosDispatch16Signal: accept outstanding signal\n",
|
|
t->Process->ProcessId,
|
|
t->ThreadId);
|
|
}
|
|
#endif // DBG
|
|
mhold.u.Dispatch16Signal.usFlagNum =
|
|
sighandler.outstandingsig[a->usFlagArg - 1].usFlagNum;
|
|
sighandler.outstandingsig[a->usFlagArg - 1].usFlagNum = 0;
|
|
mhold.u.Dispatch16Signal.usFlagArg =
|
|
sighandler.outstandingsig[a->usFlagArg - 1].usFlagArg;
|
|
sighandler.outstandingsig[a->usFlagArg - 1].usFlagArg = 0;
|
|
mhold.u.Dispatch16Signal.pidProcess =
|
|
sighandler.outstandingsig[a->usFlagArg - 1].pidProcess;
|
|
sighandler.outstandingsig[a->usFlagArg - 1].pidProcess = 0;
|
|
mhold.u.Dispatch16Signal.routine =
|
|
sighandler.outstandingsig[a->usFlagArg - 1].routine;
|
|
sighandler.outstandingsig[a->usFlagArg - 1].routine = 0;
|
|
mhold.u.Dispatch16Signal.sighandleraddr =
|
|
sighandler.outstandingsig[a->usFlagArg - 1].sighandleraddr;
|
|
sighandler.outstandingsig[a->usFlagArg - 1].sighandleraddr = 0;
|
|
//
|
|
// Write Signal table back to process
|
|
//
|
|
Status = NtWriteVirtualMemory(Process->ProcessHandle,
|
|
(PVOID) a->sighandleraddr,
|
|
(PVOID) &sighandler,
|
|
sizeof(sighandler),
|
|
NULL);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
ASSERT(FALSE);
|
|
__leave;
|
|
}
|
|
if (Os2GetSigHandlerRec(&mhold, &sighandler, &Process)) {
|
|
__leave;
|
|
}
|
|
if (sighandler.action[a->usFlagArg - 1] == SIGA_ACKNOWLEDGE_AND_ACCEPT) {
|
|
sighandler.action[a->usFlagArg - 1] = SIGA_ACCEPT;
|
|
}
|
|
a = &mhold.u.Dispatch16Signal;
|
|
}
|
|
else if (sighandler.action[a->usFlagNum - 1] == SIGA_ACKNOWLEDGE ||
|
|
sighandler.action[a->usFlagNum - 1] == SIGA_ACKNOWLEDGE_AND_ACCEPT ||
|
|
sighandler.fholdenable) {
|
|
//
|
|
// See if we already are holding an unacknowleged signal or a hold
|
|
// signal
|
|
//
|
|
if (sighandler.outstandingsig[a->usFlagNum - 1].sighandleraddr != 0) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d,%d]Os2DosDispatch16Signal: Outstanding signal wasn't pended, refuse\n",
|
|
t->Process->ProcessId,
|
|
t->ThreadId);
|
|
}
|
|
#endif // DBG
|
|
m->ReturnedErrorValue = ERROR_SIGNAL_PENDING;
|
|
__leave;
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d,%d]Os2DosDispatch16Signal: Save outstanding signal\n",
|
|
t->Process->ProcessId,
|
|
t->ThreadId);
|
|
}
|
|
#endif // DBG
|
|
//
|
|
// Save this signal till other is processed
|
|
//
|
|
sighandler.outstandingsig[a->usFlagNum - 1].usFlagNum = a->usFlagNum;
|
|
sighandler.outstandingsig[a->usFlagNum - 1].usFlagArg = a->usFlagArg;
|
|
sighandler.outstandingsig[a->usFlagNum - 1].pidProcess = a->pidProcess;
|
|
sighandler.outstandingsig[a->usFlagNum - 1].routine = a->routine;
|
|
sighandler.outstandingsig[a->usFlagNum - 1].sighandleraddr =
|
|
a->sighandleraddr;
|
|
//
|
|
// Write Signal table back to process
|
|
//
|
|
Status = NtWriteVirtualMemory(Process->ProcessHandle,
|
|
(PVOID) a->sighandleraddr,
|
|
(PVOID) &sighandler,
|
|
sizeof(sighandler),
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
ASSERT(FALSE);
|
|
}
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Check if we should ignore signal
|
|
//
|
|
if (sighandler.action[a->usFlagNum - 1] == SIGA_IGNORE) {
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
__leave;
|
|
}
|
|
//
|
|
// Check to see if we can accept the signal
|
|
//
|
|
if (sighandler.action[a->usFlagNum - 1] != SIGA_ACCEPT) {
|
|
m->ReturnedErrorValue = ERROR_SIGNAL_REFUSED;
|
|
__leave;
|
|
}
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d,%d]Os2DosDispatch16Signal: Accept signal\n",
|
|
t->Process->ProcessId,
|
|
t->ThreadId);
|
|
}
|
|
#endif // DBG
|
|
//
|
|
// Disable this signal till we get a SIGA_ACKNOWLEDGE
|
|
//
|
|
sighandler.action[a->usFlagNum - 1] = SIGA_ACKNOWLEDGE;
|
|
//
|
|
// Write Signal table back to process
|
|
//
|
|
Status = NtWriteVirtualMemory(Process->ProcessHandle,
|
|
(PVOID) a->sighandleraddr,
|
|
(PVOID) &sighandler,
|
|
sizeof(sighandler),
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
ASSERT(FALSE);
|
|
__leave;
|
|
}
|
|
//
|
|
// Search list to find thread 1. The signal handler function has to
|
|
// run in thread 1's context
|
|
//
|
|
if (Thread1 == NULL){
|
|
m->ReturnedErrorValue = ERROR_INVALID_PROCID;
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
DbgPrint("Os2Dispatch16Signal: Thread1 is already gone\n");
|
|
}
|
|
#endif
|
|
ASSERT(FALSE);
|
|
__leave;
|
|
}
|
|
if (Thread1->Dying == TRUE || !(Thread1->Flags & OS2_THREAD_THREAD1)) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_PROCID;
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
DbgPrint("Os2Dispatch16Signal: Thread1->Dying\n");
|
|
}
|
|
#endif
|
|
__leave;
|
|
}
|
|
if((m->ReturnedErrorValue = Os2DispatchToHandler(Thread1,a)) != NO_ERROR) {
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d,%d]Os2DosDispatch16Signal: Skip signal\n",
|
|
t->Process->ProcessId,
|
|
t->ThreadId);
|
|
}
|
|
#endif // DBG
|
|
sighandler.action[a->usFlagNum - 1] = SIGA_ACCEPT;
|
|
Status = NtWriteVirtualMemory(Process->ProcessHandle,
|
|
(PVOID) a->sighandleraddr,
|
|
(PVOID) &sighandler,
|
|
sizeof(sighandler),
|
|
NULL);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY;
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
Status = NtResumeThread(Thread1->ThreadHandle, &SuspendCount);
|
|
#if DBG
|
|
IF_OS2_DEBUG( SIG ) {
|
|
DbgPrint("[%d]Os2DosDispatch16Signal: resume thread, count = %d, Status=%x\n",
|
|
Process->ProcessId,
|
|
SuspendCount,
|
|
Status);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for error return if found
|
|
//
|
|
if (m->ReturnedErrorValue != 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (Thread1 != NULL) {
|
|
Status = NtAlertThread(Thread1->ThreadHandle);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("[%d]Os2DosDispatch16Signal: Fail to alert thread, Status=%x\n",
|
|
Process->ProcessId,
|
|
Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // DBG
|
|
Os2CompleteResumeThread(Thread1);
|
|
}
|
|
|
|
//
|
|
// For any other signal other than kill process & flag process
|
|
// we are done
|
|
//
|
|
if (a->usFlagNum == SIG_CTRLC || a->usFlagNum == SIG_CTRLBREAK) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check scope of signal, if scope is flag subtree continue sending
|
|
// signal to childern processes otherwise return
|
|
//
|
|
if (a->fscope) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (NextProcess != NULL) {
|
|
Process = NextProcess;
|
|
if (ParentProcess == CONTAINING_RECORD(Process->SiblingLink.Flink,
|
|
OS2_PROCESS,
|
|
ChildrenList)) {
|
|
NextProcess = NULL;
|
|
}
|
|
else {
|
|
NextProcess = CONTAINING_RECORD(Process->SiblingLink.Flink,
|
|
OS2_PROCESS,
|
|
SiblingLink);
|
|
}
|
|
goto doanother;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DispatchVector(
|
|
IN PDBGKM_APIMSG ReceiveMsg,
|
|
POS2_THREAD Thread,
|
|
CONTEXT Context
|
|
)
|
|
{
|
|
|
|
struct VectorHandlerRec {
|
|
ULONG VecHandler[6];
|
|
ULONG doscallssel;
|
|
};
|
|
struct VectorHandlerRec VectorHandler;
|
|
ULONG NewSp;
|
|
USHORT Args[4];
|
|
USHORT FPUStatus = 0;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Read Vector handler Record for this process
|
|
//
|
|
Status = NtReadVirtualMemory(Thread->Process->ProcessHandle,
|
|
Thread->Process->VectorHandler,
|
|
(PVOID) &VectorHandler,
|
|
sizeof(struct VectorHandlerRec),
|
|
NULL
|
|
);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
return TRUE; // Dispatch failed
|
|
}
|
|
//
|
|
// Place on stack of process the machine status word and the return
|
|
// CS:IP of the process which is running before changing the CS:IP
|
|
// to the vector handler.
|
|
//
|
|
Args[3] = (USHORT) Context.EFlags; // flags
|
|
Args[2] = (USHORT) Context.SegCs; // CS
|
|
Args[1] = (USHORT) (Context.Eip & 0xffff); // IP
|
|
|
|
//
|
|
// Change CS:IP to vector handler address
|
|
//
|
|
switch(ReceiveMsg->u.Exception.ExceptionRecord.ExceptionCode) {
|
|
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
|
Context.SegCs = VectorHandler.VecHandler[0] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[0] & 0xffff;
|
|
break;
|
|
case STATUS_INTEGER_OVERFLOW:
|
|
Context.SegCs = VectorHandler.VecHandler[1] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[1] & 0xffff;
|
|
break;
|
|
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
|
Context.SegCs = VectorHandler.VecHandler[2] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[2] & 0xffff;
|
|
break;
|
|
case STATUS_ILLEGAL_INSTRUCTION:
|
|
Context.SegCs = VectorHandler.VecHandler[3] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[3] & 0xffff;
|
|
break;
|
|
case STATUS_ILLEGAL_FLOAT_CONTEXT:
|
|
Context.SegCs = VectorHandler.VecHandler[4] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[4] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_DENORMAL_OPERAND:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_INEXACT_RESULT:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_INVALID_OPERATION:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_OVERFLOW:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_STACK_CHECK:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
case STATUS_FLOAT_UNDERFLOW:
|
|
FPUStatus = (USHORT) Context.FloatSave.StatusWord;
|
|
Context.SegCs = VectorHandler.VecHandler[5] >> 16;
|
|
Context.Eip = VectorHandler.VecHandler[5] & 0xffff;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check to see if a vector handler exists
|
|
//
|
|
if (Context.SegCs == (VectorHandler.doscallssel >> 16)) {
|
|
return TRUE; // Dispatch failed
|
|
}
|
|
|
|
if (FPUStatus != 0) {
|
|
//
|
|
// mask floating point exceptions
|
|
//
|
|
Context.FloatSave.ControlWord |= 0x3f;
|
|
|
|
Args[0] = FPUStatus;
|
|
}
|
|
|
|
//
|
|
// Remove 32-bit eip, cs, Eflags from stack put on 16-bit ip, cs, flags
|
|
//
|
|
NewSp = (ULONG)(SELTOFLAT(Context.SegSs)) + (USHORT) Context.Esp;
|
|
Context.Esp += (FPUStatus == 0 ? 3 : 4) * sizeof(USHORT);
|
|
NewSp += (FPUStatus == 0 ? 3 : 4) * sizeof(USHORT);
|
|
Status = NtWriteVirtualMemory( Thread->Process->ProcessHandle,
|
|
(PVOID) NewSp,
|
|
(FPUStatus == 0 ? &Args[1] : &Args[0]),
|
|
(FPUStatus == 0 ? 3 : 4) * sizeof(USHORT),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
return TRUE; // Dispatch failed
|
|
}
|
|
|
|
Context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
|
|
Status = NtSetContextThread(Thread->ThreadHandle, &Context);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
return TRUE; // Dispatch failed
|
|
}
|
|
|
|
return FALSE; // Dispatch completed
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
Os2IssueSignalTree(
|
|
IN POS2_PROCESS RootProcess,
|
|
IN int Signal
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine recursively issues a "Signal" signal to thread one
|
|
of each process in a tree.
|
|
|
|
Arguments:
|
|
|
|
RootProcess - root process of tree to issue Signal to
|
|
|
|
Signal - signal to send
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
|
|
Os2IssueSignal( RootProcess, Signal );
|
|
|
|
ListHead = &RootProcess->ChildrenList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Os2IssueSignalTree( CONTAINING_RECORD( ListNext,
|
|
OS2_PROCESS,
|
|
SiblingLink
|
|
),
|
|
Signal
|
|
);
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
}
|
|
|
|
|
|
APIRET
|
|
Os2IssueSignal(
|
|
IN POS2_PROCESS Process,
|
|
IN int Signal
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine recursively issues a "Signal" signal to thread one
|
|
of a process.
|
|
|
|
Arguments:
|
|
|
|
Process - process to Signal
|
|
|
|
Signal - signal to send
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_PROCID - the specified process is dying.
|
|
|
|
ERROR_SIGNAL_PENDING - the specified signal is already pending for the
|
|
thread.
|
|
|
|
--*/
|
|
|
|
{
|
|
POS2_THREAD Thread1;
|
|
APIRET rc;
|
|
Thread1 = CONTAINING_RECORD( Process->ThreadList.Flink, OS2_THREAD, Link );
|
|
if (IsListEmpty( &Process->ThreadList )) {
|
|
//
|
|
// Process Termination in progress, no threads left to signal
|
|
//
|
|
|
|
#if DBG
|
|
IF_OS2_DEBUG( EXCEPTIONS ) {
|
|
DbgPrint("Os2IssueSignal: Process is dying\n");
|
|
}
|
|
#endif
|
|
return(ERROR_INVALID_PROCID);
|
|
}
|
|
if (Thread1->Dying == TRUE) {
|
|
rc = ERROR_INVALID_PROCID;
|
|
}
|
|
else {
|
|
rc = SendSignalException(Thread1, Signal);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosRegisterCtrlHandler(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
|
|
POS2_REGISTER_HANDLER a = &m->u.DosRegisterCtrlHandler;
|
|
POS2_PROCESS Process;
|
|
POS2_SESSION Session;
|
|
POS2_REGISTER_HANDLER_REC pRec;
|
|
POS2_REGISTER_HANDLER_REC pPRec;
|
|
|
|
Process = t->Process;
|
|
Session = Process->Session;
|
|
|
|
if (a->fAction == SIGA_ENABLE_HANDLING) {
|
|
Process->CtrlHandlerFlag = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
if (a->fAction == SIGA_KILL) {
|
|
pRec = Session->RegisterCtrlHandler;
|
|
pPRec = pRec;
|
|
while (pRec != NULL) {
|
|
if (pRec->Process == Process && pRec->Signal == a->usFlagNum) {
|
|
if (pPRec != Session->RegisterCtrlHandler) {
|
|
pPRec->Link = pRec->Link;
|
|
}
|
|
else {
|
|
Session->RegisterCtrlHandler = pRec->Link;
|
|
}
|
|
RtlFreeHeap(Os2Heap, 0, pRec);
|
|
break;
|
|
}
|
|
pPRec = pRec;
|
|
pRec = pRec->Link;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
pPRec = (POS2_REGISTER_HANDLER_REC)
|
|
RtlAllocateHeap(Os2Heap, 0,
|
|
sizeof(OS2_REGISTER_HANDLER_REC));
|
|
if (pPRec == NULL) {
|
|
#if DBG
|
|
DbgPrint("Os2DosRegisterCtrlHandler, no memory for heap\n");
|
|
#endif
|
|
ASSERT(FALSE);
|
|
return (TRUE);
|
|
}
|
|
pPRec->Signal = a->usFlagNum;
|
|
pPRec->fAction = a->fAction;
|
|
pPRec->Process = Process;
|
|
|
|
|
|
if (Session->RegisterCtrlHandler == NULL) {
|
|
Session->RegisterCtrlHandler = pPRec;
|
|
pPRec->Link = NULL;
|
|
}
|
|
else {
|
|
pRec = Session->RegisterCtrlHandler;
|
|
Session->RegisterCtrlHandler = pPRec;
|
|
pPRec->Link = pRec;
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
VOID
|
|
Os2DeRegisterCtrlHandler(
|
|
POS2_PROCESS Process
|
|
)
|
|
{
|
|
|
|
POS2_REGISTER_HANDLER_REC pRec;
|
|
POS2_REGISTER_HANDLER_REC pPRec;
|
|
POS2_REGISTER_HANDLER_REC pRecTmp;
|
|
|
|
POS2_SESSION Session;
|
|
|
|
Session = Process->Session;
|
|
if(Session!=NULL) {
|
|
pRec = Session->RegisterCtrlHandler;
|
|
pPRec = pRec;
|
|
|
|
while (pRec != NULL) {
|
|
if (pRec->Process == Process) {
|
|
if (pRec == Session->RegisterCtrlHandler) {
|
|
Session->RegisterCtrlHandler = pRec->Link;
|
|
pPRec = pRec->Link;
|
|
}
|
|
else {
|
|
pPRec->Link = pRec->Link;
|
|
}
|
|
pRecTmp = pRec->Link;
|
|
RtlFreeHeap(Os2Heap, 0, pRec);
|
|
pRec = pRecTmp;
|
|
|
|
}
|
|
else {
|
|
pPRec = pRec;
|
|
pRec = pRec->Link;
|
|
}
|
|
|
|
}
|
|
}
|
|
return;
|
|
|
|
}
|