3 * service control manager
5 * ReactOS Operating System
7 * --------------------------------------------------------------------
9 * This software is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this software; see the file COPYING.LIB. If not, write
21 * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
26 /* INCLUDES *****************************************************************/
28 #define NTOS_MODE_USER
40 /* TYPES *********************************************************************/
42 typedef struct _SERVICE_GROUP
44 LIST_ENTRY GroupListEntry;
45 UNICODE_STRING GroupName;
47 BOOLEAN ServicesRunning;
49 } SERVICE_GROUP, *PSERVICE_GROUP;
52 typedef struct _SERVICE
54 LIST_ENTRY ServiceListEntry;
55 UNICODE_STRING ServiceName;
56 UNICODE_STRING RegistryPath;
57 UNICODE_STRING ServiceGroup;
64 BOOLEAN ServiceRunning;
65 BOOLEAN ServiceVisited;
67 HANDLE ControlPipeHandle;
73 /* GLOBALS *******************************************************************/
75 LIST_ENTRY GroupListHead;
76 LIST_ENTRY ServiceListHead;
79 /* FUNCTIONS *****************************************************************/
81 static NTSTATUS STDCALL
82 CreateGroupListRoutine(PWSTR ValueName,
91 if (ValueType == REG_SZ)
93 DPRINT("Data: '%S'\n", (PWCHAR)ValueData);
95 Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),
97 sizeof(SERVICE_GROUP));
100 return(STATUS_INSUFFICIENT_RESOURCES);
103 if (!RtlCreateUnicodeString(&Group->GroupName,
106 return(STATUS_INSUFFICIENT_RESOURCES);
110 InsertTailList(&GroupListHead,
111 &Group->GroupListEntry);
114 return(STATUS_SUCCESS);
118 static NTSTATUS STDCALL
119 CreateServiceListEntry(PUNICODE_STRING ServiceName)
121 RTL_QUERY_REGISTRY_TABLE QueryTable[6];
122 PSERVICE Service = NULL;
125 DPRINT("Service: '%wZ'\n", ServiceName);
127 /* Allocate service entry */
128 Service = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
132 return(STATUS_INSUFFICIENT_RESOURCES);
135 /* Copy service name */
136 Service->ServiceName.Length = ServiceName->Length;
137 Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
138 Service->ServiceName.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
139 Service->ServiceName.MaximumLength);
140 if (Service->ServiceName.Buffer == NULL)
142 HeapFree(GetProcessHeap(), 0, Service);
143 return(STATUS_INSUFFICIENT_RESOURCES);
145 RtlCopyMemory(Service->ServiceName.Buffer,
147 ServiceName->Length);
148 Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
150 /* Build registry path */
151 Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
152 Service->RegistryPath.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
153 MAX_PATH * sizeof(WCHAR));
154 if (Service->ServiceName.Buffer == NULL)
156 HeapFree(GetProcessHeap(), 0, Service->ServiceName.Buffer);
157 HeapFree(GetProcessHeap(), 0, Service);
158 return(STATUS_INSUFFICIENT_RESOURCES);
160 wcscpy(Service->RegistryPath.Buffer,
161 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
162 wcscat(Service->RegistryPath.Buffer,
163 Service->ServiceName.Buffer);
164 Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
166 /* Get service data */
167 RtlZeroMemory(&QueryTable,
170 QueryTable[0].Name = L"Start";
171 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
172 QueryTable[0].EntryContext = &Service->Start;
174 QueryTable[1].Name = L"Type";
175 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
176 QueryTable[1].EntryContext = &Service->Type;
178 QueryTable[2].Name = L"ErrorControl";
179 QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
180 QueryTable[2].EntryContext = &Service->ErrorControl;
182 QueryTable[3].Name = L"Group";
183 QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
184 QueryTable[3].EntryContext = &Service->ServiceGroup;
186 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
191 if (!NT_SUCCESS(Status))
193 PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
194 RtlFreeUnicodeString(&Service->RegistryPath);
195 RtlFreeUnicodeString(&Service->ServiceName);
196 HeapFree(GetProcessHeap(), 0, Service);
200 DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
201 DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
202 DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
203 DPRINT("Start %lx Type %lx ErrorControl %lx\n",
204 Service->Start, Service->Type, Service->ErrorControl);
206 /* Append service entry */
207 InsertTailList(&ServiceListHead,
208 &Service->ServiceListEntry);
210 return(STATUS_SUCCESS);
215 ScmCreateServiceDataBase(VOID)
217 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
218 OBJECT_ATTRIBUTES ObjectAttributes;
219 UNICODE_STRING ServicesKeyName;
220 UNICODE_STRING SubKeyName;
225 PKEY_BASIC_INFORMATION KeyInfo = NULL;
226 ULONG KeyInfoLength = 0;
227 ULONG ReturnedLength;
229 DPRINT("ScmCreateServiceDataBase() called\n");
231 /* Initialize basic variables */
232 InitializeListHead(&GroupListHead);
233 InitializeListHead(&ServiceListHead);
235 /* Build group order list */
236 RtlZeroMemory(&QueryTable,
239 QueryTable[0].Name = L"List";
240 QueryTable[0].QueryRoutine = CreateGroupListRoutine;
242 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
243 L"ServiceGroupOrder",
247 if (!NT_SUCCESS(Status))
250 RtlInitUnicodeStringFromLiteral(&ServicesKeyName,
251 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
253 InitializeObjectAttributes(&ObjectAttributes,
255 OBJ_CASE_INSENSITIVE,
259 Status = RtlpNtOpenKey(&ServicesKey,
263 if (!NT_SUCCESS(Status))
266 /* Allocate key info buffer */
267 KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
268 KeyInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KeyInfoLength);
271 NtClose(ServicesKey);
272 return(STATUS_INSUFFICIENT_RESOURCES);
278 Status = NtEnumerateKey(ServicesKey,
284 if (NT_SUCCESS(Status))
286 if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
289 SubKeyName.Length = KeyInfo->NameLength;
290 SubKeyName.MaximumLength = KeyInfo->NameLength + sizeof(WCHAR);
291 SubKeyName.Buffer = KeyInfo->Name;
292 SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
294 DPRINT("KeyName: '%wZ'\n", &SubKeyName);
295 Status = CreateServiceListEntry(&SubKeyName);
299 if (!NT_SUCCESS(Status))
305 HeapFree(GetProcessHeap(), 0, KeyInfo);
306 NtClose(ServicesKey);
308 DPRINT("ScmCreateServiceDataBase() done\n");
310 return(STATUS_SUCCESS);
315 ScmCheckDriver(PSERVICE Service)
317 OBJECT_ATTRIBUTES ObjectAttributes;
318 UNICODE_STRING DirName;
321 POBJDIR_INFORMATION DirInfo;
325 PLIST_ENTRY GroupEntry;
326 PSERVICE_GROUP CurrentGroup;
328 DPRINT("ScmCheckDriver(%wZ) called\n", &Service->ServiceName);
330 if (Service->Type == SERVICE_KERNEL_DRIVER)
332 RtlInitUnicodeStringFromLiteral(&DirName,
337 RtlInitUnicodeStringFromLiteral(&DirName,
341 InitializeObjectAttributes(&ObjectAttributes,
347 Status = NtOpenDirectoryObject(&DirHandle,
348 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
350 if (!NT_SUCCESS(Status))
355 BufferLength = sizeof(OBJDIR_INFORMATION) +
356 2 * MAX_PATH * sizeof(WCHAR);
357 DirInfo = HeapAlloc(GetProcessHeap(),
364 Status = NtQueryDirectoryObject(DirHandle,
371 if (Status == STATUS_NO_MORE_ENTRIES)
373 /* FIXME: Add current service to 'failed service' list */
374 DPRINT("Service '%wZ' failed\n", &Service->ServiceName);
378 if (!NT_SUCCESS(Status))
381 DPRINT("Comparing: '%wZ' '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
383 if (RtlEqualUnicodeString(&Service->ServiceName, &DirInfo->ObjectName, TRUE))
385 DPRINT("Found: '%wZ' '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
387 /* Mark service as 'running' */
388 Service->ServiceRunning = TRUE;
390 /* Find the driver's group and mark it as 'running' */
391 if (Service->ServiceGroup.Buffer != NULL)
393 GroupEntry = GroupListHead.Flink;
394 while (GroupEntry != &GroupListHead)
396 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
398 DPRINT("Checking group '%wZ'\n", &CurrentGroup->GroupName);
399 if (RtlEqualUnicodeString(&Service->ServiceGroup, &CurrentGroup->GroupName, TRUE))
401 CurrentGroup->ServicesRunning = TRUE;
404 GroupEntry = GroupEntry->Flink;
411 HeapFree(GetProcessHeap(),
416 return(STATUS_SUCCESS);
421 ScmGetBootAndSystemDriverState(VOID)
423 PLIST_ENTRY ServiceEntry;
424 PSERVICE CurrentService;
427 DPRINT("ScmGetBootAndSystemDriverState() called\n");
429 ServiceEntry = ServiceListHead.Flink;
430 while (ServiceEntry != &ServiceListHead)
432 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
434 if (CurrentService->Start == SERVICE_BOOT_START ||
435 CurrentService->Start == SERVICE_SYSTEM_START)
438 DPRINT(" Checking service: %wZ\n", &CurrentService->ServiceName);
440 ScmCheckDriver(CurrentService);
442 ServiceEntry = ServiceEntry->Flink;
445 DPRINT("ScmGetBootAndSystemDriverState() done\n");
450 ScmStartService(PSERVICE Service,
451 PSERVICE_GROUP Group)
453 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
454 PROCESS_INFORMATION ProcessInformation;
455 STARTUPINFOW StartupInfo;
456 UNICODE_STRING ImagePath;
461 DPRINT("ScmStartService() called\n");
463 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
465 if (Service->Type == SERVICE_KERNEL_DRIVER ||
466 Service->Type == SERVICE_FILE_SYSTEM_DRIVER ||
467 Service->Type == SERVICE_RECOGNIZER_DRIVER)
470 DPRINT(" Path: %wZ\n", &Service->RegistryPath);
471 Status = NtLoadDriver(&Service->RegistryPath);
475 RtlInitUnicodeString(&ImagePath, NULL);
477 /* Get service data */
478 RtlZeroMemory(&QueryTable,
481 QueryTable[0].Name = L"Type";
482 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
483 QueryTable[0].EntryContext = &Type;
485 QueryTable[1].Name = L"ImagePath";
486 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
487 QueryTable[1].EntryContext = &ImagePath;
489 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
490 Service->ServiceName.Buffer,
494 if (NT_SUCCESS(Status))
496 DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
497 DPRINT("Type: %lx\n", Type);
499 /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
500 Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
502 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
508 DPRINT1("CreateNamedPipeW() done\n");
509 if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
511 DPRINT1("Failed to create control pipe!\n");
512 Status = STATUS_UNSUCCESSFUL;
516 StartupInfo.cb = sizeof(StartupInfo);
517 StartupInfo.lpReserved = NULL;
518 StartupInfo.lpDesktop = NULL;
519 StartupInfo.lpTitle = NULL;
520 StartupInfo.dwFlags = 0;
521 StartupInfo.cbReserved2 = 0;
522 StartupInfo.lpReserved2 = 0;
524 Result = CreateProcessW(ImagePath.Buffer,
529 DETACHED_PROCESS | CREATE_SUSPENDED,
533 &ProcessInformation);
534 RtlFreeUnicodeString(&ImagePath);
538 /* Close control pipe */
539 CloseHandle(Service->ControlPipeHandle);
540 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
542 DPRINT("Starting '%S' failed!\n", Service->ServiceName.Buffer);
543 Status = STATUS_UNSUCCESSFUL;
547 DPRINT1("Process Id: %lu Handle %lx\n",
548 ProcessInformation.dwProcessId,
549 ProcessInformation.hProcess);
550 DPRINT1("Tread Id: %lu Handle %lx\n",
551 ProcessInformation.dwThreadId,
552 ProcessInformation.hThread);
554 /* Get process and thread ids */
555 Service->ProcessId = ProcessInformation.dwProcessId;
556 Service->ThreadId = ProcessInformation.dwThreadId;
559 ResumeThread(ProcessInformation.hThread);
561 /* FIXME: connect control pipe */
562 if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
564 DPRINT1("Control pipe connected!\n");
565 Status = STATUS_SUCCESS;
569 DPRINT1("Connecting control pipe failed!\n");
571 /* Close control pipe */
572 CloseHandle(Service->ControlPipeHandle);
573 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
574 Service->ProcessId = 0;
575 Service->ThreadId = 0;
576 Status = STATUS_UNSUCCESSFUL;
579 /* Close process and thread handle */
580 CloseHandle(ProcessInformation.hThread);
581 CloseHandle(ProcessInformation.hProcess);
587 if (NT_SUCCESS(Status))
591 Group->ServicesRunning = TRUE;
593 Service->ServiceRunning = TRUE;
598 if (CurrentService->ErrorControl == 1)
603 else if (CurrentService->ErrorControl == 2)
605 if (IsLastKnownGood == FALSE)
607 /* Boot last known good configuration */
611 else if (CurrentService->ErrorControl == 3)
613 if (IsLastKnownGood == FALSE)
615 /* Boot last known good configuration */
627 return(STATUS_SUCCESS);
632 ScmAutoStartServices(VOID)
634 PLIST_ENTRY GroupEntry;
635 PLIST_ENTRY ServiceEntry;
636 PSERVICE_GROUP CurrentGroup;
637 PSERVICE CurrentService;
640 /* Clear 'ServiceVisited' flag */
641 ServiceEntry = ServiceListHead.Flink;
642 while (ServiceEntry != &ServiceListHead)
644 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
645 CurrentService->ServiceVisited = FALSE;
646 ServiceEntry = ServiceEntry->Flink;
649 /* Start all services which are members of an existing group */
650 GroupEntry = GroupListHead.Flink;
651 while (GroupEntry != &GroupListHead)
653 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
655 DPRINT("Group '%wZ'\n", &CurrentGroup->GroupName);
657 ServiceEntry = ServiceListHead.Flink;
658 while (ServiceEntry != &ServiceListHead)
660 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
662 if ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
663 (CurrentService->Start == SERVICE_AUTO_START) &&
664 (CurrentService->ServiceVisited == FALSE))
666 CurrentService->ServiceVisited = TRUE;
667 ScmStartService(CurrentService,
671 ServiceEntry = ServiceEntry->Flink;
674 GroupEntry = GroupEntry->Flink;
677 /* Start all services which are members of any non-existing group */
678 ServiceEntry = ServiceListHead.Flink;
679 while (ServiceEntry != &ServiceListHead)
681 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
683 if ((CurrentGroup->GroupName.Length > 0) &&
684 (CurrentService->Start == SERVICE_AUTO_START) &&
685 (CurrentService->ServiceVisited == FALSE))
687 CurrentService->ServiceVisited = TRUE;
688 ScmStartService(CurrentService,
692 ServiceEntry = ServiceEntry->Flink;
695 /* Start all services which are not a member of any group */
696 ServiceEntry = ServiceListHead.Flink;
697 while (ServiceEntry != &ServiceListHead)
699 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
701 if ((CurrentGroup->GroupName.Length == 0) &&
702 (CurrentService->Start == SERVICE_AUTO_START) &&
703 (CurrentService->ServiceVisited == FALSE))
705 CurrentService->ServiceVisited = TRUE;
706 ScmStartService(CurrentService,
710 ServiceEntry = ServiceEntry->Flink;
713 /* Clear 'ServiceVisited' flag again */
714 ServiceEntry = ServiceListHead.Flink;
715 while (ServiceEntry != &ServiceListHead)
717 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
718 CurrentService->ServiceVisited = FALSE;
719 ServiceEntry = ServiceEntry->Flink;