From 17c51414f248b814fea516d86b3bb5ecb7ca6b12 Mon Sep 17 00:00:00 2001 From: Eric Kohl Date: Sun, 17 Jul 2011 15:45:03 +0000 Subject: [PATCH] [SERVICES] Implement service image records. This will enable us to run executables that contain multiple services and control them individually. svn path=/trunk/; revision=52717 --- reactos/base/system/services/database.c | 391 +++++++++++++++-------- reactos/base/system/services/rpcserver.c | 14 +- reactos/base/system/services/services.h | 15 +- 3 files changed, 273 insertions(+), 147 deletions(-) diff --git a/reactos/base/system/services/database.c b/reactos/base/system/services/database.c index 0d7ec77e152..51c8d2cc364 100644 --- a/reactos/base/system/services/database.c +++ b/reactos/base/system/services/database.c @@ -25,6 +25,7 @@ /* GLOBALS *******************************************************************/ +LIST_ENTRY ImageListHead; LIST_ENTRY ServiceListHead; static RTL_RESOURCE DatabaseLock; @@ -34,6 +35,227 @@ static CRITICAL_SECTION ControlServiceCriticalSection; /* FUNCTIONS *****************************************************************/ +static DWORD +ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage) +{ + WCHAR szControlPipeName[MAX_PATH + 1]; + HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE; + DWORD ServiceCurrent = 0; + DWORD KeyDisposition; + DWORD dwKeySize; + DWORD dwError; + + /* Get the service number */ + /* TODO: Create registry entry with correct write access */ + dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL, + REG_OPTION_VOLATILE, + KEY_WRITE | KEY_READ, + NULL, + &hServiceCurrentKey, + &KeyDisposition); + if (dwError != ERROR_SUCCESS) + { + DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError); + return dwError; + } + + if (KeyDisposition == REG_OPENED_EXISTING_KEY) + { + dwKeySize = sizeof(DWORD); + dwError = RegQueryValueExW(hServiceCurrentKey, + L"", 0, NULL, (BYTE*)&ServiceCurrent, &dwKeySize); + + if (dwError != ERROR_SUCCESS) + { + RegCloseKey(hServiceCurrentKey); + DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError); + return dwError; + } + + ServiceCurrent++; + } + + dwError = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent)); + + RegCloseKey(hServiceCurrentKey); + + if (dwError != ERROR_SUCCESS) + { + DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError); + return dwError; + } + + /* Create '\\.\pipe\net\NtControlPipeXXX' instance */ + swprintf(szControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent); + + DPRINT("PipeName: %S\n", szControlPipeName); + + pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 100, + 8000, + 4, + 30000, + NULL); + DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName); + if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE) + { + DPRINT1("Failed to create control pipe!\n"); + return GetLastError(); + } + + return ERROR_SUCCESS; +} + + +static PSERVICE_IMAGE +ScmGetServiceImageByImagePath(LPWSTR lpImagePath) +{ + PLIST_ENTRY ImageEntry; + PSERVICE_IMAGE CurrentImage; + + DPRINT("ScmGetServiceImageByImagePath() called\n"); + + ImageEntry = ImageListHead.Flink; + while (ImageEntry != &ImageListHead) + { + CurrentImage = CONTAINING_RECORD(ImageEntry, + SERVICE_IMAGE, + ImageListEntry); + if (_wcsicmp(CurrentImage->szImagePath, lpImagePath) == 0) + { + DPRINT("Found image: '%S'\n", CurrentImage->szImagePath); + return CurrentImage; + } + + ImageEntry = ImageEntry->Flink; + } + + DPRINT1("Couldn't find a matching image\n"); + + return NULL; + +} + + +static DWORD +ScmCreateOrReferenceServiceImage(PSERVICE pService) +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + UNICODE_STRING ImagePath; + PSERVICE_IMAGE pServiceImage = NULL; + NTSTATUS Status; + DWORD dwError = ERROR_SUCCESS; + + DPRINT("ScmCreateOrReferenceServiceImage()"); + + RtlInitUnicodeString(&ImagePath, NULL); + + /* Get service data */ + RtlZeroMemory(&QueryTable, + sizeof(QueryTable)); + + QueryTable[0].Name = L"ImagePath"; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[0].EntryContext = &ImagePath; + + Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + pService->lpServiceName, + QueryTable, + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status); + return RtlNtStatusToDosError(Status); + } + + DPRINT("ImagePath: '%wZ'\n", &ImagePath); + + pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer); + if (pServiceImage == NULL) + { + /* Create a new service image */ + pServiceImage = (PSERVICE_IMAGE)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(SERVICE_IMAGE) + ((wcslen(ImagePath.Buffer) + 1) * sizeof(WCHAR))); + if (pServiceImage == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + pServiceImage->dwImageRunCount = 1; + pServiceImage->hControlPipe = INVALID_HANDLE_VALUE; + pServiceImage->hProcess = INVALID_HANDLE_VALUE; + + /* Set the image path */ + wcscpy(pServiceImage->szImagePath, + ImagePath.Buffer); + + RtlFreeUnicodeString(&ImagePath); + + /* Create the control pipe */ + dwError = ScmCreateNewControlPipe(pServiceImage); + if (dwError != ERROR_SUCCESS) + { + HeapFree(GetProcessHeap(), 0, pServiceImage); + goto done; + } + + /* FIXME: Add more initialization code here */ + + + /* Append service record */ + InsertTailList(&ImageListHead, + &pServiceImage->ImageListEntry); + + pService->lpImage = pServiceImage; + } + else + { + /* Create a new service image */ + pService->lpImage->dwImageRunCount++; + } + +done:; + RtlFreeUnicodeString(&ImagePath); + + return dwError; +} + + +static VOID +ScmDereferenceServiceImage(PSERVICE_IMAGE pServiceImage) +{ + DPRINT1("ScmDereferenceServiceImage() called\n"); + + pServiceImage->dwImageRunCount--; + + if (pServiceImage->dwImageRunCount == 0) + { + DPRINT1("dwImageRunCount == 0\n"); + + /* FIXME: Terminate the process */ + + /* Remove the service image from the list */ + RemoveEntryList(&pServiceImage->ImageListEntry); + + /* Close the control pipe */ + if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE) + CloseHandle(pServiceImage->hControlPipe); + + /* Close the process handle */ + if (pServiceImage->hProcess != INVALID_HANDLE_VALUE) + CloseHandle(pServiceImage->hProcess); + + /* Release the service image */ + HeapFree(GetProcessHeap(), 0, pServiceImage); + } +} + PSERVICE ScmGetServiceEntryByName(LPCWSTR lpServiceName) @@ -131,7 +353,7 @@ ScmCreateNewServiceRecord(LPCWSTR lpServiceName, DPRINT("Service: '%S'\n", lpServiceName); /* Allocate service entry */ - lpService = (SERVICE*) HeapAlloc(GetProcessHeap(), + lpService = (SERVICE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR))); if (lpService == NULL) @@ -173,9 +395,9 @@ ScmDeleteServiceRecord(PSERVICE lpService) lpService->lpDisplayName != lpService->lpServiceName) HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName); - /* Decrement the image reference counter */ + /* Dereference the service image */ if (lpService->lpImage) - lpService->lpImage->dwServiceRefCount--; + ScmDereferenceServiceImage(lpService->lpImage); /* Decrement the group reference counter */ if (lpService->lpGroup) @@ -183,9 +405,6 @@ ScmDeleteServiceRecord(PSERVICE lpService) /* FIXME: SecurityDescriptor */ - /* Close the control pipe */ - if (lpService->ControlPipeHandle != INVALID_HANDLE_VALUE) - CloseHandle(lpService->ControlPipeHandle); /* Remove the Service from the List */ RemoveEntryList(&lpService->ServiceListEntry); @@ -330,6 +549,12 @@ done:; if (lpDisplayName != NULL) HeapFree(GetProcessHeap(), 0, lpDisplayName); + if (lpService != NULL) + { + if (lpService->lpImage != NULL) + ScmDereferenceServiceImage(lpService->lpImage); + } + return dwError; } @@ -479,6 +704,7 @@ ScmCreateServiceDatabase(VOID) return dwError; /* Initialize basic variables */ + InitializeListHead(&ImageListHead); InitializeListHead(&ServiceListHead); /* Initialize the database lock */ @@ -711,14 +937,14 @@ ScmControlService(PSERVICE Service, wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName); /* Send the control packet */ - WriteFile(Service->ControlPipeHandle, + WriteFile(Service->lpImage->hControlPipe, ControlPacket, sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)), &dwWriteCount, NULL); /* Read the reply */ - ReadFile(Service->ControlPipeHandle, + ReadFile(Service->lpImage->hControlPipe, &ReplyPacket, sizeof(SCM_REPLY_PACKET), &dwReadCount, @@ -734,6 +960,12 @@ ScmControlService(PSERVICE Service, dwError = ReplyPacket.dwError; } + if (dwError == ERROR_SUCCESS && + dwControl == SERVICE_CONTROL_STOP) + { + ScmDereferenceServiceImage(Service->lpImage); + } + LeaveCriticalSection(&ControlServiceCriticalSection); DPRINT("ScmControlService() done\n"); @@ -804,14 +1036,14 @@ ScmSendStartCommand(PSERVICE Service, *Ptr = 0; /* Send the start command */ - WriteFile(Service->ControlPipeHandle, + WriteFile(Service->lpImage->hControlPipe, ControlPacket, sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR), &dwWriteCount, NULL); /* Read the reply */ - ReadFile(Service->ControlPipeHandle, + ReadFile(Service->lpImage->hControlPipe, &ReplyPacket, sizeof(SCM_REPLY_PACKET), &dwReadCount, @@ -838,108 +1070,12 @@ ScmStartUserModeService(PSERVICE Service, DWORD argc, LPWSTR *argv) { - RTL_QUERY_REGISTRY_TABLE QueryTable[3]; PROCESS_INFORMATION ProcessInformation; STARTUPINFOW StartupInfo; - UNICODE_STRING ImagePath; - ULONG Type; - DWORD ServiceCurrent = 0; BOOL Result; - NTSTATUS Status; DWORD dwError = ERROR_SUCCESS; - WCHAR NtControlPipeName[MAX_PATH + 1]; - HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE; - DWORD KeyDisposition; DWORD dwProcessId; - RtlInitUnicodeString(&ImagePath, NULL); - - /* Get service data */ - RtlZeroMemory(&QueryTable, - sizeof(QueryTable)); - - QueryTable[0].Name = L"Type"; - QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; - QueryTable[0].EntryContext = &Type; - - QueryTable[1].Name = L"ImagePath"; - QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; - QueryTable[1].EntryContext = &ImagePath; - - Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, - Service->lpServiceName, - QueryTable, - NULL, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status); - return RtlNtStatusToDosError(Status); - } - DPRINT("ImagePath: '%S'\n", ImagePath.Buffer); - DPRINT("Type: %lx\n", Type); - - /* Get the service number */ - /* TODO: Create registry entry with correct write access */ - Status = RegCreateKeyExW(HKEY_LOCAL_MACHINE, - L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL, - REG_OPTION_VOLATILE, - KEY_WRITE | KEY_READ, - NULL, - &hServiceCurrentKey, - &KeyDisposition); - - if (ERROR_SUCCESS != Status) - { - DPRINT1("RegCreateKeyEx() failed with status %u\n", Status); - return Status; - } - - if (REG_OPENED_EXISTING_KEY == KeyDisposition) - { - DWORD KeySize = sizeof(ServiceCurrent); - Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize); - - if (ERROR_SUCCESS != Status) - { - RegCloseKey(hServiceCurrentKey); - DPRINT1("RegQueryValueEx() failed with status %u\n", Status); - return Status; - } - - ServiceCurrent++; - } - - Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent)); - - RegCloseKey(hServiceCurrentKey); - - if (ERROR_SUCCESS != Status) - { - DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status); - return Status; - } - - /* Create '\\.\pipe\net\NtControlPipeXXX' instance */ - swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent); - - DPRINT("Service: %p ImagePath: %wZ PipeName: %S\n", Service, &ImagePath, NtControlPipeName); - - Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - 100, - 8000, - 4, - 30000, - NULL); - DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName); - if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE) - { - DPRINT1("Failed to create control pipe!\n"); - return GetLastError(); - } - StartupInfo.cb = sizeof(StartupInfo); StartupInfo.lpReserved = NULL; StartupInfo.lpDesktop = NULL; @@ -949,7 +1085,7 @@ ScmStartUserModeService(PSERVICE Service, StartupInfo.lpReserved2 = 0; Result = CreateProcessW(NULL, - ImagePath.Buffer, + Service->lpImage->szImagePath, NULL, NULL, FALSE, @@ -958,15 +1094,9 @@ ScmStartUserModeService(PSERVICE Service, NULL, &StartupInfo, &ProcessInformation); - RtlFreeUnicodeString(&ImagePath); - if (!Result) { dwError = GetLastError(); - /* Close control pipe */ - CloseHandle(Service->ControlPipeHandle); - Service->ControlPipeHandle = INVALID_HANDLE_VALUE; - DPRINT1("Starting '%S' failed!\n", Service->lpServiceName); return dwError; } @@ -978,15 +1108,15 @@ ScmStartUserModeService(PSERVICE Service, ProcessInformation.dwThreadId, ProcessInformation.hThread); - /* Get process and thread ids */ - Service->ProcessId = ProcessInformation.dwProcessId; - Service->ThreadId = ProcessInformation.dwThreadId; + /* Get process handle and id */ + Service->lpImage->dwProcessId = ProcessInformation.dwProcessId; + Service->lpImage->hProcess = ProcessInformation.hProcess; /* Resume Thread */ ResumeThread(ProcessInformation.hThread); /* Connect control pipe */ - if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ? + if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ? TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED) { DWORD dwRead = 0; @@ -994,7 +1124,7 @@ ScmStartUserModeService(PSERVICE Service, DPRINT("Control pipe connected!\n"); /* Read SERVICE_STATUS_HANDLE from pipe */ - if (!ReadFile(Service->ControlPipeHandle, + if (!ReadFile(Service->lpImage->hControlPipe, (LPVOID)&dwProcessId, sizeof(DWORD), &dwRead, @@ -1015,17 +1145,10 @@ ScmStartUserModeService(PSERVICE Service, else { DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError); - - /* Close control pipe */ - CloseHandle(Service->ControlPipeHandle); - Service->ControlPipeHandle = INVALID_HANDLE_VALUE; - Service->ProcessId = 0; - Service->ThreadId = 0; } - /* Close process and thread handle */ + /* Close thread handle */ CloseHandle(ProcessInformation.hThread); - CloseHandle(ProcessInformation.hProcess); return dwError; } @@ -1051,7 +1174,6 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv) return ERROR_SERVICE_ALREADY_RUNNING; } - Service->ControlPipeHandle = INVALID_HANDLE_VALUE; DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType); if (Service->Status.dwServiceType & SERVICE_DRIVER) @@ -1067,14 +1189,23 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv) else { /* Start user-mode service */ - dwError = ScmStartUserModeService(Service, argc, argv); + dwError = ScmCreateOrReferenceServiceImage(Service); if (dwError == ERROR_SUCCESS) { + dwError = ScmStartUserModeService(Service, argc, argv); + if (dwError == ERROR_SUCCESS) + { #ifdef USE_SERVICE_START_PENDING - Service->Status.dwCurrentState = SERVICE_START_PENDING; + Service->Status.dwCurrentState = SERVICE_START_PENDING; #else - Service->Status.dwCurrentState = SERVICE_RUNNING; + Service->Status.dwCurrentState = SERVICE_RUNNING; #endif + } + else + { + ScmDereferenceServiceImage(Service->lpImage); + Service->lpImage = NULL; + } } } diff --git a/reactos/base/system/services/rpcserver.c b/reactos/base/system/services/rpcserver.c index 58d8b5d3452..4c251278cab 100644 --- a/reactos/base/system/services/rpcserver.c +++ b/reactos/base/system/services/rpcserver.c @@ -723,15 +723,6 @@ DWORD RControlService( if ((dwError == ERROR_SUCCESS) && (pcbBytesNeeded)) dwError = ERROR_DEPENDENT_SERVICES_RUNNING; - if (dwError == ERROR_SUCCESS && - dwControl == SERVICE_CONTROL_STOP && - lpServiceStatus->dwCurrentState == SERVICE_STOPPED) - { - lpService->ProcessId = 0; /* FIXME */ - lpService->ThreadId = 0; - } - - return dwError; } @@ -4897,7 +4888,7 @@ DWORD RQueryServiceStatusEx( &lpService->Status, sizeof(SERVICE_STATUS)); - lpStatus->dwProcessId = lpService->ProcessId; /* FIXME */ + lpStatus->dwProcessId = (lpService->lpImage != NULL) ? lpService->lpImage->dwProcessId : 0; /* FIXME */ lpStatus->dwServiceFlags = 0; /* FIXME */ /* Unlock the service database */ @@ -5282,7 +5273,8 @@ DWORD REnumServicesStatusExW( memcpy(&lpStatusPtr->ServiceStatusProcess, &CurrentService->Status, sizeof(SERVICE_STATUS)); - lpStatusPtr->ServiceStatusProcess.dwProcessId = CurrentService->ProcessId; /* FIXME */ + lpStatusPtr->ServiceStatusProcess.dwProcessId = + (CurrentService->lpImage != NULL) ? CurrentService->lpImage->dwProcessId : 0; /* FIXME */ lpStatusPtr->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */ lpStatusPtr++; diff --git a/reactos/base/system/services/services.h b/reactos/base/system/services/services.h index f080af63bd9..dc9c6098da6 100644 --- a/reactos/base/system/services/services.h +++ b/reactos/base/system/services/services.h @@ -27,8 +27,14 @@ typedef struct _SERVICE_GROUP typedef struct _SERVICE_IMAGE { - DWORD dwServiceRefCount; // Number of running services of this image - DWORD Dummy; + LIST_ENTRY ImageListEntry; + DWORD dwImageRunCount; + + HANDLE hControlPipe; + HANDLE hProcess; + DWORD dwProcessId; + + WCHAR szImagePath[1]; } SERVICE_IMAGE, *PSERVICE_IMAGE; @@ -54,10 +60,6 @@ typedef struct _SERVICE BOOLEAN ServiceVisited; - HANDLE ControlPipeHandle; - ULONG ProcessId; - ULONG ThreadId; - WCHAR szServiceName[1]; } SERVICE, *PSERVICE; @@ -66,6 +68,7 @@ typedef struct _SERVICE extern LIST_ENTRY ServiceListHead; extern LIST_ENTRY GroupListHead; +extern LIST_ENTRY ImageListHead; extern BOOL ScmShutdown; -- 2.17.1