/********************************************************************/
/**               Copyright(c) 1989 Microsoft Corporation.	   **/
/********************************************************************/

//***
//
// Filename: 	main.c
//
// Description: This module contains the main procedure of the AFP server
//		service. It will contain code to initialize and install
//		itself and the kernel-mode AFP Server. It also contains
//		code to respond to the server controller. It will also
//		handle service shutdown.
//
//		??? Does the service controller log start/stop events etc ??
//		    if not log it.
//		
// History:
//		May 11,1990.	NarenG		Created original version.
//
#define DEFINE_AFP_GLOBALS	// This will cause AfpGlobals to be defined.
#include "afpsvcp.h"

// Prototypes of functions used only within this module.
//
VOID
AfpMain(
	IN DWORD 	argc,
	IN LPWSTR * 	lpwsServiceArgs
);

VOID
AfpCleanupAndExit(
	IN DWORD 	dwError
);

VOID
AfpControlResponse(
	IN DWORD 	dwControlCode
);


//**
//
// Call:	main.c
//
// Returns:	none.
//
// Description: Will simply register the entry point of the AFP server
//		service with the service controller. The service controller
//		will capture this thread. It will be freed only when
//		the service is shutdown. At that point we will simply exit
//		the process.
//
void
_cdecl
main( int argc, unsigned char * argv[] )
{
SERVICE_TABLE_ENTRY	AfpServiceDispatchTable[2];

#ifdef DBG

    CONSOLE_SCREEN_BUFFER_INFO csbi;
    COORD coord;
    (VOID)AllocConsole( );
    (VOID)GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE),
                		      &csbi
                 		    );
    coord.X = (SHORT)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
    coord.Y = (SHORT)((csbi.srWindow.Bottom - csbi.srWindow.Top + 1) * 20);
    (VOID)SetConsoleScreenBufferSize( GetStdHandle(STD_OUTPUT_HANDLE),
                		      coord
                		    );
#endif

    AFP_UNREFERENCED( argc );
    AFP_UNREFERENCED( argv );


    AfpServiceDispatchTable[0].lpServiceName = AFP_SERVICE_NAME;
    AfpServiceDispatchTable[0].lpServiceProc = AfpMain;
    AfpServiceDispatchTable[1].lpServiceName = NULL;
    AfpServiceDispatchTable[1].lpServiceProc = NULL;

    if ( !StartServiceCtrlDispatcher( AfpServiceDispatchTable ) )
	AfpLogEvent( AFPLOG_CANT_START, 0, NULL,
		     GetLastError(), EVENTLOG_ERROR_TYPE );

    ExitProcess(0);

}

//**
//
// Call:	AfpMain
//
// Returns:	none.
//
// Description: This is the main procedure for the Afp Server Service. It
//		will be called when the service is supposed to start itself.
//		It will do all service wide initialization.
//
VOID
AfpMain( DWORD	  argc,		// Command line arguments. Will be ignored.
	 LPWSTR * lpwsServiceArgs
)
{
DWORD	dwRetCode;


    AFP_UNREFERENCED( argc );
    AFP_UNREFERENCED( lpwsServiceArgs );

    // NULL out all the globals
    //
    ZeroMemory( (LPBYTE)&AfpGlobals, sizeof(AfpGlobals) );

    // Register the service control handler with the service controller
    //
    AfpGlobals.hServiceStatus = RegisterServiceCtrlHandler(AFP_SERVICE_NAME,
							   AfpControlResponse );

    if ( AfpGlobals.hServiceStatus == (SERVICE_STATUS_HANDLE)0 ) {
	    AfpLogEvent( AFPLOG_CANT_START, 0, NULL,
                    GetLastError(), EVENTLOG_ERROR_TYPE );
	    AfpCleanupAndExit( GetLastError() );
        return;
    }

    AfpGlobals.ServiceStatus.dwServiceType  	      = SERVICE_WIN32;
    AfpGlobals.ServiceStatus.dwCurrentState 	      = SERVICE_START_PENDING;
    AfpGlobals.ServiceStatus.dwControlsAccepted       = 0;
    AfpGlobals.ServiceStatus.dwWin32ExitCode 	      = NO_ERROR;
    AfpGlobals.ServiceStatus.dwServiceSpecificExitCode= 0;
    AfpGlobals.ServiceStatus.dwCheckPoint 	      = 1;
    AfpGlobals.ServiceStatus.dwWaitHint 	      =AFP_SERVICE_INSTALL_TIME;

    AfpAnnounceServiceStatus();

    // Read in registry information and initialize the kernel-mode
    // server. Initialize the server to accept RPC calls. Initialize
    // all global vriables etc.
    //
    if ( dwRetCode = AfpInitialize() )
    {
        if (AfpGlobals.dwServerState & AFPSTATE_BLOCKED_ON_DOMINFO)
        {
	        AfpCleanupAndExit( NO_ERROR );
        }
        else
        {
	        AfpCleanupAndExit( dwRetCode );
        }
        return;
    }


    // Set the MAC bit for NetServerEnum
    //
    if ( !I_ScSetServiceBits( AfpGlobals.hServiceStatus,
			      SV_TYPE_AFP,
			      TRUE,
	                      TRUE,
			      NULL ))
    {

	    dwRetCode = GetLastError();
	    AfpLogEvent( AFPLOG_CANT_START, 0, NULL,
		                    GetLastError(), EVENTLOG_ERROR_TYPE );
        AfpCleanupAndExit( dwRetCode );
        return;
    }

    // now tell the service controller that we are up
    //
    if (AfpGlobals.ServiceStatus.dwCurrentState == SERVICE_START_PENDING)
    {
        AfpGlobals.ServiceStatus.dwCurrentState     = SERVICE_RUNNING;
        AfpGlobals.ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
		    			          SERVICE_ACCEPT_PAUSE_CONTINUE;
        AfpGlobals.ServiceStatus.dwCheckPoint       = 0;
        AfpGlobals.ServiceStatus.dwWaitHint         = 0;

        AfpAnnounceServiceStatus();
    }


    // Start listening for RPC admin client calls. This will block
    // until RpcMgmtStopServerListening is called while processing a
    // STOP_SERVICE control request.
    //
    if ( dwRetCode = RpcServerListen( 1,
				      RPC_C_LISTEN_MAX_CALLS_DEFAULT,
				      0 ) )	// Blocking mode
    {

	    AfpLogEvent( AFPLOG_CANT_INIT_RPC, 0, NULL,
		                        dwRetCode, EVENTLOG_ERROR_TYPE );
    }

    AfpCleanupAndExit( dwRetCode );

}

//**
//
// Call:	AfpCleanupAndExit
//
// Returns:	none
//
// Description: Will free any allocated memory, deinitialize RPC, deinitialize
//		the kernel-mode server and unload it if it was loaded.
//		This could have been called due to an error on SERVICE_START
//		or normal termination.
//
VOID
AfpCleanupAndExit(
	IN DWORD dwError
)
{

    AFP_PRINT( ("AFPSVC_main: Cleaning up and exiting Code = %d\n", dwError));

    // Tear down and free everything
    //
    AfpTerminate();

    if ( dwError == NO_ERROR )
    	AfpGlobals.ServiceStatus.dwWin32ExitCode = NO_ERROR;
    else {
    	AfpGlobals.ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
	//AFP_ASSERT(0);
    }

    AfpGlobals.ServiceStatus.dwCurrentState 		= SERVICE_STOPPED;
    AfpGlobals.ServiceStatus.dwCheckPoint   		= 0;
    AfpGlobals.ServiceStatus.dwWaitHint     		= 0;
    AfpGlobals.ServiceStatus.dwServiceSpecificExitCode 	= dwError;

    AfpAnnounceServiceStatus();

    return;
}

//**
//
// Call:	AfpControlResponse
//
// Returns:	none
//
// Description: Will respond to control requests from the service controller.
//
VOID
AfpControlResponse( IN DWORD dwControlCode )
{
AFP_REQUEST_PACKET	AfpRequestPkt;
DWORD			dwRetCode;

    switch( dwControlCode ) {

    case SERVICE_CONTROL_STOP:

	if ( (AfpGlobals.ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
	     ||
	     (AfpGlobals.ServiceStatus.dwCurrentState == SERVICE_STOPPED ))
	    break;

	// Announce that we are stopping
	//
    	AfpGlobals.ServiceStatus.dwCurrentState        = SERVICE_STOP_PENDING;
    	AfpGlobals.ServiceStatus.dwControlsAccepted    = 0;
    	AfpGlobals.ServiceStatus.dwCheckPoint          = 1;
    	AfpGlobals.ServiceStatus.dwWaitHint            = AFP_SERVICE_STOP_TIME;

    	AfpAnnounceServiceStatus();

        // if srvrhlpr thread is blocked retrying to get domain info, unblock it
        SetEvent(AfpGlobals.heventSrvrHlprSpecial);

        // if srvrhlpr thread was blocked, no more init was done, so we're done
        if (AfpGlobals.dwServerState & AFPSTATE_BLOCKED_ON_DOMINFO)
        {
            return;
        }

	// This call will unblock the main thread that had called
	// RpcServerListen. We let that thread do the announcing
	// while cleaning up.
	//
    if ( (dwRetCode = 
            RpcMgmtStopServerListening( (RPC_BINDING_HANDLE)NULL ))
            != RPC_S_OK )
    {
        ASSERT (0);
    }


	return;

    case SERVICE_CONTROL_PAUSE:

	if ( (AfpGlobals.ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING)
	     ||
	     (AfpGlobals.ServiceStatus.dwCurrentState == SERVICE_PAUSED ))
	    break;

    	AfpGlobals.ServiceStatus.dwCurrentState     = SERVICE_PAUSE_PENDING;
    	AfpGlobals.ServiceStatus.dwControlsAccepted = 0;
    	AfpGlobals.ServiceStatus.dwCheckPoint       = 0;
    	AfpGlobals.ServiceStatus.dwWaitHint 	    = AFP_SERVICE_PAUSE_TIME;

    	AfpAnnounceServiceStatus();


	// Tell the kernel-mode that we want to pause.
  	//
	AfpRequestPkt.dwRequestCode = OP_SERVICE_PAUSE;
        AfpRequestPkt.dwApiType     = AFP_API_TYPE_COMMAND;

	dwRetCode = AfpServerIOCtrl( &AfpRequestPkt );

	AFP_ASSERT( dwRetCode == NO_ERROR );

    	AfpGlobals.ServiceStatus.dwCheckPoint       = 0;
    	AfpGlobals.ServiceStatus.dwWaitHint         = 0;
    	AfpGlobals.ServiceStatus.dwCurrentState     = SERVICE_PAUSED;
    	AfpGlobals.ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
					          SERVICE_ACCEPT_PAUSE_CONTINUE;
	break;

    case SERVICE_CONTROL_CONTINUE:

	if ( (AfpGlobals.ServiceStatus.dwCurrentState==SERVICE_CONTINUE_PENDING)
	     ||
	     (AfpGlobals.ServiceStatus.dwCurrentState == SERVICE_RUNNING ))
	    break;

    	AfpGlobals.ServiceStatus.dwCurrentState     = SERVICE_CONTINUE_PENDING;
    	AfpGlobals.ServiceStatus.dwControlsAccepted = 0;
    	AfpGlobals.ServiceStatus.dwCheckPoint       = 0;
    	AfpGlobals.ServiceStatus.dwWaitHint         = AFP_SERVICE_CONTINUE_TIME;

    	AfpAnnounceServiceStatus();

	// Tell the kernel-mode that we want to continue.
  	//
	AfpRequestPkt.dwRequestCode = OP_SERVICE_CONTINUE;
        AfpRequestPkt.dwApiType     = AFP_API_TYPE_COMMAND;

	dwRetCode = AfpServerIOCtrl( &AfpRequestPkt );

	AFP_ASSERT( dwRetCode == NO_ERROR );

    	AfpGlobals.ServiceStatus.dwCheckPoint       = 0;
    	AfpGlobals.ServiceStatus.dwWaitHint 	    = 0;
    	AfpGlobals.ServiceStatus.dwCurrentState     = SERVICE_RUNNING;
    	AfpGlobals.ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
					          SERVICE_ACCEPT_PAUSE_CONTINUE;

	break;

    case SERVICE_CONTROL_INTERROGATE:
	break;

    default:
	break;

    }

    AfpAnnounceServiceStatus();
}

//**
//
// Call:	AfpAnnounceServiceStatus
//
// Returns:	none
//
// Description: Will simly call SetServiceStatus to inform the service
//		control manager of this service's current status.
//
VOID
AfpAnnounceServiceStatus( VOID )
{
BOOL dwRetCode;


    dwRetCode = SetServiceStatus( AfpGlobals.hServiceStatus,
				  &(AfpGlobals.ServiceStatus) );

    AFP_ASSERT( dwRetCode == TRUE );

}