// borfscm:  Service Control Manager routines.

#include "stdafx.h"
#include "borfrras.h"

using namespace std;


// The termination condition.
extern BORF_RUNNING eRunning;

// The status struct through which we communicate with the Service Control Manager.
SERVICE_STATUS mServiceStatus;

// A handle for mServiceStatus;
SERVICE_STATUS_HANDLE hServiceStatus;



HRESULT BorfServiceInstall( void )
{
/**
 *  Installs the service and creates event logger registry keys.
 */

	// A result buffer for regular APIs.
	DWORD dwResult;

	// A result buffer for Registry APIs.
	LONG lResult;

	// An error buffer.
	DWORD dwError;

	// A handle for opening the SCM.
	SC_HANDLE hManager;

	// A handle for installing the service.
	SC_HANDLE hService;

	// A handle for opening the registry.
	HKEY hKey;

	// A registry key name.
	CString sKey;

	// A buffer for calling GetModuleFileName().
	TCHAR wszFile [1024];
	
	// Bitmask for supported event types.
	DWORD dwTypesSupported = 7;

	// Get the fully-qualified program name.
	dwResult = GetModuleFileNameEx(
		GetCurrentProcess(),
		NULL,
		wszFile,
		sizeof( wszFile ) -1 );

	if( dwResult == 0 )
	{
		dwError = GetLastError();
		wcerr << L"BorfServiceInstall: GetModuleFileName: " << dwError << endl;
		return dwResult;
	}

	
	// Open the NT service manager.
	hManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

	// Check the result.
	if( hManager == NULL )
	{
		// Get the error code.
		dwError = GetLastError();

		// Print an informative message.
		wcerr << L"Error: Unable to open the service control manager. Check your privileges." << endl;
		wcerr << L"BorfGetOptions: OpenSCManager: " << dwError << endl;

		return dwResult;
	}

	// Install the service.
	hService = CreateService(
		hManager,
		BORF_KNOB_SERVICE_NAME,     // Short service name, which is used by the system.
		L"Borf Monitor",            // Long service name. which is seen by the user.
		SERVICE_ALL_ACCESS,         // Access level.
		SERVICE_WIN32_OWN_PROCESS,  // Service type.
		SERVICE_DEMAND_START,       // Startup type.
		SERVICE_ERROR_NORMAL,       // Error type.
		wszFile,                    // Fully-qualified program name.
		NULL,                       // Load order.
		NULL,                       // Tag indentifier.
		NULL,                       // Dependencies.
		NULL,                       // Account name.
		NULL );                     // Password.
	
	// Check the result.
	if( hService == NULL )
	{
		// Get the error code.
		dwError = GetLastError();

		// Print an informative message.
		wcerr << L"Error: Unable to install \"" << wszFile << L"\" as a service." << endl;
		wcerr << L"BorfGetOptions: CreateService: " << dwError << endl;

		return dwError;
	}

	// Release the SCM handle.
	CloseServiceHandle( hManager );

	// Create the registry key name
	sKey.Format( L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", BORF_KNOB_SERVICE_NAME );


	// Open the event log facility key.
	lResult = RegCreateKeyEx(
		HKEY_LOCAL_MACHINE,       // Hive key.
		(LPCTSTR)sKey,            // Sub key.
		0,                        // Reserved parameter.
		NULL,                     // Class.
		REG_OPTION_NON_VOLATILE,  // Persistence.
		KEY_ALL_ACCESS,           // Permissions.
		NULL,                     // Inheritance.
		&hKey,                    // Return value.
		NULL );                   // Disposition.

	if( lResult != ERROR_SUCCESS )
	{
		wcerr << L"Error: Unable to open the registry." << endl;
		wcerr << L"BorfInstallService: RegCreateKeyEx: " << lResult << endl;
		return lResult;
	}

	
	lResult = RegSetValueEx(
		hKey,                                  // Key name.
		(LPCTSTR)L"EventMessageFile",          // Value name.
		0,                                     // Reserved parameter.
		REG_SZ,                                // Value type.
		(const BYTE*)wszFile,                  // Value data.
		wcslen( wszFile ) * sizeof( TCHAR ) ); // Data length.

	if( lResult != ERROR_SUCCESS )
	{
		wcerr << L"Error: Unable to create the EventMessageFile registry key." << endl;
		wcerr << L"BorfInstallService: RegSetValueEx: " << lResult << endl;
		return lResult;
	}

		  
	lResult = RegSetValueEx(
		hKey,                           // Key name.
		(LPCTSTR)L"TypesSupported",     // Value name.
		0,                              // Reserved parameter.
		REG_DWORD,                      // Value type.
		(const BYTE*)&dwTypesSupported, // Value data.
		sizeof( dwTypesSupported ) );   // Data length.

	if( lResult != ERROR_SUCCESS )
	{
		wcerr << L"Error: Unable to create the TypesSupported registry key." << endl;
		wcerr << L"BorfInstallService: RegSetValueEx: " << lResult << endl;
		return lResult;
	}


	// Close the registry key.
	RegCloseKey( hKey );

	// Log the installation.
	BorfEvent(
		EVENTLOG_INFORMATION_TYPE,
		IDM_INSTALLED,
		0,
		NULL,
		BORF_KNOB_SERVICE_NAME,
		NULL );

	// Print an informative message.
	wcout << L"Success: Installed \"" << wszFile << L"\" as a service." << endl;

	return S_OK;

} // BorfServiceInstall



HRESULT BorfServiceUninstall( void )
{
/**
 *  Uninstalls the service and deletes event logger registry keys.
 */

	// An error buffer.
	DWORD dwError;

	// A result buffer for Registry APIs.
	LONG lResult;

	// Handles for uninstalling the service.
	SC_HANDLE hManager;
	SC_HANDLE hService;

	// A registry key name.
	CString sKey;


	// Open the NT service manager.
	hManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

	// Check the result.
	if( hManager == NULL )
	{
		// Get the error code.
		dwError = GetLastError();

		// Print an informative message.
		wcerr << L"BorfGetOptions: OpenSCManager: " << dwError << endl;
		wcerr << L"Error: Unable to open the service control manager. Check your privileges." << endl;
		
		return dwError;
	}

	
	// Load the service.
	hService = OpenService( hManager, BORF_KNOB_SERVICE_NAME, SERVICE_ALL_ACCESS );

	if( hService == NULL )
	{
		// Get the error code.
		dwError = GetLastError();

		// Print an informative error.
		wcerr << L"BorfGetOptions: OpenService: " << dwError << endl;
		wcerr << L"Error: Unable to open the service. Check whether it is actually installed." << endl;
		
		return dwError;
	}


	// Uninstall the service.
	if( DeleteService( hService ) )
	{
		// Release the handle.
		CloseServiceHandle( hService );
	}

	else
	{

		// Get the error code.
		dwError = GetLastError();

		// Complain to the user.
		wcerr << L"Error: Unable to uninstall the service." << endl;

		switch( dwError )
		{
			case ERROR_ACCESS_DENIED:
				wcerr << L"BorfGetOptions: DeleteService: ERROR_ACCESS_DENIED" << endl;
				break;

			case ERROR_INVALID_HANDLE:
				wcerr << L"BorfGetOptions: DeleteService: ERROR_INVALID_HANDLE" << endl;
				break;

			case ERROR_SERVICE_MARKED_FOR_DELETE:
				wcerr << L"BorfGetOptions: DeleteService: ERROR_SERVICE_MARKED_FOR_DELETE" << endl;
				break;

			default:
				wcerr << L"BorfGetOptions: DeleteService: " << dwError << endl;
				break;
		}

		// Release the handle.
		CloseServiceHandle( hService );

		return dwError;
		
	} // if DeleteService


	// Create the registry key name of the event logger.
	sKey.Format( L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", BORF_KNOB_SERVICE_NAME );

	// Delete the registry key for the event logger.
	lResult = RegDeleteKey(
		HKEY_LOCAL_MACHINE,
		(LPCTSTR)sKey );

	if( lResult != ERROR_SUCCESS )
	{
		wcerr << "Error: Unable to delete the registry key." << endl;
		wcerr << "BorfServiceUninstall: RegDeleteKey: " << lResult << endl;
		return lResult;
	}


	// Log the uninstallation.
	BorfEvent(
		EVENTLOG_INFORMATION_TYPE,
		IDM_UNINSTALLED,
		0,
		NULL,
		BORF_KNOB_SERVICE_NAME,
		NULL );


	// Print an informative message.
	wcout << L"Success: Uninstalled the borfrras service." << endl;

	return S_OK;


} // BorfServiceUninstall



HRESULT BorfServiceCheck( void )
{
/**
 *  Checks whether the borf service is installed.
 *    S_OK     -> The service is installed.
 *    S_FALSE  -> The service is not installed.
 */

	// TODO:
	return S_OK;

} // BorfServiceCheck



void WINAPI BorfServiceControlHandler( DWORD dwOpcode )
{
/**
 *  The service control manager communicates with the service thread through this function.
 *
 */

    switch( dwOpcode) 
    { 
		
        case SERVICE_CONTROL_PAUSE: 
			
			// TODO: We do not register the pause command.
			mServiceStatus.dwCurrentState = SERVICE_PAUSED; 
			break; 
 
        case SERVICE_CONTROL_CONTINUE: 

			// TODO: We do not register the pause command.
            mServiceStatus.dwCurrentState = SERVICE_RUNNING; 
            break; 
 
        case SERVICE_CONTROL_STOP: 

			// Set the service control boilerplate.
			mServiceStatus.dwCheckPoint    = 0; 
            mServiceStatus.dwCurrentState  = SERVICE_STOPPED; 
            mServiceStatus.dwWaitHint      = 0; 
			mServiceStatus.dwWin32ExitCode = 0; 

			// Set the termination condition.
			eRunning = BORF_RUNNING_STOPPED;

			// Tell the SCM that we have stopped.
            SetServiceStatus( hServiceStatus, &mServiceStatus );

			// Log that we stopped.
			BorfEvent(
				EVENTLOG_INFORMATION_TYPE,
				IDM_STOPPED,
				0,
				NULL,
				BORF_KNOB_SERVICE_NAME,
				NULL );

			break;
 
        case SERVICE_CONTROL_INTERROGATE: 
			
			// TODO:
            break; 

    } // switch dwOpcode

} // BorfServiceControlHandler



void WINAPI BorfServiceMain( DWORD argc, LPTSTR *argv )
{
/**
 *  This is the entry point for the service thread.
 */

	// Set the service control boilerplate.
	mServiceStatus.dwCheckPoint              = 0; 
	mServiceStatus.dwControlsAccepted        = SERVICE_ACCEPT_STOP; 
	mServiceStatus.dwCurrentState            = SERVICE_START_PENDING; 
	mServiceStatus.dwServiceSpecificExitCode = 0; 
	mServiceStatus.dwServiceType             = SERVICE_WIN32; 
	mServiceStatus.dwWaitHint                = 0; 
	mServiceStatus.dwWin32ExitCode           = 0; 

	// Register our control handler with the SCM.
    hServiceStatus = RegisterServiceCtrlHandler( BORF_KNOB_SERVICE_NAME, BorfServiceControlHandler );  

    if( hServiceStatus == NULL ) 
    {
		// TODO:
        return; 
    }     

	// Change our status.
   	mServiceStatus.dwCheckPoint   = 0; 
	mServiceStatus.dwCurrentState = SERVICE_RUNNING; 
	mServiceStatus.dwWaitHint     = 0;  

	// Commit our status.
    if( ! SetServiceStatus( hServiceStatus, &mServiceStatus ) ) 
    { 
		// TODO:
		return;
    } 

	// Initialize the termination condition.
	eRunning = BORF_RUNNING_SERVICE;

	// Log that we started.
	BorfEvent(
		EVENTLOG_INFORMATION_TYPE,
		IDM_STARTED,
		0,
		NULL,
		BORF_KNOB_SERVICE_NAME,
		NULL );


	// Enter the main loop.  Discard the result.
	BorfMain();

} // borfServiceMain

// eof