windows-nt/Source/XPSP1/NT/base/subsys/posix/psxss/srvtask.c

1973 lines
50 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*--
Copyright (c) 1989 Microsoft Corporation
Module Name:
srvtask.c
Abstract:
Implementation of PSX Process Structure APIs.
Author:
Mark Lucovsky (markl) 08-Mar-1989
Revision History:
--*/
#include "psxsrv.h"
#include "sesport.h"
#ifdef _MIPS_
#include "psxmips.h"
#endif
#ifdef _ALPHA_
#include "psxalpha.h"
#endif
#ifdef _X86_
#include "psxi386.h"
#endif
#ifdef _PPC_
#include "psxppc.h"
#endif
#ifdef _IA64_
#include "psxia64.h"
#endif
#include <time.h>
extern VOID
PsxFreeDirectories(
IN PPSX_PROCESS
);
VOID
ConvertPathToWin(char *path);
BOOLEAN
PsxFork(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements posix fork() API
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
PPSX_PROCESS ForkProcess, NewProcess;
HANDLE NewProcessHandle, NewThreadHandle;
THREAD_BASIC_INFORMATION ThreadBasicInfo;
PVOID ExcptList;
CONTEXT Context;
ULONG Psp;
NTSTATUS st;
INITIAL_TEB InitialTeb;
CLIENT_ID ClientId;
PPSX_FORK_MSG args;
HANDLE h;
args = &m->u.Fork;
if (p->Flags & P_NO_FORK) {
//
// This process may not fork; it's context has been
// rearranged to make it call the PdxNullApiCaller.
//
m->Error = EINTR;
m->Signal = SIGCONT;
return TRUE;
}
//
// Impersonate the client to insure that the new process will belong
// to him instead of to us.
//
st = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
if (!NT_SUCCESS(st)) {
m->Error = EAGAIN;
return TRUE;
}
//
// Create a new process to be the child. The ExceptionPort is
// initialized to PsxApiPort.
//
st = NtCreateProcess(&NewProcessHandle, PROCESS_ALL_ACCESS, NULL,
p->Process, TRUE, NULL, NULL, PsxApiPort);
if (!NT_SUCCESS(st)) {
EndImpersonation();
m->Error = EAGAIN;
return TRUE;
}
{
ULONG HardErrorMode = 0; // disable popups
st = NtSetInformationProcess(
NewProcessHandle,
ProcessDefaultHardErrorMode,
(PVOID)&HardErrorMode,
sizeof(HardErrorMode)
);
ASSERT(NT_SUCCESS(st));
}
Context.ContextFlags = CONTEXT_FULL;
st = NtGetContextThread(p->Thread, &Context);
if (!NT_SUCCESS(st)) {
NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
NtClose(NewProcessHandle);
EndImpersonation();
m->Error = EINVAL;
return TRUE;
}
InitialTeb.OldInitialTeb.OldStackBase = NULL;
InitialTeb.OldInitialTeb.OldStackLimit = NULL;
#ifdef _IA64_
InitialTeb.OldInitialTeb.OldBStoreLimit = NULL;
InitialTeb.BStoreLimit = args->BStoreLimit;
#endif
InitialTeb.StackBase = args->StackBase;
InitialTeb.StackLimit = args->StackLimit;
InitialTeb.StackAllocationBase = args->StackAllocationBase;
SetPsxForkReturn(Context);
st = NtCreateThread(&NewThreadHandle, THREAD_ALL_ACCESS, NULL,
NewProcessHandle, &ClientId, &Context, &InitialTeb, TRUE);
EndImpersonation();
if (!NT_SUCCESS(st)) {
st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
ASSERT(NT_SUCCESS(st));
NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
NtClose(NewProcessHandle);
m->Error = EAGAIN;
return TRUE;
}
//
// Allocate a process structure.
//
NewProcess = PsxAllocateProcess(&ClientId);
if (NULL == NewProcess) {
st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
ASSERT(NT_SUCCESS(st));
st = NtResumeThread(NewThreadHandle,&Psp);
ASSERT(NT_SUCCESS(st));
NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
NtClose(NewProcessHandle);
NtClose(NewThreadHandle);
m->Error = EAGAIN;
return TRUE;
}
//
// Copy the ExceptionList pointer from the parent process TEB
// to the child process TEB.
//
st = NtQueryInformationThread(p->Thread, ThreadBasicInformation,
(PVOID)&ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL);
ASSERT(NT_SUCCESS(st));
// XXX.mjb: The actual address we want to read is
// TebBaseAddress->NtTib.ExceptionList, but I happen to know
// that these are equivalent.
//
st = NtReadVirtualMemory(p->Process,
(PVOID)ThreadBasicInfo.TebBaseAddress,
(PVOID)&ExcptList, sizeof(ExcptList), NULL);
ASSERT(NT_SUCCESS(st));
st = NtQueryInformationThread(NewThreadHandle, ThreadBasicInformation,
(PVOID)&ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL);
ASSERT(NT_SUCCESS(st));
st = NtWriteVirtualMemory(NewProcessHandle,
(PVOID)ThreadBasicInfo.TebBaseAddress,
(PVOID)&ExcptList, sizeof(ExcptList), NULL);
ASSERT(NT_SUCCESS(st));
ForkProcess = p;
//
// The new process is allocated locked, but we need to lock the
// parent (ForkProcess).
//
AcquireProcessLock(ForkProcess);
st = PsxInitializeProcess(NewProcess, ForkProcess, 0L, NewProcessHandle,
NewThreadHandle, NULL);
if (!NT_SUCCESS(st)) {
st = NtTerminateProcess(NewProcessHandle, STATUS_SUCCESS);
ASSERT(NT_SUCCESS(st));
st = NtResumeThread(NewThreadHandle,&Psp);
ASSERT(NT_SUCCESS(st));
st = NtWaitForSingleObject(NewProcessHandle, FALSE, NULL);
NtClose(NewProcessHandle);
NtClose(NewThreadHandle);
m->Error = EAGAIN;
return TRUE;
}
NewProcess->InitialPebPsxData.Length =
sizeof(ForkProcess->InitialPebPsxData);
NewProcess->InitialPebPsxData.ClientStartAddress = NULL;
if (NULL != ForkProcess->PsxSession->Terminal) {
NewProcess->InitialPebPsxData.SessionPortHandle =
(HANDLE)ForkProcess->PsxSession->Terminal->UniqueId;
} else {
//
// What if there are no open file descriptors?
//
if (NULL != ForkProcess->ProcessFileTable[0].SystemOpenFileDesc)
NewProcess->InitialPebPsxData.SessionPortHandle =
(HANDLE)ForkProcess->ProcessFileTable[0].SystemOpenFileDesc->Terminal->UniqueId;
}
m->ReturnValue = NewProcess->Pid;
st = NtResumeThread(NewThreadHandle, &Psp);
ASSERT(NT_SUCCESS(st));
return TRUE;
}
BOOLEAN
PsxExec(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements posix execve() API
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
RTL_USER_PROCESS_INFORMATION ProcInfo;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PPSX_EXEC_MSG args;
WCHAR ImageFileString[1024];
ANSI_STRING CommandLine;
ANSI_STRING CWD;
NTSTATUS Status;
ULONG whocares;
ULONG i;
KERNEL_USER_TIMES ProcessTime;
ULONG PosixTime, Remainder;
UNICODE_STRING uCWD, uImageFileName;
PUNICODE_STRING u;
PVOID SaveDirectoryPrefix;
HANDLE h;
USHORT len;
args = &m->u.Exec;
//
// If pathname is too big, then return error. This should
// really allow for names that are longer because of PSX
// prefixes
//
if (args->Path.Length > PATH_MAX) {
m->Error = ENAMETOOLONG;
return TRUE;
}
AcquireProcessLock(p);
if (Exited == p->State) {
ReleaseProcessLock(p);
return FALSE;
}
//
// Capture the pathname
//
Status = NtReadVirtualMemory(p->Process, args->Path.Buffer,
&ImageFileString[0], args->Path.Length, NULL);
if (!NT_SUCCESS(Status)) {
ReleaseProcessLock(p);
m->Error = ENOMEM;
return TRUE;
}
uImageFileName.Buffer = &ImageFileString[0];
uImageFileName.Length = args->Path.Length;
uImageFileName.MaximumLength = args->Path.Length;
//
// Propagate Current Working Directory
//
SaveDirectoryPrefix = (PVOID)p->DirectoryPrefix;
if (!PsxPropagateDirectories(p)) {
ReleaseProcessLock(p);
p->DirectoryPrefix = SaveDirectoryPrefix;
m->Error = ENOMEM;
return TRUE;
}
//
// Format the process parameters
//
CommandLine.Buffer = args->Args;
CommandLine.Length = ARG_MAX;
CommandLine.MaximumLength = ARG_MAX;
PSX_GET_STRLEN(DOSDEVICE_A,len);
CWD.Buffer = p->DirectoryPrefix->NtCurrentWorkingDirectory.Buffer + len;
CWD.MaximumLength = p->DirectoryPrefix->NtCurrentWorkingDirectory.Length - len;
CWD.Length = CWD.MaximumLength;
Status = RtlAnsiStringToUnicodeString(&uCWD, &CWD, TRUE);
if (!NT_SUCCESS(Status)) {
PsxFreeDirectories(p);
p->DirectoryPrefix = SaveDirectoryPrefix;
ReleaseProcessLock(p);
m->Error = ENOMEM;
return TRUE;
}
//
// Somewhere along the line someone changes the old process's
// DllPath to Unicode. If we pass a NULL DllPath here, we find
// that Ansi is expected, and we can't find the Dll. So here we
// take the Unicode DllPath, convert to Ansi, and pass it
// explicitly.
//
u = (PUNICODE_STRING)&NtCurrentPeb()->ProcessParameters->DllPath;
Status = RtlCreateProcessParameters(&ProcessParameters, &uImageFileName,
u, &uCWD, (PUNICODE_STRING)&CommandLine, NULL, NULL, NULL, NULL, NULL);
#ifndef EXEC_FOREIGN
RtlFreeUnicodeString(&uCWD);
#endif
if (!NT_SUCCESS(Status)) {
#ifdef EXEC_FOREIGN
RtlFreeUnicodeString(&uCWD);
#endif
PsxFreeDirectories(p);
p->DirectoryPrefix = SaveDirectoryPrefix;
ReleaseProcessLock(p);
m->Error = ENOMEM;
return TRUE;
}
//
// Create the process and thread. We impersonate the client so that
// they end up being owned by him, instead of owned by us.
//
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
if (NT_SUCCESS(Status)) {
Status = RtlCreateUserProcess(&uImageFileName, 0, ProcessParameters,
NULL, NULL, p->Process, FALSE, NULL, NULL, &ProcInfo);
EndImpersonation();
}
if (!NT_SUCCESS(Status)) {
#ifdef EXEC_FOREIGN
RtlFreeUnicodeString(&uCWD);
#endif
PsxFreeDirectories(p);
p->DirectoryPrefix = SaveDirectoryPrefix;
ReleaseProcessLock(p);
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
m->Error = PsxStatusToErrnoPath(&uImageFileName);
return TRUE;
}
m->Error = PsxStatusToErrno(Status);
return TRUE;
}
RtlDestroyProcessParameters(ProcessParameters);
//
// Set the exception port for the new process so we'll find out
// if he takes a fault.
//
Status = NtSetInformationProcess(ProcInfo.Process,
ProcessExceptionPort, (PVOID)&PsxApiPort, sizeof(PsxApiPort));
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtSetInfoProcess: 0x%x\n", Status));
}
ASSERT(NT_SUCCESS(Status));
{
ULONG HardErrorMode = 0; // disable popups
Status = NtSetInformationProcess(
ProcInfo.Process,
ProcessDefaultHardErrorMode,
(PVOID)&HardErrorMode,
sizeof(HardErrorMode)
);
ASSERT(NT_SUCCESS(Status));
}
//
// check to make sure it is a POSIX app
//
if (ProcInfo.ImageInformation.SubSystemType !=
IMAGE_SUBSYSTEM_POSIX_CUI) {
//
// The image is not a Posix program. Tear down the
// process we just created in the usual way, and then
// call the windows subsystem to do the same thing
// over.
//
PsxFreeDirectories(p);
p->DirectoryPrefix = SaveDirectoryPrefix;
#ifndef EXEC_FOREIGN
ReleaseProcessLock(p);
#endif
Status = NtTerminateProcess(ProcInfo.Process, STATUS_SUCCESS);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtTerminateProcess: 0x%x\n", Status));
}
Status = NtWaitForSingleObject(ProcInfo.Process, FALSE, NULL);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtWaitForSingleObject: 0x%x\n", Status));
}
#ifdef EXEC_FOREIGN
Status = ExecForeignImage(p, m, &uImageFileName, &uCWD);
ReleaseProcessLock(p);
RtlFreeUnicodeString(&uCWD);
if (!NT_SUCCESS(Status)) {
m->Error = ENOEXEC;
return TRUE;
}
return FALSE;
#else
m->Error = ENOEXEC;
return TRUE;
#endif
}
#ifdef EXEC_FOREIGN
RtlFreeUnicodeString(&uCWD);
#endif
//
// Close open files that have their close-on-exec bit set.
//
for (i = 0; i < OPEN_MAX; ++i) {
if (NULL != p->ProcessFileTable[i].SystemOpenFileDesc &&
p->ProcessFileTable[i].Flags & PSX_FD_CLOSE_ON_EXEC) {
(void)DeallocateFd(p, i);
}
}
AcquireProcessStructureLock();
//
// 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);
ASSERT(NT_SUCCESS(Status));
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.KernelTime,
10000, &Remainder).LowPart;
p->ProcessTimes.tms_stime += PosixTime;
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.UserTime,
10000, &Remainder).LowPart;
p->ProcessTimes.tms_utime += PosixTime;
//
// Terminate the current process, and munge in the
// new process
//
RemoveEntryList(&p->ClientIdHashLinks);
p->ClientIdHashLinks.Flink = p->ClientIdHashLinks.Blink = NULL;
Status = NtTerminateProcess(p->Process, STATUS_SUCCESS);
ASSERT(NT_SUCCESS(Status));
Status = NtWaitForSingleObject(p->Process, FALSE, NULL);
ASSERT(NT_SUCCESS(Status));
Status = NtClose(p->ClientPort);
ASSERT(NT_SUCCESS(Status));
Status = NtClose(p->Process);
ASSERT(NT_SUCCESS(Status));
Status = NtClose(p->Thread);
ASSERT(NT_SUCCESS(Status));
p->Process = ProcInfo.Process;
p->Thread = ProcInfo.Thread;
p->ClientId = ProcInfo.ClientId;
p->ClientPort = NULL;
InsertTailList(&ClientIdHashTable[CIDTOHASHINDEX(&p->ClientId)],
&p->ClientIdHashLinks);
p->Flags |= P_HAS_EXECED;
ReleaseProcessStructureLock();
//
// Restore signals being caught to SIG_DFL
// --- Posix does not specify what to do w/ associated flags or mask
//
for (i = 0; i < _SIGMAXSIGNO; i++) {
if (p->SignalDataBase.SignalDisposition[i].sa_handler !=
SIG_IGN) {
p->SignalDataBase.SignalDisposition[i].sa_handler =
SIG_DFL;
}
}
//
// Since we don't reply to the old process (he's gone now), we need
// to start the new process's InPsx count at zero.
//
p->InPsx = 0;
ReleaseProcessLock(p);
ExecProcessFileTable(p);
if (p->ProcessIsBeingDebugged && PsxpDebuggerActive) {
Status = NtSetInformationProcess(p->Process, ProcessDebugPort,
(PVOID)&PsxpDebugPort, sizeof(HANDLE));
if (!NT_SUCCESS(Status)) {
p->ProcessIsBeingDebugged = FALSE;
}
}
Status = NtResumeThread(p->Thread,&whocares);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtResumeThread: 0x%x\n", Status));
}
ASSERT(NT_SUCCESS(Status) && whocares == 1);
return FALSE;
}
BOOLEAN
PsxGetIds(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function provides all the support needed to implement
getpid(), getppid(), getuid(), geteuid(), getgid(), and getegid().
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
PPSX_GETIDS_MSG args;
args = &m->u.GetIds;
args->Pid = p->Pid;
args->ParentPid = p->ParentPid;
args->GroupId = p->ProcessGroupId;
args->RealUid = p->RealUid;
args->EffectiveUid = p->EffectiveUid;
args->RealGid = p->RealGid;
args->EffectiveGid = p->EffectiveGid;
return TRUE;
}
BOOLEAN
PsxExit(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the _exit() API.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
PPSX_EXIT_MSG args;
args = &m->u.Exit;
Exit(p, (args->ExitStatus & 0xff) << 8);
return FALSE;
}
VOID
WaitPidHandler(
IN PPSX_PROCESS p,
IN PINTCB IntControlBlock,
IN PSX_INTERRUPTREASON InterruptReason,
IN int Signal
)
/*++
Routine Description:
This function is called whenever a process that is in a waitpid wait
is sent a signal, or has a child stop/terminate that could possibly
satisfy a wait.
This function is responsible for unlocking the process.
Arguments:
p - Supplies the address of the process being interrupted.
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;
PPSX_WAITPID_MSG args;
PPSX_PROCESS cp;
BOOLEAN WaitSatisfied;
pid_t TargetProcess;
pid_t TargetGroup;
enum _WaitType { AnyProcess, SpecificProcess, SpecificGroup };
enum _WaitType WaitType;
RtlLeaveCriticalSection(&BlockLock);
m = IntControlBlock->IntMessage;
args = &m->u.WaitPid;
AcquireProcessStructureLock();
p->State = Active;
if (InterruptReason == SignalInterrupt) {
ReleaseProcessStructureLock();
RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
m->Error = EINTR;
m->Signal = Signal;
ApiReply(p,m,NULL);
RtlFreeHeap(PsxHeap, 0, (PVOID)m);
return;
}
WaitSatisfied = FALSE;
TargetProcess = SPECIALPID;
TargetGroup = SPECIALPID;
WaitType = AnyProcess;
if (args->Pid <= 0 && args->Pid != (pid_t)-1) {
WaitType = SpecificGroup;
//
// Process group id is specified.
//
if (args->Pid == 0) {
TargetGroup = p->ProcessGroupId;
} else {
TargetGroup = -1 * args->Pid;
}
} else {
if (args->Pid != (pid_t)-1) {
TargetProcess = args->Pid;
WaitType = SpecificProcess;
}
}
//
// Scan process table
//
for (cp = FirstProcess; cp < LastProcess; cp++) {
if (cp->Flags & P_FREE) {
continue;
}
//
// Just look at processes that could possibly satisfy a wait.
// - Processes that have exited
// - Stopped processes that have not previously satisfied a
// wait (if WUNTRACED was set)
//
if (cp->State == Exited ||
(cp->State == Stopped && (args->Options & WUNTRACED) &&
!(cp->Flags & P_WAITED))) {
if (cp->ParentPid != p->Pid) {
continue;
}
switch (WaitType) {
case AnyProcess:
m->ReturnValue = cp->Pid;
args->StatLocValue = cp->ExitStatus;
break;
case SpecificProcess:
if ( cp->Pid == TargetProcess ) {
m->ReturnValue = cp->Pid;
args->StatLocValue = cp->ExitStatus;
}
break;
case SpecificGroup:
if ( cp->ProcessGroupId == TargetGroup){
m->ReturnValue = cp->Pid;
args->StatLocValue = cp->ExitStatus;
}
break;
}
if ( m->ReturnValue ) {
//
// wait was satisfied
//
if ( cp->State == Exited ) {
p->ProcessTimes.tms_cstime += (cp->ProcessTimes.tms_stime
+ cp->ProcessTimes.tms_cstime);
p->ProcessTimes.tms_cutime += (cp->ProcessTimes.tms_utime
+ cp->ProcessTimes.tms_cutime);
//
// Deallocate the process
//
cp->Flags |= P_FREE;
RtlDeleteCriticalSection(&cp->ProcessLock);
} else {
//
// set bit so this stopped process won't satisfy another
// wait until it stops again or exits.
//
cp->Flags |= P_WAITED;
args->StatLocValue = cp->ExitStatus | (1L << 30);
}
WaitSatisfied = TRUE;
break;
}
}
}
if ( WaitSatisfied ) {
ReleaseProcessStructureLock();
RtlFreeHeap(PsxHeap, 0, (PVOID)IntControlBlock);
ApiReply(p,m,NULL);
RtlFreeHeap(PsxHeap, 0, (PVOID)m);
return;
}
//
// Rewait
//
p->State = Waiting;
(void)BlockProcess(p,
NULL,
WaitPidHandler,
m,
NULL,
&PsxProcessStructureLock);
}
BOOLEAN
PsxWaitPid(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the wait() and waitpid() APIs.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
NTSTATUS Status;
PPSX_WAITPID_MSG args;
PPSX_PROCESS cp;
pid_t TargetProcess;
pid_t TargetGroup;
BOOLEAN EmitEchild;
enum _WaitType { AnyProcess, SpecificProcess, SpecificGroup };
enum _WaitType WaitType;
args = &m->u.WaitPid;
//
// Test for invalid options
//
if (args->Options & ~(WNOHANG|WUNTRACED)) {
m->Error = EINVAL;
return TRUE;
}
TargetProcess = SPECIALPID;
TargetGroup = SPECIALPID;
WaitType = AnyProcess;
if (args->Pid <= 0 && args->Pid != (pid_t)-1) {
WaitType = SpecificGroup;
if (args->Pid == 0) {
TargetGroup = p->ProcessGroupId;
} else {
TargetGroup = -1 * args->Pid;
}
} else {
if (args->Pid != (pid_t)-1) {
TargetProcess = args->Pid;
WaitType = SpecificProcess;
}
}
AcquireProcessStructureLock();
EmitEchild = TRUE;
//
// Scan process table
//
for (cp = FirstProcess; cp < LastProcess; cp++) {
if (cp->Flags & P_FREE) {
continue;
}
//
// Until we know whether or not there is a process that
// could possibly satisfy a wait, we have to keep looking
// at all processes.
//
if (EmitEchild) {
if (WaitType == SpecificGroup) {
if (cp->ParentPid == p->Pid &&
cp->ProcessGroupId == TargetGroup) {
EmitEchild = FALSE;
}
} else {
if (cp->ParentPid == p->Pid) {
if (WaitType == SpecificProcess) {
if (cp->Pid == TargetProcess) {
EmitEchild = FALSE;
}
} else {
EmitEchild = FALSE;
}
}
}
}
//
// Just look at processes that could possibly satisfy a wait.
// - Processes that have exited
// - Stopped processes that have not previously satisfied
// a wait (if WUNTRACED was set)
//
if (cp->State == Exited ||
(cp->State == Stopped && (args->Options & WUNTRACED) &&
!(cp->Flags & P_WAITED))) {
if (cp->ParentPid != p->Pid) {
continue;
}
switch (WaitType) {
case AnyProcess:
m->ReturnValue = cp->Pid;
args->StatLocValue = cp->ExitStatus;
break;
case SpecificProcess:
if (cp->Pid == TargetProcess) {
m->ReturnValue = cp->Pid;
args->StatLocValue = cp->ExitStatus;
}
break;
case SpecificGroup:
if (cp->ProcessGroupId == TargetGroup) {
m->ReturnValue = cp->Pid;
args->StatLocValue = cp->ExitStatus;
}
break;
}
if (m->ReturnValue) {
//
// wait was satisfied
//
if ( cp->State == Exited ) {
p->ProcessTimes.tms_cstime += (cp->ProcessTimes.tms_stime
+ cp->ProcessTimes.tms_cstime);
p->ProcessTimes.tms_cutime += (cp->ProcessTimes.tms_utime
+ cp->ProcessTimes.tms_cutime);
//
// Deallocate the process
//
cp->Flags |= P_FREE;
RtlDeleteCriticalSection(&cp->ProcessLock);
} else {
//
// Set a bit to keep this stopped process from satisfying
// another wait.
//
cp->Flags |= P_WAITED;
args->StatLocValue = cp->ExitStatus | (1L << 30);
}
ReleaseProcessStructureLock();
return TRUE;
}
}
}
if (EmitEchild) {
m->Error = ECHILD;
ReleaseProcessStructureLock();
return TRUE;
}
if (args->Options & WNOHANG) {
m->ReturnValue = 0;
args->StatLocValue = 0;
ReleaseProcessStructureLock();
return TRUE;
}
//
// Make the process sleep until the wait can be satisfied.
//
p->State = Waiting;
Status = BlockProcess(p, NULL, WaitPidHandler, m, NULL,
&PsxProcessStructureLock);
if (!NT_SUCCESS(Status)) {
m->Error = PsxStatusToErrno(Status);
return TRUE;
}
//
// The process has successfully been blocked. Don't reply to the api
// request message.
//
return FALSE;
}
BOOLEAN
PsxSetSid(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the setsid() API.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
PPSX_SESSION OldSession;
//
// 1003.1-90 (4.3.2.4): EPERM when the calling process
// is already a process group leader.
//
if (p->Pid == p->ProcessGroupId) {
m->Error = EPERM;
return TRUE;
}
//
// Create a new session with no controlling tty and make
// the calling process the session leader.
//
AcquireProcessStructureLock();
LockNtSessionList();
RemoveEntryList(&p->GroupLinks);
InitializeListHead(&p->GroupLinks);
//
// Make the process the leader of his process group.
//
p->ProcessGroupId = p->Pid;
OldSession = p->PsxSession;
p->PsxSession = PsxAllocateSession(NULL, p->Pid);
UnlockNtSessionList();
ReleaseProcessStructureLock();
DEREFERENCE_PSX_SESSION(OldSession, 0);
m->ReturnValue = (pid_t)p->ProcessGroupId;
return TRUE;
}
BOOLEAN
PsxSetPGroupId(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the setpgid() API.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
PPSX_SETPGROUPID_MSG args;
pid_t TargetPid, TargetGroup;
PPSX_PROCESS cp, Target;
BOOLEAN EmitEsrch;
args = &m->u.SetPGroupId;
if (args->Pid < 0 || args->Pgid < 0) {
m->Error = EINVAL;
return TRUE;
}
TargetPid = (args->Pid ? args->Pid : p->Pid);
TargetGroup = (args->Pgid ? args->Pgid : TargetPid);
AcquireProcessStructureLock();
LockNtSessionList();
if ( p->Pid == TargetPid ) {
Target = p;
} else {
//
// Scan process table
//
EmitEsrch = TRUE;
for (cp = FirstProcess ;cp < LastProcess ;cp++ ) {
if ( cp->Flags & P_FREE ) {
continue;
}
if ( cp->ParentPid == p->Pid && cp->Pid == TargetPid ) {
EmitEsrch = FALSE;
Target = cp;
break;
}
}
if ( EmitEsrch ) {
m->Error = ESRCH;
goto done;
}
}
//
// If target is a child who has executed an exec, then report error
//
if (Target->ParentPid == p->Pid && (Target->Flags & P_HAS_EXECED)) {
m->Error = EACCES;
goto done;
}
//
// If target is a session leader, then report error
//
if ( Target->PsxSession->SessionLeader == TargetPid ) {
m->Error = EPERM;
goto done;
}
//
// If target is not in the same session as the calling process, then
// report error
//
if ( Target->PsxSession != p->PsxSession ) {
m->Error = EPERM;
goto done;
}
if ( TargetPid != TargetGroup ) {
//
// Scan process table looking for a pgrp id the same
// as TargetGroup and is in the same session is the calling process
//
EmitEsrch = TRUE;
for (cp = FirstProcess ;cp < LastProcess ;cp++ ) {
if ( cp->Flags & P_FREE ) {
continue;
}
if ( cp->ProcessGroupId == TargetGroup &&
p->PsxSession == cp->PsxSession ) {
EmitEsrch = FALSE;
break;
}
}
if ( EmitEsrch ) {
m->Error = EPERM;
goto done;
}
} else {
cp = Target;
}
//
// Everything is ok, so set the Target's pgrp id to the specified id
//
RemoveEntryList(&Target->GroupLinks);
if (cp != Target) {
InsertHeadList(&cp->GroupLinks, &Target->GroupLinks);
} else {
InitializeListHead(&Target->GroupLinks);
}
Target->ProcessGroupId = TargetGroup;
done:
UnlockNtSessionList();
ReleaseProcessStructureLock();
return TRUE;
}
BOOLEAN
PsxGetProcessTimes(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the times() API.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
--*/
{
PPSX_GETPROCESSTIMES_MSG args;
NTSTATUS Status;
KERNEL_USER_TIMES ProcessTime;
ULONG PosixTime, Remainder;
ULONG LengthNeeded;
args = &m->u.GetProcessTimes;
{
LARGE_INTEGER DelayInterval;
DelayInterval.HighPart = 0;
DelayInterval.LowPart = 100;
NtDelayExecution(TRUE, &DelayInterval);
}
//
// Get the time for this process and add to the accumulated time for
// the process
//
Status = NtQueryInformationProcess(p->Process, ProcessTimes,
(PVOID)&ProcessTime, sizeof(ProcessTime), &LengthNeeded);
ASSERT(NT_SUCCESS(Status));
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.UserTime,
10000, &Remainder).LowPart;
args->ProcessTimes.tms_utime = p->ProcessTimes.tms_utime + PosixTime;
PosixTime = RtlExtendedLargeIntegerDivide(ProcessTime.KernelTime,
10000, &Remainder).LowPart;
args->ProcessTimes.tms_stime = p->ProcessTimes.tms_stime + PosixTime;
args->ProcessTimes.tms_cutime = p->ProcessTimes.tms_cutime;
args->ProcessTimes.tms_cstime = p->ProcessTimes.tms_cstime;
return TRUE;
}
BOOLEAN
PsxGetGroups(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the getgroups() API.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
XXX.mjb:
NT essentially puts no limit on the number of supplementary groups
a user may belong to. This is bad for Posix, since we want a limit,
and we want it small enough that people don't mess themselves up by
allocating an array[NGROUPS_MAX] of gid_t.
--*/
{
PPSX_GETGROUPS_MSG args;
NTSTATUS Status;
HANDLE TokenHandle;
TOKEN_GROUPS *pGroups;
ULONG outlen, i, j;
gid_t *GroupList;
args = &m->u.GetGroups;
//
// Check args->GroupList for group array address validity.
//
//
// Examine the new process's token to figure out what the
// uid's should be.
//
Status = NtOpenProcessToken(p->Process, GENERIC_READ,
&TokenHandle);
ASSERT(NT_SUCCESS(Status));
//
// Get the supplemental groups.
//
Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
0, &outlen);
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
pGroups = RtlAllocateHeap(PsxHeap, 0, outlen);
if (NULL == pGroups) {
//
// We don't have enough memory to hold the list of the process's
// groups. What is there to do except return an error?
//
NtClose(TokenHandle);
m->Error = ENOMEM;
return TRUE;
}
Status = NtQueryInformationToken(TokenHandle, TokenGroups, (PVOID)pGroups,
outlen, &outlen);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: NtQueryInformationToken failed: 0x%x\n",
Status));
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
NtClose(TokenHandle);
m->Error = EACCES;
return TRUE;
}
//
// If the user has passed in a listsize of 0, then we only return
// the number of supplementary groups, without writing anything in
// the group array.
//
if (0 == args->NGroups) {
m->ReturnValue = pGroups->GroupCount;
m->Error = 0;
NtClose(TokenHandle);
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
return TRUE;
}
if ((ULONG)args->NGroups < pGroups->GroupCount) {
if (args->NGroups < NGROUPS_MAX) {
m->Error = EINVAL;
return TRUE;
}
//
// XXX.mjb: We're having a problem here. The caller has
// allocated space for the maximum number of groups that the
// user can have, according to the _NGROUPS_MAX limit, but
// the user actually has more groups than that. This can
// happen because NT's limit is indeterminate.
//
// ignore the groups that we cannot return.
pGroups->GroupCount = args->NGroups;
}
//
// Make an array of gid_t's and copy it to the user address space.
//
GroupList = RtlAllocateHeap(PsxHeap, 0, pGroups->GroupCount *
sizeof(gid_t));
if (NULL == GroupList) {
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
NtClose(TokenHandle);
m->Error = ENOMEM;
return TRUE;
}
for (i = 0, j = 0; i < pGroups->GroupCount; ++i) {
PSID Sid = pGroups->Groups[i].Sid;
GroupList[j] = MakePosixId(Sid);
if (0 != GroupList[j]) {
++j;
}
}
//
// Copy GroupList to client's address space, at the place
// specified.
//
Status = NtWriteVirtualMemory(p->Process, args->GroupList,
GroupList, j * sizeof(gid_t), NULL);
RtlFreeHeap(PsxHeap, 0, (PVOID)GroupList);
if (!NT_SUCCESS(Status)) {
m->Error = PsxStatusToErrno(Status);
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
NtClose(TokenHandle);
return TRUE;
}
m->ReturnValue = j;
RtlFreeHeap(PsxHeap, 0, (PVOID)pGroups);
NtClose(TokenHandle);
return TRUE;
}
BOOLEAN
PsxGetLogin(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the getlogin() API.
Arguments:
p - Supplies the address of the calling process
m - Supplies the address of the related message
Return Value:
TRUE - Always succeeds and generates a reply
XXX.mjb: this routine is never called; getlogin() is implemented as
"getpwuid(getuid())->pw_name", kind of. See client-side getlogin().
--*/
{
PPSX_GETLOGIN_MSG args;
NTSTATUS Status;
args = &m->u.GetLogin;
args->LoginName; // w.r.t. our address space
m->Error = ENOSYS;
return TRUE;
}
BOOLEAN
PsxSysconf(
IN PPSX_PROCESS p,
IN PPSX_API_MSG m
)
/*++
Routine Description:
This function implements the sysconf() api.
Arguments:
p - Supplies the address of the calling process.
m - Supplies the address of the related message.
Return Value:
TRUE - Always succeeds and generates a reply.
--*/
{
PPSX_SYSCONF_MSG args;
NTSTATUS Status;
long value;
PPSX_PROCESS Process;
args = &m->u.Sysconf;
switch (args->Name) {
case _SC_ARG_MAX:
value = ARG_MAX;
break;
case _SC_CHILD_MAX:
//
// This is the reason sysconf is implemented in the
// server. We don't bother to grab any locks, since the
// result is out of date by the time it gets back to the
// user anyway.
//
value = 1;
for (Process = FirstProcess; Process < LastProcess; ++Process) {
if (Process->Flags & P_FREE) {
++value;
}
}
break;
case _SC_CLK_TCK:
value = CLK_TCK;
break;
case _SC_NGROUPS_MAX:
value = NGROUPS_MAX;
break;
case _SC_OPEN_MAX:
value = OPEN_MAX;
break;
case _SC_JOB_CONTROL:
#ifdef _POSIX_JOB_CONTROL
value = 1;
break;
#else
value = 0;
break;
#endif
case _SC_SAVED_IDS:
#ifdef _POSIX_SAVED_IDS
value = 1;
#else
value = 0;
#endif
break;
case _SC_VERSION:
value = _POSIX_VERSION;
break;
case _SC_STREAM_MAX:
value = STREAM_MAX;
break;
case _SC_TZNAME_MAX:
value = TZNAME_MAX;
break;
default:
value = -1;
m->Error = EINVAL;
}
m->ReturnValue = value;
return TRUE;
}
#ifdef EXEC_FOREIGN
#define UNICODE
#include <windows.h>
DWORD ForeignProcessWait(PVOID);
NTSTATUS
ExecForeignImage(
PPSX_PROCESS p,
PPSX_API_MSG m,
PUNICODE_STRING Image,
PUNICODE_STRING CurDir
)
{
PPSX_EXEC_MSG args;
NTSTATUS Status;
LPWSTR CommandLine;
LPVOID Environment;
STARTUPINFO StartInfo;
PROCESS_INFORMATION ProcInfo;
BOOL Success = FALSE;
char **ppch;
PWCHAR pwc;
ULONG flags;
ULONG ThreadId;
HANDLE ForeignProc;
ULONG len;
args = &m->u.Exec;
//
// Convert argv array to command line format.
//
CommandLine = RtlAllocateHeap(PsxHeap, 0, ARG_MAX);
if (NULL == CommandLine) {
return STATUS_NO_MEMORY;
}
CommandLine[0] = 0;
ppch = (PVOID)args->Args;
for (ppch = (PVOID)args->Args; NULL != *ppch; ++ppch) {
ANSI_STRING A;
UNICODE_STRING U;
// this breaks on args with spaces in them
A.Buffer = *ppch + (ULONG)args->Args;
A.Length = A.MaximumLength = strlen(A.Buffer);
U.Buffer = &CommandLine[wcslen(CommandLine)];
U.Length = 0;
U.MaximumLength = ARG_MAX;
Status = RtlAnsiStringToUnicodeString(&U, &A, FALSE);
ASSERT(NT_SUCCESS(Status));
wcscat((PVOID)CommandLine, L" ");
}
Status = RtlCreateEnvironment(FALSE, &Environment);
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(PsxHeap, 0, CommandLine);
return Status;
}
for (++ppch; NULL != *ppch; ++ppch) {
ANSI_STRING aName, aValue;
UNICODE_STRING nU, vU;
char *pch;
pch = strchr(*ppch + (ULONG)args->Args, '=');
ASSERT(NULL != pch);
*pch = '\0';
aName.Buffer = *ppch + (ULONG)args->Args;
aName.Length = strlen(aName.Buffer);
aName.MaximumLength = aName.Length;
aValue.Buffer = pch + 1;
aValue.Length = strlen(aValue.Buffer);
aValue.MaximumLength = aName.Length;
if (0 == stricmp("PATH", aName.Buffer)) {
ConvertPathToWin(aValue.Buffer);
aValue.Length = strlen(aValue.Buffer);
}
*pch = '=';
Status = RtlAnsiStringToUnicodeString(&nU, &aName, TRUE);
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(PsxHeap, 0, CommandLine);
RtlDestroyEnvironment(Environment);
return Status;
}
Status = RtlAnsiStringToUnicodeString(&vU, &aValue, TRUE);
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(PsxHeap, 0, nU.Buffer);
RtlFreeHeap(PsxHeap, 0, CommandLine);
RtlDestroyEnvironment(Environment);
return Status;
}
Status = RtlSetEnvironmentVariable_U(&Environment, &nU, &vU);
RtlFreeHeap(PsxHeap, 0, nU.Buffer);
RtlFreeHeap(PsxHeap, 0, vU.Buffer);
if (!NT_SUCCESS(Status)) {
KdPrint(("PSXSS: RtlSetEnvVar: 0x%x\n", Status));
RtlFreeHeap(PsxHeap, 0, CommandLine);
RtlDestroyEnvironment(Environment);
return Status;
}
}
// Make Image into correct format: "X:\path", return to original
// after we're done.
pwc = Image->Buffer;
PSX_GET_SIZEOF(DOSDEVICE_W,len);
Image->Buffer += (len - 2)/2;
Image->Length -= (len - 2);
flags = CREATE_SUSPENDED|CREATE_NEW_CONSOLE|
CREATE_NEW_PROCESS_GROUP|CREATE_UNICODE_ENVIRONMENT;
// set up StartInfo
StartInfo.cb = sizeof(STARTUPINFO);
StartInfo.lpReserved = 0;
StartInfo.lpDesktop = NULL;
StartInfo.lpTitle = NULL;
StartInfo.dwX = StartInfo.dwY = 0;
StartInfo.dwXSize = StartInfo.dwYSize = 400;
StartInfo.dwFlags = 0;
StartInfo.wShowWindow = SW_SHOWDEFAULT;
StartInfo.cbReserved2 = 0;
StartInfo.lpReserved2 = NULL;
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
if (NT_SUCCESS(Status)) {
Success = CreateProcess(
Image->Buffer, // address of module name
CommandLine, // command line
NULL, // process security attr
NULL, // thread security attr
FALSE, // inherit handles
flags, // creation flags
Environment, // address of new environ
CurDir->Buffer, // current working dir
&StartInfo, // startup info
&ProcInfo // process information
);
EndImpersonation();
}
// restore image name
Image->Buffer = pwc;
(void)RtlDestroyEnvironment(Environment);
// ASSERT(NT_SUCCESS(Status));
RtlFreeHeap(PsxHeap, 0, CommandLine);
if (!Success) {
KdPrint(("PSXSS: CreateProcess: %d\n", GetLastError()));
return STATUS_UNSUCCESSFUL;
}
p->Thread = ProcInfo.hThread;
p->Process = ProcInfo.hProcess;
// set bit in the process to indicate it's a foreign
// image type
p->Flags |= P_FOREIGN_EXEC;
// create additional thread in psxss to wait for new
// process to exit.
p->BlockingThread = NULL;
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
if (NT_SUCCESS(Status)) {
p->BlockingThread = CreateThread(
NULL,
0,
ForeignProcessWait,
(PVOID)p,
CREATE_SUSPENDED,
&ThreadId
);
EndImpersonation();
if (NULL == p->BlockingThread) {
//XXX.mjb: clean process
Status = STATUS_UNSUCCESSFUL;
}
}
if (!NT_SUCCESS(Status)) {
return Status;
}
Success = ResumeThread(p->BlockingThread);
ASSERT(Success);
Success = ResumeThread(p->Thread);
ASSERT(Success);
return STATUS_SUCCESS;
}
DWORD
ForeignProcessWait(PVOID arg)
{
PPSX_PROCESS p = arg;
ULONG ExitStatus;
WaitForSingleObject(p->Process, (DWORD)-1);
Exit(p, ExitStatus);
p->BlockingThread = NULL;
ExitThread(0);
//NOTREACHED
ASSERT(0);
return 0;
}
//
// Change a posix-type path variable to win32 path. The given
// buffer is modified in place.
//
VOID
ConvertPathToWin(char *path)
{
char *pch;
pch = path;
while (*path) {
// change ':' to ';'
if (':' == *path) {
*pch = ';';
++path;
++pch;
continue;
}
// change "//X" to "X:"
if ('/' == *path && '/' == path[1]) {
path += 2;
*pch = *path;
++pch;
*pch = ':';
++path;
++pch;
continue;
}
// change slash to backslash
if ('/' == *path) {
*pch = '\\';
++path;
++pch;
continue;
}
*pch = *path;
++pch;
++path;
}
*pch = '\0';
}
#endif /* EXEC_FOREIGN */