/*++

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 ***********************/