windows-nt/Source/XPSP1/NT/base/screg/sc/sctest/conftest.c
2020-09-26 16:20:57 +08:00

813 lines
23 KiB
C

/*++
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 <windows.h>
#include <assert.h> // assert().
#include <tchar.h> // _tcscmp
#include <debugfmt.h> // FORMAT_ equates.
#include <winsvc.h>
#include <scdebug.h> // STATIC.
#include <sctest.h> // TRACE_MSG macros, UNEXPECTED_MSG(), etc.
#include <stdio.h> // 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