|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
termsrv.c
Abstract:
Terminal Server routines for supporting multiple sessions.
Author:
Revision History:
--*/
#include "smsrvp.h"
#include <ntexapi.h>
#include <winerror.h>
#include "stdio.h"
HANDLE SmpSessionsObjectDirectory;
extern PSECURITY_DESCRIPTOR SmpLiberalSecurityDescriptor;
NTSTATUS SmpSetProcessMuSessionId ( IN HANDLE Process, IN ULONG MuSessionId )
/*++
Routine Description:
This function sets the multiuser session ID for a process.
Arguments:
Process - Supplies the handle of the process to set the ID for.
MuSessionId - Supplies the ID to give to the supplied process.
Return Value:
NTSTATUS.
--*/
{ NTSTATUS Status; PROCESS_SESSION_INFORMATION ProcessInfo;
ProcessInfo.SessionId = MuSessionId;
Status = NtSetInformationProcess (Process, ProcessSessionInformation, &ProcessInfo, sizeof( ProcessInfo ) );
if ( !NT_SUCCESS( Status ) ) { KdPrint(( "SMSS: SetProcessMuSessionId, Process=%x, Status=%x\n", Process, Status )); }
return Status; }
NTSTATUS SmpTerminateProcessAndWait ( IN HANDLE Process, IN ULONG Seconds )
/*++
Routine Description:
This function terminates the process and waits for it to die.
Arguments:
Process - Supplies the handle of the process to terminate.
Seconds - Supplies the number of seconds to wait for the process to die.
Return Value:
NTSTATUS.
--*/
{ NTSTATUS Status; ULONG mSecs; LARGE_INTEGER Timeout;
//
// Try to terminate the process.
//
Status = NtTerminateProcess( Process, STATUS_SUCCESS );
if ( !NT_SUCCESS( Status ) && Status != STATUS_PROCESS_IS_TERMINATING ) { KdPrint(( "SMSS: Terminate=0x%x\n", Status )); return Status; }
//
// Wait for the process to die.
//
mSecs = Seconds * 1000; Timeout = RtlEnlargedIntegerMultiply( mSecs, -10000 );
Status = NtWaitForSingleObject( Process, FALSE, &Timeout );
return Status; }
NTSTATUS SmpGetProcessMuSessionId ( IN HANDLE Process, OUT PULONG MuSessionId )
/*++
Routine Description:
This function gets the multiuser session ID for a process.
Arguments:
Process - Supplies the handle of the process to get the ID for.
MuSessionId - Supplies the location to place the ID in.
Return Value:
NTSTATUS.
--*/
{ NTSTATUS Status; PROCESS_SESSION_INFORMATION ProcessInfo;
Status = NtQueryInformationProcess (Process, ProcessSessionInformation, &ProcessInfo, sizeof( ProcessInfo ), NULL );
if ( !NT_SUCCESS( Status ) ) { KdPrint(( "SMSS: GetProcessMuSessionId, Process=%x, Status=%x\n", Process, Status )); *MuSessionId = 0; } else { *MuSessionId = ProcessInfo.SessionId; }
return Status; }
NTSTATUS SmpTerminateCSR( IN ULONG MuSessionId )
/*++
Routine Description:
This function terminates all known subsystems for this MuSessionId. Also closes all LPC ports and all process handles.
Arguments:
MuSessionId - Supplies the session ID to terminate subsystems for.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS Status = STATUS_SUCCESS; PLIST_ENTRY Next; PLIST_ENTRY Tmp; PSMPKNOWNSUBSYS KnownSubSys; CLIENT_ID ClientId; HANDLE Process;
RtlEnterCriticalSection( &SmpKnownSubSysLock );
//
// Force all subsystems of this session to exit.
//
Next = SmpKnownSubSysHead.Flink; while ( Next != &SmpKnownSubSysHead ) {
KnownSubSys = CONTAINING_RECORD(Next,SMPKNOWNSUBSYS,Links); Next = Next->Flink;
if ( KnownSubSys->MuSessionId != MuSessionId ) { continue; }
ClientId = KnownSubSys->InitialClientId; Process = KnownSubSys->Process;
//
// Reference the Subsystem so it does not go away while we using it.
//
SmpReferenceKnownSubSys(KnownSubSys);
//
// Set deleting so that this subsys will go away when refcount reaches
// zero.
//
KnownSubSys->Deleting = TRUE;
//
// Unlock the SubSystemList as we don't want to leave it locked
// around a wait.
//
RtlLeaveCriticalSection( &SmpKnownSubSysLock );
Status = SmpTerminateProcessAndWait( Process, 10 );
if ( Status != STATUS_SUCCESS ) { KdPrint(( "SMSS: Subsystem type %d failed to terminate\n", KnownSubSys->ImageType )); } RtlEnterCriticalSection( &SmpKnownSubSysLock );
//
// Must look for entry again.
// BUGBUG: why re-look ? ICASRV shouldn't allow it to be deleted, but..
//
Tmp = SmpKnownSubSysHead.Flink;
while ( Tmp != &SmpKnownSubSysHead ) {
KnownSubSys = CONTAINING_RECORD(Tmp,SMPKNOWNSUBSYS,Links);
if ( KnownSubSys->InitialClientId.UniqueProcess == ClientId.UniqueProcess ) { //
// Remove the KnownSubSys block from the list.
//
RemoveEntryList( &KnownSubSys->Links );
//
// Dereference the subsystem. If this is the last reference,
// the subsystem will be deleted.
//
SmpDeferenceKnownSubSys(KnownSubSys);
break; } Tmp = Tmp->Flink; }
//
// Since we have waited, we must restart from the top of the list.
//
Next = SmpKnownSubSysHead.Flink;
}
//
// Unlock SubSystemList.
//
RtlLeaveCriticalSection( &SmpKnownSubSysLock );
return Status; }
NTSTATUS SmpStartCsr( IN PSMAPIMSG SmApiMsg, IN PSMP_CLIENT_CONTEXT CallingClient, IN HANDLE CallPort )
/*++
Routine Description:
This function creates a CSRSS system for a MuSessionId, returning the initial program and the windows subsystem. The console only returns the initial program and windows subsystem since it is started at boot.
Arguments:
TBD.
Return Value:
NTSTATUS.
--*/
{ NTSTATUS St; NTSTATUS Status; PSMSTARTCSR args; ULONG MuSessionId; UNICODE_STRING InitialCommand; UNICODE_STRING DefaultInitialCommand; ULONG_PTR InitialCommandProcessId; ULONG_PTR WindowsSubSysProcessId; HANDLE InitialCommandProcess; extern ULONG SmpInitialCommandProcessId; extern ULONG SmpWindowsSubSysProcessId; PVOID State; LOGICAL TerminateCSR;
TerminateCSR = FALSE;
args = &SmApiMsg->u.StartCsr; MuSessionId = args->MuSessionId;
InitialCommand.Length = (USHORT)args->InitialCommandLength; InitialCommand.MaximumLength = (USHORT)args->InitialCommandLength; InitialCommand.Buffer = args->InitialCommand;
//
// Things are different for the console.
// SM starts him up and passes his ID back here.
//
if ( !MuSessionId ) { args->WindowsSubSysProcessId = SmpWindowsSubSysProcessId; args->InitialCommandProcessId = SmpInitialCommandProcessId; return STATUS_SUCCESS; }
//
// Load subsystems for this session.
//
WindowsSubSysProcessId = 0;
Status = SmpLoadSubSystemsForMuSession (&MuSessionId, &WindowsSubSysProcessId, &DefaultInitialCommand );
if ( Status != STATUS_SUCCESS ) {
DbgPrint( "SMSS: SmpStartCsr, SmpLoadSubSystemsForMuSession Failed. Status=%x\n", Status );
goto nostart; }
//
// Start the initial command for this session.
//
if ( InitialCommand.Length == 0 ) {
Status = SmpExecuteInitialCommand( MuSessionId, &DefaultInitialCommand, &InitialCommandProcess, &InitialCommandProcessId ); } else { Status = SmpExecuteInitialCommand( MuSessionId, &InitialCommand, &InitialCommandProcess, &InitialCommandProcessId ); }
if ( !NT_SUCCESS( Status ) ) {
TerminateCSR = TRUE; DbgPrint( "SMSS: SmpStartCsr, SmpExecuteInitialCommand Failed. Status=%x\n", Status );
goto nostart;
}
NtClose( InitialCommandProcess ); // This handle isn't needed
args->InitialCommandProcessId = InitialCommandProcessId; args->WindowsSubSysProcessId = WindowsSubSysProcessId; args->MuSessionId = MuSessionId;
nostart:
if ((AttachedSessionId != (-1)) && NT_SUCCESS(SmpAcquirePrivilege( SE_LOAD_DRIVER_PRIVILEGE, &State ))) {
//
// If we are attached to a session space, leave it
// so we can create a new one.
//
St = NtSetSystemInformation (SystemSessionDetach, (PVOID)&AttachedSessionId, sizeof(MuSessionId));
if (NT_SUCCESS(St)) { AttachedSessionId = (-1); } else {
//
// This has to succeed otherwise we will bugcheck while trying to
// create another session.
//
#if DBG
DbgPrint( "SMSS: SmpStartCsr, Couldn't Detach from Session Space. Status=%x\n", St);
DbgBreakPoint (); #endif
}
SmpReleasePrivilege( State ); }
if ( TerminateCSR == TRUE ) {
St = SmpTerminateCSR( MuSessionId );
#if DBG
if (!NT_SUCCESS(St)) { DbgPrint( "SMSS: SmpStartCsr, Couldn't Terminate CSR. Status=%x\n", St); DbgBreakPoint(); } #endif
}
return Status;
}
NTSTATUS SmpStopCsr( IN PSMAPIMSG SmApiMsg, IN PSMP_CLIENT_CONTEXT CallingClient, IN HANDLE CallPort )
/*++
Routine Description:
This function terminates all known subsystems for this MuSessionId. Also closes all LPC ports and all process handles.
Arguments:
TBD.
Return Value:
NTSTATUS.
--*/
{ PSMSTOPCSR args; ULONG MuSessionId;
args = &SmApiMsg->u.StopCsr; MuSessionId = args->MuSessionId;
return SmpTerminateCSR( MuSessionId ); }
BOOLEAN SmpCheckDuplicateMuSessionId( IN ULONG MuSessionId )
/*++
Routine Description:
This function looks for this MuSessionId in the known subsystems.
Arguments:
MuSessionId - Supplies the session ID to look for.
Return Value:
TRUE if found, FALSE if not.
--*/
{ PLIST_ENTRY Next; PSMPKNOWNSUBSYS KnownSubSys;
RtlEnterCriticalSection( &SmpKnownSubSysLock );
Next = SmpKnownSubSysHead.Flink; while ( Next != &SmpKnownSubSysHead ) {
KnownSubSys = CONTAINING_RECORD(Next,SMPKNOWNSUBSYS,Links);
if ( KnownSubSys->MuSessionId == MuSessionId ) { RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return TRUE; } Next = Next->Flink; }
RtlLeaveCriticalSection( &SmpKnownSubSysLock );
return FALSE; }
|