1959 lines
50 KiB
C
1959 lines
50 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
process.c
|
||
|
||
Abstract:
|
||
|
||
Implementation of PSX Process Structure Backbone.
|
||
|
||
Author:
|
||
|
||
Mark Lucovsky (markl) 08-Mar-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "psxsrv.h"
|
||
#include "seposix.h"
|
||
#include "sesport.h"
|
||
|
||
NTSTATUS
|
||
PsxInitializeProcessStructure()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the PSX process structure. This includes
|
||
creating an initial process table, open file tables, CLIENT_ID to PID hash
|
||
table, and PID allocator.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Status.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG i;
|
||
PPSX_PROCESS Process;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Initialize PsxProcessStructureLock
|
||
//
|
||
|
||
Status = RtlInitializeCriticalSection(&BlockLock);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
InitializeListHead(&DefaultBlockList);
|
||
|
||
Status = RtlInitializeCriticalSection(&PsxProcessStructureLock);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Initialize the ClientIdHashTable
|
||
//
|
||
|
||
for (i = 0; i < CIDHASHSIZE ; i++) {
|
||
InitializeListHead(&ClientIdHashTable[i]);
|
||
}
|
||
|
||
//
|
||
// Initialize Process Table
|
||
//
|
||
|
||
FirstProcess = PsxProcessTable;
|
||
LastProcess = &PsxProcessTable[32-1];
|
||
|
||
for (Process = FirstProcess; Process < LastProcess; Process++) {
|
||
Process->Flags |= P_FREE;
|
||
Process->SequenceNumber = 0;
|
||
}
|
||
return Status;
|
||
|
||
}
|
||
|
||
PPSX_PROCESS
|
||
PsxAllocateProcess (
|
||
IN PCLIENT_ID ClientId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates a slot in the process table for the process
|
||
with the specified CLIENT_ID. Once a free process table slot is
|
||
allocated, the process is placed in the ClientIdHashTable. The process
|
||
slot sequence number is incremented, and a Pid is assigned to the
|
||
process. The process' process lock in initialized, and the process
|
||
table lock is released. A pointer to the new process is returned.
|
||
The process lock of the new process remains held so that the caller
|
||
can further initialize the process.
|
||
|
||
|
||
Arguments:
|
||
|
||
ClientId - Supplies the address of the client id associated with the
|
||
process. Only the UniqueProcess portion of the ClientId is used.
|
||
|
||
Return Value:
|
||
|
||
Null - No free process table slot could be located.
|
||
|
||
Non-Null - A pointer to the allocated process. The process' process lock is
|
||
held, the process can be located in the ClientIdHashTable, and
|
||
the process has a Pid.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPSX_PROCESS Process;
|
||
ULONG index;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Lock Process Table
|
||
//
|
||
|
||
AcquireProcessStructureLock();
|
||
|
||
for (Process = FirstProcess, index = 0; Process < LastProcess;
|
||
Process++, index++) {
|
||
if (!(Process->Flags & P_FREE)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Slot Found. Mark process as allocated and free process table
|
||
// lock.
|
||
//
|
||
|
||
Process->Flags &= ~P_FREE;
|
||
Process->ClientId = *ClientId;
|
||
Process->ClientPort = NULL;
|
||
|
||
//
|
||
// Place Process In CLIENT_ID Hash Table
|
||
//
|
||
|
||
InsertTailList(&ClientIdHashTable[CIDTOHASHINDEX(ClientId)],
|
||
&Process->ClientIdHashLinks);
|
||
|
||
if (++Process->SequenceNumber > 255)
|
||
Process->SequenceNumber = 1;
|
||
|
||
MAKEPID(Process->Pid, Process->SequenceNumber, index);
|
||
|
||
if (Process->Pid == SPECIALPID) {
|
||
KdPrint(("PSXSS: seq %d, index %d\n", Process->SequenceNumber, index));
|
||
}
|
||
ASSERT(Process->Pid != SPECIALPID);
|
||
|
||
Status = RtlInitializeCriticalSection(&Process->ProcessLock);
|
||
if (!NT_SUCCESS(Status)) {
|
||
ReleaseProcessStructureLock();
|
||
return NULL;
|
||
}
|
||
|
||
AcquireProcessLock(Process);
|
||
|
||
//
|
||
// Unlock Process Table
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
|
||
IF_PSX_DEBUG(EXEC) {
|
||
KdPrint(("PsxAllocateProcess: Process = %lx, ClientId %lx.%lx "
|
||
"Pid %lx\n", Process, Process->ClientId.UniqueProcess,
|
||
Process->ClientId.UniqueThread, Process->Pid));
|
||
}
|
||
return Process;
|
||
}
|
||
|
||
//
|
||
// Unlock Process Table
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
return (PPSX_PROCESS)NULL;
|
||
}
|
||
|
||
|
||
PPSX_PROCESS
|
||
PsxLocateProcessByClientId (
|
||
IN PCLIENT_ID ClientId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function attempts to locate the process with the specified CLIENT_ID.
|
||
It depends on the way PsxAllocateProcess allocates a process table
|
||
slot, assigns it a CLIENT_ID, and places it in the ClientIdHashTable while
|
||
holding the PsxProcessStructureLock. Only the UniqueProcess portion of the
|
||
specified CLIENT_ID is used.
|
||
|
||
Arguments:
|
||
|
||
ClientId - Supplies the client id associated with the process to be located.
|
||
|
||
Return Value:
|
||
|
||
Null - No process whose client id matches the specified ClientId parameter
|
||
is located in the ClientIdHashTable.
|
||
|
||
Non-Null - Returns the address of the process whose CLIENT_ID matches the
|
||
specified ClientId.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPSX_PROCESS Process;
|
||
PLIST_ENTRY head, next;
|
||
|
||
head = &ClientIdHashTable[CIDTOHASHINDEX(ClientId)];
|
||
|
||
//
|
||
// Lock Process Table
|
||
//
|
||
|
||
AcquireProcessStructureLock();
|
||
|
||
next = head->Flink;
|
||
while (next != head) {
|
||
Process = CONTAINING_RECORD(next, PSX_PROCESS, ClientIdHashLinks);
|
||
if (Process->ClientId.UniqueProcess == ClientId->UniqueProcess &&
|
||
Process->ClientId.UniqueThread == ClientId->UniqueThread) {
|
||
|
||
//
|
||
// Unlock Process Table
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
return Process;
|
||
}
|
||
next = next->Flink;
|
||
}
|
||
|
||
//
|
||
// Unlock Process Table
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
return (PPSX_PROCESS)NULL;
|
||
}
|
||
|
||
PPSX_PROCESS
|
||
PsxLocateProcessBySession (
|
||
IN PPSX_SESSION Session
|
||
)
|
||
{
|
||
PPSX_PROCESS Process;
|
||
|
||
//
|
||
// Lock Process Table
|
||
//
|
||
|
||
AcquireProcessStructureLock();
|
||
|
||
for (Process = FirstProcess; Process < LastProcess; Process++) {
|
||
if (!(Process->Flags & P_FREE)) {
|
||
continue;
|
||
}
|
||
|
||
if (Process->PsxSession == Session) {
|
||
|
||
//
|
||
// Unlock Process Table
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
return Process;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Unlock Process Table
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
return (PPSX_PROCESS)NULL;
|
||
}
|
||
|
||
BOOLEAN
|
||
IsGroupOrphaned(
|
||
IN pid_t ProcessGroup
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to determine if the specified ProcessGroup
|
||
is orphaned.
|
||
|
||
An orphaned process group is a group where the parent of every member
|
||
is itself a member, or is not a member of the group's session. This
|
||
definition is a little flawed since it assumes that processes without
|
||
a parent are inherited by a real process in a different session.
|
||
|
||
|
||
Arguments:
|
||
|
||
ProcessGroup - Supplies the process group to check
|
||
|
||
Return Value:
|
||
|
||
TRUE - The process group is orphaned.
|
||
|
||
FALSE - The process group is not orphaned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPSX_PROCESS Parent, FirstMember, NextMember;
|
||
PLIST_ENTRY Next;
|
||
BOOLEAN Orphaned,MemberFound;
|
||
|
||
LockNtSessionList();
|
||
|
||
//
|
||
// First locate a member of the group by scanning the process
|
||
// table. Once a group member is found, then whip through the
|
||
// group list looking to see if any member's parent is not in
|
||
// the session of the group, or whose parent is SPECIALPID
|
||
//
|
||
|
||
MemberFound = FALSE;
|
||
|
||
for (FirstMember = FirstProcess; FirstMember < LastProcess; FirstMember++) {
|
||
if (FirstMember->Flags & P_FREE) {
|
||
continue;
|
||
}
|
||
|
||
if (FirstMember->ProcessGroupId == ProcessGroup) {
|
||
MemberFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
ASSERT(MemberFound);
|
||
|
||
Orphaned = FALSE;
|
||
|
||
//
|
||
// Scan the group. For each member, look at the members parent.
|
||
//
|
||
// If the parent is SPECIALPID, then the group is orphaned,
|
||
// else if the parent is not in the same group and is in a
|
||
// different session, then the group is oprphaned
|
||
//
|
||
|
||
NextMember = FirstMember;
|
||
do {
|
||
|
||
//
|
||
// The parent of a member is not in the same session so the
|
||
// group is orphaned.
|
||
//
|
||
|
||
if (NextMember->ParentPid == SPECIALPID) {
|
||
Orphaned = TRUE;
|
||
break;
|
||
} else {
|
||
//
|
||
// The member has a parent, so see if the parent is in
|
||
// a different group and in a different session. If this
|
||
// is the case, then the group is orphaned.
|
||
//
|
||
|
||
Parent = PIDTOPROCESS(NextMember->ParentPid);
|
||
|
||
if (Parent->ProcessGroupId != ProcessGroup &&
|
||
Parent->PsxSession != FirstMember->PsxSession) {
|
||
Orphaned = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
Next = NextMember->GroupLinks.Flink;
|
||
NextMember = CONTAINING_RECORD(Next, PSX_PROCESS, GroupLinks);
|
||
|
||
} while (NextMember != FirstMember);
|
||
|
||
UnlockNtSessionList();
|
||
|
||
return Orphaned;
|
||
}
|
||
|
||
BOOLEAN
|
||
PsxStopProcess(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m,
|
||
IN ULONG Signal,
|
||
IN sigset_t *RestoreBlockSigset OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called by PendingSignalHandledInside in response
|
||
to a pending stop signal. It simply blocks the process awaiting a
|
||
SIGCONT signal.
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the process to stop
|
||
|
||
m - Supplies the reply message to be generated when the process is
|
||
continued
|
||
|
||
Signal - Supplies the signal number used to stop the process.
|
||
If the stop signal is anything other than SIGSTOP, a check is
|
||
made to see if the processes group is orphaned before stopping
|
||
the process.
|
||
|
||
RestoreBlockSigset - Contains an optional blocked signal mask to
|
||
be restured during the reply.
|
||
|
||
Locks - Process lock always released before return.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The process was stopped by this call.
|
||
|
||
FALSE - The process was not stopped.
|
||
|
||
--*/
|
||
|
||
{
|
||
sigset_t RestoreBlockMask;
|
||
PPSX_PROCESS Parent;
|
||
NTSTATUS Status;
|
||
|
||
if (ARGUMENT_PRESENT(RestoreBlockSigset)) {
|
||
RestoreBlockMask = *RestoreBlockSigset;
|
||
} else {
|
||
RestoreBlockMask = (sigset_t)0xffffffff;
|
||
}
|
||
|
||
ReleaseProcessLock(p);
|
||
|
||
AcquireProcessStructureLock();
|
||
|
||
if (Signal != SIGSTOP) {
|
||
if (IsGroupOrphaned(p->ProcessGroupId)) {
|
||
ReleaseProcessStructureLock();
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
p->State = Stopped;
|
||
p->ExitStatus = (Signal << 8) | 0177;
|
||
p->Flags &= ~P_WAITED; // proc may satisfy wait
|
||
|
||
//
|
||
// Possibly generate a SIGCHLD to the parent, and satisfy
|
||
// a parent wait
|
||
//
|
||
|
||
if (p->ParentPid != SPECIALPID) {
|
||
|
||
Parent = PIDTOPROCESS(p->ParentPid);
|
||
AcquireProcessLock(Parent);
|
||
|
||
//
|
||
// if the SA_NOCLDSTOP flag is not sent, then SIGCHLD the parent
|
||
//
|
||
|
||
if (!Parent->SignalDataBase.SignalDisposition[SIGCHLD-1].sa_flags
|
||
& SA_NOCLDSTOP) {
|
||
PsxSignalProcess(Parent,SIGCHLD);
|
||
}
|
||
|
||
ReleaseProcessLock(Parent);
|
||
|
||
if (Parent->State == Waiting) {
|
||
|
||
Status = BlockProcess(p, (PVOID)RestoreBlockMask,
|
||
PsxStopProcessHandler, m, FALSE, &PsxProcessStructureLock);
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
UnblockProcess(Parent, WaitSatisfyInterrupt, FALSE, 0);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
Status = BlockProcess(p, (PVOID)RestoreBlockMask, PsxStopProcessHandler,
|
||
m, FALSE, &PsxProcessStructureLock);
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
PsxStopProcessHandler(
|
||
IN PPSX_PROCESS p,
|
||
IN PINTCB IntControlBlock,
|
||
IN PSX_INTERRUPTREASON InterruptReason,
|
||
IN int Signal
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure is called when a stopped process is continued.
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the process being continued.
|
||
|
||
IntControlBlock - Supplies the address of the interrupt control block.
|
||
|
||
InterruptReason - Supplies the reason that this process is being
|
||
interrupted. Not used in this handler.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPSX_API_MSG m;
|
||
sigset_t RestoreBlockSigset;
|
||
|
||
UNREFERENCED_PARAMETER(InterruptReason);
|
||
|
||
RtlLeaveCriticalSection(&BlockLock);
|
||
|
||
RestoreBlockSigset = (sigset_t)((ULONG_PTR)IntControlBlock->IntContext);
|
||
|
||
m = IntControlBlock->IntMessage;
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
|
||
|
||
m->Signal = Signal;
|
||
|
||
if ((ULONG)RestoreBlockSigset == 0xffffffff) {
|
||
ApiReply(p, m ,NULL);
|
||
} else {
|
||
ApiReply(p, m, &RestoreBlockSigset);
|
||
}
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)m);
|
||
}
|
||
|
||
VOID
|
||
PsxSignalProcess(
|
||
IN PPSX_PROCESS p,
|
||
IN ULONG Signal
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function causes the specified signal to be sent to the specified
|
||
process. Thsi works by simply marking the signal as pending, and then
|
||
making the process looking at ats pending signals.
|
||
|
||
This function must be called with the PsxProcessTable locked
|
||
|
||
Getting the process to look at its pending signals has three basic cases:
|
||
|
||
1) The process is not in PSX.
|
||
|
||
Cause the process to call __NullPosixAPI
|
||
|
||
2) The process is inside PSX and is not interruptible
|
||
|
||
Simply set signal to pending
|
||
|
||
3) The process is inside PSX and is interruptible
|
||
|
||
Dispatch to the process' interrupt control block
|
||
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the process to signal.
|
||
|
||
Signal - Supplies the Signal value.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
sigset_t Pending;
|
||
|
||
ASSERT(NULL != p);
|
||
|
||
AcquireProcessLock(p);
|
||
|
||
//
|
||
// This can happen due to exit recursion
|
||
//
|
||
|
||
if (p->State == Exited) {
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// if signal is SIGCONT, then clear any pending stop signals
|
||
// if signal is a stop signal, then clear any pending SIGCONT
|
||
//
|
||
|
||
switch (Signal) {
|
||
case SIGCONT:
|
||
p->SignalDataBase.PendingSignalMask &= ~_SIGSTOPSIGNALS;
|
||
|
||
if (p->State == Stopped) {
|
||
SIGADDSET(&p->SignalDataBase.PendingSignalMask,SIGCONT);
|
||
p->State = Active;
|
||
ReleaseProcessLock(p);
|
||
UnblockProcess(p, SignalInterrupt, FALSE, Signal);
|
||
return;
|
||
}
|
||
break;
|
||
case SIGSTOP:
|
||
case SIGTSTP:
|
||
case SIGTTIN:
|
||
case SIGTTOU:
|
||
//
|
||
// XXX.mjb: FIX to deal w/ orphaned group
|
||
//
|
||
|
||
p->SignalDataBase.PendingSignalMask &= ~(1L << (SIGCONT - 1));
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If signal is being ignored, don't add it to the pending set.
|
||
//
|
||
|
||
if (p->SignalDataBase.SignalDisposition[Signal-1].sa_handler
|
||
== (_handler)SIG_IGN) {
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Add signal to pending set. If any pending but not blocked signals
|
||
// exist for the process, then maybe make the process take a look at
|
||
// its pending signals.
|
||
//
|
||
|
||
SIGADDSET(&p->SignalDataBase.PendingSignalMask, Signal);
|
||
|
||
Pending = p->SignalDataBase.PendingSignalMask &
|
||
~p->SignalDataBase.BlockedSignalMask;
|
||
|
||
//
|
||
// If the signal is defaulted and the default action is to ignore,
|
||
// add it to the pending set but don't interrupt the system call.
|
||
// This allows a process to block sigchld and then handle it later
|
||
// by changing the action and then unblocking.
|
||
//
|
||
|
||
if (Signal == SIGCHLD &&
|
||
p->SignalDataBase.SignalDisposition[Signal-1].sa_handler
|
||
== (_handler)SIG_DFL) {
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
|
||
|
||
if (!Pending) {
|
||
|
||
// Fast path: no pending unblocked signals.
|
||
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
|
||
if (p->InPsx > 0) {
|
||
if (Stopped == p->State && SIGKILL != Signal) {
|
||
|
||
//
|
||
// While a process is stopped, any additional signals that
|
||
// are sent to the process shall not be delivered until the
|
||
// process is continued except SIGKILL, which always terminates
|
||
// the receiving process. 1003.1-1990 3.3.1.3 (1b)
|
||
//
|
||
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
|
||
// XXX.mjb: I'm tense about this code. The process being
|
||
// killed is InPsx, and we're unblocking him. But what if
|
||
// a processes is killing himself, or there is more than one
|
||
// api request thread and the other process is executing a
|
||
// system call, but is not blocked?
|
||
|
||
ReleaseProcessLock(p);
|
||
UnblockProcess(p, SignalInterrupt, FALSE, Signal);
|
||
return;
|
||
}
|
||
|
||
if (p->State == Unconnected) {
|
||
// leave the signal pending
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
|
||
ReleaseProcessLock(p);
|
||
|
||
//
|
||
// Drag Process inside so that it looks at its signals. We set
|
||
// the NO_FORK bit to ensure that the process calls the NullApi
|
||
// before he calls fork. We want to avoid a situation where the
|
||
// process stack is modified by RtlRemoteCall while the process
|
||
// is blocked in lpc in fork(), and we copy his stack to a child
|
||
// process.
|
||
//
|
||
|
||
p->Flags |= P_NO_FORK;
|
||
|
||
Status = RtlRemoteCall(p->Process, p->Thread, (PVOID)p->NullApiCaller,
|
||
0, NULL, TRUE, FALSE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("Dragging proc 0x%x inside for sig %d\n", p->Pid, Signal));
|
||
KdPrint(("PSXSS: RtlRemoteCall: 0x%x\n", Status));
|
||
}
|
||
}
|
||
|
||
static NTSTATUS
|
||
InitProcNoParent(
|
||
PPSX_PROCESS NewProcess,
|
||
IN PPSX_SESSION Session OPTIONAL,
|
||
IN ULONG SessionId OPTIONAL
|
||
);
|
||
|
||
static void
|
||
InitProcFromParent(
|
||
PPSX_PROCESS NewProcess,
|
||
PPSX_PROCESS ForkProcess
|
||
);
|
||
|
||
NTSTATUS
|
||
PsxInitializeProcess(
|
||
IN PPSX_PROCESS NewProcess,
|
||
IN PPSX_PROCESS ForkProcess OPTIONAL,
|
||
IN ULONG SessionId OPTIONAL,
|
||
IN HANDLE ProcessHandle,
|
||
IN HANDLE ThreadHandle,
|
||
IN PPSX_SESSION Session OPTIONAL
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
NewProcess->IntControlBlock = (PINTCB)NULL;
|
||
NewProcess->State = Unconnected;
|
||
NewProcess->Flags = 0;
|
||
NewProcess->Process = ProcessHandle;
|
||
NewProcess->Thread = ThreadHandle;
|
||
NewProcess->AlarmTimer = NULL;
|
||
NewProcess->ProcessIsBeingDebugged = FALSE;
|
||
|
||
NewProcess->BlockingThread = (HANDLE)NULL;
|
||
|
||
RtlZeroMemory((PVOID)&NewProcess->ProcessTimes, sizeof(struct tms));
|
||
|
||
if (ARGUMENT_PRESENT(ForkProcess)) {
|
||
InitProcFromParent(NewProcess, ForkProcess);
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = InitProcNoParent(NewProcess, Session, SessionId);
|
||
}
|
||
ReleaseProcessLock(NewProcess);
|
||
|
||
return Status;
|
||
}
|
||
|
||
static void
|
||
InitProcFromParent(
|
||
PPSX_PROCESS NewProcess,
|
||
PPSX_PROCESS ForkProcess
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Propagate Process Attributes
|
||
//
|
||
|
||
//
|
||
// InPsx -- this new process will return directly to user-land,
|
||
// without the posix subsystem replying to his message. Therefore,
|
||
// we set InPsx to 0 here.
|
||
//
|
||
|
||
NewProcess->InPsx = 0;
|
||
NewProcess->ParentPid = ForkProcess->Pid;
|
||
NewProcess->ProcessGroupId = ForkProcess->ProcessGroupId;
|
||
InsertHeadList(&ForkProcess->GroupLinks,
|
||
&NewProcess->GroupLinks);
|
||
|
||
REFERENCE_PSX_SESSION(ForkProcess->PsxSession);
|
||
NewProcess->PsxSession = ForkProcess->PsxSession;
|
||
|
||
NewProcess->EffectiveUid = ForkProcess->EffectiveUid;
|
||
NewProcess->RealUid = ForkProcess->RealUid;
|
||
NewProcess->EffectiveGid = ForkProcess->EffectiveGid;
|
||
NewProcess->RealGid = ForkProcess->RealGid;
|
||
NewProcess->DirectoryPrefix = ForkProcess->DirectoryPrefix;
|
||
NewProcess->FileModeCreationMask = ForkProcess->FileModeCreationMask;
|
||
|
||
if (ForkProcess->ProcessIsBeingDebugged && PsxpDebuggerActive) {
|
||
Status = NtSetInformationProcess(NewProcess->Process,
|
||
ProcessDebugPort, (PVOID)&PsxpDebugPort,
|
||
sizeof(HANDLE));
|
||
if (NT_SUCCESS(Status)) {
|
||
NewProcess->ProcessIsBeingDebugged = TRUE;
|
||
NewProcess->DebugUiClientId = ForkProcess->DebugUiClientId;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fork the signal database
|
||
//
|
||
|
||
NewProcess->SignalDataBase = ForkProcess->SignalDataBase;
|
||
SIGEMPTYSET(&NewProcess->SignalDataBase.PendingSignalMask);
|
||
|
||
//
|
||
// Fork The Open File Table
|
||
//
|
||
|
||
ForkProcessFileTable(ForkProcess, NewProcess);
|
||
ReleaseProcessLock(ForkProcess);
|
||
}
|
||
|
||
|
||
static NTSTATUS
|
||
InitProcNoParent(
|
||
PPSX_PROCESS NewProcess,
|
||
IN PPSX_SESSION Session,
|
||
IN ULONG SessionId OPTIONAL
|
||
)
|
||
{
|
||
HANDLE TokenHandle = NULL;
|
||
TOKEN_PRIMARY_GROUP *pGroup = NULL;
|
||
TOKEN_GROUPS *pGroups = NULL;
|
||
PTOKEN_USER pTokenUser = NULL;
|
||
ULONG LengthNeeded;
|
||
PULONG pu;
|
||
PSID_AND_ATTRIBUTES pSA;
|
||
NTSTATUS Status;
|
||
PFILEDESCRIPTOR Fd;
|
||
ULONG i;
|
||
PSID SecondGroupChoice; // to be used if primary group is
|
||
// no good.
|
||
BOOLEAN PrimaryGroupOk; // is primary group good?
|
||
|
||
NewProcess->ParentPid = SPECIALPID;
|
||
NewProcess->ProcessGroupId = NewProcess->Pid; // process group
|
||
InitializeListHead(&NewProcess->GroupLinks);
|
||
|
||
//
|
||
// This process doesn't get to user-land by returning from an lpc
|
||
// api, so we take the opportunity to set InPsx to zero here.
|
||
//
|
||
NewProcess->InPsx = 0;
|
||
|
||
Session->SessionLeader = NewProcess->Pid;
|
||
NewProcess->PsxSession = Session;
|
||
|
||
//XXX.mjb: is this really right? Or &= ~P_HAS_EXECED?
|
||
NewProcess->Flags |= P_HAS_EXECED;
|
||
NewProcess->DirectoryPrefix = NULL;
|
||
|
||
//
|
||
// Signal DataBase
|
||
//
|
||
|
||
SIGEMPTYSET(&NewProcess->SignalDataBase.BlockedSignalMask);
|
||
SIGEMPTYSET(&NewProcess->SignalDataBase.PendingSignalMask);
|
||
SIGEMPTYSET(&NewProcess->SignalDataBase.SigSuspendMask);
|
||
for (i = 0; i < _SIGMAXSIGNO; i++) {
|
||
NewProcess->SignalDataBase.SignalDisposition[i].sa_handler =
|
||
(_handler)SIG_DFL;
|
||
NewProcess->SignalDataBase.SignalDisposition[i].sa_flags = 0;
|
||
SIGEMPTYSET(&NewProcess->SignalDataBase.SignalDisposition[i].sa_mask);
|
||
}
|
||
|
||
Fd = NewProcess->ProcessFileTable;
|
||
|
||
RtlEnterCriticalSection(&SystemOpenFileLock);
|
||
{
|
||
for (i = 0; i < OPEN_MAX; i++, Fd++) {
|
||
Fd->SystemOpenFileDesc = (PSYSTEMOPENFILE)NULL;
|
||
Fd->Flags = 0;
|
||
}
|
||
} RtlLeaveCriticalSection(&SystemOpenFileLock);
|
||
|
||
//
|
||
// Examine the new process's token to figure out what the
|
||
// uid should be.
|
||
//
|
||
|
||
Status = NtOpenProcessToken(NewProcess->Process, GENERIC_READ,
|
||
&TokenHandle);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenUser,
|
||
NULL, 0, &LengthNeeded);
|
||
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
|
||
|
||
pTokenUser = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
|
||
if (NULL == pTokenUser) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto out;
|
||
}
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenUser,
|
||
(PVOID)pTokenUser, LengthNeeded, &LengthNeeded);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: NtQueryInfoToken: 0x%x\n", Status));
|
||
}
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
NewProcess->EffectiveUid = NewProcess->RealUid =
|
||
MakePosixId(pTokenUser->User.Sid);
|
||
|
||
//
|
||
// Figure out the primary group. Security allows the user to
|
||
// set his primary group to whatever he wants, so we make sure
|
||
// that his choice is reasonable. If his PrimaryGroup is in
|
||
// the group array, he's fine. If it's not, we make his
|
||
// PrimaryGroup the first group from the group array other
|
||
// than World. If there are none other than World, that's
|
||
// what he gets.
|
||
//
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
|
||
NULL, 0, &LengthNeeded);
|
||
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
|
||
|
||
pGroup = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
|
||
if (NULL == pGroup) {
|
||
NewProcess->EffectiveGid = NewProcess->RealGid = 0;
|
||
Status = STATUS_NO_MEMORY;
|
||
goto out;
|
||
}
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenPrimaryGroup,
|
||
(PVOID)pGroup, LengthNeeded, &LengthNeeded);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
|
||
0, &LengthNeeded);
|
||
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
|
||
|
||
pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
|
||
if (NULL == pGroups) {
|
||
NewProcess->EffectiveGid = NewProcess->RealGid = 0;
|
||
Status = STATUS_NO_MEMORY;
|
||
goto out;
|
||
}
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenGroups,
|
||
(PVOID)pGroups,
|
||
LengthNeeded, &LengthNeeded);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
SecondGroupChoice = NULL;
|
||
PrimaryGroupOk = FALSE;
|
||
|
||
for (i = 0; i < pGroups->GroupCount; ++i) {
|
||
if (RtlEqualSid(pGroups->Groups[i].Sid, pGroup->PrimaryGroup)) {
|
||
PrimaryGroupOk = TRUE;
|
||
}
|
||
|
||
//
|
||
// Our second group choice is the first group in the list
|
||
// that is not the World group.
|
||
//
|
||
|
||
if (NULL == SecondGroupChoice &&
|
||
SE_WORLD_POSIX_ID != MakePosixId(pGroups->Groups[i].Sid)) {
|
||
SecondGroupChoice = pGroups->Groups[i].Sid;
|
||
}
|
||
}
|
||
|
||
if (PrimaryGroupOk) {
|
||
NewProcess->EffectiveGid = NewProcess->RealGid =
|
||
MakePosixId(pGroup->PrimaryGroup);
|
||
} else if (NULL != SecondGroupChoice) {
|
||
NewProcess->EffectiveGid = NewProcess->RealGid =
|
||
MakePosixId(SecondGroupChoice);
|
||
} else {
|
||
NewProcess->EffectiveGid = NewProcess->RealGid =
|
||
SE_WORLD_POSIX_ID;
|
||
}
|
||
|
||
out:
|
||
if (pGroup) RtlFreeHeap(PsxHeap, 0, (PVOID)pGroup);
|
||
if (pGroups) RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
|
||
if (pTokenUser) RtlFreeHeap(PsxHeap, 0, (PVOID)pTokenUser);
|
||
if (TokenHandle) NtClose(TokenHandle);
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// PsxCreateProcess -- this routine is called only by PsxCreateConSession
|
||
// to create the first process for a brand-new session.
|
||
//
|
||
|
||
BOOLEAN
|
||
PsxCreateProcess(
|
||
PPSX_EXEC_INFO ExecInfo,
|
||
OUT PPSX_PROCESS *NewProcess,
|
||
IN HANDLE ParentProcess,
|
||
IN PPSX_SESSION Session
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING ImageFileName;
|
||
UNICODE_STRING Path;
|
||
UNICODE_STRING LibPath;
|
||
UNICODE_STRING CWD;
|
||
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
||
RTL_USER_PROCESS_INFORMATION ProcessInformation;
|
||
PPSX_PROCESS Process;
|
||
|
||
Path.Buffer = NULL;
|
||
LibPath.Buffer = NULL;
|
||
CWD.Buffer = NULL;
|
||
Status = RtlAnsiStringToUnicodeString(&Path, &ExecInfo->Path, TRUE);
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = RtlAnsiStringToUnicodeString(&LibPath, &ExecInfo->LibPath, TRUE);
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = RtlAnsiStringToUnicodeString(&CWD, &ExecInfo->CWD, TRUE);
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
//
|
||
// Beware - ExecInfo->ArgV is used to pass uninterpreted binary
|
||
// data. It is NOT an ANSI string. So don't try to convert
|
||
// to Unicode. Just lie to RtlCreateProcessParameters.
|
||
//
|
||
|
||
Status = RtlCreateProcessParameters(&ProcessParameters, &Path,
|
||
&LibPath, &CWD, (PUNICODE_STRING)&ExecInfo->Argv, NULL,
|
||
NULL, NULL, NULL, NULL);
|
||
}
|
||
|
||
RtlFreeUnicodeString( &Path );
|
||
RtlFreeUnicodeString( &LibPath );
|
||
RtlFreeUnicodeString( &CWD );
|
||
if (!NT_SUCCESS(Status)) {
|
||
return FALSE; // ENOMEM
|
||
}
|
||
ProcessParameters->CurrentDirectory.Handle = NULL;
|
||
|
||
ImageFileName = ProcessParameters->ImagePathName;
|
||
ImageFileName.Buffer = (PWSTR)
|
||
((PCHAR)ImageFileName.Buffer + (ULONG_PTR)ProcessParameters);
|
||
|
||
IF_PSX_DEBUG(EXEC) {
|
||
KdPrint(("PSXSS: Creating process %wZ\n", &ImageFileName));
|
||
}
|
||
|
||
Status = RtlCreateUserProcess(&ImageFileName, OBJ_CASE_INSENSITIVE,
|
||
ProcessParameters, NULL,
|
||
NULL, ParentProcess, TRUE, NULL, NULL, &ProcessInformation);
|
||
|
||
RtlDestroyProcessParameters(ProcessParameters);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: RtlCreateUserProcess: 0x%x\n", Status));
|
||
return FALSE; // ENOEXEC
|
||
}
|
||
|
||
if (ProcessInformation.ImageInformation.SubSystemType !=
|
||
IMAGE_SUBSYSTEM_POSIX_CUI) {
|
||
NtClose(ProcessInformation.Process);
|
||
return FALSE; // ENOEXEC
|
||
}
|
||
|
||
Status = NtSetInformationProcess(ProcessInformation.Process,
|
||
ProcessExceptionPort, (PVOID)&PsxApiPort, sizeof(PsxApiPort));
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: NtSetInfoProc: 0x%x\n", Status));
|
||
}
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
{
|
||
ULONG HardErrorMode = 0; // disable popups
|
||
Status = NtSetInformationProcess(
|
||
ProcessInformation.Process,
|
||
ProcessDefaultHardErrorMode,
|
||
(PVOID)&HardErrorMode,
|
||
sizeof(HardErrorMode)
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
}
|
||
|
||
Process = PsxAllocateProcess(&ProcessInformation.ClientId);
|
||
|
||
if (!Process) {
|
||
Status = NtTerminateProcess(ProcessInformation.Process,
|
||
STATUS_SUCCESS);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
NtClose(ProcessInformation.Process);
|
||
NtClose(ProcessInformation.Thread);
|
||
return FALSE; // EAGAIN
|
||
}
|
||
|
||
if (! PsxInitializeDirectories(Process, &ExecInfo->CWD)) {
|
||
Status = NtTerminateProcess(ProcessInformation.Process,
|
||
STATUS_SUCCESS);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
NtClose(ProcessInformation.Process);
|
||
NtClose(ProcessInformation.Thread);
|
||
return FALSE;
|
||
}
|
||
|
||
Status = PsxInitializeProcess(Process, NULL, 0L,
|
||
ProcessInformation.Process, ProcessInformation.Thread, Session);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix->PsxRoot.Buffer);
|
||
RtlFreeHeap(PsxHeap, 0,
|
||
Process->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
|
||
RtlFreeHeap(PsxHeap, 0, Process->DirectoryPrefix);
|
||
Process->DirectoryPrefix = NULL;
|
||
Status = NtTerminateProcess(ProcessInformation.Process,
|
||
STATUS_SUCCESS);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
NtClose(ProcessInformation.Process);
|
||
NtClose(ProcessInformation.Thread);
|
||
return FALSE;
|
||
}
|
||
|
||
Process->InitialPebPsxData.Length = sizeof(Process->InitialPebPsxData);
|
||
Process->InitialPebPsxData.ClientStartAddress = NULL;
|
||
|
||
//
|
||
// Set the session port. The client constructs the port name from the
|
||
// unique id, opens the port, and sets the actual handle.
|
||
//
|
||
|
||
Process->InitialPebPsxData.SessionPortHandle =
|
||
(HANDLE)Process->PsxSession->Terminal->UniqueId;
|
||
|
||
*NewProcess = Process;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
PPSX_SESSION
|
||
PsxAllocateSession(
|
||
IN PPSX_CONTROLLING_TTY Terminal OPTIONAL,
|
||
IN pid_t SessionLeader
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to allocate and initialize a posix
|
||
session.
|
||
|
||
Each session may be associated with a controlling terminal. If
|
||
the Terminal parameter is specified, and if the terminal is
|
||
associated with a session, then no new session is created, and the
|
||
process simply joins the session as a new process group. This
|
||
case can only occur in a double handoff... (a foreign app is execed,
|
||
and then it execs a POSIX app with a terminal stdin that is the same
|
||
as the one it left with.
|
||
|
||
Arguments:
|
||
|
||
Terminal - An optional parameter, that if supplied specifies the
|
||
controlling terminal to be associated with the session.
|
||
|
||
SessionLeader - Supplies the process id of the session leader for
|
||
this session.
|
||
|
||
Return Value:
|
||
|
||
Returns a pointer to the new session.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPSX_SESSION Session;
|
||
|
||
if (ARGUMENT_PRESENT(Terminal)) {
|
||
//
|
||
// Look inside terminal to see if it is associated with a session.
|
||
// If not, then create a session attached to the terminal
|
||
// (logon to posix). If it is associated with a session, then become
|
||
// a new group in the session and dis-regard SessionLeader parameter
|
||
//
|
||
|
||
;
|
||
}
|
||
|
||
//
|
||
// Allocate storage for the session and initialize the session
|
||
//
|
||
|
||
Session = RtlAllocateHeap(PsxHeap, 0,sizeof(PSX_SESSION));
|
||
if (NULL == Session) {
|
||
return NULL;
|
||
}
|
||
Session->ReferenceCount = 1;
|
||
Session->SessionLeader = SessionLeader;
|
||
Session->Terminal = Terminal;
|
||
|
||
if (ARGUMENT_PRESENT(Terminal)) {
|
||
Terminal->ForegroundProcessGroup = SessionLeader;
|
||
Terminal->Session = Session;
|
||
}
|
||
return Session;
|
||
}
|
||
|
||
VOID
|
||
PsxDeallocateSession(
|
||
IN PPSX_SESSION Session
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deallocates a posix session and frees the terminal
|
||
so that it can become associated with new sessions.
|
||
|
||
Arguments:
|
||
|
||
Session - Supplies the address of the posix session being deallocated
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
if (Session->Terminal) {
|
||
Session->Terminal->ForegroundProcessGroup = SPECIALPID;
|
||
Session->Terminal->Session = NULL;
|
||
|
||
RtlDeleteCriticalSection(&Session->Terminal->Lock);
|
||
|
||
if (NULL != Session->Terminal->IoBuffer) {
|
||
Status = NtUnmapViewOfSection(NtCurrentProcess(),
|
||
Session->Terminal->IoBuffer);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
}
|
||
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Session->Terminal);
|
||
}
|
||
|
||
UnlockNtSessionList();
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Session);
|
||
}
|
||
|
||
VOID
|
||
PsxTerminateProcessBySignal(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m,
|
||
IN ULONG Signal
|
||
)
|
||
{
|
||
UNREFERENCED_PARAMETER(m);
|
||
|
||
Exit(p, Signal);
|
||
}
|
||
|
||
VOID
|
||
Exit(
|
||
IN PPSX_PROCESS p,
|
||
IN ULONG ExitStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function process termination. It is called either
|
||
from PsxExit as a result of an applications call to _exit(),
|
||
or from within PSX to terminate a process.
|
||
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the exiting process
|
||
|
||
ExitStatus - Supplies the exit status for the exiting process
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPSX_PROCESS cp, WaitingParent,Parent;
|
||
PPSX_PROCESS FirstMember, NextMember;
|
||
PLIST_ENTRY Next;
|
||
NTSTATUS Status;
|
||
HANDLE AlarmTimer;
|
||
BOOLEAN OrphanedAGroupRelatedChild, AlreadyOrphaned, PreviousTimerState;
|
||
KERNEL_USER_TIMES ProcessTime;
|
||
BOOLEAN WaitForProcess;
|
||
|
||
if ((ULONG)-1 == ExitStatus) {
|
||
//
|
||
// This process is dying because of some extraordinary event,
|
||
// and we should make sure that we clean up no matter what.
|
||
// XXX.mjb: ExitStatus could be different and more informative,
|
||
// but I haven't had a chance to test that.
|
||
//
|
||
|
||
WaitForProcess = FALSE;
|
||
ExitStatus = 0;
|
||
} else {
|
||
WaitForProcess = TRUE;
|
||
}
|
||
|
||
|
||
AcquireProcessLock(p);
|
||
if (Exited == p->State) {
|
||
ReleaseProcessLock(p);
|
||
return;
|
||
}
|
||
p->State = Exited;
|
||
p->Flags &= ~P_WAITED; // proc may satisfy wait
|
||
|
||
//
|
||
// The goal is that after we've changed the process state, no one else
|
||
// will try to muck with this process. So we should be able to
|
||
// release the process lock now.
|
||
//
|
||
|
||
ReleaseProcessLock(p);
|
||
|
||
p->ExitStatus = ExitStatus;
|
||
|
||
WaitingParent = NULL;
|
||
Parent = NULL;
|
||
OrphanedAGroupRelatedChild = FALSE;
|
||
|
||
//
|
||
// If this is a local directory prefix, then deallocate it
|
||
//
|
||
|
||
if (!IS_DIRECTORY_PREFIX_REMOTE(p->DirectoryPrefix)) {
|
||
if (p->DirectoryPrefix) {
|
||
if (p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0,
|
||
(PVOID)p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
|
||
}
|
||
if (p->DirectoryPrefix->PsxCurrentWorkingDirectory.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0,
|
||
(PVOID)p->DirectoryPrefix->PsxCurrentWorkingDirectory.Buffer);
|
||
}
|
||
if (p->DirectoryPrefix->PsxRoot.Buffer) {
|
||
RtlFreeHeap(PsxHeap,
|
||
0,(PVOID)p->DirectoryPrefix->PsxRoot.Buffer);
|
||
}
|
||
RtlFreeHeap(PsxHeap, 0,(PVOID)p->DirectoryPrefix);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Lock the process table so that we can clear process' AlarmTimer
|
||
// interlocked with AlarmApcRoutine. Can not use ProcessLock since
|
||
// it is deallocated during process exit.
|
||
//
|
||
// REVISIT deallocation of process lock...
|
||
//
|
||
|
||
AcquireProcessStructureLock();
|
||
|
||
//
|
||
// Cancel and close alarm timer if present
|
||
//
|
||
|
||
if (p->AlarmTimer) {
|
||
AlarmTimer = p->AlarmTimer;
|
||
p->AlarmTimer = NULL;
|
||
|
||
//
|
||
// Unlock process table while doing timer cleanup
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
|
||
Status = NtCancelTimer(AlarmTimer, &PreviousTimerState);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: NtCancelTimer: 0x%x\n", Status));
|
||
}
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = NtClose(AlarmTimer);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Lock the process table so that we can scan process table.
|
||
//
|
||
|
||
AcquireProcessStructureLock();
|
||
}
|
||
|
||
//
|
||
// Scan process table looking for children, and job control
|
||
// related processes.
|
||
//
|
||
|
||
AlreadyOrphaned = IsGroupOrphaned(p->ProcessGroupId);
|
||
|
||
for (cp = FirstProcess; cp < LastProcess; cp++) {
|
||
|
||
//
|
||
// Only look at non-free slots
|
||
//
|
||
|
||
if (cp->Flags & P_FREE) {
|
||
continue;
|
||
}
|
||
|
||
if (cp->ParentPid == p->Pid) {
|
||
|
||
//
|
||
// Orphan and send SIGHUP to each child process
|
||
//
|
||
|
||
cp->ParentPid = SPECIALPID;
|
||
if (cp->State == Exited) {
|
||
|
||
//
|
||
// Orphaning an exited process is almost like an
|
||
// implicit wait in the sense that process resources
|
||
// are freed immediately.
|
||
//
|
||
|
||
cp->Flags |= P_FREE;
|
||
|
||
//
|
||
// Remove Process from CLIENT_ID Hash Table
|
||
//
|
||
|
||
RemoveEntryList(&cp->ClientIdHashLinks);
|
||
ASSERT(NULL != cp);
|
||
|
||
try {
|
||
RtlDeleteCriticalSection(&cp->ProcessLock);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
KdPrint(("PSXSS: took a fault\n"));
|
||
}
|
||
|
||
} else {
|
||
if (cp->ProcessGroupId == p->ProcessGroupId) {
|
||
OrphanedAGroupRelatedChild = TRUE;
|
||
}
|
||
// PsxSignalProcess(cp, SIGHUP);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (p->ParentPid != SPECIALPID) {
|
||
//
|
||
// The process has a parent. Process termination could satisfy a
|
||
// wait() or waitpid(). Parent must also be signaled.
|
||
//
|
||
|
||
|
||
Parent = PIDTOPROCESS(p->ParentPid);
|
||
|
||
//
|
||
// See if parent is waiting. Arange for wait completion...
|
||
//
|
||
|
||
if (Parent->State == Waiting) {
|
||
WaitingParent = Parent;
|
||
}
|
||
|
||
PsxSignalProcess(Parent, SIGCHLD);
|
||
}
|
||
|
||
//
|
||
// Check to see if the exiting processes group is already orphaned.
|
||
// If so, then do not do anything. Otherwise, remove him from the
|
||
// group list and then whip through the group. If any stopped processes
|
||
// are found, then SIGCONT, SIGHUP all processes in the group
|
||
//
|
||
|
||
LockNtSessionList();
|
||
|
||
FirstMember = CONTAINING_RECORD(p->GroupLinks.Flink, PSX_PROCESS,
|
||
GroupLinks);
|
||
RemoveEntryList(&p->GroupLinks);
|
||
InitializeListHead(&p->GroupLinks);
|
||
|
||
if (!AlreadyOrphaned && FirstMember != p) {
|
||
|
||
//
|
||
// See if exit causes group to be orphaned. This is the
|
||
// case if the exiting process is an orphan, or if the
|
||
// parent is in a different session.
|
||
//
|
||
|
||
if ( OrphanedAGroupRelatedChild ||
|
||
(Parent == NULL) ||
|
||
(Parent != NULL && Parent->PsxSession != p->PsxSession) ) {
|
||
|
||
//
|
||
// Scan the group. Looking for stopped processes. If a stopped
|
||
// process is found, then for each process in the group,
|
||
// send a SIGCONT followed by a SIGHUP.
|
||
//
|
||
|
||
NextMember = FirstMember;
|
||
do {
|
||
if (NextMember->State == Stopped) {
|
||
//
|
||
// A stopped group member was found. Scan the group
|
||
// and SIGCONT, SIGHUP each member
|
||
//
|
||
|
||
NextMember = FirstMember;
|
||
do {
|
||
Next = NextMember->GroupLinks.Flink;
|
||
PsxSignalProcess(NextMember, SIGCONT);
|
||
PsxSignalProcess(NextMember, SIGHUP);
|
||
NextMember = CONTAINING_RECORD(Next, PSX_PROCESS,
|
||
GroupLinks);
|
||
} while (NextMember != FirstMember);
|
||
break;
|
||
}
|
||
Next = NextMember->GroupLinks.Flink;
|
||
NextMember = CONTAINING_RECORD(Next, PSX_PROCESS, GroupLinks);
|
||
} while (NextMember != FirstMember);
|
||
}
|
||
}
|
||
|
||
UnlockNtSessionList();
|
||
|
||
//
|
||
// Unlock process table here; we depend on having just one api
|
||
// thread, so no one can call fork while we're still closing the
|
||
// files and so forth.
|
||
//
|
||
|
||
ReleaseProcessStructureLock();
|
||
|
||
//
|
||
// Now just close files, deallocate the session, close the thread,
|
||
// terminate the process, and close the process.
|
||
//
|
||
|
||
CloseProcessFileTable(p);
|
||
|
||
if (!(p->Flags & P_FOREIGN_EXEC)) {
|
||
|
||
//
|
||
// When a foreign process has exited, it's responsible
|
||
// for shooting its own threads.
|
||
//
|
||
|
||
Status = NtTerminateThread(p->Thread, ExitStatus);
|
||
#ifdef PSX_MORE_ERRORS
|
||
if (!NT_SUCCESS(Status)) {
|
||
// XXX.mjb: this may fail if the thread has already died
|
||
// for some reason, f.e. been shot by pview.
|
||
KdPrint(("PSXSS: NtTerminateThread: 0x%x\n", Status));
|
||
}
|
||
#endif
|
||
}
|
||
|
||
p->Flags &= ~P_FOREIGN_EXEC;
|
||
|
||
//
|
||
// We like to call NtWaitForSingle object on the thread handle,
|
||
// but we don't do that if the process has
|
||
// died in some unusual way, like the dll init routine failed.
|
||
//
|
||
|
||
if (WaitForProcess) {
|
||
Status = NtWaitForSingleObject(p->Thread, TRUE, NULL);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: Exit: NtWait: 0x%x\n", Status));
|
||
}
|
||
}
|
||
|
||
Status = NtClose(p->Thread);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: NtClose dead thread: 0x%x\n", Status));
|
||
}
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// We like to wait on the process handle here, sometimes we don't
|
||
// do that, see above.
|
||
//
|
||
|
||
if (WaitForProcess) {
|
||
Status = NtWaitForSingleObject(p->Process, TRUE, NULL);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: Exit: NtWait: 0x%x\n", Status));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the time for this process and add to the accumulated time for
|
||
// the process
|
||
//
|
||
|
||
Status = NtQueryInformationProcess(p->Process, ProcessTimes,
|
||
(PVOID)&ProcessTime, sizeof(KERNEL_USER_TIMES), NULL);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: NtQueryInfoProc: 0x%x\n", Status));
|
||
} else {
|
||
ULONG Remainder;
|
||
ULONG PosixTime;
|
||
|
||
PosixTime = RtlExtendedLargeIntegerDivide(
|
||
ProcessTime.KernelTime, 10000, &Remainder).LowPart;
|
||
p->ProcessTimes.tms_stime += PosixTime;
|
||
|
||
PosixTime = RtlExtendedLargeIntegerDivide(
|
||
ProcessTime.UserTime, 10000, &Remainder).LowPart;
|
||
p->ProcessTimes.tms_utime += PosixTime;
|
||
}
|
||
|
||
NtClose(p->Process);
|
||
|
||
DEREFERENCE_PSX_SESSION(p->PsxSession, ExitStatus >> 8);
|
||
|
||
Status = NtClose(p->ClientPort);
|
||
#ifdef PSX_MORE_ERRORS
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: NtClose: 0x%x\n", Status));
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Remove the process from ClientIdHashTable; this must be done
|
||
// now rather than in wait(), because unless we do it now a new
|
||
// process could be created with the same ClientId as this zombie.
|
||
// This process shouldn't be making any more api requests, and
|
||
// that's the only time we look processes up by ClientId (right?).
|
||
//
|
||
|
||
RemoveEntryList(&p->ClientIdHashLinks);
|
||
InitializeListHead(&p->ClientIdHashLinks);
|
||
|
||
if (p->ParentPid == SPECIALPID) {
|
||
//
|
||
// Parent won't clean up after wait, so do all clean up here
|
||
//
|
||
p->Flags |= P_FREE;
|
||
|
||
//
|
||
// REVISIT... This might have to stay around
|
||
//
|
||
|
||
RtlDeleteCriticalSection(&p->ProcessLock);
|
||
}
|
||
|
||
if (NULL != WaitingParent) {
|
||
UnblockProcess(WaitingParent, WaitSatisfyInterrupt, FALSE, 0);
|
||
}
|
||
}
|
||
|
||
PSZ PsxpDefaultRoot = "\\DosDevices\\C:";
|
||
|
||
|
||
BOOLEAN
|
||
PsxInitializeDirectories(
|
||
IN PPSX_PROCESS Process,
|
||
IN PANSI_STRING pCwd
|
||
)
|
||
{
|
||
PPSX_DIRECTORY_PREFIX DirectoryPrefix;
|
||
PSZ Root;
|
||
char sbWorkingDirectory[512];
|
||
char sbRoot[512];
|
||
ULONG len;
|
||
|
||
PSX_GET_SESSION_NAME_A(sbWorkingDirectory, DOSDEVICE_A);
|
||
(void)strcat(sbWorkingDirectory, pCwd->Buffer);
|
||
|
||
(void)strcpy(sbRoot, sbWorkingDirectory);
|
||
|
||
PSX_GET_STRLEN(PsxpDefaultRoot,len);
|
||
sbRoot[len] = '\0';
|
||
|
||
Root = sbRoot;
|
||
|
||
//
|
||
// The current working directory should end in a "\". We add one
|
||
// if it isn't there already.
|
||
//
|
||
|
||
if ('\\' != sbWorkingDirectory[strlen(sbWorkingDirectory) - 1]) {
|
||
(void)strcat(sbWorkingDirectory, "\\");
|
||
}
|
||
|
||
DirectoryPrefix = RtlAllocateHeap(PsxHeap, 0,sizeof(*DirectoryPrefix));
|
||
if (NULL == DirectoryPrefix) {
|
||
return FALSE; // ENOMEM
|
||
}
|
||
|
||
DirectoryPrefix->PsxRoot.Buffer = NULL;
|
||
|
||
DirectoryPrefix->NtCurrentWorkingDirectory.Buffer =
|
||
RtlAllocateHeap(PsxHeap, 0, strlen(sbWorkingDirectory) + 1);
|
||
if (NULL == DirectoryPrefix->NtCurrentWorkingDirectory.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0, DirectoryPrefix);
|
||
return FALSE;
|
||
}
|
||
|
||
DirectoryPrefix->NtCurrentWorkingDirectory.Length =
|
||
(USHORT)strlen(sbWorkingDirectory);
|
||
|
||
DirectoryPrefix->NtCurrentWorkingDirectory.MaximumLength =
|
||
DirectoryPrefix->NtCurrentWorkingDirectory.Length + 1;
|
||
|
||
RtlMoveMemory(DirectoryPrefix->NtCurrentWorkingDirectory.Buffer,
|
||
sbWorkingDirectory,
|
||
DirectoryPrefix->NtCurrentWorkingDirectory.MaximumLength);
|
||
|
||
DirectoryPrefix->PsxCurrentWorkingDirectory.Length = 0;
|
||
|
||
DirectoryPrefix->PsxRoot.Buffer = RtlAllocateHeap(PsxHeap, 0,
|
||
strlen(Root) + 1);
|
||
if (! DirectoryPrefix->PsxRoot.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0,
|
||
DirectoryPrefix->NtCurrentWorkingDirectory.Buffer);
|
||
RtlFreeHeap(PsxHeap, 0, DirectoryPrefix);
|
||
return FALSE;
|
||
}
|
||
|
||
DirectoryPrefix->PsxRoot.Length = (USHORT)strlen(Root);
|
||
|
||
DirectoryPrefix->PsxRoot.MaximumLength =
|
||
DirectoryPrefix->PsxRoot.Length + 1;
|
||
|
||
RtlMoveMemory(DirectoryPrefix->PsxRoot.Buffer, Root,
|
||
DirectoryPrefix->PsxRoot.MaximumLength);
|
||
|
||
Process->DirectoryPrefix = DirectoryPrefix;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// PsxPropagateDirectories --
|
||
//
|
||
// Read the root directory, the current working directory
|
||
// from an existing process.
|
||
//
|
||
BOOLEAN
|
||
PsxPropagateDirectories(
|
||
IN PPSX_PROCESS Process
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PPSX_DIRECTORY_PREFIX Prefix = NULL;
|
||
STRING ClientPrefix;
|
||
PVOID p;
|
||
|
||
Prefix = RtlAllocateHeap(PsxHeap, 0, sizeof(*Prefix));
|
||
if (NULL == Prefix) {
|
||
goto ErrorExit;
|
||
}
|
||
Prefix->PsxRoot.Buffer = NULL;
|
||
Prefix->NtCurrentWorkingDirectory.Buffer = NULL;
|
||
|
||
Status = NtReadVirtualMemory(Process->Process,
|
||
(PVOID)MAKE_DIRECTORY_PREFIX_VALID(Process->DirectoryPrefix),
|
||
(PVOID)Prefix, sizeof(*Prefix), NULL);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
//
|
||
// Capture the Root and NtCurrentWorkingDirectory
|
||
//
|
||
// XXX.mjb: TODO sanity check lengths ?
|
||
//
|
||
|
||
ClientPrefix = Prefix->PsxRoot;
|
||
|
||
p = RtlAllocateHeap(PsxHeap, 0, ClientPrefix.Length);
|
||
if (NULL == p) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
Prefix->PsxRoot.Buffer = p;
|
||
|
||
Status = NtReadVirtualMemory(Process->Process, ClientPrefix.Buffer,
|
||
Prefix->PsxRoot.Buffer, ClientPrefix.Length, NULL);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
|
||
Prefix->PsxRoot.Length = ClientPrefix.Length;
|
||
Prefix->PsxRoot.MaximumLength = ClientPrefix.Length;
|
||
|
||
ClientPrefix = Prefix->NtCurrentWorkingDirectory;
|
||
|
||
|
||
p = RtlAllocateHeap(PsxHeap, 0, ClientPrefix.Length);
|
||
if (NULL == p) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
Prefix->NtCurrentWorkingDirectory.Buffer = p;
|
||
|
||
Status = NtReadVirtualMemory(Process->Process, ClientPrefix.Buffer,
|
||
Prefix->NtCurrentWorkingDirectory.Buffer,
|
||
ClientPrefix.Length, NULL);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
|
||
Prefix->NtCurrentWorkingDirectory.Length = ClientPrefix.Length;
|
||
Prefix->NtCurrentWorkingDirectory.MaximumLength = ClientPrefix.Length;
|
||
|
||
Prefix->PsxCurrentWorkingDirectory.Buffer = NULL;
|
||
Prefix->PsxCurrentWorkingDirectory.Length = 0;
|
||
Prefix->PsxCurrentWorkingDirectory.MaximumLength = 0;
|
||
|
||
Process->DirectoryPrefix = Prefix;
|
||
|
||
return TRUE;
|
||
|
||
ErrorExit:
|
||
|
||
if (NULL != Prefix) {
|
||
if (NULL != Prefix->NtCurrentWorkingDirectory.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->NtCurrentWorkingDirectory.Buffer);
|
||
}
|
||
if (NULL != Prefix->PsxRoot.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->PsxRoot.Buffer);
|
||
}
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix);
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// When we allocate space in PsxPropagateDirectories, and then find
|
||
// that we can't use it, we free it with PsxFreeDirectories.
|
||
//
|
||
VOID
|
||
PsxFreeDirectories(
|
||
IN PPSX_PROCESS p
|
||
)
|
||
{
|
||
PPSX_DIRECTORY_PREFIX Prefix;
|
||
|
||
Prefix = p->DirectoryPrefix;
|
||
|
||
if (NULL != Prefix->NtCurrentWorkingDirectory.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0, Prefix->NtCurrentWorkingDirectory.Buffer);
|
||
}
|
||
if (NULL != Prefix->PsxRoot.Buffer) {
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix->PsxRoot.Buffer);
|
||
}
|
||
if (NULL != Prefix) {
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)Prefix);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Locate a Posix Session by it's Unique Id.
|
||
|
||
Arguments:
|
||
|
||
UniqueId - the session's unique id.
|
||
|
||
Return Value:
|
||
|
||
NULL - no such session could be found.
|
||
|
||
Non-NULL - the found session.
|
||
|
||
--*/
|
||
|
||
PPSX_SESSION
|
||
PsxLocateSessionByUniqueId(
|
||
ULONG UniqueId
|
||
)
|
||
{
|
||
PPSX_PROCESS Process;
|
||
|
||
//
|
||
// We don't have a list of sessions at the moment, so we
|
||
// linear-scan the process table, checking the session of
|
||
// each process to see if it's the one we want.
|
||
//
|
||
|
||
AcquireProcessStructureLock();
|
||
|
||
for (Process = FirstProcess; Process < LastProcess; Process++) {
|
||
if (Process->Flags & P_FREE) {
|
||
continue;
|
||
}
|
||
|
||
if (NULL == Process->PsxSession->Terminal)
|
||
continue;
|
||
|
||
if (Process->PsxSession->Terminal->UniqueId == UniqueId) {
|
||
ReleaseProcessStructureLock();
|
||
return Process->PsxSession;
|
||
}
|
||
}
|
||
|
||
ReleaseProcessStructureLock();
|
||
return NULL;
|
||
}
|