Windows2003-3790/termsrv/winsta/server/wininit.c
2020-09-30 16:53:55 +02:00

457 lines
13 KiB
C

/*************************************************************************
*
* wininit.c
*
* Window station init and destroy routines
*
* Copyright Microsoft Corporation, 1998
*
*
*************************************************************************/
/*
* Includes
*/
#include "precomp.h"
#pragma hdrstop
/*
* Local data
*/
#define LOGOFF_TIMER 120000L
#define MODULE_SIZE 1024 /* Default size for retrive of module data */
#define VDDATA_LENGTH 1024
/*
* Internal Procedures
*/
VOID StartLogonTimers( PWINSTATION );
VOID IdleTimeout( ULONG );
VOID LogonTimeout( ULONG );
VOID IdleLogoffTimeout( ULONG );
VOID LogoffTimeout( ULONG );
/*******************************************************************************
*
* StartLogonTimers
*
* This routine is called when an user is logged on.
* Timers are started for idle input and total logon time.
*
* ENTRY:
* None.
*
* EXIT:
* None.
*
******************************************************************************/
VOID
StartLogonTimers( PWINSTATION pWinStation )
{
int Status;
ULONG Timer;
BOOL bValidHelpSession;
// for Session0 and any console sessions, timeouts don't make sense
if ( ( pWinStation->LogonId != 0 ) && ( pWinStation->LogonId != USER_SHARED_DATA->ActiveConsoleId ) ) {
if( TSIsSessionHelpSession(pWinStation, &bValidHelpSession) ) {
ASSERT( TRUE == bValidHelpSession );
return;
}
if ( Timer = pWinStation->Config.Config.User.MaxIdleTime ) {
if ( !pWinStation->fIdleTimer ) {
Status = IcaTimerCreate( 0, &pWinStation->hIdleTimer );
if ( NT_SUCCESS( Status ) )
pWinStation->fIdleTimer = TRUE;
else
DBGPRINT(( "StartLogonTimers - failed to create idle timer \n" ));
}
if ( pWinStation->fIdleTimer )
IcaTimerStart( pWinStation->hIdleTimer, IdleTimeout,
LongToPtr( pWinStation->LogonId ), Timer );
}
if ( Timer = pWinStation->Config.Config.User.MaxConnectionTime ) {
if ( !pWinStation->fLogonTimer ) {
Status = IcaTimerCreate( 0, &pWinStation->hLogonTimer );
if ( NT_SUCCESS( Status ) )
pWinStation->fLogonTimer = TRUE;
else
DBGPRINT(( "StartLogonTimers - failed to create logon timer \n" ));
}
if ( pWinStation->fLogonTimer )
IcaTimerStart( pWinStation->hLogonTimer, LogonTimeout,
LongToPtr( pWinStation->LogonId ), Timer );
}
}
}
/*******************************************************************************
*
* IdleTimeout
*
* This routine is called when the idle timer expires.
* Send the user a warning message and start timer to logoff in 2 minutes.
*
* ENTRY:
* LogonId
*
* EXIT:
* None.
*
******************************************************************************/
VOID IdleTimeout( ULONG LogonId )
{
LARGE_INTEGER liT;
ULONG ulTimeDelta;
ICA_STACK_LAST_INPUT_TIME Ica_Stack_Last_Input_Time;
NTSTATUS Status;
ULONG cbReturned;
PWINSTATION pWinStation;
WINSTATION_APIMSG msg;
pWinStation = FindWinStationById( LogonId, FALSE );
if ( !pWinStation )
return;
if ( !pWinStation->hStack )
goto done;
if ( !pWinStation->fIdleTimer )
goto done;
// Check for availability
if ( pWinStation->pWsx &&
pWinStation->pWsx->pWsxIcaStackIoControl ) {
Status = pWinStation->pWsx->pWsxIcaStackIoControl(
pWinStation->pWsxContext,
pWinStation->hIca,
pWinStation->hStack,
IOCTL_ICA_STACK_QUERY_LAST_INPUT_TIME,
NULL,
0,
&Ica_Stack_Last_Input_Time,
sizeof( Ica_Stack_Last_Input_Time ),
&cbReturned );
}
else {
Status = STATUS_INVALID_PARAMETER;
}
if ( !NT_SUCCESS( Status ) ) {
goto done;
}
/*
* Check if there was input during the idle time
*/
NtQuerySystemTime( &liT );
// calculate delta in time & convert from 100ns unit to milliseconds
liT = RtlExtendedLargeIntegerDivide(
RtlLargeIntegerSubtract( liT, Ica_Stack_Last_Input_Time.LastInputTime ),
10000, NULL );
ulTimeDelta = (ULONG)liT.LowTime;
TRACE((hTrace,TC_ICASRV,TT_API1, "IdleTimeout: delta = %d, max idle = %d\n", ulTimeDelta,
pWinStation->Config.Config.User.MaxIdleTime ));
if ( ulTimeDelta < pWinStation->Config.Config.User.MaxIdleTime ) {
IcaTimerStart( pWinStation->hIdleTimer, IdleTimeout, LongToPtr( LogonId ),
pWinStation->Config.Config.User.MaxIdleTime - ulTimeDelta );
} else {
TCHAR szTitle[128];
TCHAR szMsg[256];
int cchTitle, cchMessage;
IcaTimerStart( pWinStation->hIdleTimer, IdleLogoffTimeout,
LongToPtr( LogonId ), LOGOFF_TIMER );
if ( !(cchTitle = LoadString(hModuleWin, STR_CITRIX_IDLE_TITLE, szTitle, sizeof(szTitle)/sizeof(TCHAR))) )
goto done;
if ( pWinStation->Config.Config.User.fResetBroken )
{
if ( !(cchMessage = LoadString(hModuleWin, STR_CITRIX_IDLE_MSG_LOGOFF, szMsg, sizeof(szMsg)/sizeof(TCHAR)) ))
goto done;
}
else
{
if ( !(cchMessage = LoadString(hModuleWin, STR_CITRIX_IDLE_MSG_DISCON, szMsg, sizeof(szMsg)/sizeof(TCHAR)) ))
goto done;
}
msg.u.SendMessage.pTitle = szTitle;
msg.u.SendMessage.TitleLength = (cchTitle+1) * sizeof(TCHAR);
msg.u.SendMessage.pMessage = szMsg;
msg.u.SendMessage.MessageLength = (cchMessage+1) * sizeof(TCHAR);
msg.u.SendMessage.Style = MB_OK | MB_ICONSTOP;
msg.u.SendMessage.Timeout = (ULONG)LOGOFF_TIMER/1000;
msg.u.SendMessage.Response = 0;
msg.u.SendMessage.DoNotWait = TRUE;
msg.u.SendMessage.DoNotWaitForCorrectDesktop = FALSE;
// since we dont care abou response, or message delievery status;
msg.u.SendMessage.pStatus = NULL;
msg.u.SendMessage.pResponse = NULL;
msg.u.SendMessage.hEvent = NULL;
msg.ApiNumber = SMWinStationDoMessage;
Status = SendWinStationCommand( pWinStation, &msg, 0 );
}
done:
ReleaseWinStation( pWinStation );
}
/*******************************************************************************
*
* LogonTimeout
*
* This routine is called when the logon timer expires.
* Send the user a warning message and start timer to logoff in 2 minutes.
*
* ENTRY:
* LogonId
*
* EXIT:
* None.
*
******************************************************************************/
VOID LogonTimeout( ULONG LogonId )
{
TCHAR szTitle[128];
TCHAR szMsg[256];
PWINSTATION pWinStation;
NTSTATUS Status;
WINSTATION_APIMSG msg;
int cchTitle, cchMsg;
pWinStation = FindWinStationById( LogonId, FALSE );
if ( !pWinStation )
return;
if ( !pWinStation->fLogonTimer)
goto done;
if ( !(cchTitle = LoadString(hModuleWin, STR_CITRIX_LOGON_TITLE, szTitle, sizeof(szTitle)/sizeof(TCHAR)) ))
goto done;
if ( pWinStation->Config.Config.User.fResetBroken )
{
if ( !(cchMsg = LoadString(hModuleWin, STR_CITRIX_LOGON_MSG_LOGOFF, szMsg, sizeof(szMsg)/sizeof(TCHAR)) ))
goto done;
}
else
{
if ( !(cchMsg = LoadString(hModuleWin, STR_CITRIX_LOGON_MSG_DISCON, szMsg, sizeof(szMsg)/sizeof(TCHAR)) ))
goto done;
}
msg.u.SendMessage.pTitle = szTitle;
msg.u.SendMessage.TitleLength = ( cchTitle+1 ) * sizeof(TCHAR);
msg.u.SendMessage.pMessage = szMsg;
msg.u.SendMessage.MessageLength = ( cchMsg+1 ) * sizeof(TCHAR);
msg.u.SendMessage.Style = MB_OK | MB_ICONSTOP;
msg.u.SendMessage.Timeout = (ULONG)LOGOFF_TIMER/1000;
msg.u.SendMessage.Response = 0;
msg.u.SendMessage.DoNotWait = TRUE;
msg.u.SendMessage.DoNotWaitForCorrectDesktop = FALSE;
// since we dont care abou response, or message delievery status;
msg.u.SendMessage.pStatus = NULL;
msg.u.SendMessage.pResponse = NULL;
msg.u.SendMessage.hEvent = NULL;
msg.ApiNumber = SMWinStationDoMessage;
Status = SendWinStationCommand( pWinStation, &msg, 0 );
IcaTimerStart( pWinStation->hLogonTimer, LogoffTimeout,
LongToPtr( LogonId ), LOGOFF_TIMER );
if (pWinStation->fIdleTimer) {
pWinStation->fIdleTimer = FALSE;
IcaTimerClose( pWinStation->hIdleTimer );
}
done:
ReleaseWinStation( pWinStation );
}
/*******************************************************************************
*
* IdleLogoffTimeout
*
* This routine is called when the logoff timer expires.
* Check for input before logging user off
*
* ENTRY:
* LogonId
*
* EXIT:
* None.
*
******************************************************************************/
VOID IdleLogoffTimeout( ULONG LogonId )
{
LARGE_INTEGER liT;
ULONG ulTimeDelta;
ICA_STACK_LAST_INPUT_TIME Ica_Stack_Last_Input_Time;
NTSTATUS Status;
ULONG cbReturned;
PWINSTATION pWinStation;
pWinStation = FindWinStationById( LogonId, FALSE );
if ( !pWinStation )
return;
if ( !pWinStation->hStack )
goto done;
if ( !pWinStation->fIdleTimer )
goto done;
// Check for availability
if ( pWinStation->pWsx &&
pWinStation->pWsx->pWsxIcaStackIoControl ) {
Status = pWinStation->pWsx->pWsxIcaStackIoControl(
pWinStation->pWsxContext,
pWinStation->hIca,
pWinStation->hStack,
IOCTL_ICA_STACK_QUERY_LAST_INPUT_TIME,
NULL,
0,
&Ica_Stack_Last_Input_Time,
sizeof( Ica_Stack_Last_Input_Time ),
&cbReturned );
}
else {
Status = STATUS_INVALID_PARAMETER;
}
if ( !NT_SUCCESS( Status ) ) {
goto done;
}
NtQuerySystemTime( &liT );
liT = RtlExtendedLargeIntegerDivide(
RtlLargeIntegerSubtract( liT, Ica_Stack_Last_Input_Time.LastInputTime ),
10000, NULL );
ulTimeDelta = (ULONG)liT.LowTime;
TRACE((hTrace,TC_ICASRV,TT_API1, "IdleTimeout: delta = %d, max idle = %d\n", ulTimeDelta,
LOGOFF_TIMER ));
if ( ulTimeDelta < LOGOFF_TIMER ) {
IcaTimerStart( pWinStation->hIdleTimer, IdleTimeout, LongToPtr( LogonId ),
pWinStation->Config.Config.User.MaxIdleTime - ulTimeDelta );
} else
LogoffTimeout( LogonId );
done:
ReleaseWinStation( pWinStation );
}
/*******************************************************************************
*
* LogoffTimeout
*
* This routine is called when the logoff timer expires.
* Log user off and disconnect the winstation.
*
* ENTRY:
* LogonId - LogonId to logout
*
* EXIT:
* None.
*
******************************************************************************/
VOID LogoffTimeout(ULONG LogonId)
{
PWINSTATION pWinStation;
pWinStation = FindWinStationById( LogonId, FALSE );
if ( !pWinStation )
return;
//
// Report disconnect reason back to client
//
if(pWinStation->WinStationName[0] &&
pWinStation->pWsx &&
pWinStation->pWsx->pWsxSetErrorInfo &&
pWinStation->pWsxContext)
{
ULONG discReason = 0;
if(pWinStation->fIdleTimer)
{
discReason = TS_ERRINFO_IDLE_TIMEOUT;
}
else if(pWinStation->fLogonTimer)
{
discReason = TS_ERRINFO_LOGON_TIMEOUT;
}
if(discReason)
{
pWinStation->pWsx->pWsxSetErrorInfo(
pWinStation->pWsxContext,
discReason,
FALSE); //stack lock not held
}
}
if ( pWinStation->Config.Config.User.fResetBroken ) {
ReleaseWinStation( pWinStation );
QueueWinStationReset( LogonId );
}
else {
ReleaseWinStation( pWinStation );
QueueWinStationDisconnect( LogonId );
}
}
/*******************************************************************************
*
* DisconnectTimeout
*
* This routine is called when the disconnect timer expires.
* Reset the winstation.
*
* ENTRY:
* LogonId
*
* EXIT:
* None.
*
******************************************************************************/
VOID DisconnectTimeout( ULONG LogonId )
{
//This timer pops for a disconnected session
//so there is no need to report an error back to
//the client
QueueWinStationReset( LogonId );
}