You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
818 lines
21 KiB
818 lines
21 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// File: ktcontrol.cxx
|
|
//
|
|
// Contents: Kerberos Tunneller, service infrastructure
|
|
//
|
|
// History: 28-Jun-2001 t-ryanj Created
|
|
//
|
|
//------------------------------------------------------------------------
|
|
#include "ktdebug.h"
|
|
#include "ktcontrol.h"
|
|
#include "ktcore.h"
|
|
#include "ktmem.h"
|
|
#include "ktsock.h"
|
|
#include "kthttp.h"
|
|
|
|
VOID WINAPI
|
|
KtMainServiceEntry(
|
|
IN DWORD argc,
|
|
IN LPTSTR argv[]
|
|
);
|
|
|
|
DWORD WINAPI
|
|
KtControlHandlerEx(
|
|
DWORD dwControl,
|
|
DWORD dwEventType,
|
|
PVOID pvEventData,
|
|
PVOID pvContext
|
|
);
|
|
|
|
BOOL
|
|
KtStartupInit(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KtContinueService(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KtPauseService(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KtStopService(
|
|
IN DWORD dwWin32ErrorCode
|
|
);
|
|
|
|
BOOL
|
|
KtBeginStateTransition(
|
|
IN DWORD dwUltimateState,
|
|
IN DWORD dwWaitHint
|
|
);
|
|
|
|
BOOL
|
|
KtFinishStateTransition(
|
|
VOID
|
|
);
|
|
|
|
HANDLE KtIocp = NULL;
|
|
HANDLE KtStateTransitionLock = NULL;
|
|
LPTSTR KtServiceName = TEXT("kerbtunnel");
|
|
SERVICE_TABLE_ENTRY KtServiceTable[] = { { KtServiceName, KtMainServiceEntry },
|
|
{ NULL, NULL } };
|
|
SERVICE_STATUS KtServiceStatus = {0};
|
|
SERVICE_STATUS_HANDLE KtServiceStatusHandle = NULL;
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtStartServiceCtrlDispatcher
|
|
//
|
|
// Synopsis: Starts the Service Control Dispatcher.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value.
|
|
// If FALSE, GetLastError() for more info.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtStartServiceCtrlDispatcher(
|
|
VOID
|
|
)
|
|
{
|
|
return StartServiceCtrlDispatcher( KtServiceTable );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtFinishStateTransition
|
|
//
|
|
// Synopsis: Closes out a state transition, reporting the new state
|
|
// to service control.
|
|
//
|
|
// Effects: Modifies KtServiceStatus, the global service state.
|
|
//
|
|
// Signals KtStateTransitionLock, signifying the completion of
|
|
// a state transition.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE on success, FALSE on failure.
|
|
// If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtFinishStateTransition(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fSuccess = TRUE;
|
|
DWORD dwUltimateState;
|
|
|
|
//
|
|
// First, we determine what state we want to be in at the end of the
|
|
// transition.
|
|
//
|
|
|
|
switch( KtServiceStatus.dwCurrentState )
|
|
{
|
|
case SERVICE_START_PENDING:
|
|
case SERVICE_CONTINUE_PENDING:
|
|
dwUltimateState = SERVICE_RUNNING;
|
|
break;
|
|
|
|
case SERVICE_STOP_PENDING:
|
|
dwUltimateState = SERVICE_STOPPED;
|
|
break;
|
|
|
|
case SERVICE_PAUSE_PENDING:
|
|
dwUltimateState = SERVICE_PAUSED;
|
|
break;
|
|
|
|
default:
|
|
DebugLog( DEB_WARN, "%s(%d): Unhandled case: 0x%x.\n", __FILE__, __LINE__, KtServiceStatus.dwCurrentState );
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
DsysAssert( KtServiceStatus.dwCurrentState == SERVICE_START_PENDING ||
|
|
KtServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING ||
|
|
KtServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ||
|
|
KtServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Time to construct and set the new state.
|
|
//
|
|
// Checkpoint and WaitHint are always 0 for non-pending states,
|
|
// and we only report errors when punting, which is handled by
|
|
// a different routine.
|
|
//
|
|
|
|
KtServiceStatus.dwCheckPoint = KtServiceStatus.dwWaitHint = 0;
|
|
KtServiceStatus.dwWin32ExitCode = NO_ERROR;
|
|
KtServiceStatus.dwServiceSpecificExitCode = 0;
|
|
KtServiceStatus.dwCurrentState = dwUltimateState;
|
|
|
|
if( !SetServiceStatus( KtServiceStatusHandle, &KtServiceStatus ) )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error from SetServiceStatus: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Now we signal the StateTransition event, indicating that it's
|
|
// alright to begin a new state transition.
|
|
//
|
|
|
|
if( !SetEvent( KtStateTransitionLock ) )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error from SetEvent: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
DebugLog( DEB_TRACE, "%s(%d): State transition completed.\n", __FILE__, __LINE__ );
|
|
|
|
Cleanup:
|
|
return fSuccess;
|
|
|
|
Error:
|
|
/* TODO: Event log, and maybe something drastic. */
|
|
fSuccess = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtBeginStateTransition
|
|
//
|
|
// Synopsis: Enters a state transition, reporting an appropriate pending
|
|
// state to Service Control and requesting dwWaitHint millisec
|
|
// to complete the transition.
|
|
//
|
|
// Effects: Modifies KtServiceStatus, the global service status.
|
|
// Waits for KtStateTransitionLock to begin state transition.
|
|
//
|
|
// Arguments: dwUltimateState - One of SERVICE_STOPPED, SERVICE_RUNNING,
|
|
// or SERVICE_PAUSED.
|
|
//
|
|
// dwWaitHint - Estimated millisec before next state report.
|
|
// Service Control may kill the service if it does not
|
|
// report again within the time specified. (See Notes.)
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value. If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes: If 0 is supplied as dwWaitHint, this call will go directly
|
|
// to the ultimate state rather than a pending state.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtBeginStateTransition(
|
|
IN DWORD dwUltimateState,
|
|
IN DWORD dwWaitHint
|
|
)
|
|
{
|
|
BOOL fSuccess = TRUE;
|
|
DWORD dwPendingState = 0;
|
|
|
|
//
|
|
// dwUltimateState is our target state, so determine which pending
|
|
// state to be in in the meantime.
|
|
//
|
|
|
|
switch( dwUltimateState )
|
|
{
|
|
case SERVICE_STOPPED:
|
|
dwPendingState = SERVICE_STOP_PENDING;
|
|
break;
|
|
|
|
case SERVICE_RUNNING:
|
|
dwPendingState = (KtServiceStatus.dwCurrentState == SERVICE_PAUSED) ? SERVICE_CONTINUE_PENDING : SERVICE_START_PENDING;
|
|
break;
|
|
|
|
case SERVICE_PAUSED:
|
|
dwPendingState = SERVICE_PAUSE_PENDING;
|
|
break;
|
|
|
|
default:
|
|
DebugLog( DEB_WARN, "%s(%d): Invalid parameter to KtBeginStateTransition: 0x%x.\n", dwUltimateState );
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
DsysAssert( dwUltimateState == SERVICE_STOPPED ||
|
|
dwUltimateState == SERVICE_RUNNING ||
|
|
dwUltimateState == SERVICE_PAUSED );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Wait for any pending state transitions to complete before
|
|
// starting a new one.
|
|
//
|
|
|
|
WaitForSingleObjectEx( KtStateTransitionLock, INFINITE, FALSE );
|
|
DebugLog( DEB_TRACE, "%s(%d): Beginning State Transition.\n", __FILE__, __LINE__ );
|
|
|
|
//
|
|
// If dwWaitHint was given as 0, we want to go right ahead
|
|
// and set the target state, otherwise we set the appropriate
|
|
// pending state.
|
|
//
|
|
|
|
if( dwWaitHint == 0 )
|
|
{
|
|
if( !KtFinishStateTransition() )
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're ready to initialize and set the state.
|
|
//
|
|
// Checkpoint is 1 since this will be the first checkpoint
|
|
// in the new pending state, WaitHint as supplied, and
|
|
// no errors are reported except when stopping.
|
|
//
|
|
|
|
KtServiceStatus.dwCheckPoint = 1;
|
|
KtServiceStatus.dwWaitHint = dwWaitHint;
|
|
KtServiceStatus.dwWin32ExitCode = NO_ERROR;
|
|
KtServiceStatus.dwServiceSpecificExitCode = 0;
|
|
KtServiceStatus.dwCurrentState = dwPendingState;
|
|
|
|
if( !SetServiceStatus( KtServiceStatusHandle, &KtServiceStatus ) )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error from SetServiceStatus: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return fSuccess;
|
|
|
|
Error:
|
|
fSuccess = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtControlHandlerEx
|
|
//
|
|
// Synopsis: Service Control Handler, called by Service Control to
|
|
// process control events.
|
|
//
|
|
// Effects: Posts to the i/o completion port to wake a service thread.
|
|
//
|
|
// Arguments: dwControl - The control to handle.
|
|
// dwEventType - Extended info for certain control events.
|
|
// pvEventData - Additional info for certain control events.
|
|
// pvContext - User defined data. (Unused here.)
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Winerror code, spec. NO_ERROR or ERROR_CALL_NOT_IMPLEMENTED.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DWORD WINAPI
|
|
KtControlHandlerEx(
|
|
IN DWORD dwControl,
|
|
IN DWORD dwEventType,
|
|
IN PVOID pvEventData,
|
|
IN PVOID pvContext
|
|
)
|
|
{
|
|
DWORD dwReturn = NO_ERROR;
|
|
BOOL fPostControlToServiceThread = FALSE;
|
|
|
|
//
|
|
// If we're told to change state, initiate an appropriate state
|
|
// transition. To service an INTERROGATE request we merely report
|
|
// our current status. No other control events are implemented.
|
|
//
|
|
|
|
switch( dwControl )
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
DebugLog( DEB_TRACE, "%s(%d): SERVICE_CONTROL_%s recieved.\n", __FILE__, __LINE__, (dwControl==SERVICE_CONTROL_STOP)?"STOP":"SHUTDOWN" );
|
|
KtBeginStateTransition( SERVICE_STOPPED, 2000 );
|
|
fPostControlToServiceThread = TRUE;
|
|
break;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
DebugLog( DEB_TRACE, "%s(%d): SERVICE_CONTROL_PAUSE recieved.\n", __FILE__, __LINE__ );
|
|
KtBeginStateTransition( SERVICE_PAUSED, 2000 );
|
|
fPostControlToServiceThread = TRUE;
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
DebugLog( DEB_TRACE, "%s(%d): SERVICE_CONTROL_CONTINUE recieved.\n", __FILE__, __LINE__ );
|
|
KtBeginStateTransition( SERVICE_RUNNING, 2000 );
|
|
fPostControlToServiceThread = TRUE;
|
|
break;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
DebugLog( DEB_TRACE, "%s(%d): SERVICE_CONTROL_CONTINUE recieved.\n", __FILE__, __LINE__ );
|
|
SetServiceStatus( KtServiceStatusHandle, &KtServiceStatus );
|
|
dwReturn = NO_ERROR;
|
|
break;
|
|
|
|
case SERVICE_CONTROL_PARAMCHANGE:
|
|
case SERVICE_CONTROL_NETBINDADD:
|
|
case SERVICE_CONTROL_NETBINDREMOVE:
|
|
case SERVICE_CONTROL_NETBINDENABLE:
|
|
case SERVICE_CONTROL_NETBINDDISABLE:
|
|
case SERVICE_CONTROL_DEVICEEVENT:
|
|
case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
|
|
case SERVICE_CONTROL_POWEREVENT:
|
|
case SERVICE_CONTROL_SESSIONCHANGE:
|
|
DebugLog( DEB_WARN, "%s(%d): Unsupported control recieved.\n", __FILE__, __LINE__ );
|
|
dwReturn = ERROR_CALL_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
default:
|
|
DebugLog( DEB_WARN, "%s(%d): Unhandled case: 0x%x.\n", __FILE__, __LINE__, dwControl );
|
|
dwReturn = ERROR_CALL_NOT_IMPLEMENTED;
|
|
DsysAssert( dwControl == SERVICE_CONTROL_STOP ||
|
|
dwControl == SERVICE_CONTROL_SHUTDOWN ||
|
|
dwControl == SERVICE_CONTROL_PAUSE ||
|
|
dwControl == SERVICE_CONTROL_CONTINUE ||
|
|
dwControl == SERVICE_CONTROL_INTERROGATE ||
|
|
dwControl == SERVICE_CONTROL_DEVICEEVENT ||
|
|
dwControl == SERVICE_CONTROL_HARDWAREPROFILECHANGE ||
|
|
dwControl == SERVICE_CONTROL_POWEREVENT ||
|
|
dwControl == SERVICE_CONTROL_PARAMCHANGE ||
|
|
dwControl == SERVICE_CONTROL_NETBINDADD ||
|
|
dwControl == SERVICE_CONTROL_NETBINDREMOVE ||
|
|
dwControl == SERVICE_CONTROL_NETBINDENABLE ||
|
|
dwControl == SERVICE_CONTROL_NETBINDDISABLE ||
|
|
dwControl == SERVICE_CONTROL_DEVICEEVENT ||
|
|
dwControl == SERVICE_CONTROL_HARDWAREPROFILECHANGE ||
|
|
dwControl == SERVICE_CONTROL_POWEREVENT ||
|
|
dwControl == SERVICE_CONTROL_SESSIONCHANGE );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we're changing state, we need to wake up a service thread
|
|
// to do the actual work, so we post an event to the Completion
|
|
// port with the key KTCK_SERVICE_CONTROL. Since no data transfer
|
|
// actually took place, we can pass the control event in as the
|
|
// "number of bytes transferred" without ambiguity.
|
|
//
|
|
|
|
if( fPostControlToServiceThread )
|
|
{
|
|
BOOL IocpSuccess;
|
|
|
|
IocpSuccess = PostQueuedCompletionStatus( KtIocp,
|
|
dwControl,
|
|
KTCK_SERVICE_CONTROL,
|
|
NULL );
|
|
if( !IocpSuccess )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error posting completion status to I/O completion port: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
dwReturn = GetLastError();
|
|
/* TODO: Event log, possibly something drastic. */
|
|
}
|
|
}
|
|
|
|
return dwReturn;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtStartupInit
|
|
//
|
|
// Synopsis: Does the startup initialization for the service.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value.
|
|
// If initialization fails, KtServiceStatus.dwWin32ErrorCode is set
|
|
// to an appropriate error code, and the service stops.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtStartupInit(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
//
|
|
// Initialize the Service Status structure.
|
|
//
|
|
|
|
KtServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
KtServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
KtServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
|
|
KtServiceStatus.dwWin32ExitCode = NO_ERROR;
|
|
KtServiceStatus.dwServiceSpecificExitCode = 0;
|
|
KtServiceStatus.dwCheckPoint = 0;
|
|
KtServiceStatus.dwWaitHint = 2000;
|
|
|
|
//
|
|
// First thing we need to do is get a handle on our status so we can
|
|
// tell Service Control what we're doing.
|
|
// The SERVICE_STATUS_HANDLE returned by this call need not be closed.
|
|
//
|
|
|
|
DsysAssert(KtServiceStatusHandle == NULL);
|
|
KtServiceStatusHandle = RegisterServiceCtrlHandlerEx( KtServiceName,
|
|
KtControlHandlerEx,
|
|
NULL );
|
|
if( KtServiceStatusHandle == NULL )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error from RegisterServiceCtrlHandlerEx: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Next we create an event (auto reset, initially signalled) that we'll
|
|
// use to make sure we aren't in multiple state transitions simultaneously.
|
|
//
|
|
|
|
DsysAssert(KtStateTransitionLock == NULL );
|
|
KtStateTransitionLock = CreateEvent( NULL,
|
|
FALSE, // NOT manual reset
|
|
TRUE, // initially signalled
|
|
NULL );
|
|
if( KtStateTransitionLock == NULL )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error from CreateEvent: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Now we can signal Service Control that we're starting.
|
|
//
|
|
|
|
if( !KtBeginStateTransition( SERVICE_RUNNING, 2000 ) )
|
|
goto Error;
|
|
|
|
//
|
|
// Create an unassociated I/O Completion port that we'll use to queue
|
|
// events to be serviced.
|
|
//
|
|
|
|
DsysAssert(KtIocp == NULL);
|
|
KtIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
|
|
if( KtIocp == NULL )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error from CreateIoCompletionPort: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Initialize memory routines
|
|
//
|
|
|
|
if( !KtInitMem() )
|
|
goto Error;
|
|
|
|
//
|
|
// Initialize Winsock
|
|
//
|
|
|
|
if( !KtInitWinsock() )
|
|
goto Error;
|
|
|
|
//
|
|
// Initialize WinInet
|
|
//
|
|
|
|
if( !KtInitHttp() )
|
|
goto Error;
|
|
|
|
//
|
|
// Initialize contexts
|
|
//
|
|
|
|
if( !KtInitContexts() )
|
|
goto Error;
|
|
|
|
//
|
|
// Finish bringing up the service.
|
|
//
|
|
|
|
KtContinueService();
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
Error:
|
|
//
|
|
// We couldn't get the resources we needed to start, so punt the service.
|
|
// Note that KtStopService cleans up any resources we may have successfully
|
|
// allocated in this routine.
|
|
//
|
|
|
|
/* TODO: Event log. */
|
|
KtStopService( GetLastError() );
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtContinueService
|
|
//
|
|
// Synopsis: Continues the service. This is not only the opposite of
|
|
// pause, but the second half of the initialization necessary
|
|
// to start the service in the first place.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KtContinueService(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// To continue the service we start listening for new connections.
|
|
//
|
|
|
|
if( !KtStartListening() )
|
|
goto Error;
|
|
|
|
KtFinishStateTransition();
|
|
|
|
return;
|
|
|
|
Error:
|
|
/* TODO: Event log. */
|
|
DebugLog( DEB_ERROR, "%s(%d): Unable to bring up service. Shutting down with error code 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
KtStopService( GetLastError() );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtPauseService
|
|
//
|
|
// Synopsis: Pauses the service.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KtPauseService(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// To pause the service, we stop making new connections, but we can
|
|
// leave any open connections open.
|
|
//
|
|
|
|
KtStopListening();
|
|
KtFinishStateTransition();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtStopService
|
|
//
|
|
// Synopsis: Stops the service.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KtStopService(
|
|
DWORD dwWin32ErrorCode
|
|
)
|
|
{
|
|
//
|
|
// Release all our resources.
|
|
//
|
|
|
|
KtStopListening();
|
|
KtCleanupContexts();
|
|
KtCleanupHttp();
|
|
KtCleanupWinsock();
|
|
KtCleanupMem();
|
|
if( KtStateTransitionLock )
|
|
CloseHandle(KtStateTransitionLock);
|
|
if( KtIocp )
|
|
CloseHandle(KtIocp);
|
|
|
|
//
|
|
// Tell service control that we're done.
|
|
//
|
|
|
|
KtServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
KtServiceStatus.dwCheckPoint = 0;
|
|
KtServiceStatus.dwWaitHint = 0;
|
|
KtServiceStatus.dwWin32ExitCode = dwWin32ErrorCode;
|
|
KtServiceStatus.dwServiceSpecificExitCode = 0;
|
|
|
|
if( KtServiceStatusHandle )
|
|
SetServiceStatus( KtServiceStatusHandle, &KtServiceStatus );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtServiceControlEvent
|
|
//
|
|
// Synopsis: Dispatches control events to the appropriate routine to
|
|
// do the state change.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: dwControl - Code of the control event.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KtServiceControlEvent(
|
|
DWORD dwControl
|
|
)
|
|
{
|
|
switch( dwControl )
|
|
{
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
KtContinueService();
|
|
break;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
KtPauseService();
|
|
break;
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
KtStopService(NO_ERROR);
|
|
break;
|
|
|
|
default:
|
|
DebugLog( DEB_WARN, "%s(%d): Unhandled case: 0x%x.", __FILE__, __LINE__, dwControl );
|
|
DsysAssert( dwControl == SERVICE_CONTROL_CONTINUE ||
|
|
dwControl == SERVICE_CONTROL_PAUSE ||
|
|
dwControl == SERVICE_CONTROL_STOP ||
|
|
dwControl == SERVICE_CONTROL_SHUTDOWN );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtMainServiceEntry
|
|
//
|
|
// Synopsis: This initializes the service, then acts as the main service
|
|
// thread.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID WINAPI
|
|
KtMainServiceEntry(
|
|
IN DWORD argc,
|
|
IN LPTSTR argv[]
|
|
)
|
|
{
|
|
//
|
|
// Do startup initialization.
|
|
//
|
|
|
|
if( !KtStartupInit() )
|
|
goto Cleanup;
|
|
|
|
//
|
|
// Do our thing.
|
|
//
|
|
|
|
KtThreadCore();
|
|
|
|
Cleanup:
|
|
ExitProcess( KtServiceStatus.dwWin32ExitCode );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtIsStopped
|
|
//
|
|
// Synopsis: Determines whether the service is still running.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE if the service has stopped, FALSE otherwise.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtIsStopped(
|
|
VOID
|
|
)
|
|
{
|
|
return (KtServiceStatus.dwCurrentState == SERVICE_STOPPED);
|
|
}
|