/* * PROJECT: ReactOS Service Control Manager * LICENSE: GPL - See COPYING in the top level directory * FILE: base/system/services/database.c * PURPOSE: Database control interface * COPYRIGHT: Copyright 2002-2006 Eric Kohl * Copyright 2006 Hervé Poussineau * Copyright 2007 Ged Murphy * Gregor Brunmar * */ /* INCLUDES *****************************************************************/ #include "services.h" #define NDEBUG #include /* * Uncomment the line below to start services * using the SERVICE_START_PENDING state */ // #define USE_SERVICE_START_PENDING /* GLOBALS *******************************************************************/ LIST_ENTRY ServiceListHead; static RTL_RESOURCE DatabaseLock; static DWORD dwResumeCount = 1; /* FUNCTIONS *****************************************************************/ PSERVICE ScmGetServiceEntryByName(LPCWSTR lpServiceName) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; DPRINT("ScmGetServiceEntryByName() called\n"); ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0) { DPRINT("Found service: '%S'\n", CurrentService->lpServiceName); return CurrentService; } ServiceEntry = ServiceEntry->Flink; } DPRINT("Couldn't find a matching service\n"); return NULL; } PSERVICE ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; DPRINT("ScmGetServiceEntryByDisplayName() called\n"); ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0) { DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName); return CurrentService; } ServiceEntry = ServiceEntry->Flink; } DPRINT("Couldn't find a matching service\n"); return NULL; } PSERVICE ScmGetServiceEntryByResumeCount(DWORD dwResumeCount) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; DPRINT("ScmGetServiceEntryByResumeCount() called\n"); ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if (CurrentService->dwResumeCount > dwResumeCount) { DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName); return CurrentService; } ServiceEntry = ServiceEntry->Flink; } DPRINT("Couldn't find a matching service\n"); return NULL; } PSERVICE ScmGetServiceEntryByClientHandle(HANDLE Handle) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; DPRINT("ScmGetServiceEntryByClientHandle() called\n"); DPRINT("looking for %p\n", Handle); ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if (CurrentService->hClient == Handle) { DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName); return CurrentService; } ServiceEntry = ServiceEntry->Flink; } DPRINT("Couldn't find a matching service\n"); return NULL; } DWORD ScmCreateNewServiceRecord(LPCWSTR lpServiceName, PSERVICE *lpServiceRecord) { PSERVICE lpService = NULL; DPRINT("Service: '%S'\n", lpServiceName); /* Allocate service entry */ lpService = (SERVICE*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR))); if (lpService == NULL) return ERROR_NOT_ENOUGH_MEMORY; *lpServiceRecord = lpService; /* Copy service name */ wcscpy(lpService->szServiceName, lpServiceName); lpService->lpServiceName = lpService->szServiceName; lpService->lpDisplayName = lpService->lpServiceName; /* Set the resume count */ lpService->dwResumeCount = dwResumeCount++; /* Append service record */ InsertTailList(&ServiceListHead, &lpService->ServiceListEntry); /* Initialize the service status */ lpService->Status.dwCurrentState = SERVICE_STOPPED; lpService->Status.dwControlsAccepted = 0; lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED; lpService->Status.dwServiceSpecificExitCode = 0; lpService->Status.dwCheckPoint = 0; lpService->Status.dwWaitHint = 2000; /* 2 seconds */ return ERROR_SUCCESS; } VOID ScmDeleteServiceRecord(PSERVICE lpService) { DPRINT("Deleting Service %S\n", lpService->lpServiceName); /* Delete the display name */ if (lpService->lpDisplayName != NULL && lpService->lpDisplayName != lpService->lpServiceName) HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName); /* Decrement the image reference counter */ if (lpService->lpImage) lpService->lpImage->dwServiceRefCount--; /* Decrement the group reference counter */ if (lpService->lpGroup) lpService->lpGroup->dwRefCount--; /* FIXME: SecurityDescriptor */ /* Close the control pipe */ if (lpService->ControlPipeHandle != INVALID_HANDLE_VALUE) CloseHandle(lpService->ControlPipeHandle); /* Remove the Service from the List */ RemoveEntryList(&lpService->ServiceListEntry); DPRINT("Deleted Service %S\n", lpService->lpServiceName); /* Delete the service record */ HeapFree(GetProcessHeap(), 0, lpService); DPRINT("Done\n"); } static DWORD CreateServiceListEntry(LPCWSTR lpServiceName, HKEY hServiceKey) { PSERVICE lpService = NULL; LPWSTR lpDisplayName = NULL; LPWSTR lpGroup = NULL; DWORD dwSize; DWORD dwError; DWORD dwServiceType; DWORD dwStartType; DWORD dwErrorControl; DWORD dwTagId; DPRINT("Service: '%S'\n", lpServiceName); if (*lpServiceName == L'{') return ERROR_SUCCESS; dwSize = sizeof(DWORD); dwError = RegQueryValueExW(hServiceKey, L"Type", NULL, NULL, (LPBYTE)&dwServiceType, &dwSize); if (dwError != ERROR_SUCCESS) return ERROR_SUCCESS; if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) && ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) && (dwServiceType != SERVICE_KERNEL_DRIVER) && (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER)) return ERROR_SUCCESS; DPRINT("Service type: %lx\n", dwServiceType); dwSize = sizeof(DWORD); dwError = RegQueryValueExW(hServiceKey, L"Start", NULL, NULL, (LPBYTE)&dwStartType, &dwSize); if (dwError != ERROR_SUCCESS) return ERROR_SUCCESS; DPRINT("Start type: %lx\n", dwStartType); dwSize = sizeof(DWORD); dwError = RegQueryValueExW(hServiceKey, L"ErrorControl", NULL, NULL, (LPBYTE)&dwErrorControl, &dwSize); if (dwError != ERROR_SUCCESS) return ERROR_SUCCESS; DPRINT("Error control: %lx\n", dwErrorControl); dwError = RegQueryValueExW(hServiceKey, L"Tag", NULL, NULL, (LPBYTE)&dwTagId, &dwSize); if (dwError != ERROR_SUCCESS) dwTagId = 0; DPRINT("Tag: %lx\n", dwTagId); dwError = ScmReadString(hServiceKey, L"Group", &lpGroup); if (dwError != ERROR_SUCCESS) lpGroup = NULL; DPRINT("Group: %S\n", lpGroup); dwError = ScmReadString(hServiceKey, L"DisplayName", &lpDisplayName); if (dwError != ERROR_SUCCESS) lpDisplayName = NULL; DPRINT("Display name: %S\n", lpDisplayName); dwError = ScmCreateNewServiceRecord(lpServiceName, &lpService); if (dwError != ERROR_SUCCESS) goto done; lpService->Status.dwServiceType = dwServiceType; lpService->dwStartType = dwStartType; lpService->dwErrorControl = dwErrorControl; lpService->dwTag = dwTagId; if (lpGroup != NULL) { dwError = ScmSetServiceGroup(lpService, lpGroup); if (dwError != ERROR_SUCCESS) goto done; } if (lpDisplayName != NULL) { lpService->lpDisplayName = lpDisplayName; lpDisplayName = NULL; } DPRINT("ServiceName: '%S'\n", lpService->lpServiceName); if (lpService->lpGroup != NULL) { DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName); } DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n", lpService->dwStartType, lpService->Status.dwServiceType, lpService->dwTag, lpService->dwErrorControl); if (ScmIsDeleteFlagSet(hServiceKey)) lpService->bDeleted = TRUE; done:; if (lpGroup != NULL) HeapFree(GetProcessHeap(), 0, lpGroup); if (lpDisplayName != NULL) HeapFree(GetProcessHeap(), 0, lpDisplayName); return dwError; } DWORD ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey) { DWORD dwRet, dwMaxSubkeyLen = 0, dwSize; WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf; HKEY hSubKey = 0; dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); if (!dwRet) { /* Find the maximum subkey length so that we can allocate a buffer */ dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL, &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL); if (!dwRet) { dwMaxSubkeyLen++; if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR)) /* Name too big: alloc a buffer for it */ lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR)); if(!lpszName) dwRet = ERROR_NOT_ENOUGH_MEMORY; else { while (dwRet == ERROR_SUCCESS) { dwSize = dwMaxSubkeyLen; dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL); if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA) dwRet = ScmDeleteRegKey(hSubKey, lpszName); } if (dwRet == ERROR_NO_MORE_ITEMS) dwRet = ERROR_SUCCESS; if (lpszName != szNameBuf) HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */ } } RegCloseKey(hSubKey); if (!dwRet) dwRet = RegDeleteKeyW(hKey, lpszSubKey); } return dwRet; } VOID ScmDeleteMarkedServices(VOID) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; HKEY hServicesKey; DWORD dwError; ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); ServiceEntry = ServiceEntry->Flink; if (CurrentService->bDeleted == TRUE) { dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services", 0, DELETE, &hServicesKey); if (dwError == ERROR_SUCCESS) { dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName); RegCloseKey(hServicesKey); if (dwError == ERROR_SUCCESS) { RemoveEntryList(&CurrentService->ServiceListEntry); HeapFree(GetProcessHeap(), 0, CurrentService); } } if (dwError != ERROR_SUCCESS) DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName); } } } VOID WaitForLSA(VOID) { HANDLE hEvent; DWORD dwError; DPRINT("WaitForLSA() called\n"); hEvent = CreateEventW(NULL, TRUE, FALSE, L"LSA_RPC_SERVER_ACTIVE"); if (hEvent == NULL) { dwError = GetLastError(); DPRINT1("Failed to create the notication event (Error %lu)\n", dwError); if (dwError == ERROR_ALREADY_EXISTS) { hEvent = OpenEventW(SYNCHRONIZE, FALSE, L"LSA_RPC_SERVER_ACTIVE"); if (hEvent != NULL) { DPRINT1("Could not open the notification event!\n"); return; } } } DPRINT("Wait for LSA!\n"); WaitForSingleObject(hEvent, INFINITE); DPRINT("LSA is available!\n"); CloseHandle(hEvent); DPRINT("WaitForLSA() done\n"); } DWORD ScmCreateServiceDatabase(VOID) { WCHAR szSubKey[MAX_PATH]; HKEY hServicesKey; HKEY hServiceKey; DWORD dwSubKey; DWORD dwSubKeyLength; FILETIME ftLastChanged; DWORD dwError; DPRINT("ScmCreateServiceDatabase() called\n"); dwError = ScmCreateGroupList(); if (dwError != ERROR_SUCCESS) return dwError; /* Initialize basic variables */ InitializeListHead(&ServiceListHead); /* Initialize the database lock */ RtlInitializeResource(&DatabaseLock); dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services", 0, KEY_READ, &hServicesKey); if (dwError != ERROR_SUCCESS) return dwError; dwSubKey = 0; for (;;) { dwSubKeyLength = MAX_PATH; dwError = RegEnumKeyExW(hServicesKey, dwSubKey, szSubKey, &dwSubKeyLength, NULL, NULL, NULL, &ftLastChanged); if (dwError == ERROR_SUCCESS && szSubKey[0] != L'{') { DPRINT("SubKeyName: '%S'\n", szSubKey); dwError = RegOpenKeyExW(hServicesKey, szSubKey, 0, KEY_READ, &hServiceKey); if (dwError == ERROR_SUCCESS) { dwError = CreateServiceListEntry(szSubKey, hServiceKey); RegCloseKey(hServiceKey); } } if (dwError != ERROR_SUCCESS) break; dwSubKey++; } RegCloseKey(hServicesKey); /* Wait for LSA */ WaitForLSA(); /* Delete services that are marked for delete */ ScmDeleteMarkedServices(); DPRINT("ScmCreateServiceDatabase() done\n"); return ERROR_SUCCESS; } VOID ScmShutdownServiceDatabase(VOID) { DPRINT("ScmShutdownServiceDatabase() called\n"); ScmDeleteMarkedServices(); RtlDeleteResource(&DatabaseLock); DPRINT("ScmShutdownServiceDatabase() done\n"); } static NTSTATUS ScmCheckDriver(PSERVICE Service) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DirName; HANDLE DirHandle; NTSTATUS Status; POBJECT_DIRECTORY_INFORMATION DirInfo; ULONG BufferLength; ULONG DataLength; ULONG Index; DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName); if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER) { RtlInitUnicodeString(&DirName, L"\\Driver"); } else { RtlInitUnicodeString(&DirName, L"\\FileSystem"); } InitializeObjectAttributes(&ObjectAttributes, &DirName, 0, NULL, NULL); Status = NtOpenDirectoryObject(&DirHandle, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { return Status; } BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) + 2 * MAX_PATH * sizeof(WCHAR); DirInfo = (OBJECT_DIRECTORY_INFORMATION*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BufferLength); Index = 0; while (TRUE) { Status = NtQueryDirectoryObject(DirHandle, DirInfo, BufferLength, TRUE, FALSE, &Index, &DataLength); if (Status == STATUS_NO_MORE_ENTRIES) { /* FIXME: Add current service to 'failed service' list */ DPRINT("Service '%S' failed\n", Service->lpServiceName); break; } if (!NT_SUCCESS(Status)) break; DPRINT("Comparing: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name); if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0) { DPRINT("Found: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name); /* Mark service as 'running' */ Service->Status.dwCurrentState = SERVICE_RUNNING; /* Mark the service group as 'running' */ if (Service->lpGroup != NULL) { Service->lpGroup->ServicesRunning = TRUE; } break; } } HeapFree(GetProcessHeap(), 0, DirInfo); NtClose(DirHandle); return STATUS_SUCCESS; } VOID ScmGetBootAndSystemDriverState(VOID) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; DPRINT("ScmGetBootAndSystemDriverState() called\n"); ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if (CurrentService->dwStartType == SERVICE_BOOT_START || CurrentService->dwStartType == SERVICE_SYSTEM_START) { /* Check driver */ DPRINT(" Checking service: %S\n", CurrentService->lpServiceName); ScmCheckDriver(CurrentService); } ServiceEntry = ServiceEntry->Flink; } DPRINT("ScmGetBootAndSystemDriverState() done\n"); } DWORD ScmControlService(PSERVICE Service, DWORD dwControl, LPSERVICE_STATUS lpServiceStatus) { PSCM_CONTROL_PACKET ControlPacket; DWORD Count; DWORD TotalLength; DPRINT("ScmControlService() called\n"); TotalLength = wcslen(Service->lpServiceName) + 1; ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR))); if (ControlPacket == NULL) return ERROR_NOT_ENOUGH_MEMORY; ControlPacket->dwControl = dwControl; ControlPacket->hClient = Service->hClient; ControlPacket->dwSize = TotalLength; wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName); /* Send the control packet */ WriteFile(Service->ControlPipeHandle, ControlPacket, sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)), &Count, NULL); /* FIXME: Read the reply */ /* Release the contol packet */ HeapFree(GetProcessHeap(), 0, ControlPacket); RtlCopyMemory(lpServiceStatus, &Service->Status, sizeof(SERVICE_STATUS)); DPRINT("ScmControlService) done\n"); return ERROR_SUCCESS; } static DWORD ScmSendStartCommand(PSERVICE Service, DWORD argc, LPWSTR *argv) { PSCM_CONTROL_PACKET ControlPacket; DWORD TotalLength; DWORD ArgsLength = 0; DWORD Length; PWSTR Ptr; DWORD Count; DPRINT("ScmSendStartCommand() called\n"); /* Calculate the total length of the start command line */ TotalLength = wcslen(Service->lpServiceName) + 1; if (argc > 0) { for (Count = 0; Count < argc; Count++) { DPRINT("Arg: %S\n", argv[Count]); Length = wcslen(argv[Count]) + 1; TotalLength += Length; ArgsLength += Length; } } TotalLength++; DPRINT("ArgsLength: %ld TotalLength: %ld\n", ArgsLength, TotalLength); /* Allocate a control packet */ ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR)); if (ControlPacket == NULL) return ERROR_NOT_ENOUGH_MEMORY; ControlPacket->dwControl = SERVICE_CONTROL_START; ControlPacket->hClient = Service->hClient; ControlPacket->dwSize = TotalLength; Ptr = &ControlPacket->szArguments[0]; wcscpy(Ptr, Service->lpServiceName); Ptr += (wcslen(Service->lpServiceName) + 1); /* Copy argument list */ if (argc > 0) { UNIMPLEMENTED; DPRINT1("Arguments sent to service ignored!\n"); #if 0 memcpy(Ptr, Arguments, ArgsLength); Ptr += ArgsLength; #endif } /* Terminate the argument list */ *Ptr = 0; /* Send the start command */ WriteFile(Service->ControlPipeHandle, ControlPacket, sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR), &Count, NULL); /* FIXME: Read the reply */ /* Release the contol packet */ HeapFree(GetProcessHeap(), 0, ControlPacket); DPRINT("ScmSendStartCommand() done\n"); return ERROR_SUCCESS; } static DWORD 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; 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); 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; StartupInfo.lpTitle = NULL; StartupInfo.dwFlags = 0; StartupInfo.cbReserved2 = 0; StartupInfo.lpReserved2 = 0; Result = CreateProcessW(NULL, ImagePath.Buffer, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED, NULL, 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; } DPRINT("Process Id: %lu Handle %lx\n", ProcessInformation.dwProcessId, ProcessInformation.hProcess); DPRINT("Thread Id: %lu Handle %lx\n", ProcessInformation.dwThreadId, ProcessInformation.hThread); /* Get process and thread ids */ Service->ProcessId = ProcessInformation.dwProcessId; Service->ThreadId = ProcessInformation.dwThreadId; /* Resume Thread */ ResumeThread(ProcessInformation.hThread); /* Connect control pipe */ if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ? TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED) { DWORD dwRead = 0; DPRINT("Control pipe connected!\n"); /* Read SERVICE_STATUS_HANDLE from pipe */ if (!ReadFile(Service->ControlPipeHandle, (LPVOID)&Service->hClient, sizeof(DWORD), &dwRead, NULL)) { dwError = GetLastError(); DPRINT1("Reading the service control pipe failed (Error %lu)\n", dwError); } else { DPRINT("Received service status %lu\n", Service->hClient); /* Send start command */ dwError = ScmSendStartCommand(Service, argc, argv); } } 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 */ CloseHandle(ProcessInformation.hThread); CloseHandle(ProcessInformation.hProcess); return dwError; } DWORD ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv) { PSERVICE_GROUP Group = Service->lpGroup; DWORD dwError = ERROR_SUCCESS; DPRINT("ScmStartService() called\n"); Service->ControlPipeHandle = INVALID_HANDLE_VALUE; DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType); if (Service->Status.dwServiceType & SERVICE_DRIVER) { /* Load driver */ dwError = ScmLoadDriver(Service); if (dwError == ERROR_SUCCESS) { Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP; Service->Status.dwCurrentState = SERVICE_RUNNING; } } else { /* Start user-mode service */ dwError = ScmStartUserModeService(Service, argc, argv); if (dwError == ERROR_SUCCESS) { #ifdef USE_SERVICE_START_PENDING Service->Status.dwCurrentState = SERVICE_START_PENDING; #else Service->Status.dwCurrentState = SERVICE_RUNNING; #endif } } DPRINT("ScmStartService() done (Error %lu)\n", dwError); if (dwError == ERROR_SUCCESS) { if (Group != NULL) { Group->ServicesRunning = TRUE; } } #if 0 else { switch (Service->ErrorControl) { case SERVICE_ERROR_NORMAL: /* FIXME: Log error */ break; case SERVICE_ERROR_SEVERE: if (IsLastKnownGood == FALSE) { /* FIXME: Boot last known good configuration */ } break; case SERVICE_ERROR_CRITICAL: if (IsLastKnownGood == FALSE) { /* FIXME: Boot last known good configuration */ } else { /* FIXME: BSOD! */ } break; } } #endif return dwError; } VOID ScmAutoStartServices(VOID) { PLIST_ENTRY GroupEntry; PLIST_ENTRY ServiceEntry; PSERVICE_GROUP CurrentGroup; PSERVICE CurrentService; ULONG i; /* Clear 'ServiceVisited' flag */ ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); CurrentService->ServiceVisited = FALSE; ServiceEntry = ServiceEntry->Flink; } /* Start all services which are members of an existing group */ GroupEntry = GroupListHead.Flink; while (GroupEntry != &GroupListHead) { CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry); DPRINT("Group '%S'\n", CurrentGroup->lpGroupName); /* Start all services witch have a valid tag */ for (i = 0; i < CurrentGroup->TagCount; i++) { ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if ((CurrentService->lpGroup == CurrentGroup) && (CurrentService->dwStartType == SERVICE_AUTO_START) && (CurrentService->ServiceVisited == FALSE) && (CurrentService->dwTag == CurrentGroup->TagArray[i])) { CurrentService->ServiceVisited = TRUE; ScmStartService(CurrentService, 0, NULL); } ServiceEntry = ServiceEntry->Flink; } } /* Start all services which have an invalid tag or which do not have a tag */ ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if ((CurrentService->lpGroup == CurrentGroup) && (CurrentService->dwStartType == SERVICE_AUTO_START) && (CurrentService->ServiceVisited == FALSE)) { CurrentService->ServiceVisited = TRUE; ScmStartService(CurrentService, 0, NULL); } ServiceEntry = ServiceEntry->Flink; } GroupEntry = GroupEntry->Flink; } /* Start all services which are members of any non-existing group */ ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if ((CurrentService->lpGroup != NULL) && (CurrentService->dwStartType == SERVICE_AUTO_START) && (CurrentService->ServiceVisited == FALSE)) { CurrentService->ServiceVisited = TRUE; ScmStartService(CurrentService, 0, NULL); } ServiceEntry = ServiceEntry->Flink; } /* Start all services which are not a member of any group */ ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if ((CurrentService->lpGroup == NULL) && (CurrentService->dwStartType == SERVICE_AUTO_START) && (CurrentService->ServiceVisited == FALSE)) { CurrentService->ServiceVisited = TRUE; ScmStartService(CurrentService, 0, NULL); } ServiceEntry = ServiceEntry->Flink; } /* Clear 'ServiceVisited' flag again */ ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); CurrentService->ServiceVisited = FALSE; ServiceEntry = ServiceEntry->Flink; } } VOID ScmAutoShutdownServices(VOID) { PLIST_ENTRY ServiceEntry; PSERVICE CurrentService; SERVICE_STATUS ServiceStatus; DPRINT("ScmAutoShutdownServices() called\n"); ServiceEntry = ServiceListHead.Flink; while (ServiceEntry != &ServiceListHead) { CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry); if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING || CurrentService->Status.dwCurrentState == SERVICE_START_PENDING) { /* shutdown service */ ScmControlService(CurrentService, SERVICE_CONTROL_STOP, &ServiceStatus); } ServiceEntry = ServiceEntry->Flink; } DPRINT("ScmGetBootAndSystemDriverState() done\n"); } /* EOF */