/**********************************************************************/
/**                       Microsoft Windows NT                       **/
/**                Copyright(c) Microsoft Corp., 1995                **/
/**********************************************************************/

/*
    main.cxx

    This module contains the main startup code for a Gibraltar Service.
    
    Since gibraltar service startup is a common operation, this source
    code will be shared with all services.

    To do this, you only need to set certain #defines to your specific service.

    See %MSNROOT%\apps\mail\[pop3,smtp]\server\main.cxx for examples.

    FILE HISTORY:
        KeithMo     07-Mar-1993     Created.
        rkamicar    20-Dec-1995     Modified for sharing
*/

//
//  Private globals.
//

DEFINE_TSVC_INFO_INTERFACE();
DECLARE_DEBUG_PRINTS_OBJECT( );
DECLARE_DEBUG_VARIABLE( );

#define INITIALIZE_IPC          0x00000001
#define INITIALIZE_SOCKETS      0x00000002
#define INITIALIZE_ACCESS       0x00000004
#define INITIALIZE_SERVICE      0x00000008
#define INITIALIZE_CONNECTIONS  0x00000010
#define INITIALIZE_DISCOVERY    0x00000020

DWORD GlobalInitializeStatus = 0;
BOOL ServiceBooted = FALSE;

//
//  Global startup named event
//
HANDLE          ghStartupEvent = INVALID_HANDLE_VALUE;

//
//
// Shared TCPSVCS.EXE data
//

PTCPSVCS_GLOBAL_DATA pTcpsvcsGlobalData;

//
//  Private prototypes.
//

APIERR InitializeService( LPVOID pContext );
APIERR TerminateService( LPVOID pContext );

//+---------------------------------------------------------------------------
//
//  Function:
//
//      DllEntryPoint
//
//  Synopsis:
//  Arguments:
//  Returns:
//      See Win32 SDK
//
//  History:
//
//      Richard Kamicar     (rkamicar)              5 January 1996
//
//  Notes:
//
//      If we find we need this per service, we can move it out of here..
//
//----------------------------------------------------------------------------
BOOL WINAPI
DllEntryPoint(HINSTANCE hInst, DWORD dwReason, LPVOID lpvContext)
{
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:

        //
        // To help performance, cancel thread attach and detach notifications
        //
        DisableThreadLibraryCalls((HMODULE) hInst);

        break;

    case DLL_PROCESS_DETACH:

         if( ghStartupEvent != INVALID_HANDLE_VALUE ) 
         {
            _VERIFY( CloseHandle( ghStartupEvent ) );
         }

        break;

    case DLL_THREAD_ATTACH:
        break;

    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}

BOOL WINAPI DllMain (HANDLE hInst, ULONG dwReason, LPVOID lpvReserve)
{
  return DllEntryPoint((HINSTANCE) hInst, dwReason, lpvReserve);
}

//
//  Public functions.
//

/*******************************************************************

    NAME:       ServiceEntry

    SYNOPSIS:   This is the "real" entrypoint for the service.  When
                the Service Controller dispatcher is requested to
                start a service, it creates a thread that will begin
                executing this routine.

    ENTRY:      cArgs - Number of command line arguments to this service.

                pArgs - Pointers to the command line arguments.

                pGlobalData - Points to global data shared amongst all
                    services that live in TCPSVCS.EXE.

    EXIT:       Does not return until service is stopped.

    HISTORY:
        KeithMo     07-Mar-1993 Created.
        KeithMo     07-Jan-1994 Modified for use as a DLL.

********************************************************************/
VOID ServiceEntry( DWORD                cArgs,
                   LPWSTR               pArgs[],
                   PTCPSVCS_GLOBAL_DATA pGlobalData )
{
    APIERR err = NO_ERROR;

    InitAsyncTrace();
    TraceQuietEnter( "ServiceEntry");

    //
    //  Save the global data pointer.
    //

    pTcpsvcsGlobalData = pGlobalData;

    //
    //  Initialize the service status structure.
    //

    DebugTrace( 0, "new TSVC_INFO( %s)", XXX_SERVICE_NAME);

    g_pTsvcInfo = new TSVC_INFO( XXX_SERVICE_NAME,
                                 XXX_MODULE_NAME,
                                 XXX_PARAMETERS_KEY_A,
                                 XXX_ANONYMOUS_SECRET_W,
                                 XXX_ROOT_SECRET_W,
                                 XXX_INET,
                                 InitializeService,
                                 TerminateService );

    //
    //  If we couldn't allocate memory for the service info struct, then the
    //  machine is really hosed -- we can't even log..
    //
    if (!g_pTsvcInfo || !g_pTsvcInfo->IsValid())
    {
        FatalTrace( 0, "new TSVC_INFO( %s) failed: %x",
            XXX_SERVICE_NAME, g_pTsvcInfo);
        if (g_pTsvcInfo != NULL) 
        {
            delete g_pTsvcInfo;
            g_pTsvcInfo = NULL;
        }
        goto out;
    }
    //
    // save the global pointer for rpc thread
    //
    g_pTsvcInfo->SetTcpsvcsGlobalData( pTcpsvcsGlobalData);
    //
    //  This blocks until the service is shutdown
    //
    err = g_pTsvcInfo->StartServiceOperation( SERVICE_CTRL_HANDLER() );

    delete g_pTsvcInfo;
    g_pTsvcInfo = NULL;

out:    
    TermAsyncTrace( );

}   // ServiceEntry

//
//  Private functions.
//


/*******************************************************************

    NAME:       InitializeService

    SYNOPSIS:   Initializes the various W3 Service components.

    EXIT:       If successful, then every component has been
                successfully initialized.

    RETURNS:    APIERR - NO_ERROR if successful, otherwise a Win32
                    status code.

    HISTORY:
        KeithMo     07-Mar-1993 Created.

********************************************************************/
APIERR
InitializeService( LPVOID pContext )
{
    APIERR err;
    LPTSVC_INFO ptsi = (LPTSVC_INFO ) pContext;

    TraceFunctEnter("InitializeService");

    //
    //  Create a startup named event. If this already exists, refuse to boot !
    //
    HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, XXX_NAMED_EVENT);
    if( !hEvent || GetLastError() != 0 ) 
    {

        if( hEvent) 
        {
            _VERIFY( CloseHandle( hEvent ) );
        }

        g_pTsvcInfo->LogEvent(
                XXX_BOOT_ERROR,
                0,
                (const CHAR **)NULL,
                ERROR_SERVICE_ALREADY_RUNNING
                );

        return ERROR_SERVICE_ALREADY_RUNNING ;
    }

    //  set the global startup event. this is closed when our DLL_PROCESS_DETACH
    ghStartupEvent = hEvent;
    ServiceBooted  = TRUE;

    g_pTsvcInfo->LogEvent(
                XXX_EVENT_SERVICE_STARTED,
                0,
                (const CHAR **)NULL,
                0
                );

    //
    //  Initialize various components.  The ordering of the
    //  components is somewhat limited.  Globals should be
    //  initialized first, then the event logger.  After
    //  the event logger is initialized, the other components
    //  may be initialized in any order with one exception.
    //  InitializeSockets must be the last initialization
    //  routine called.  It kicks off the main socket connection
    //  thread.
    //

     if(( err = InitializeGlobals()))
     {
        FatalTrace( 0, "InitializeGlobals failed, err=%d.", err); 
        TraceFunctLeave();
        return err;
     }

    if( (err = ptsi->InitializeIpc( (UCHAR *) "ncacn_np",
                                     (UCHAR *) XXX_NAMED_PIPE,
                                     XXX_ServerIfHandle)) != NO_ERROR)
    {
        FatalTrace( 0, "InitializeIpc failed, err=%d.", err); 
        TraceFunctLeave();
        return err;
    }

    GlobalInitializeStatus |= INITIALIZE_IPC;

    if((err = g_pTsvcInfo->InitializeDiscovery( NULL)))
    {
        FatalTrace( 0, "InitializeDiscovery failed, err=%d.", err); 
        TraceFunctLeave();
        return err;
    }

    GlobalInitializeStatus |= INITIALIZE_DISCOVERY;

    if((err = InitializeSockets()))
    {
        FatalTrace( 0, "InitializeSockets failed, err=%d.", err); 
        TraceFunctLeave();
        return err;
    }

    GlobalInitializeStatus |= INITIALIZE_SOCKETS;
 
    //
    //  InitializeConnection
    //

    if ( !g_pTsvcInfo->InitializeConnections(
                                        &XXX_OnConnect,
                                        &XXX_OnConnectEx,
                                        &XXX_Completion,
                                        XXX_SECURE_PORT,
                                        0 ))
    {

        err = GetLastError();
        g_pTsvcInfo->LogEvent(
                    XXX_EVENT_CANNOT_INITIALIZE_WINSOCK,
                    0,
                    (const CHAR **)NULL,
                    err
                    );

        ErrorTrace(0,"InitializeConnections failed, error %d",err );
        return err;
    }

     GlobalInitializeStatus |= INITIALIZE_CONNECTIONS;

    //
    //  Success!
    //

    TraceFunctLeave();
    
    return NO_ERROR;

}   // InitializeService

/*******************************************************************

    NAME:       TerminateService

    SYNOPSIS:   Terminates the various W3 Service components.

    EXIT:       If successful, then every component has been
                successfully terminated.

    HISTORY:
        KeithMo     07-Mar-1993 Created.

********************************************************************/
APIERR TerminateService( LPVOID pContext )
{
    LPTSVC_INFO ptsi = (LPTSVC_INFO ) pContext;
    DWORD err;

    TraceFunctEnter("TerminateService");

    if(!ServiceBooted)
    {
        return NO_ERROR;    
    }

    ServiceBooted = FALSE;

    _ASSERT(ptsi == g_pTsvcInfo);
    
    //
    //  Components should be terminated in reverse
    //  initialization order.
    //

    //
    // must happen after CleanupConnections so no new conns accepted
    //
    if ( GlobalInitializeStatus & INITIALIZE_CONNECTIONS)
    {
        g_pTsvcInfo->CleanupConnections();
    }

    if (XXX_g_Config != NULL)
    {
        XXX_g_Config->DisconnectAllConnections();
    }
    
    if ( GlobalInitializeStatus & INITIALIZE_SOCKETS) 
    {
        TerminateSockets();
    }

    if ( GlobalInitializeStatus & INITIALIZE_DISCOVERY) 
    {
        if ( (err = ptsi->TerminateDiscovery()) != NO_ERROR)
        {
            ErrorTrace(0, "TerminateDiscovery() failed. Error = %u", err);
        }
    }

    if ( GlobalInitializeStatus & INITIALIZE_IPC)
    {
        if ( (err = ptsi->CleanupIpc( XXX_ServerIfHandle)) != NO_ERROR)
        {
            ErrorTrace(0, "CleanupIpc() failed. Error = %u", err);
        }
    }

    TerminateGlobals();

    g_pTsvcInfo->LogEvent(
                XXX_EVENT_SERVICE_STOPPED,
                0,
                (const CHAR **)NULL,
                0
                );

    TraceFunctLeave();
    return NO_ERROR;

}  // TerminateService