1 /* $Id: database.c,v 1.11 2003/11/14 17:13:33 weiden Exp $
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 PDIRECTORY_BASIC_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(DIRECTORY_BASIC_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
;
426 DPRINT("ScmGetBootAndSystemDriverState() called\n");
428 ServiceEntry
= ServiceListHead
.Flink
;
429 while (ServiceEntry
!= &ServiceListHead
)
431 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
433 if (CurrentService
->Start
== SERVICE_BOOT_START
||
434 CurrentService
->Start
== SERVICE_SYSTEM_START
)
437 DPRINT(" Checking service: %wZ\n", &CurrentService
->ServiceName
);
439 ScmCheckDriver(CurrentService
);
441 ServiceEntry
= ServiceEntry
->Flink
;
444 DPRINT("ScmGetBootAndSystemDriverState() done\n");
449 ScmStartService(PSERVICE Service
,
450 PSERVICE_GROUP Group
)
452 RTL_QUERY_REGISTRY_TABLE QueryTable
[3];
453 PROCESS_INFORMATION ProcessInformation
;
454 STARTUPINFOW StartupInfo
;
455 UNICODE_STRING ImagePath
;
460 DPRINT("ScmStartService() called\n");
462 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
464 if (Service
->Type
== SERVICE_KERNEL_DRIVER
||
465 Service
->Type
== SERVICE_FILE_SYSTEM_DRIVER
||
466 Service
->Type
== SERVICE_RECOGNIZER_DRIVER
)
469 DPRINT(" Path: %wZ\n", &Service
->RegistryPath
);
470 Status
= NtLoadDriver(&Service
->RegistryPath
);
474 RtlInitUnicodeString(&ImagePath
, NULL
);
476 /* Get service data */
477 RtlZeroMemory(&QueryTable
,
480 QueryTable
[0].Name
= L
"Type";
481 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
482 QueryTable
[0].EntryContext
= &Type
;
484 QueryTable
[1].Name
= L
"ImagePath";
485 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
486 QueryTable
[1].EntryContext
= &ImagePath
;
488 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
489 Service
->ServiceName
.Buffer
,
493 if (NT_SUCCESS(Status
))
495 DPRINT("ImagePath: '%S'\n", ImagePath
.Buffer
);
496 DPRINT("Type: %lx\n", Type
);
498 /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
499 Service
->ControlPipeHandle
= CreateNamedPipeW(L
"\\\\.\\pipe\\net\\NtControlPipe",
501 PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT
,
507 DPRINT("CreateNamedPipeW() done\n");
508 if (Service
->ControlPipeHandle
== INVALID_HANDLE_VALUE
)
510 DPRINT1("Failed to create control pipe!\n");
511 Status
= STATUS_UNSUCCESSFUL
;
515 StartupInfo
.cb
= sizeof(StartupInfo
);
516 StartupInfo
.lpReserved
= NULL
;
517 StartupInfo
.lpDesktop
= NULL
;
518 StartupInfo
.lpTitle
= NULL
;
519 StartupInfo
.dwFlags
= 0;
520 StartupInfo
.cbReserved2
= 0;
521 StartupInfo
.lpReserved2
= 0;
523 Result
= CreateProcessW(ImagePath
.Buffer
,
528 DETACHED_PROCESS
| CREATE_SUSPENDED
,
532 &ProcessInformation
);
533 RtlFreeUnicodeString(&ImagePath
);
537 /* Close control pipe */
538 CloseHandle(Service
->ControlPipeHandle
);
539 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
541 DPRINT("Starting '%S' failed!\n", Service
->ServiceName
.Buffer
);
542 Status
= STATUS_UNSUCCESSFUL
;
546 DPRINT("Process Id: %lu Handle %lx\n",
547 ProcessInformation
.dwProcessId
,
548 ProcessInformation
.hProcess
);
549 DPRINT("Thread Id: %lu Handle %lx\n",
550 ProcessInformation
.dwThreadId
,
551 ProcessInformation
.hThread
);
553 /* Get process and thread ids */
554 Service
->ProcessId
= ProcessInformation
.dwProcessId
;
555 Service
->ThreadId
= ProcessInformation
.dwThreadId
;
558 ResumeThread(ProcessInformation
.hThread
);
560 /* FIXME: connect control pipe */
561 if (ConnectNamedPipe(Service
->ControlPipeHandle
, NULL
))
563 DPRINT("Control pipe connected!\n");
564 Status
= STATUS_SUCCESS
;
568 DPRINT1("Connecting control pipe failed!\n");
570 /* Close control pipe */
571 CloseHandle(Service
->ControlPipeHandle
);
572 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
573 Service
->ProcessId
= 0;
574 Service
->ThreadId
= 0;
575 Status
= STATUS_UNSUCCESSFUL
;
578 /* Close process and thread handle */
579 CloseHandle(ProcessInformation
.hThread
);
580 CloseHandle(ProcessInformation
.hProcess
);
586 if (NT_SUCCESS(Status
))
590 Group
->ServicesRunning
= TRUE
;
592 Service
->ServiceRunning
= TRUE
;
597 if (CurrentService
->ErrorControl
== 1)
602 else if (CurrentService
->ErrorControl
== 2)
604 if (IsLastKnownGood
== FALSE
)
606 /* Boot last known good configuration */
610 else if (CurrentService
->ErrorControl
== 3)
612 if (IsLastKnownGood
== FALSE
)
614 /* Boot last known good configuration */
626 return(STATUS_SUCCESS
);
631 ScmAutoStartServices(VOID
)
633 PLIST_ENTRY GroupEntry
;
634 PLIST_ENTRY ServiceEntry
;
635 PSERVICE_GROUP CurrentGroup
;
636 PSERVICE CurrentService
;
638 /* Clear 'ServiceVisited' flag */
639 ServiceEntry
= ServiceListHead
.Flink
;
640 while (ServiceEntry
!= &ServiceListHead
)
642 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
643 CurrentService
->ServiceVisited
= FALSE
;
644 ServiceEntry
= ServiceEntry
->Flink
;
647 /* Start all services which are members of an existing group */
648 GroupEntry
= GroupListHead
.Flink
;
649 while (GroupEntry
!= &GroupListHead
)
651 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
653 DPRINT("Group '%wZ'\n", &CurrentGroup
->GroupName
);
655 ServiceEntry
= ServiceListHead
.Flink
;
656 while (ServiceEntry
!= &ServiceListHead
)
658 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
660 if ((RtlEqualUnicodeString(&CurrentGroup
->GroupName
, &CurrentService
->ServiceGroup
, TRUE
)) &&
661 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
662 (CurrentService
->ServiceVisited
== FALSE
))
664 CurrentService
->ServiceVisited
= TRUE
;
665 ScmStartService(CurrentService
,
669 ServiceEntry
= ServiceEntry
->Flink
;
672 GroupEntry
= GroupEntry
->Flink
;
675 /* Start all services which are members of any non-existing group */
676 ServiceEntry
= ServiceListHead
.Flink
;
677 while (ServiceEntry
!= &ServiceListHead
)
679 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
681 if ((CurrentGroup
->GroupName
.Length
> 0) &&
682 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
683 (CurrentService
->ServiceVisited
== FALSE
))
685 CurrentService
->ServiceVisited
= TRUE
;
686 ScmStartService(CurrentService
,
690 ServiceEntry
= ServiceEntry
->Flink
;
693 /* Start all services which are not a member of any group */
694 ServiceEntry
= ServiceListHead
.Flink
;
695 while (ServiceEntry
!= &ServiceListHead
)
697 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
699 if ((CurrentGroup
->GroupName
.Length
== 0) &&
700 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
701 (CurrentService
->ServiceVisited
== FALSE
))
703 CurrentService
->ServiceVisited
= TRUE
;
704 ScmStartService(CurrentService
,
708 ServiceEntry
= ServiceEntry
->Flink
;
711 /* Clear 'ServiceVisited' flag again */
712 ServiceEntry
= ServiceListHead
.Flink
;
713 while (ServiceEntry
!= &ServiceListHead
)
715 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
716 CurrentService
->ServiceVisited
= FALSE
;
717 ServiceEntry
= ServiceEntry
->Flink
;