691 lines
14 KiB
C++
691 lines
14 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 1997 Microsoft Corporation
|
||
|
|
||
|
Module Name :
|
||
|
|
||
|
svcinfo.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains the common code for the sti services which involves the
|
||
|
Service Controller dispatch functions.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Vlad Sadovsky (vlads) 22-Sep-1997
|
||
|
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
User Mode - Win32
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
22-Sep-1997 VladS created
|
||
|
|
||
|
--*/
|
||
|
|
||
|
//
|
||
|
// Include Headers
|
||
|
//
|
||
|
|
||
|
#include "cplusinc.h"
|
||
|
#include "sticomm.h"
|
||
|
|
||
|
#include <svcinfo.h>
|
||
|
|
||
|
BOOL g_fIgnoreSC = TRUE;
|
||
|
|
||
|
SVC_INFO::SVC_INFO(
|
||
|
IN LPCTSTR lpszServiceName,
|
||
|
IN TCHAR * lpszModuleName,
|
||
|
IN PFN_SERVICE_SPECIFIC_INITIALIZE pfnInitialize,
|
||
|
IN PFN_SERVICE_SPECIFIC_CLEANUP pfnCleanup,
|
||
|
IN PFN_SERVICE_SPECIFIC_PNPPWRHANDLER pfnPnpPower
|
||
|
)
|
||
|
/*++
|
||
|
Desrcription:
|
||
|
|
||
|
Contructor for SVC_INFO class.
|
||
|
This constructs a new service info object for the service specified.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
lpszServiceName
|
||
|
name of the service to be created.
|
||
|
|
||
|
lpszModuleName
|
||
|
name of the module for loading string resources.
|
||
|
|
||
|
pfnInitialize
|
||
|
pointer to function to be called for initialization of
|
||
|
service specific data
|
||
|
|
||
|
pfnCleanup
|
||
|
pointer to function to be called for cleanup of service
|
||
|
specific data
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
ASSERT( pfnInitialize != NULL && pfnCleanup != NULL && pfnPnpPower!=NULL);
|
||
|
|
||
|
m_sServiceName.Copy(lpszServiceName) ;
|
||
|
m_sModuleName.Copy(lpszModuleName);
|
||
|
|
||
|
//
|
||
|
// Initialize the service status structure.
|
||
|
//
|
||
|
|
||
|
m_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
||
|
m_svcStatus.dwCurrentState = SERVICE_STOPPED;
|
||
|
m_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
|
||
|
| SERVICE_ACCEPT_PAUSE_CONTINUE
|
||
|
| SERVICE_ACCEPT_SHUTDOWN;
|
||
|
m_svcStatus.dwWin32ExitCode = NO_ERROR;
|
||
|
m_svcStatus.dwServiceSpecificExitCode = NO_ERROR;
|
||
|
m_svcStatus.dwCheckPoint = 0;
|
||
|
m_svcStatus.dwWaitHint = 0;
|
||
|
|
||
|
//
|
||
|
// Initialize Call back functions
|
||
|
//
|
||
|
|
||
|
m_pfnInitialize = pfnInitialize;
|
||
|
m_pfnCleanup = pfnCleanup;
|
||
|
m_pfnPnpPower = pfnPnpPower;
|
||
|
|
||
|
m_dwSignature = SIGNATURE_SVC;
|
||
|
|
||
|
m_hShutdownEvent= NULL;
|
||
|
|
||
|
return;
|
||
|
|
||
|
} // SVC_INFO::SVC_INFO()
|
||
|
|
||
|
|
||
|
SVC_INFO::~SVC_INFO( VOID)
|
||
|
/*++
|
||
|
|
||
|
Description:
|
||
|
|
||
|
Cleanup the SvcInfo object. If the service is not already
|
||
|
terminated, it terminates the service before cleanup.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if ( m_hShutdownEvent != NULL) {
|
||
|
|
||
|
::CloseHandle( m_hShutdownEvent);
|
||
|
}
|
||
|
|
||
|
m_dwSignature = SIGNATURE_SVC_FREE;
|
||
|
|
||
|
} // SVC_INFO::~SVC_INFO()
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
SVC_INFO::StartServiceOperation(
|
||
|
IN PFN_SERVICE_CTRL_HANDLER pfnCtrlHandler
|
||
|
)
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
Starts the operation of service instantiated in the given
|
||
|
Service Info Object.
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pfnCtrlHandler
|
||
|
pointer to a callback function for handling dispatch of
|
||
|
service controller requests. A separate function is required
|
||
|
since Service Controller call back function does not send
|
||
|
context information.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
NO_ERROR on success and Win32 error code if any failure.
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
DWORD err;
|
||
|
DWORD cbBuffer;
|
||
|
BOOL fInitCalled = FALSE;
|
||
|
|
||
|
if ( !IsValid()) {
|
||
|
|
||
|
//
|
||
|
// Not successfully initialized.
|
||
|
//
|
||
|
|
||
|
return ( ERROR_INVALID_FUNCTION);
|
||
|
}
|
||
|
|
||
|
if ( !g_fIgnoreSC ) {
|
||
|
|
||
|
m_hsvcStatus = RegisterServiceCtrlHandler(
|
||
|
QueryServiceName(),
|
||
|
pfnCtrlHandler
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Register the Control Handler routine.
|
||
|
//
|
||
|
|
||
|
if( m_hsvcStatus == NULL_SERVICE_STATUS_HANDLE ) {
|
||
|
|
||
|
err = GetLastError();
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
} else {
|
||
|
m_hsvcStatus = NULL_SERVICE_STATUS_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the service status.
|
||
|
//
|
||
|
|
||
|
err = UpdateServiceStatus( SERVICE_START_PENDING,
|
||
|
NO_ERROR,
|
||
|
1,
|
||
|
SERVICE_START_WAIT_HINT );
|
||
|
|
||
|
if( err != NO_ERROR ) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the service common components
|
||
|
//
|
||
|
#ifdef BUGBUG
|
||
|
if ( !InitializeNTSecurity()) {
|
||
|
err = GetLastError();
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Initialize the various service specific components.
|
||
|
//
|
||
|
|
||
|
err = ( *m_pfnInitialize)( this);
|
||
|
fInitCalled = TRUE;
|
||
|
|
||
|
if( err != NO_ERROR ) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create shutdown event.
|
||
|
//
|
||
|
|
||
|
m_hShutdownEvent = CreateEvent( NULL, // lpsaSecurity
|
||
|
TRUE, // fManualReset
|
||
|
FALSE, // fInitialState
|
||
|
NULL ); // lpszEventName
|
||
|
|
||
|
if( m_hShutdownEvent == NULL )
|
||
|
{
|
||
|
err = GetLastError();
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Update the service status.
|
||
|
//
|
||
|
|
||
|
err = UpdateServiceStatus( SERVICE_RUNNING,
|
||
|
NO_ERROR,
|
||
|
0,
|
||
|
0 );
|
||
|
|
||
|
if( err != NO_ERROR ) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Wait for the shutdown event.
|
||
|
//
|
||
|
|
||
|
err = WaitForSingleObject( m_hShutdownEvent,
|
||
|
INFINITE );
|
||
|
|
||
|
if ( err != WAIT_OBJECT_0) {
|
||
|
|
||
|
//
|
||
|
// Error. Unable to wait for single object.
|
||
|
//
|
||
|
}
|
||
|
//
|
||
|
// Stop time. Tell the Service Controller that we're stopping,
|
||
|
// then terminate the various service components.
|
||
|
//
|
||
|
|
||
|
UpdateServiceStatus( SERVICE_STOP_PENDING,
|
||
|
0,
|
||
|
1,
|
||
|
SERVICE_STOP_WAIT_HINT );
|
||
|
|
||
|
|
||
|
//
|
||
|
// Destroy the shutdown event.
|
||
|
//
|
||
|
|
||
|
if( m_hShutdownEvent != NULL ) {
|
||
|
|
||
|
if ( ! CloseHandle( m_hShutdownEvent ) ) {
|
||
|
|
||
|
err = GetLastError();
|
||
|
}
|
||
|
|
||
|
m_hShutdownEvent = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the service status.
|
||
|
//
|
||
|
//
|
||
|
// Log successful start
|
||
|
|
||
|
err = UpdateServiceStatus( SERVICE_RUNNING,
|
||
|
NO_ERROR,
|
||
|
0,
|
||
|
0 );
|
||
|
|
||
|
if( err != NO_ERROR )
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
if ( fInitCalled) {
|
||
|
//
|
||
|
// Cleanup partially initialized modules
|
||
|
//
|
||
|
DWORD err1 = ( *m_pfnCleanup)( this);
|
||
|
|
||
|
if ( err1 != NO_ERROR) {
|
||
|
//
|
||
|
// Compound errors possible
|
||
|
//
|
||
|
if ( err != NO_ERROR) {
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we managed to actually connect to the Service Controller,
|
||
|
// then tell it that we're stopped.
|
||
|
//
|
||
|
|
||
|
if ( m_hsvcStatus != NULL_SERVICE_STATUS_HANDLE )
|
||
|
{
|
||
|
UpdateServiceStatus( SERVICE_STOPPED,
|
||
|
err,
|
||
|
0,
|
||
|
0 );
|
||
|
}
|
||
|
|
||
|
return ( err);
|
||
|
|
||
|
} // SVC_INFO::StartServiceOperation()
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
SVC_INFO::UpdateServiceStatus(
|
||
|
IN DWORD dwState,
|
||
|
IN DWORD dwWin32ExitCode,
|
||
|
IN DWORD dwCheckPoint,
|
||
|
IN DWORD dwWaitHint )
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
Updates the local copy status of service controller status
|
||
|
and reports it to the service controller.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
dwState - New service state.
|
||
|
|
||
|
dwWin32ExitCode - Service exit code.
|
||
|
|
||
|
dwCheckPoint - Check point for lengthy state transitions.
|
||
|
|
||
|
dwWaitHint - Wait hint for lengthy state transitions.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
NO_ERROR on success and returns Win32 error if failure.
|
||
|
On success the status is reported to service controller.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
m_svcStatus.dwCurrentState = dwState;
|
||
|
m_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
|
||
|
m_svcStatus.dwCheckPoint = dwCheckPoint;
|
||
|
m_svcStatus.dwWaitHint = dwWaitHint;
|
||
|
|
||
|
if ( !g_fIgnoreSC ) {
|
||
|
|
||
|
return ReportServiceStatus();
|
||
|
|
||
|
} else {
|
||
|
|
||
|
return ( NO_ERROR);
|
||
|
}
|
||
|
|
||
|
} // SVC_INFO::UpdateServiceStatus()
|
||
|
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
SVC_INFO::ReportServiceStatus( VOID)
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
Wraps the call to SetServiceStatus() function.
|
||
|
Prints the service status data if need be
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
NO_ERROR if successful. other Win32 error code on failure.
|
||
|
If successfull the new status has been reported to the service
|
||
|
controller.
|
||
|
--*/
|
||
|
{
|
||
|
DWORD err = NO_ERROR;
|
||
|
|
||
|
if ( !g_fIgnoreSC ) {
|
||
|
|
||
|
if( !SetServiceStatus( m_hsvcStatus, &m_svcStatus ) ) {
|
||
|
|
||
|
err = GetLastError();
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
err = NO_ERROR;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
} // SVC_INFO::ReportServiceStatus()
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SVC_INFO::ServiceCtrlHandler ( IN DWORD dwOpCode)
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
This function received control requests from the service controller.
|
||
|
It runs in the context of service controller's dispatcher thread and
|
||
|
performs the requested function.
|
||
|
( Note: Avoid time consuming operations in this function.)
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
dwOpCode
|
||
|
indicates the requested operation. This should be
|
||
|
one of the SERVICE_CONTROL_* manifests.
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
None. If successful, then the state of the service might be changed.
|
||
|
|
||
|
Note:
|
||
|
if an operation ( especially SERVICE_CONTROL_STOP) is very lengthy,
|
||
|
then this routine should report a STOP_PENDING status and create
|
||
|
a worker thread to do the dirty work. The worker thread would then
|
||
|
perform the necessary work and for reporting timely wait hints and
|
||
|
final SERVICE_STOPPED status.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Interpret the opcode.
|
||
|
//
|
||
|
|
||
|
switch( dwOpCode )
|
||
|
{
|
||
|
case SERVICE_CONTROL_INTERROGATE :
|
||
|
InterrogateService();
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_STOP :
|
||
|
StopService();
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_PAUSE :
|
||
|
PauseService();
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_CONTINUE :
|
||
|
ContinueService();
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_SHUTDOWN :
|
||
|
ShutdownService();
|
||
|
break;
|
||
|
|
||
|
default :
|
||
|
ASSERTSZ(FALSE,TEXT("Unrecognized Service Opcode"));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Report the current service status back to the Service
|
||
|
// Controller. The workers called to implement the OpCodes
|
||
|
// should set the m_svcStatus.dwCurrentState field if
|
||
|
// the service status changed.
|
||
|
//
|
||
|
|
||
|
ReportServiceStatus();
|
||
|
|
||
|
} // SVC_INFO::ServiceCtrlHandler()
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SVC_INFO::InterrogateService( VOID )
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
This function interrogates with the service status.
|
||
|
Actually, nothing needs to be done here; the
|
||
|
status is always updated after a service control.
|
||
|
We have this function here to provide useful
|
||
|
debug info.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
return;
|
||
|
|
||
|
} // SVC_INFO::InterrogateService()
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SVC_INFO::StopService( VOID )
|
||
|
/*++
|
||
|
Description:
|
||
|
Stops the service. If the stop cannot be performed in a
|
||
|
timely manner, a worker thread needs to be created to do the
|
||
|
original cleanup work.
|
||
|
|
||
|
Returns:
|
||
|
None. If successful, then the service will be stopped.
|
||
|
The final action of this function is signal the handle for
|
||
|
shutdown event. This will release the main thread which does
|
||
|
necessary cleanup work.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
m_svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
|
m_svcStatus.dwCheckPoint = 0;
|
||
|
|
||
|
SetEvent( m_hShutdownEvent);
|
||
|
|
||
|
return;
|
||
|
} // SVC_INFO::StopService()
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SVC_INFO::PauseService( VOID )
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
This function pauses the service. When the service is paused,
|
||
|
no new user sessions are to be accepted, but existing connections
|
||
|
are not effected.
|
||
|
|
||
|
This function must update the SERVICE_STATUS::dwCurrentState
|
||
|
field before returning.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
None. If successful the service is paused.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
m_svcStatus.dwCurrentState = SERVICE_PAUSED;
|
||
|
|
||
|
return;
|
||
|
} // SVC_INFO::PauseService()
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SVC_INFO::ContinueService( VOID )
|
||
|
/*++
|
||
|
|
||
|
Description:
|
||
|
This function restarts ( continues) a paused service. This
|
||
|
will return the service to the running state.
|
||
|
|
||
|
This function must update the m_svcStatus.dwCurrentState
|
||
|
field to running mode before returning.
|
||
|
|
||
|
Returns:
|
||
|
None. If successful then the service is running.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
m_svcStatus.dwCurrentState = SERVICE_RUNNING;
|
||
|
|
||
|
return;
|
||
|
} // SVC_INFO::ContinueService()
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SVC_INFO::ShutdownService( VOID )
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
This function performs the shutdown on a service.
|
||
|
This is called during system shutdown.
|
||
|
|
||
|
This function is time constrained. The service controller gives a
|
||
|
maximum of 20 seconds for shutdown for all active services.
|
||
|
Only timely operations should be performed in this function.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
None. If successful, the service is shutdown.
|
||
|
--*/
|
||
|
{
|
||
|
DWORD dwCurrentState;
|
||
|
|
||
|
//
|
||
|
// Verify state of the service
|
||
|
//
|
||
|
dwCurrentState = QueryCurrentServiceState();
|
||
|
|
||
|
if ((dwCurrentState !=SERVICE_PAUSED) &&
|
||
|
(dwCurrentState !=SERVICE_RUNNING) ) {
|
||
|
|
||
|
ASSERT( FALSE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
|
m_svcStatus.dwCheckPoint = 0;
|
||
|
|
||
|
SetEvent( m_hShutdownEvent);
|
||
|
|
||
|
//
|
||
|
// Stop time. Tell the Service Controller that we're stopping,
|
||
|
// then terminate the various service components.
|
||
|
//
|
||
|
|
||
|
UpdateServiceStatus( SERVICE_STOP_PENDING,
|
||
|
0,
|
||
|
1,
|
||
|
SERVICE_STOP_WAIT_HINT );
|
||
|
|
||
|
|
||
|
DWORD err = ( *m_pfnCleanup)( this);
|
||
|
|
||
|
UpdateServiceStatus( SERVICE_STOPPED,
|
||
|
err,
|
||
|
0,
|
||
|
0 );
|
||
|
|
||
|
return;
|
||
|
|
||
|
} // SVC_INFO::ShutdownService()
|
||
|
|
||
|
|
||
|
//
|
||
|
// IUnknown methods. Used only for reference counting
|
||
|
//
|
||
|
STDMETHODIMP
|
||
|
SVC_INFO::QueryInterface( REFIID riid, LPVOID * ppvObj)
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG)
|
||
|
SVC_INFO::AddRef( void)
|
||
|
{
|
||
|
::InterlockedIncrement(&m_cRef);
|
||
|
return m_cRef;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG)
|
||
|
SVC_INFO::Release( void)
|
||
|
{
|
||
|
LONG cNew;
|
||
|
|
||
|
if(!(cNew = ::InterlockedDecrement(&m_cRef))) {
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
return cNew;
|
||
|
}
|
||
|
|
||
|
/************************ End of File ***********************/
|
||
|
|