/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    services.c

Abstract:

    Routines to manage nt service configurations for promotion and demotion

Author:

    Colin Brace    ColinBr     March 29, 1999.

Environment:

    User Mode

Revision History:

--*/
#include <setpch.h>
#include <dssetp.h>

#include <malloc.h>  // alloca

#include <lmcons.h>  // net api definitions
#include <lmsvc.h>   // service names
#include <ismapi.h>  //defines ISM_SERVICE_CONTROL_REMOVE_STOP

#include "services.h"

//
// These last 3 magic values supplied by Shirish Koti (koti) to setup up
// ras services for macintosh on a domain controller
//
#define DSROLEP_MSV10_PATH    L"SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0"
#define DSROLEP_RASSFM_NAME   L"Auth2"
#define DSROLEP_RASSFM_VALUE  L"RASSFM"

//
// Global Data for this module
//

//
// Table based data for the intrinsic nt services
//
typedef struct _DSROLEP_SERVICE_ITEM
{
    LPWSTR ServiceName;       // name of the service to configure

    ULONG  ConfigureOn;       // the dsrole flag to enable the service

    ULONG  ConfigureOff;      // the dsrole flag to disable the service

    ULONG  RevertSettings;    // the dsrole flags to use to revert settings

    LPWSTR Dependencies[3]; // the dependencies the service has when enabled

} DSROLEP_SERVICE_ITEM;

//
// These are services that run on machines that are part of a domain
//
DSROLEP_SERVICE_ITEM DsRoleDomainServices[] = 
{
    {
        SERVICE_W32TIME,
        DSROLEP_SERVICE_AUTOSTART,
        DSROLEP_SERVICE_DEMANDSTART,
        DSROLEP_SERVICES_INVALID,
        NULL, NULL, NULL
    },
    {
        SERVICE_NETLOGON,
        DSROLEP_SERVICE_AUTOSTART,
        DSROLEP_SERVICE_DEMANDSTART,
        0,
        NULL, NULL, NULL
    }
};

ULONG DsRoleDomainServicesCount = sizeof(DsRoleDomainServices) / sizeof(DsRoleDomainServices[0]);

//
// These are servers that run on machines that are domain controllers
//
DSROLEP_SERVICE_ITEM DsRoleDomainControllerServices[] = 
{
    {
        SERVICE_RPCLOCATOR,
        DSROLEP_SERVICE_AUTOSTART,
        DSROLEP_SERVICE_DEMANDSTART,
        DSROLEP_SERVICES_INVALID,
        NULL, NULL, NULL
    },
    {
        SERVICE_ISMSERV,
        DSROLEP_SERVICE_AUTOSTART,
        DSROLEP_SERVICE_DISABLED | DSROLEP_SERVICE_STOP_ISM,
        DSROLEP_SERVICES_INVALID,
        NULL, NULL, NULL
    },
    {
        SERVICE_KDC,
        DSROLEP_SERVICE_AUTOSTART,
        DSROLEP_SERVICE_DISABLED,
        DSROLEP_SERVICES_INVALID,
        NULL, NULL, NULL
    },
    {
        SERVICE_TRKSVR,
        DSROLEP_SERVICE_AUTOSTART,
        DSROLEP_SERVICE_DEMANDSTART,
        DSROLEP_SERVICES_INVALID,
        NULL, NULL, NULL
    },
    {
        SERVICE_NETLOGON,
        DSROLEP_SERVICE_AUTOSTART | DSROLEP_SERVICE_DEP_ADD,
        DSROLEP_SERVICE_AUTOSTART | DSROLEP_SERVICE_DEP_REMOVE,
        DSROLEP_SERVICES_INVALID,
        SERVICE_SERVER, NULL, NULL
    }
};

ULONG DsRoleDomainControllerServicesCount = sizeof(DsRoleDomainControllerServices) / sizeof(DsRoleDomainControllerServices[0]);

//
// Local forwards
//
DWORD
DsRolepSetRegStringValue(
    IN LPWSTR Path,
    IN LPWSTR ValueName,
    IN LPWSTR Value
    );

DWORD
DsRolepConfigureGenericServices(
    IN DSROLEP_SERVICE_ITEM *ServiceArray,
    IN ULONG                 ServiceCount,
    IN ULONG                 Flags
    );

DWORD
DsRolepMakeAdjustedDependencyList(
    IN HANDLE hSvc,
    IN DWORD  ServiceOptions,
    IN LPWSTR Dependency,
    OUT LPWSTR *DependenyList
    );

DWORD
DsRolepGetServiceConfig(
    IN SC_HANDLE hScMgr,
    IN LPWSTR ServiceName,
    IN SC_HANDLE ServiceHandle,
    IN LPQUERY_SERVICE_CONFIG *ServiceConfig
    );
    
//
// Small helper functions
//
DWORD DsRolepFlagsToServiceFlags(
    IN DWORD f
    )
{

    if ( FLAG_ON( f, DSROLEP_SERVICE_BOOTSTART ) ) return SERVICE_BOOT_START;
    if ( FLAG_ON( f, DSROLEP_SERVICE_SYSTEM_START ) ) return SERVICE_SYSTEM_START;
    if ( FLAG_ON( f, DSROLEP_SERVICE_AUTOSTART ) ) return SERVICE_AUTO_START;
    if ( FLAG_ON( f, DSROLEP_SERVICE_DEMANDSTART ) ) return SERVICE_DEMAND_START;
    if ( FLAG_ON( f, DSROLEP_SERVICE_DISABLED ) ) return SERVICE_DISABLED;
    
    // No flag, no change
    return SERVICE_NO_CHANGE;
}

DWORD DsRolepServiceFlagsToDsRolepFlags(
    IN DWORD f
    )
{

    if ( f == SERVICE_BOOT_START ) return DSROLEP_SERVICE_BOOTSTART;
    if ( f == SERVICE_SYSTEM_START ) return DSROLEP_SERVICE_SYSTEM_START;
    if ( f == SERVICE_AUTO_START ) return DSROLEP_SERVICE_AUTOSTART;
    if ( f == SERVICE_DEMAND_START ) return DSROLEP_SERVICE_DEMANDSTART;
    if ( f == SERVICE_DISABLED ) return DSROLEP_SERVICE_DISABLED;
    if ( f == SERVICE_NO_CHANGE ) return 0;

    ASSERT( FALSE && !"Unknown service start type" );

    // This is safe
    return DSROLEP_SERVICE_DEMANDSTART;
}

//
// Exported (from this file) functions
//
DWORD
DsRolepConfigureDomainControllerServices(
    IN DWORD Flags
    )

/*++

Routine Description

Parameters

Return Values

    ERROR_SUCCESS if no errors; a system service error otherwise.

--*/
{
    DWORD WinError = ERROR_SUCCESS;

    //
    // Configure the registry for RASSFM service
    //
    if ( FLAG_ON( Flags, DSROLEP_SERVICES_ON ) ) {

        WinError = DsRolepSetRegStringValue(DSROLEP_MSV10_PATH,
                                              DSROLEP_RASSFM_NAME,
                                              DSROLEP_RASSFM_VALUE);

        //
        // This is not fatal -- log message
        //

        WinError = ERROR_SUCCESS;
        
    }

    //
    // Configure the intrinsic nt services
    //
    WinError = DsRolepConfigureGenericServices( DsRoleDomainControllerServices,
                                                DsRoleDomainControllerServicesCount,
                                                Flags );

                                         

    //
    // No need to undo RASSFM change
    //

    return WinError;
}

DWORD
DsRolepConfigureDomainServices(
    DWORD Flags
    )
/*++

Routine Description

Parameters

Return Values

    ERROR_SUCCESS if no errors; a system service error otherwise.

--*/
{
    DWORD WinError = ERROR_SUCCESS;

    //
    // Configure the intrinsic nt services
    //
    WinError = DsRolepConfigureGenericServices( DsRoleDomainServices,
                                                DsRoleDomainServicesCount,
                                                Flags );

    return WinError;
}


DWORD
DsRolepStartNetlogon(
    VOID
    )
{
    DWORD WinError = ERROR_SUCCESS;

    WinError  = DsRolepConfigureService( SERVICE_NETLOGON,
                                         DSROLEP_SERVICE_START,
                                         NULL,
                                         NULL );
    return WinError;
}

DWORD
DsRolepStopNetlogon(
    OUT BOOLEAN *WasRunning
    )
{
    DWORD WinError = ERROR_SUCCESS;
    ULONG PreviousSettings = 0;

    WinError  = DsRolepConfigureService( SERVICE_NETLOGON,
                                         DSROLEP_SERVICE_STOP,
                                         NULL,
                                         &PreviousSettings );

    if (  (ERROR_SUCCESS == WinError) 
       && WasRunning ) {

        *WasRunning = (BOOLEAN) FLAG_ON( PreviousSettings, DSROLEP_SERVICE_START );
                       
    }

    return WinError;
}

//
// Local functions
//

DWORD
DsRolepSetRegStringValue(LPWSTR Path,
                         LPWSTR ValueName,
                         LPWSTR Value)
/*++

Routine Description

    This routine sets Value as a REG_SZ value on the value ValueName
    on the key Path

Parameters

    Path,  a registry path relative to HKLM

    ValueName, a null-terminated string

    Value, a null terminated string

Return Values

    ERROR_SUCCESS if no errors; a system service error otherwise.

--*/
{
    DWORD WinErroror = ERROR_INVALID_PARAMETER, WinErroror2;
    HKEY  hKey;

    ASSERT(Path);
    ASSERT(ValueName);
    ASSERT(Value);

    if (Path && ValueName && Value) {

        WinErroror = RegCreateKey(HKEY_LOCAL_MACHINE,
                                  Path,
                                  &hKey);

        if (ERROR_SUCCESS == WinErroror) {

            WinErroror = RegSetValueEx(hKey,
                                       ValueName,
                                       0, // reserved
                                       REG_SZ,
                                       (VOID*)Value,
                                       (wcslen(Value)+1)*sizeof(WCHAR));


            WinErroror2 = RegCloseKey(hKey);
            ASSERT(ERROR_SUCCESS == WinErroror2);

        }

    }

    DsRolepLogPrint(( DEB_TRACE,
                      "DsRolepSetRegStringValue on %ws\\%ws to %ws returned %lu\n",
                      Path,
                      ValueName,
                      Value,
                      WinErroror ));


    return WinErroror;

}

DWORD
DsRolepConfigureGenericServices(
    IN DSROLEP_SERVICE_ITEM *ServiceArray,
    IN ULONG                 ServiceCount,
    IN ULONG                 Flags
    )
/*++

Routine Description:

Arguments:

Returns:

--*/
{

    DWORD WinError = ERROR_SUCCESS;
    ULONG ServicesInstalled;



    //
    // Configure each service
    //
    for ( ServicesInstalled = 0;
            ServicesInstalled < ServiceCount && (WinError == ERROR_SUCCESS);
                ServicesInstalled++ ) {


        ULONG *RevertSettings = &ServiceArray[ServicesInstalled].RevertSettings;
        ULONG Operation = 0;

        //
        // Check for cancel before contining if we are not reverting
        //
        if ( !FLAG_ON( Flags, DSROLEP_SERVICES_REVERT ) ) {
            
            DSROLEP_CHECK_FOR_CANCEL( WinError );
            if ( ERROR_SUCCESS != WinError ) {
                break;
            }
        }

        //
        // Determine the operation flag
        //
        if ( FLAG_ON( Flags, DSROLEP_SERVICES_ON ) ) {

            Operation |= ServiceArray[ServicesInstalled].ConfigureOn;
            *RevertSettings = 0;

        } else if ( FLAG_ON( Flags, DSROLEP_SERVICES_OFF ) ) {

            Operation |= ServiceArray[ServicesInstalled].ConfigureOff;
            *RevertSettings = 0;

        } else if ( FLAG_ON( Flags, DSROLEP_SERVICES_REVERT ) ) {
            
            Operation |= ServiceArray[ServicesInstalled].RevertSettings;

            //
            // N.B. We don't want to set the revert settings when we are
            // reverting!
            //
            RevertSettings = NULL;

        }

        if ( FLAG_ON( Flags, DSROLEP_SERVICES_START ) ) {

            Operation |= DSROLEP_SERVICE_START;

        } else if ( FLAG_ON( Flags, DSROLEP_SERVICES_STOP ) ) {
            
            Operation |= DSROLEP_SERVICE_STOP;
        }

        //
        // Currently we don't handle more than one dependency
        //
        ASSERT( NULL == ServiceArray[ ServicesInstalled ].Dependencies[1] );

        // We should do something
        ASSERT( 0 != Operation );

        //
        // Configure the service
        //
        WinError = DsRolepConfigureService( ServiceArray[ ServicesInstalled ].ServiceName,
                                            Operation,
                                            ServiceArray[ ServicesInstalled ].Dependencies[0],
                                            RevertSettings
                                            );

    }

    //
    // If there is an error, undo the work already done
    //
    if (  ERROR_SUCCESS != WinError 
      && !FLAG_ON( Flags, DSROLEP_SERVICES_REVERT )  ) {

        DWORD WinError2;
        ULONG i;

        for ( i = 0; i < ServicesInstalled; i++ ) {
    
            //
            // Configure the service
            //
            WinError2 = DsRolepConfigureService( ServiceArray[ i ].ServiceName,
                                                 ServiceArray[ServicesInstalled].RevertSettings,
                                                 ServiceArray[ i ].Dependencies[0],
                                                 NULL  // we don't need to know revert settings
                                                 );
    
            //
            // This should succeed, though since this is the undo path it is
            // not critical
            //
            ASSERT( ERROR_SUCCESS == WinError2 );
        }
    }


    return WinError;
}

DWORD
DsRolepConfigureService(
    IN LPWSTR ServiceName,
    IN ULONG ServiceOptions,
    IN LPWSTR  Dependency OPTIONAL,
    OUT ULONG *RevertServiceOptions OPTIONAL
    )
/*++

Routine Description:

    Starts, stops, or modifies the configuration of a service.

Arguments:

    ServiceName - Service to configure

    ServiceOptions - Stop, start, dependency add/remove, or configure

    Dependency - a null terminated string identify a dependency

    ServiceWasRunning - Optional.  When stopping a service, the previous service state
                        is returned here

Returns:

    ERROR_SUCCESS - Success

    ERROR_INVALID_PARAMETER - A bad service option was given

--*/
{
    DWORD WinError = ERROR_SUCCESS;
    SC_HANDLE hScMgr = NULL, hSvc = NULL;
    ULONG OpenMode = 0;
    LPENUM_SERVICE_STATUS DependentServices = NULL;
    ULONG DependSvcSize = 0, DependSvcCount = 0, i;
    LPWSTR NewDependencyList = NULL;
    DWORD NewStartType = SERVICE_NO_CHANGE;
    ULONG UpdateMsgId = DSROLEEVT_CONFIGURE_SERVICE;

    //
    // If the service doesn't stop within two minutes minute, continue on
    //
    ULONG AccumulatedSleepTime;
    ULONG MaxSleepTime = 120000;


    BOOLEAN ConfigChangeRequired = FALSE;
    BOOLEAN RunChangeRequired = FALSE;

    DWORD   PreviousStartType = SERVICE_NO_CHANGE;
    BOOLEAN fServiceWasRunning = FALSE;


    //
    // Parameter checks
    //
    ASSERT( ! (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DEP_ADD )
           && (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DEP_REMOVE ))) );

    ASSERT( ! (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_AUTOSTART )
           && (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DISABLED ))) );

    ASSERT( ! (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_START )
           && (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP ))) );

    //
    // Do some logic to determine the open mode of the service
    //
    NewStartType = DsRolepFlagsToServiceFlags( ServiceOptions );

    if ( (SERVICE_NO_CHANGE != NewStartType)                ||
        FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DEP_ADD )  ||
        FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DEP_REMOVE ))
    {
        ConfigChangeRequired = TRUE;
    }

    if( ConfigChangeRequired ) {

        OpenMode |= SERVICE_CHANGE_CONFIG | SERVICE_QUERY_CONFIG;
    }

    if( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP ) ) {

        OpenMode |= SERVICE_STOP | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_QUERY_STATUS;
        UpdateMsgId = DSROLEEVT_STOP_SERVICE;
        RunChangeRequired = TRUE;
    }

    if ( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP_ISM ) ) {

        OpenMode |= SERVICE_USER_DEFINED_CONTROL;

    }

    if( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_START ) ) {

        OpenMode |= SERVICE_START | SERVICE_QUERY_STATUS;
        UpdateMsgId = DSROLEEVT_START_SERVICE;
        RunChangeRequired = TRUE;
    }
    
    //
    // Open the service control manager
    //
    hScMgr = OpenSCManager( NULL,
                            SERVICES_ACTIVE_DATABASE,
                            GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE );

    if ( hScMgr == NULL ) {

        WinError = GetLastError();
        DsRolepLogOnFailure( WinError,
                             DsRolepLogPrint(( DEB_TRACE,
                                               "Can't contact the service controller manager (%lu)\n",
                                                WinError )) );
        goto Cleanup;

    }

    //
    // Open the service
    //
    hSvc = OpenService( hScMgr,
                        ServiceName,
                        OpenMode );

    if ( hSvc == NULL ) {

        WinError = GetLastError();
        DsRolepLogOnFailure( WinError,
                             DsRolepLogPrint(( DEB_TRACE,
                                               "OpenService on %ws failed with %lu\n",
                                                ServiceName,
                                                WinError )) );
        goto Cleanup;
    } 

    
    DSROLEP_CURRENT_OP1( UpdateMsgId, ServiceName );

    //
    // Determine if the service is running if we are going to be stopping or
    // starting it
    //
    if( RunChangeRequired ) {

        SERVICE_STATUS SvcStatus;

        if( QueryServiceStatus( hSvc,&SvcStatus ) == FALSE ) {
    
            WinError = GetLastError();
            goto Cleanup;
        }
    
        if ( SvcStatus.dwCurrentState == SERVICE_RUNNING ) {
    
            fServiceWasRunning = TRUE;
                                        
        }
    }

    //
    // Determine the current start type if we are going to be changing it
    //
    if ( ConfigChangeRequired ) {

        LPQUERY_SERVICE_CONFIG ServiceConfig = NULL;
        DWORD                  Size = 0;
        BOOL                   fSuccess;

        QueryServiceConfig( hSvc,
                            ServiceConfig,
                            Size,
                            &Size );

        ASSERT( GetLastError() == ERROR_INSUFFICIENT_BUFFER );

        ServiceConfig = (LPQUERY_SERVICE_CONFIG) alloca( Size );

        fSuccess = QueryServiceConfig( hSvc,
                                       ServiceConfig,
                                       Size,
                                      &Size );

        if ( !fSuccess ) {
            WinError = GetLastError();
            goto Cleanup;
        }

        PreviousStartType = ServiceConfig->dwStartType;
    }

    //
    // Do the config change
    //
    if ( ConfigChangeRequired ) {

        //
        // Make a new dependency list
        //
    
        if ( Dependency ) {
    
            WinError = DsRolepMakeAdjustedDependencyList( hSvc,
                                                          ServiceOptions,
                                                          Dependency,
                                                          &NewDependencyList );
    
            if ( ERROR_SUCCESS != WinError ) {
                goto Cleanup;
            }
        
        }

        //
        // Change the service with new parameters
        //
        if ( ChangeServiceConfig( hSvc,
                                  SERVICE_NO_CHANGE,
                                  NewStartType,
                                  SERVICE_NO_CHANGE,
                                  NULL,
                                  NULL,
                                  0,
                                  NewDependencyList,
                                  NULL, NULL, NULL ) == FALSE ) {
    
            WinError = GetLastError();
            DsRolepLogOnFailure( WinError,
                                 DsRolepLogPrint(( DEB_TRACE,
                                                   "ChangeServiceConfig on %ws failed with %lu\n",
                                                   ServiceName,
                                                   WinError )) );

            goto Cleanup;
        }

    }

    // Stop the service.
    if ( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP ) || FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP_ISM ) ) {
    
        SERVICE_STATUS  SvcStatus;
    
        WinError = ERROR_SUCCESS;
    
        //
        // Enumerate all of the dependent services first
        //
        if(EnumDependentServices( hSvc,
                                  SERVICE_ACTIVE,
                                  NULL,
                                  0,
                                  &DependSvcSize,
                                  &DependSvcCount ) == FALSE ) {
    
            WinError = GetLastError();
        }
    
    
    
        if ( WinError == ERROR_MORE_DATA ) {
    
            DependentServices = RtlAllocateHeap( RtlProcessHeap(), 0, DependSvcSize );
    
            if ( DependentServices == NULL) {
    
                WinError = ERROR_OUTOFMEMORY;
    
            } else {
    
                if( EnumDependentServices( hSvc,
                                           SERVICE_ACTIVE,
                                           DependentServices,
                                           DependSvcSize,
                                           &DependSvcSize,
                                           &DependSvcCount ) == FALSE ) {
    
                    WinError = GetLastError();
    
                } else {
    
                    for ( i = 0; i < DependSvcCount; i++) {
    
                        DsRoleDebugOut(( DEB_TRACE,
                                          "Service %ws depends on %ws\n",
                                          DependentServices[i].lpServiceName,
                                          ServiceName ));
    
                        WinError = DsRolepConfigureService(
                                         DependentServices[i].lpServiceName,
                                         DSROLEP_SERVICE_STOP,
                                         NULL,
                                         NULL );
    
                        if ( WinError != ERROR_SUCCESS ) {
    
                            break;
                        }
    
                    }
                }
    
                RtlFreeHeap( RtlProcessHeap(), 0, DependentServices );
            }
    
        }
    
    
        if ( WinError == ERROR_SUCCESS ) {
    
            if ( (FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP_ISM )?
                  ControlService( hSvc,
                                  ISM_SERVICE_CONTROL_REMOVE_STOP,
                                  &SvcStatus ):
                  ControlService( hSvc,
                                  SERVICE_CONTROL_STOP,
                                  &SvcStatus )) == FALSE ) {
    
                WinError = GetLastError();
    
                //
                // It's not an error if the service wasn't running
                //
                if ( WinError == ERROR_SERVICE_NOT_ACTIVE ) {
    
                    WinError = ERROR_SUCCESS;
                }
    
            } else {
    
                WinError = ERROR_SUCCESS;
    
                //
                // Wait for the service to stop
                //
                AccumulatedSleepTime = 0;
                while ( TRUE ) {
    
                    if( QueryServiceStatus( hSvc,&SvcStatus ) == FALSE ) {
    
                        WinError = GetLastError();
                    }
    
                    if ( WinError != ERROR_SUCCESS ||
                                        SvcStatus.dwCurrentState == SERVICE_STOPPED) {
    
                        break;
                    
                    }

                    if ( AccumulatedSleepTime < MaxSleepTime ) {

                        if ( 0 == SvcStatus.dwWaitHint ) {

                            //if we are told not to wait we will
                            //wait for 5 seconds anyway.
                            //bug # 221482

                            Sleep ( 5000 );
                            AccumulatedSleepTime += 5000;

                        } else  {

                            Sleep( SvcStatus.dwWaitHint );
                            AccumulatedSleepTime += SvcStatus.dwWaitHint;

                        }

                    } else {

                        //
                        // Give up and return an error
                        //
                        WinError = WAIT_TIMEOUT;
                        break;
                    }
                }
            }
    
            DsRoleDebugOut(( DEB_TRACE, "StopService on %ws returned %lu\n",
                              ServiceName, WinError ));
    
        }
    
        DsRolepLogOnFailure( WinError,
                             DsRolepLogPrint(( DEB_TRACE,
                                               "StopService on %ws failed with %lu\n",
                                               ServiceName,
                                               WinError )) );

        if ( ERROR_SUCCESS != WinError ) {
            goto Cleanup;
        }
    
    }

    if ( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_START ) ) {

        //
        // See about changing its state
        //
        if ( StartService( hSvc, 0, NULL ) == FALSE ) {

            WinError = GetLastError();

        } else {

            WinError = ERROR_SUCCESS;
        }

        DsRoleDebugOut(( DEB_TRACE, "StartService on %ws returned %lu\n",
                          ServiceName, WinError ));
        DsRolepLogOnFailure( WinError,
                             DsRolepLogPrint(( DEB_TRACE,
                                               "StartService on %ws failed with %lu\n",
                                               ServiceName,
                                               WinError )) );

        if ( ERROR_SUCCESS != WinError ) {
            goto Cleanup;
        }

    }

    //
    // Success! By the time we are here, we have completed the task asked
    // of us, so set the Revert parameter
    //
    ASSERT( ERROR_SUCCESS == WinError );
    if ( RevertServiceOptions ) {

        *RevertServiceOptions = 0;

        if( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_STOP ) 
         && fServiceWasRunning   ) {

            *RevertServiceOptions |= DSROLEP_SERVICE_START;
        }
    
        if(  FLAG_ON( ServiceOptions, DSROLEP_SERVICE_START ) 
          && !fServiceWasRunning ) {

            *RevertServiceOptions |= DSROLEP_SERVICE_STOP;
        }

        if ( PreviousStartType != SERVICE_NO_CHANGE ) {
            *RevertServiceOptions |= DsRolepServiceFlagsToDsRolepFlags( PreviousStartType );
        }

        if ( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DEP_ADD ) ) {
            *RevertServiceOptions |= DSROLEP_SERVICE_DEP_REMOVE;
        }

        if ( FLAG_ON( ServiceOptions, DSROLEP_SERVICE_DEP_REMOVE ) ) {
            *RevertServiceOptions |= DSROLEP_SERVICE_DEP_ADD;
        }
    }

Cleanup:

    if ( hSvc ) {

        CloseServiceHandle( hSvc );

    }

    if ( hScMgr ) {
        
        CloseServiceHandle( hScMgr );

    }

    if ( NewDependencyList ) {

        RtlFreeHeap(RtlProcessHeap(), 0, NewDependencyList);
    }

    DsRolepLogPrint(( DEB_TRACE,
                      "Configuring service %ws to %lu returned %lu\n",
                      ServiceName,
                      ServiceOptions,
                      WinError ));

    DSROLEP_FAIL1( WinError, DSROLERES_SERVICE_CONFIGURE, ServiceName );

    return( WinError );
}

DWORD
DsRolepMakeAdjustedDependencyList(
    IN HANDLE hSvc,
    IN DWORD  ServiceOptions,
    IN LPWSTR Dependency,
    OUT LPWSTR *NewDependencyList
    )
/*++

Routine Description

    This function adds or removes Dependency from the service referred to
    by hSvc.

Parameters

    hSvc, a handle to an open service

    ServiceOptions,  either DSROLEP_SERVICE_DEP_REMOVE or DSROLEP_SERVICE_DEP_ADD

    Dependency, null terminated string

    NewDependencyList, a block list of strings to freed by the caller

Return Values

    ERROR_SUCCESS if no errors; a system service error otherwise.

--*/
{
    DWORD WinError = STATUS_SUCCESS;
    BOOLEAN fDone = FALSE;

    WCHAR *CurrentDependency;
    ULONG CurrentDependencyLength;

    ULONG DependencySize;
    ULONG DependencyListSize;
    ULONG NewDependencyListSize;

    LPWSTR TempDependencyList = NULL;
    WCHAR  *CurrNewList;

    LPQUERY_SERVICE_CONFIG ServiceConfigInfo=NULL;

    //
    // Query for the existing dependencies
    //
    WinError = DsRolepGetServiceConfig(NULL,
                                       NULL,
                                       hSvc,
                                       &ServiceConfigInfo);

    if (ERROR_SUCCESS != WinError) {
        goto Cleanup;
    }


    if (FLAG_ON(ServiceOptions, DSROLEP_SERVICE_DEP_ADD)) {


        // Get the size of the dependency
        DependencySize = (wcslen(Dependency) + 1)*sizeof(WCHAR); // for NULL

        // Get the size of the dependency list
        DependencyListSize = 0;
        CurrentDependency = ServiceConfigInfo->lpDependencies;
        while (CurrentDependency && *CurrentDependency != L'\0') {

            // Get the current list size
            if (!_wcsicmp(CurrentDependency, Dependency)) {
                //
                // Dependency is already here
                //
                break;
                fDone = TRUE;
            }

            CurrentDependencyLength = wcslen(CurrentDependency) + 1; // for NULL
            DependencyListSize += CurrentDependencyLength * sizeof(WCHAR);

            CurrentDependency += CurrentDependencyLength;

        }

        if ( fDone ) {

            WinError = ERROR_SUCCESS;
            goto Cleanup;
        }


        // Calculate the size of the new dependency list
        NewDependencyListSize = DependencyListSize +
                                DependencySize     +
                                sizeof(WCHAR);  // the whole string of strings
                                                // NULL terminated
        //
        // Now allocate a space to hold the new dependency array
        //
        TempDependencyList = RtlAllocateHeap(RtlProcessHeap(),
                                             0,
                                             NewDependencyListSize);
        if (!TempDependencyList) {
            WinError = ERROR_NOT_ENOUGH_MEMORY;
            goto Cleanup;
        }

        RtlZeroMemory(TempDependencyList, NewDependencyListSize);
        RtlCopyMemory(TempDependencyList,
                      ServiceConfigInfo->lpDependencies,
                      DependencyListSize);
        RtlCopyMemory(&TempDependencyList[DependencyListSize/sizeof(WCHAR)],
                      Dependency,
                      DependencySize);

    } else if (FLAG_ON(ServiceOptions, DSROLEP_SERVICE_DEP_REMOVE)) {

        // Get the size of the dependency
        DependencySize = (wcslen(Dependency) + 1)*sizeof(WCHAR); // for NULL

        // Get the size of the dependency list
        DependencyListSize = 0;
        CurrentDependency = ServiceConfigInfo->lpDependencies;
        while (CurrentDependency && *CurrentDependency != L'\0') {

            CurrentDependencyLength = wcslen(CurrentDependency) + 1; // for NULL
            DependencyListSize += CurrentDependencyLength * sizeof(WCHAR);

            CurrentDependency += CurrentDependencyLength;

        }

        // Calculate the size of the new dependency list
        NewDependencyListSize = DependencyListSize -
                                DependencySize     +
                                sizeof(WCHAR);  // the whole string of strings
                                                // NULL terminated
        //
        // Now allocate a space to hold the new dependency array
        // This is overkill, but not much.
        //
        TempDependencyList = RtlAllocateHeap(RtlProcessHeap(),
                                             0,
                                             NewDependencyListSize);
        if (!TempDependencyList) {
            WinError = ERROR_NOT_ENOUGH_MEMORY;
            goto Cleanup;
        }

        RtlZeroMemory(TempDependencyList, NewDependencyListSize);

        CurrentDependency = ServiceConfigInfo->lpDependencies;
        CurrNewList = TempDependencyList;

        while (CurrentDependency && *CurrentDependency != L'\0') {

            CurrentDependencyLength = wcslen(CurrentDependency) + 1; // for NULL

            // Get the current list size
            if (!_wcsicmp(CurrentDependency, Dependency)) {
                //
                // This is the one - don't copy it
                //
            } else {
                wcscpy(CurrNewList, CurrentDependency);
                CurrNewList += CurrentDependencyLength;
            }

            CurrentDependency += CurrentDependencyLength;
        }

    }

Cleanup:

    if (WinError != ERROR_SUCCESS && TempDependencyList) {
        RtlFreeHeap(RtlProcessHeap(), 0, TempDependencyList);
        *NewDependencyList = NULL;
    } else {
        *NewDependencyList = TempDependencyList;
    }

    if (ServiceConfigInfo) {

        RtlFreeHeap(RtlProcessHeap(), 0, ServiceConfigInfo);
    }

    return( WinError );
}


DWORD
DsRolepGetServiceConfig(
    IN SC_HANDLE hScMgr,
    IN LPWSTR ServiceName,
    IN SC_HANDLE ServiceHandle,
    IN LPQUERY_SERVICE_CONFIG *ServiceConfig
    )
/*++

Routine Description:

Parameters:

Return Values:

    ERROR_SUCCESS
    ERROR_NOT_ENOUGH_MEMORY

--*/
{
    DWORD Win32Error;
    SC_HANDLE hService;
    ULONG SizeNeeded;

#if DBG
    if (!ServiceHandle) {
        ASSERT(ServiceName);
        ASSERT(hScMgr);
    }
#endif

    if (!ServiceHandle) {

        hService = OpenService( hScMgr,
                                ServiceName,
                                SERVICE_QUERY_CONFIG );
    } else {

        hService = ServiceHandle;

    }

    if (hService) {

        SizeNeeded = 0;
        Win32Error = ERROR_SUCCESS;
        if (!QueryServiceConfig(hService,
                                NULL,
                                0,
                                &SizeNeeded)) {

            Win32Error = GetLastError();

        }
        ASSERT(Win32Error == ERROR_INSUFFICIENT_BUFFER);
        ASSERT( SizeNeeded > 0 );

        *ServiceConfig = RtlAllocateHeap(RtlProcessHeap(),
                                         0,
                                         SizeNeeded);
        if (*ServiceConfig) {

            Win32Error = ERROR_SUCCESS;
            if (!QueryServiceConfig(hService,
                                    *ServiceConfig,
                                    SizeNeeded,
                                    &SizeNeeded)) {

                Win32Error = GetLastError();
            }

        } else {

            Win32Error = ERROR_NOT_ENOUGH_MEMORY;
        }

        if (!ServiceHandle) {
            CloseServiceHandle(hService);
        }

    } else {

        Win32Error = GetLastError();

    }

    DsRolepLogOnFailure( Win32Error,
                         DsRolepLogPrint(( DEB_TRACE,
                                           "DsRolepGetServiceConfig on %ws failed with %lu\n",
                                            ServiceName,
                                            Win32Error )) );

    return Win32Error;

}