/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    net\routing\ipx\sap\svcsif.c

Abstract:

    SAP interface with service controller
    (API for standalone SAP agent part of services.exe)

Author:

    Vadim Eydelman  05-15-1995

Revision History:

--*/

#include "sapp.h"

// SAP service status (when operating standalone outside of router)
SERVICE_STATUS_HANDLE   ServiceStatusHandle;
SERVICE_STATUS          ServiceStatus;
volatile HANDLE         ServiceThreadHdl;

VOID
ServiceMain (
    DWORD                   argc,
    LPTSTR                  argv[]
    );


VOID APIENTRY
ResumeServiceThread (
    ULONG_PTR   param
    ) {
    return ;
}


VOID
ServiceHandler (
    DWORD   fdwControl
    ) {
    ASSERT (ServiceStatusHandle!=0);

    EnterCriticalSection (&OperationalStateLock);
    switch (fdwControl) {
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
#if DBG
            DbgPrint ("IPXSAP: Service control stop/shutdown.\n");
#endif
            if (ServiceIfActive) {
                ServiceIfActive = FALSE;
                if (ServiceThreadHdl!=NULL) {
                    BOOL    res;
                    HANDLE  localHdl = ServiceThreadHdl;
                    ServiceThreadHdl = NULL;
#if DBG
                    DbgPrint ("IPXSAP: Resuming service thread.\n");
#endif
                    res = QueueUserAPC (
                                ResumeServiceThread,
                                localHdl,
                                0);
                    ASSERTMSG ("Could not queue APC to service thread ", res);
                    CloseHandle (localHdl);
                    }
                ServiceStatus.dwCheckPoint += 1;
                ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
                }
            else
                Trace (DEBUG_FAILURES,
                        "SAP service has already been told to stop.");
            break;

        case SERVICE_CONTROL_INTERROGATE:
#if DBG
            DbgPrint ("IPXSAP: Service control interrogate.\n");
#endif
            switch (OperationalState) {
                case OPER_STATE_UP:
                    if (ServiceIfActive)
                        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
                    else
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                    break;
                case OPER_STATE_STARTING:
                    if (ServiceIfActive) {
                        ServiceStatus.dwCheckPoint += 1;
                        ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
                        }
                    else
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                    break;
                case OPER_STATE_STOPPING:
                    if (ServiceIfActive)
                            // This is the case when router is being stopped
                            // SAP will be restarted soon
                        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
                    else {
                        ServiceStatus.dwCheckPoint += 1;
                        ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
                        }
                    break;
                case OPER_STATE_DOWN:
                    if (ServiceIfActive) {
                            // This is the case when service is being started
                        ServiceStatus.dwCheckPoint += 1;
                        ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
                        }
                    else
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                    break;
                default:
                    ASSERTMSG ("SAP is in unknown state ", FALSE);
                }
            break;
        default:
            Trace (DEBUG_FAILURES,
                    "Service control handler called with unknown"
                    " or unsupported code %d.", fdwControl);
            break;

        }
#if DBG
    DbgPrint ("IPXSAP: Service control setting current state to %d.\n",
                            ServiceStatus.dwCurrentState);
#endif
    SetServiceStatus (ServiceStatusHandle, &ServiceStatus);
    LeaveCriticalSection (&OperationalStateLock);
    }


/*++
*******************************************************************
        S e r v i c e M a i n
Routine Description:
    Entry point to be called by service controller to start SAP agent
    (when SAP is not part of the router but is a standalone service,
    though running in the router process)
Arguments:
    argc - number of string arguments passed to service
    argv - array of string arguments passed to service
Return Value:
    None
*******************************************************************
--*/
VOID
ServiceMain (
    DWORD   argc,
    LPWSTR  argv[]
    ) {
    DWORD       rc;

    ServiceStatusHandle = RegisterServiceCtrlHandler (
                            TEXT("nwsapagent"), ServiceHandler);
    if (!ServiceStatusHandle)
    {
        return;
    }

    ServiceStatus.dwServiceType  = SERVICE_WIN32_SHARE_PROCESS;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    SetServiceStatus (ServiceStatusHandle, &ServiceStatus);

    rc = DuplicateHandle (GetCurrentProcess (),
                            GetCurrentThread (),
                            GetCurrentProcess (),
                            (HANDLE *)&ServiceThreadHdl,
                            0,
                            FALSE,
                            DUPLICATE_SAME_ACCESS );
    ASSERTMSG ("Could not duplicate service thread handle ", rc);

    EnterCriticalSection (&OperationalStateLock);
    ServiceIfActive = TRUE;

    if (RouterIfActive) {
        ServiceStatus.dwCurrentState     = SERVICE_RUNNING;
        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
                                         | SERVICE_ACCEPT_SHUTDOWN;
        SetServiceStatus (ServiceStatusHandle, &ServiceStatus);

        LeaveCriticalSection (&OperationalStateLock);
#if DBG
        DbgPrint ("IPXSAP: Suspending service thread.\n");
#endif
        while (ServiceThreadHdl!=NULL) {
            SleepEx (INFINITE, TRUE);
#if DBG
            DbgPrint ("IPXSAP: Service thread awakened.\n");
#endif
            }
#if DBG
        DbgPrint ("IPXSAP: Service thread resumed.\n");
#endif
        EnterCriticalSection (&OperationalStateLock);
        if (!RouterIfActive) {
            if (OperationalState==OPER_STATE_UP)
                StopSAP ();
            LeaveCriticalSection (&OperationalStateLock);
#if DBG
            DbgPrint ("IPXSAP: Waiting for main thread to exit.\n");
#endif
            rc = WaitForSingleObject (MainThreadHdl, INFINITE);
            ASSERTMSG ("Unexpected result from wait for sap main thread ",
                                    rc== WAIT_OBJECT_0);
            EnterCriticalSection (&OperationalStateLock);
            CloseHandle (MainThreadHdl);
            MainThreadHdl = NULL;
            }
        }
    else {
        BOOL bInternalNetNumOk;

        // [pmay]
        // We use this scheme to automatically select the internal network
        // number of the machine we're running on.  If the net number is configured
        // as zero, this function will automatically select a random net num and
        // verify it's uniqueness on the net that this machine is attached to.
        DbgInitialize (hDLLInstance);
        if (AutoValidateInternalNetNum(&bInternalNetNumOk, DEBUG_ADAPTERS) == NO_ERROR) {
            if (!bInternalNetNumOk) {
                if (PnpAutoSelectInternalNetNumber(DEBUG_ADAPTERS) != NO_ERROR)
                    Trace(DEBUG_ADAPTERS, "StartRouter: Auto selection of net number failed.");
                else
                    AutoWaitForValidIntNetNum (10, NULL);
            }
        }

        ServiceStatus.dwWin32ExitCode = CreateAllComponents (NULL);
        if (ServiceStatus.dwWin32ExitCode==NO_ERROR) {
            // We use the thread that we were launched in
            // as IO thread
            ServiceStatus.dwWin32ExitCode = StartSAP ();
            if (ServiceStatus.dwWin32ExitCode==NO_ERROR) {
                LeaveCriticalSection (&OperationalStateLock);

                ServiceStatus.dwCurrentState     = SERVICE_RUNNING;
                ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
                                                 | SERVICE_ACCEPT_SHUTDOWN;
                SetServiceStatus (ServiceStatusHandle, &ServiceStatus);

#if DBG
                DbgPrint ("IPXSAP: Suspending service thread.\n");
#endif
                while (ServiceThreadHdl!=NULL) {
                    SleepEx (INFINITE, TRUE);
#if DBG
                    DbgPrint ("IPXSAP: Service thread awakened.\n");
#endif
                    }
#if DBG
                DbgPrint ("IPXSAP: Service thread resumed.\n");
#endif

                EnterCriticalSection (&OperationalStateLock);
                ServiceIfActive = FALSE;
                if (!RouterIfActive) {
                    if (OperationalState==OPER_STATE_UP)
                        StopSAP ();
                    LeaveCriticalSection (&OperationalStateLock);
#if DBG
                    DbgPrint ("IPXSAP: Waiting for main thread to exit.\n");
#endif
                    rc = WaitForSingleObject (MainThreadHdl, INFINITE);
                    ASSERTMSG ("Unexpected result from wait for sap main thread ",
                                            rc== WAIT_OBJECT_0);
                    EnterCriticalSection (&OperationalStateLock);
                    }
                CloseHandle (MainThreadHdl);
                MainThreadHdl = NULL;
                }
            else {
                DeleteAllComponents ();
                }
            }
        }
    ServiceIfActive = FALSE;
    LeaveCriticalSection (&OperationalStateLock);

#if DBG
    DbgPrint ("IPXSAP: Service stopped.\n");
#endif
    ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus (ServiceStatusHandle, &ServiceStatus);
    }