/*++ Copyright (c) 1992 Microsoft Corporation Module Name: ConfTest.c Abstract: This file contains tests of the Service Controller's config APIs: ChangeServiceConfig CreateService DeleteService QueryServiceConfig TestChangeConfig2 CompareFailureActions TestQueryConfig2 TestConfig2APIs Author: John Rogers (JohnRo) 22-Apr-1992 Environment: User Mode - Win32 Revision History: 22-Apr-1992 JohnRo Created. 17-Oct-1996 AnirudhS Added Config2 API tests. --*/ // // INCLUDES // // #define UNICODE #include #include // assert(). #include // _tcscmp #include // FORMAT_ equates. #include #include // STATIC. #include // TRACE_MSG macros, UNEXPECTED_MSG(), etc. #include // printf() #define BUF_SIZE 1024 // arbitrary //#define NEW_SERVICE_NAME TEXT("FLARP") #define NEW_SERVICE_NAME TEXT("Simple") #define TEST_SERVICE_DESCR TEXT("A servile service") #define LENGTH(array) (sizeof(array)/sizeof((array)[0])) #define OPTIONAL_LPTSTR(optStr) \ ( ( (optStr) != NULL ) ? (optStr) : TEXT("(NONE)") ) VOID DumpServiceConfig( IN LPQUERY_SERVICE_CONFIG Config ) { (VOID) printf( "Service config:\n" ); (VOID) printf( " Service type: " FORMAT_HEX_DWORD "\n", Config->dwServiceType ); (VOID) printf( " Start type: " FORMAT_DWORD "\n", Config->dwStartType ); (VOID) printf( " Error control: " FORMAT_DWORD "\n", Config->dwErrorControl ); (VOID) printf( " Binary path: " FORMAT_LPTSTR "\n", Config->lpBinaryPathName ); (VOID) printf( " Load order group: " FORMAT_LPTSTR "\n", OPTIONAL_LPTSTR( Config->lpBinaryPathName ) ); (VOID) printf( " Dependencies: " FORMAT_LPTSTR "\n", OPTIONAL_LPTSTR( Config->lpBinaryPathName ) ); (VOID) printf( " Service start name: " FORMAT_LPTSTR "\n", Config->lpBinaryPathName ); } // DumpServiceConfig SC_HANDLE TestCreateService( IN SC_HANDLE hScManager, IN LPTSTR ServiceName, IN DWORD ExpectedStatus ) { DWORD ApiStatus; SC_HANDLE hService; hService = CreateService( hScManager, // SC manager handle ServiceName, // service name NULL, // display name GENERIC_ALL, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_DISABLED, // start type SERVICE_ERROR_NORMAL, // error control TEXT("simple.exe"), // binary load path name // TEXT("\\nt\\system32\\bogus.exe"),// binary load path name NULL, // no load order group NULL, // no tag NULL, // no dependencies NULL, // start name (domain\username) // TEXT(".\\JohnRoDaemon"), // start name (domain\username) NULL); // no password. if (hService == NULL) { ApiStatus = GetLastError(); } else { ApiStatus = NO_ERROR; } TRACE_MSG2( "TestCreateService: back from CreateService, " "API status is " FORMAT_DWORD ", expecting " FORMAT_DWORD "\n", ApiStatus, ExpectedStatus ); assert( ApiStatus == ExpectedStatus ); return (hService); } VOID TestDeleteService( IN SC_HANDLE hScManager, IN LPTSTR ServiceName, IN DWORD DesiredAccess, IN DWORD OpenExpectedStatus, IN DWORD DeleteExpectedStatus ) { DWORD ApiStatus = NO_ERROR; SC_HANDLE hService; ////////////////////////////////////////////////////////////////////// hService = OpenService( hScManager, ServiceName, DesiredAccess ); if (hService == NULL) { ApiStatus = GetLastError(); } TRACE_MSG2( "TestDeleteService: back from OpenService, " "API status is " FORMAT_DWORD ", expecting " FORMAT_DWORD "\n", ApiStatus, OpenExpectedStatus ); assert( ApiStatus == OpenExpectedStatus ); if (ApiStatus != NO_ERROR) { return; } ////////////////////////////////////////////////////////////////////// if ( !DeleteService( hService ) ) { ApiStatus = GetLastError(); } TRACE_MSG2( "TestDeleteService: back from DeleteService, " "API status is " FORMAT_DWORD ", expecting " FORMAT_DWORD "\n", ApiStatus, DeleteExpectedStatus ); assert( ApiStatus == DeleteExpectedStatus ); ////////////////////////////////////////////////////////////////////// if ( !CloseServiceHandle( hService ) ) { ApiStatus = GetLastError(); } TRACE_MSG2( "TestDeleteService: back from CloseService, " "API status is " FORMAT_DWORD ", expecting " FORMAT_DWORD "\n", ApiStatus, NO_ERROR ); assert( ApiStatus == NO_ERROR ); } VOID TestQueryConfig( IN SC_HANDLE hService, IN DWORD ExpectedStatus ) { DWORD ApiStatus; BYTE buffer[BUF_SIZE]; DWORD sizeNeeded; TRACE_MSG0( "TestQueryConfig: calling QueryServiceConfig...\n" ); if ( !QueryServiceConfig( hService, (LPVOID) buffer, BUF_SIZE, & sizeNeeded ) ) { ApiStatus = GetLastError(); } else { ApiStatus = NO_ERROR; } TRACE_MSG1( "TestQueryConfig: back from QueryServiceConfig, " "API status is " FORMAT_DWORD "\n", ApiStatus ); assert( ApiStatus == ExpectedStatus ); if (ApiStatus == NO_ERROR) { DumpServiceConfig( (LPVOID) buffer ); } } // TestQueryConfig VOID TestConfigAPIs( VOID ) { SC_HANDLE hScManager = NULL; SC_HANDLE hService = NULL; TRACE_MSG1( "handle (before anything) is " FORMAT_HEX_DWORD ".\n", (DWORD) hScManager ); /////////////////////////////////////////////////////////////////// TRACE_MSG0( "calling OpenSCManager (default)...\n" ); hScManager = OpenSCManager( NULL, // local machine. NULL, // no database name. GENERIC_ALL ); // desired access. TRACE_MSG1( "back from OpenSCManager, handle is " FORMAT_HEX_DWORD ".\n", (DWORD) hScManager ); if (hScManager == NULL) { UNEXPECTED_MSG( "from OpenSCManager (default)", GetLastError() ); goto Cleanup; } /////////////////////////////////////////////////////////////////// #ifdef TEST_BINDING_HANDLES TestQueryConfig( NULL, ERROR_INVALID_HANDLE ); #endif /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NEW_SERVICE_NAME, DELETE, // desired access ERROR_SERVICE_DOES_NOT_EXIST, NO_ERROR ); /////////////////////////////////////////////////////////////////// #ifdef TEST_BINDING_HANDLES hService = TestCreateService( NULL, NEW_SERVICE_NAME, ERROR_INVALID_HANDLE ); #endif /////////////////////////////////////////////////////////////////// hService = TestCreateService( hScManager, NULL, ERROR_INVALID_NAME ); /////////////////////////////////////////////////////////////////// hService = TestCreateService( hScManager, NEW_SERVICE_NAME, NO_ERROR ); assert( hService != NULL ); /////////////////////////////////////////////////////////////////// TestQueryConfig( hService, NO_ERROR ); /////////////////////////////////////////////////////////////////// (VOID) TestCreateService( hScManager, NEW_SERVICE_NAME, ERROR_SERVICE_EXISTS ); /////////////////////////////////////////////////////////////////// TestDeleteService( NULL, NEW_SERVICE_NAME, DELETE, // desired access ERROR_INVALID_HANDLE, NO_ERROR ); /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NULL, DELETE, // desired access NO_ERROR, ERROR_INVALID_NAME ); /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NEW_SERVICE_NAME, GENERIC_READ, // desired access NO_ERROR, ERROR_ACCESS_DENIED ); /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NEW_SERVICE_NAME, DELETE, // desired access NO_ERROR, NO_ERROR ); /////////////////////////////////////////////////////////////////// TestQueryConfig( hService, NO_ERROR ); /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NEW_SERVICE_NAME, DELETE, // desired access ERROR_SERVICE_DOES_NOT_EXIST, NO_ERROR ); Cleanup: if (hService != NULL) { TRACE_MSG0( "calling CloseServiceHandle(hService)...\n" ); if ( !CloseServiceHandle( hService ) ) { UNEXPECTED_MSG( "from CloseServiceHandle", GetLastError() ); } } if (hScManager != NULL) { TRACE_MSG0( "calling CloseServiceHandle(hScManager)...\n" ); if ( !CloseServiceHandle( hScManager ) ) { UNEXPECTED_MSG( "from CloseServiceHandle", GetLastError() ); } } } // TestConfigAPIs VOID TestChangeConfig2( IN SC_HANDLE hService, IN DWORD dwInfoLevel, IN LPVOID lpInfo, IN DWORD ExpectedStatus ) { DWORD ApiStatus; TRACE_MSG0( "TestChangeConfig2: Calling ChangeServiceConfig2...\n" ); if ( !ChangeServiceConfig2( hService, dwInfoLevel, lpInfo ) ) { ApiStatus = GetLastError(); } else { ApiStatus = NO_ERROR; } TRACE_MSG2( "TestChangeConfig2: back from ChangeServiceConfig2, " "API status %lu, expecting %lu\n", ApiStatus, ExpectedStatus ); assert( ApiStatus == ExpectedStatus ); /* if (ApiStatus == NO_ERROR) { DumpServiceConfig( (LPVOID) buffer ); } */ } // TestChangeConfig2 VOID CompareFailureActions( IN LPSERVICE_FAILURE_ACTIONS psfa1, // Expected result buffer IN LPSERVICE_FAILURE_ACTIONS psfa2 // Actual result buffer ) { if (psfa1 == NULL) { return; } assert(psfa2 != NULL); assert(psfa1->dwResetPeriod == psfa2->dwResetPeriod); if (psfa1->lpRebootMsg) { assert(_tcscmp(psfa1->lpRebootMsg, psfa2->lpRebootMsg) == 0); } else { assert(psfa2->lpRebootMsg == NULL); } if (psfa1->lpCommand) { assert(_tcscmp(psfa1->lpCommand, psfa2->lpCommand) == 0); } else { assert(psfa2->lpCommand == NULL); } assert(psfa1->cActions == psfa2->cActions); if (psfa1->cActions) { DWORD i; assert(psfa2->lpsaActions != NULL); for (i = 0; i < psfa1->cActions; i++) { assert(psfa1->lpsaActions[i].Type == psfa2->lpsaActions[i].Type); assert(psfa1->lpsaActions[i].Delay == psfa2->lpsaActions[i].Delay); } } else { assert(psfa2->lpsaActions == NULL); } } VOID TestQueryConfig2( IN SC_HANDLE hService, IN DWORD dwInfoLevel, // which configuration data is requested OUT LPBYTE lpBuffer, // pointer to service configuration buffer IN DWORD cbBufSize, // size of service configuration buffer OUT LPDWORD pcbBytesNeeded, // address of variable for bytes needed IN DWORD ExpectedStatus, IN LPVOID ExpectedBuffer ) { DWORD ApiStatus; TRACE_MSG0( "TestQueryConfig2: calling QueryServiceConfig2...\n" ); if ( !QueryServiceConfig2( hService, dwInfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded ) ) { ApiStatus = GetLastError(); } else { ApiStatus = NO_ERROR; } TRACE_MSG2( "TestQueryConfig2: back from QueryServiceConfig2, " "API status %lu, expecting %lu\n", ApiStatus, ExpectedStatus ); assert( ApiStatus == ExpectedStatus ); if (ApiStatus == NO_ERROR) { switch (dwInfoLevel) { case SERVICE_CONFIG_DESCRIPTION: { LPSERVICE_DESCRIPTION psd = (LPSERVICE_DESCRIPTION) lpBuffer; if (ExpectedBuffer == NULL) { assert(psd->lpDescription == NULL); } else { assert(psd->lpDescription != NULL); assert(_tcscmp(psd->lpDescription, (LPTSTR)ExpectedBuffer) == 0); } } break; case SERVICE_CONFIG_FAILURE_ACTIONS: CompareFailureActions((LPSERVICE_FAILURE_ACTIONS)ExpectedBuffer, (LPSERVICE_FAILURE_ACTIONS)lpBuffer); break; default: break; } } /* if (ApiStatus == NO_ERROR) { DumpServiceConfig( (LPVOID) buffer ); } */ } // TestQueryConfig2 VOID TestConfig2APIs( VOID ) { SC_HANDLE hScManager = NULL; SC_HANDLE hService = NULL; SC_ACTION saActions[] = { { SC_ACTION_RESTART, 10000 }, { SC_ACTION_NONE, 42 }, { SC_ACTION_REBOOT, 500 } }; SERVICE_FAILURE_ACTIONS sfa = { 501, // reset period TEXT("Put your shoes back on!"), // reboot message TEXT("Do badly."), // failure command LENGTH(saActions), // number of actions saActions // action array }; BYTE Buffer[200]; DWORD cbBytesNeeded; TRACE_MSG1( "handle (before anything) is " FORMAT_HEX_DWORD ".\n", (DWORD) hScManager ); /////////////////////////////////////////////////////////////////// TRACE_MSG0( "calling OpenSCManagerW (default)...\n" ); hScManager = OpenSCManager( NULL, // local machine. NULL, // no database name. GENERIC_ALL ); // desired access. TRACE_MSG1( "back from OpenSCManagerW, handle is " FORMAT_HEX_DWORD ".\n", (DWORD) hScManager ); if (hScManager == NULL) { UNEXPECTED_MSG( "from OpenSCManagerW (default)", GetLastError() ); goto Cleanup; } /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NEW_SERVICE_NAME, DELETE, // desired access ERROR_SERVICE_DOES_NOT_EXIST, NO_ERROR ); /////////////////////////////////////////////////////////////////// hService = TestCreateService( hScManager, NEW_SERVICE_NAME, NO_ERROR ); assert( hService != NULL ); memset(Buffer, -1, sizeof(Buffer)); TestQueryConfig2( hService, SERVICE_CONFIG_DESCRIPTION, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, NULL ); /////////////////////////////////////////////////////////////////// printf("Setting service description\n"); { SERVICE_DESCRIPTION sd = { TEST_SERVICE_DESCR }; TestChangeConfig2( hService, SERVICE_CONFIG_DESCRIPTION, &sd, NO_ERROR ); } TestQueryConfig2( hService, SERVICE_CONFIG_DESCRIPTION, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, TEST_SERVICE_DESCR ); // For consistency with QueryServiceConfig, the API should set // cbBytesNeeded even on a success return, even though this is not // documented #ifdef UNICODE assert(cbBytesNeeded == sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR)); #else assert(cbBytesNeeded >= sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR) && cbBytesNeeded <= sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR)*2); #endif /////////////////////////////////////////////////////////////////// printf("Setting service failure actions\n"); TestChangeConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa, NO_ERROR ); TestQueryConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, &sfa ); /////////////////////////////////////////////////////////////////// printf("Testing count 3 and NULL pointer to array\n"); sfa.lpsaActions = NULL; TestChangeConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa, NO_ERROR ); // Resulting config should not have changed sfa.lpsaActions = saActions; TestQueryConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, &sfa ); /////////////////////////////////////////////////////////////////// printf("Testing count 0 and NULL pointer to array\n"); sfa.cActions = 0; sfa.lpsaActions = NULL; TestChangeConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa, NO_ERROR ); // Resulting config should not have changed sfa.cActions = LENGTH(saActions); sfa.lpsaActions = saActions; TestQueryConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, &sfa ); /////////////////////////////////////////////////////////////////// printf("Testing count 0 and non-NULL pointer to array\n"); sfa.cActions = 0; sfa.lpsaActions = (LPSC_ACTION) 0xfefefefe; // a bad pointer TestChangeConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa, NO_ERROR ); // Resulting config should have no actions at all // However, it should still have a failure command and a reboot message sfa.dwResetPeriod = 0; sfa.lpsaActions = NULL; TestQueryConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, &sfa ); /////////////////////////////////////////////////////////////////// printf("Testing empty failure command and unchanged reboot message\n"); sfa.lpCommand = TEXT(""); sfa.lpRebootMsg = NULL; TestChangeConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa, NO_ERROR ); // Resulting config should have an empty failure command // However, it should still have a reboot message sfa.lpCommand = NULL; sfa.lpRebootMsg = TEXT("Put your shoes back on!"); TestQueryConfig2( hService, SERVICE_CONFIG_FAILURE_ACTIONS, Buffer, sizeof(Buffer), &cbBytesNeeded, NO_ERROR, &sfa ); /////////////////////////////////////////////////////////////////// printf("Testing invalid level\n"); TestChangeConfig2( hService, 101, &sfa, ERROR_INVALID_LEVEL ); /////////////////////////////////////////////////////////////////// memset(Buffer, -1, sizeof(Buffer)); TestQueryConfig2( hService, SERVICE_CONFIG_DESCRIPTION, Buffer, sizeof(Buffer), // plenty big buffer &cbBytesNeeded, NO_ERROR, TEST_SERVICE_DESCR ); /////////////////////////////////////////////////////////////////// memset(Buffer, -1, sizeof(Buffer)); TestQueryConfig2( hService, SERVICE_CONFIG_DESCRIPTION, Buffer, sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR), // just big enough &cbBytesNeeded, NO_ERROR, TEST_SERVICE_DESCR ); /////////////////////////////////////////////////////////////////// memset(Buffer, -1, sizeof(Buffer)); cbBytesNeeded = 0; TestQueryConfig2( hService, SERVICE_CONFIG_DESCRIPTION, Buffer, sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR) - 1, // 1 byte too small &cbBytesNeeded, ERROR_INSUFFICIENT_BUFFER, NULL ); #ifdef UNICODE assert(cbBytesNeeded == sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR)); #else assert(cbBytesNeeded >= sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR) && cbBytesNeeded <= sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR)*2); #endif /////////////////////////////////////////////////////////////////// memset(Buffer, -1, sizeof(Buffer)); cbBytesNeeded = 0; TestQueryConfig2( hService, SERVICE_CONFIG_DESCRIPTION, Buffer, 0, // zero size &cbBytesNeeded, ERROR_INSUFFICIENT_BUFFER, NULL ); #ifdef UNICODE assert(cbBytesNeeded == sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR)); #else assert(cbBytesNeeded >= sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR) && cbBytesNeeded <= sizeof(SERVICE_DESCRIPTION) + sizeof(TEST_SERVICE_DESCR)*2); #endif /////////////////////////////////////////////////////////////////// TestDeleteService( hScManager, NEW_SERVICE_NAME, DELETE, // desired access NO_ERROR, NO_ERROR ); Cleanup: if (hService != NULL) { TRACE_MSG0( "calling CloseServiceHandle(hService)...\n" ); if ( !CloseServiceHandle( hService ) ) { UNEXPECTED_MSG( "from CloseServiceHandle", GetLastError() ); } } if (hScManager != NULL) { TRACE_MSG0( "calling CloseServiceHandle(hScManager)...\n" ); if ( !CloseServiceHandle( hScManager ) ) { UNEXPECTED_MSG( "from CloseServiceHandle", GetLastError() ); } } } // TestConfig2APIs