/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    MAIN.C

Abstract:

    This is the main routine for the TCP/IP Services.

Author:

    David Treadwell (davidtr)   7-27-93

Revision History:

--*/

//
// INCLUDES
//

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winsvc.h>             // Service control APIs
#include <rpc.h>

#include "tcpsvcs.h"

//
// Service entry points -- thunks to real service entry points.
//

VOID
StartDns (
    IN DWORD argc,
    IN LPTSTR argv[]
    );

VOID
StartSimpTcp (
    IN DWORD argc,
    IN LPTSTR argv[]
    );

VOID
StartDhcpServer (
    IN DWORD argc,
    IN LPTSTR argv[]
    );

VOID
StartFtpSvc (
    IN DWORD argc,
    IN LPTSTR argv[]
    );

VOID
StartLpdSvc (
    IN DWORD argc,
    IN LPTSTR argv[]
    );

VOID
StartBinlSvc (
    IN DWORD argc,
    IN LPTSTR argv[]
    );

//
// Local function used by the above to load and invoke a service DLL.
//

VOID
TcpsvcsStartService (
    IN LPTSTR DllName,
    IN DWORD argc,
    IN LPTSTR argv[]
    );

//
// Used if the services Dll or entry point can't be found
//

VOID
AbortService(
    LPWSTR  ServiceName,
    DWORD   Error
    );

//
// Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
//
// Add new service entries here and in the DLL name list.
//

SERVICE_TABLE_ENTRY TcpServiceDispatchTable[] = {
                        { TEXT("Dns"),            StartDns         },
                        { TEXT("SimpTcp"),        StartSimpTcp     },
                        { TEXT("DhcpServer"),     StartDhcpServer  },
                        { TEXT("FtpSvc"),         StartFtpSvc      },
                        { TEXT("LpdSvc"),         StartLpdSvc      },
                        { TEXT("BinlSvc"),        StartBinlSvc     },
                        { NULL,                   NULL             }
                        };

//
// DLL names for all services.
//

#define DNS_DLL TEXT("dnssvc.dll")
#define SIMPTCP_DLL TEXT("simptcp.dll")
#define DHCP_SERVER_DLL TEXT("dhcpssvc.dll")
#define FTPSVC_DLL TEXT("ftpsvc.dll")
#define LPDSVC_DLL TEXT("lpdsvc.dll")
#define BINLSVC_DLL TEXT("binlsvc.dll")

//
// Global parameter data passed to each service.
//

TCPSVCS_GLOBAL_DATA TcpsvcsGlobalData;

//
// Global parameters to manage RPC server listen.
//

DWORD TcpSvcsGlobalNumRpcListenCalled = 0;
CRITICAL_SECTION TcpsvcsGlobalRpcListenCritSect;


DWORD
TcpsvcStartRpcServerListen(
    VOID
    )
/*++

Routine Description:

    This function starts RpcServerListen for this process. The first
    service that is calling this function will actually start the
    RpcServerListen, subsequent calls are just noted down in num count.

Arguments:

    None.

Return Value:

    None.

--*/
{

    RPC_STATUS Status = RPC_S_OK;

    //
    // LOCK global data.
    //

    EnterCriticalSection( &TcpsvcsGlobalRpcListenCritSect );

    //
    // if this is first RPC service, start RPC server listen.
    //

    if( TcpSvcsGlobalNumRpcListenCalled == 0 ) {

        Status = RpcServerListen(
                    1,                              // minimum num threads.
                    RPC_C_LISTEN_MAX_CALLS_DEFAULT, // max concurrent calls.
                    TRUE );                         // don't wait

    }

    TcpSvcsGlobalNumRpcListenCalled++;

    //
    // UNLOCK global data.
    //

    LeaveCriticalSection( &TcpsvcsGlobalRpcListenCritSect );

    return( Status );
}


DWORD
TcpsvcStopRpcServerListen(
    VOID
    )
/*++

Routine Description:

Arguments:

    None.

Return Value:

    None.

--*/
{
    RPC_STATUS Status = RPC_S_OK;

    //
    // LOCK global data.
    //

    EnterCriticalSection( &TcpsvcsGlobalRpcListenCritSect );

    if( TcpSvcsGlobalNumRpcListenCalled != 0 ) {

        TcpSvcsGlobalNumRpcListenCalled--;

        //
        // if this is last RPC service shutting down, stop RPC server
        // listen.
        //

        if( TcpSvcsGlobalNumRpcListenCalled == 0 ) {

            Status = RpcMgmtStopServerListening(0);

            //
            // wait for all RPC threads to go away.
            //

            if( Status == RPC_S_OK) {
                Status = RpcMgmtWaitServerListen();
            }
        }

    }

    //
    // UNLOCK global data.
    //

    LeaveCriticalSection( &TcpsvcsGlobalRpcListenCritSect );

    return( Status );

}


VOID __cdecl
main(
    VOID
    )

/*++

Routine Description:

    This is the main routine for the LANMan services.  It starts up the
    main thread that is going to handle the control requests from the
    service controller.

    It basically sets up the ControlDispatcher and, on return, exits
    from this main thread.  The call to NetServiceStartCtrlDispatcher
    does not return until all services have terminated, and this process
    can go away.

    The ControlDispatcher thread will start/stop/pause/continue any
    services.  If a service is to be started, it will create a thread
    and then call the main routine of that service.  The "main routine"
    for each service is actually an intermediate function implemented in
    this module that loads the DLL containing the server being started
    and calls its entry point.


Arguments:

    None.

Return Value:

    None.

--*/
{
    //
    //  Disable hard-error popups.
    //

    SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );

    //
    // Initialize Global Data.
    //

    InitializeCriticalSection( &TcpsvcsGlobalRpcListenCritSect );
    TcpSvcsGlobalNumRpcListenCalled = 0;

    TcpsvcsGlobalData.StartRpcServerListen = TcpsvcStartRpcServerListen;
    TcpsvcsGlobalData.StopRpcServerListen = TcpsvcStopRpcServerListen;

    //
    // Call StartServiceCtrlDispatcher to set up the control interface.
    // The API won't return until all services have been terminated. At that
    // point, we just exit.
    //

    if (! StartServiceCtrlDispatcher (
                TcpServiceDispatchTable
                )) {
        //
        // Log an event for failing to start control dispatcher
        //
        DbgPrint("TCPSVCS: Failed to start control dispatcher %lu\n",
                     GetLastError());
    }

    ExitProcess(0);
}


VOID
StartDns (
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This is the thunk routine for the DNS service.  It loads the DLL
    that contains the service and calls its main routine.

Arguments:

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    //
    // Call TcpsvcsStartService to load and run the service.
    //

    TcpsvcsStartService( DNS_DLL, argc, argv );

    return;

} // StartDns


VOID
StartSimpTcp (
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This is the thunk routine for the simple TCP/IP services.  It loads
    the DLL that contains the service and calls its main routine.

Arguments:

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    //
    // Call TcpsvcsStartService to load and run the service.
    //

    TcpsvcsStartService( SIMPTCP_DLL, argc, argv );

    return;

} // StartSimpTcp


VOID
StartDhcpServer (
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This is the thunk routine to start dhcp server services.  It loads
    the DLL that contains the service and calls its main routine.

Arguments:

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    //
    // Call TcpsvcsStartService to load and run the service.
    //

    TcpsvcsStartService( DHCP_SERVER_DLL, argc, argv );

    return;

} // StartDhcpServer



VOID
StartFtpSvc (
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This is the thunk routine for the FTP Server service.  It loads
    the DLL that contains the service and calls its main routine.

Arguments:

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    //
    // Call TcpsvcsStartService to load and run the service.
    //

    TcpsvcsStartService( FTPSVC_DLL, argc, argv );

    return;

} // StartFtpSvc


VOID
StartLpdSvc (
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This is the thunk routine for the LPD Server service.  It loads
    the DLL that contains the service and calls its main routine.

Arguments:

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    //
    // Call TcpsvcsStartService to load and run the service.
    //

    TcpsvcsStartService( LPDSVC_DLL, argc, argv );

    return;

} // StartLdpSvc


VOID
StartBinlSvc (
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This is the thunk routine for the LPD Server service.  It loads
    the DLL that contains the service and calls its main routine.

Arguments:

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    //
    // Call TcpsvcsStartService to load and run the service.
    //

    TcpsvcsStartService( BINLSVC_DLL, argc, argv );

    return;

} // StartBinlSvc


VOID
TcpsvcsStartService (
    IN LPTSTR DllName,
    IN DWORD argc,
    IN LPTSTR argv[]
    )

/*++

Routine Description:

    This routine loads the DLL that contains a service and calls its
    main routine.

Arguments:

    DllName - name of the DLL

    argc, argv - Passed through to the service

Return Value:

    None.

--*/

{
    HMODULE dllHandle;
    PTCPSVCS_SERVICE_DLL_ENTRY serviceEntry;
    BOOL ok;
    DWORD Error;
    TCHAR *FileName;
    TCHAR DllPath[MAX_PATH + 12 + 3];

    ASSERT(lstrlen(DllName) <= 12);

    if (GetSystemDirectory(DllPath, MAX_PATH) == 0) {
        Error = GetLastError();
        DbgPrint("TCPSVCS: Failed to get system directory: %ld\n", Error);
        AbortService(argv[0], Error);
        return;
    }
    lstrcat(DllPath, TEXT("\\"));

    FileName = DllPath + lstrlen(DllPath);

    lstrcpy( FileName, DllName);

    //
    // Load the DLL that contains the service.
    //

    dllHandle = LoadLibrary( DllPath );
    if ( dllHandle == NULL ) {
        Error = GetLastError();
        DbgPrint("TCPSVCS: Failed to load DLL %ws: %ld\n", DllName, Error);
        AbortService(argv[0], Error);
        return;
    }

    //
    // Get the address of the service's main entry point.  This
    // entry point has a well-known name.
    //

    serviceEntry = (PTCPSVCS_SERVICE_DLL_ENTRY)GetProcAddress(
                                                dllHandle,
                                                TCPSVCS_ENTRY_POINT_STRING
                                                );
    if ( serviceEntry == NULL ) {
        Error = GetLastError();
        DbgPrint("TCPSVCS: Can't find entry %s in DLL %ws: %ld\n",
                     TCPSVCS_ENTRY_POINT_STRING, DllName, Error);
        AbortService(argv[0], Error);
    } else {

        //
        // Call the service's main entry point.  This call doesn't return
        // until the service exits.
        //

        serviceEntry( argc, argv, &TcpsvcsGlobalData );

    }

    //
    // Wait for the control dispatcher routine to return.  This
    // works around a problem where simptcp was crashing because the
    // FreeLibrary() was happenning before the control routine returned.
    //

    Sleep( 2000 );

    //
    // Unload the DLL.
    //

    ok = FreeLibrary( dllHandle );
    if ( !ok ) {
        DbgPrint("TCPSVCS: Can't unload DLL %ws: %ld\n",
                     DllName, GetLastError());
    }

    return;

} // TcpsvcsStartService


VOID
DummyCtrlHandler(
    DWORD   Opcode
    )
/*++

Routine Description:

    This is a dummy control handler which is only used if we can't load
    a services DLL entry point.  Then we need this so we can send the
    status back to the service controller saying we are stopped, and why.

Arguments:

    OpCode - Ignored

Return Value:

    None.

--*/

{
    return;

} // DummyCtrlHandler


VOID
AbortService(
    LPWSTR  ServiceName,
    DWORD   Error)
/*++

Routine Description:

    This is called if we can't load the entry point for a service.  It
    gets a handle so it can call SetServiceStatus saying we are stopped
    and why.

Arguments:

    ServiceName - the name of the service that couldn't be started
    Error - the reason it couldn't be started

Return Value:

    None.

--*/
{
    SERVICE_STATUS_HANDLE GenericServiceStatusHandle;
    SERVICE_STATUS GenericServiceStatus;

    GenericServiceStatus.dwServiceType        = SERVICE_WIN32;
    GenericServiceStatus.dwCurrentState       = SERVICE_STOPPED;
    GenericServiceStatus.dwControlsAccepted   = SERVICE_CONTROL_STOP;
    GenericServiceStatus.dwCheckPoint         = 0;
    GenericServiceStatus.dwWaitHint           = 0;
    GenericServiceStatus.dwWin32ExitCode      = Error;
    GenericServiceStatus.dwServiceSpecificExitCode = 0;

    GenericServiceStatusHandle = RegisterServiceCtrlHandler(
                ServiceName,
                DummyCtrlHandler);

    if (GenericServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
        DbgPrint("[TCPSVCS] RegisterServiceCtrlHandler failed %d\n",
            GetLastError());
    }
    else if (!SetServiceStatus (GenericServiceStatusHandle,
                &GenericServiceStatus)) {
        DbgPrint("[TCPSVCS] SetServiceStatus error %ld\n", GetLastError());
    }

    return;
}