1 /* $Id: database.c,v 1.3 2002/06/17 15:47:32 ekohl 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
;
70 /* GLOBALS *******************************************************************/
72 LIST_ENTRY GroupListHead
= {NULL
, NULL
};
73 LIST_ENTRY ServiceListHead
= {NULL
, NULL
};
76 /* FUNCTIONS *****************************************************************/
78 static NTSTATUS STDCALL
79 CreateGroupListRoutine(PWSTR ValueName
,
88 if (ValueType
== REG_SZ
)
90 DPRINT("Data: '%S'\n", (PWCHAR
)ValueData
);
92 Group
= (PSERVICE_GROUP
)HeapAlloc(GetProcessHeap(),
94 sizeof(SERVICE_GROUP
));
97 return(STATUS_INSUFFICIENT_RESOURCES
);
100 if (!RtlCreateUnicodeString(&Group
->GroupName
,
103 return(STATUS_INSUFFICIENT_RESOURCES
);
107 InsertTailList(&GroupListHead
,
108 &Group
->GroupListEntry
);
111 return(STATUS_SUCCESS
);
115 static NTSTATUS STDCALL
116 CreateServiceListEntry(PUNICODE_STRING ServiceName
)
118 RTL_QUERY_REGISTRY_TABLE QueryTable
[6];
119 PSERVICE Service
= NULL
;
122 DPRINT("Service: '%wZ'\n", ServiceName
);
124 /* Allocate service entry */
125 Service
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
129 return(STATUS_INSUFFICIENT_RESOURCES
);
132 /* Copy service name */
133 Service
->ServiceName
.Length
= ServiceName
->Length
;
134 Service
->ServiceName
.MaximumLength
= ServiceName
->Length
+ sizeof(WCHAR
);
135 Service
->ServiceName
.Buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
136 Service
->ServiceName
.MaximumLength
);
137 if (Service
->ServiceName
.Buffer
== NULL
)
139 HeapFree(GetProcessHeap(), 0, Service
);
140 return(STATUS_INSUFFICIENT_RESOURCES
);
142 RtlCopyMemory(Service
->ServiceName
.Buffer
,
144 ServiceName
->Length
);
145 Service
->ServiceName
.Buffer
[ServiceName
->Length
/ sizeof(WCHAR
)] = 0;
147 /* Build registry path */
148 Service
->RegistryPath
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
149 Service
->RegistryPath
.Buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
150 MAX_PATH
* sizeof(WCHAR
));
151 if (Service
->ServiceName
.Buffer
== NULL
)
153 HeapFree(GetProcessHeap(), 0, Service
->ServiceName
.Buffer
);
154 HeapFree(GetProcessHeap(), 0, Service
);
155 return(STATUS_INSUFFICIENT_RESOURCES
);
157 wcscpy(Service
->RegistryPath
.Buffer
,
158 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
159 wcscat(Service
->RegistryPath
.Buffer
,
160 Service
->ServiceName
.Buffer
);
161 Service
->RegistryPath
.Length
= wcslen(Service
->RegistryPath
.Buffer
) * sizeof(WCHAR
);
163 /* Get service data */
164 RtlZeroMemory(&QueryTable
,
167 QueryTable
[0].Name
= L
"Start";
168 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
169 QueryTable
[0].EntryContext
= &Service
->Start
;
171 QueryTable
[1].Name
= L
"Type";
172 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
173 QueryTable
[1].EntryContext
= &Service
->Type
;
175 QueryTable
[2].Name
= L
"ErrorControl";
176 QueryTable
[2].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
177 QueryTable
[2].EntryContext
= &Service
->ErrorControl
;
179 QueryTable
[3].Name
= L
"Group";
180 QueryTable
[3].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
181 QueryTable
[3].EntryContext
= &Service
->ServiceGroup
;
183 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
188 if (!NT_SUCCESS(Status
))
190 PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status
);
191 RtlFreeUnicodeString(&Service
->RegistryPath
);
192 RtlFreeUnicodeString(&Service
->ServiceName
);
193 HeapFree(GetProcessHeap(), 0, Service
);
197 DPRINT("ServiceName: '%wZ'\n", &Service
->ServiceName
);
198 DPRINT("RegistryPath: '%wZ'\n", &Service
->RegistryPath
);
199 DPRINT("ServiceGroup: '%wZ'\n", &Service
->ServiceGroup
);
200 DPRINT("Start %lx Type %lx ErrorControl %lx\n",
201 Service
->Start
, Service
->Type
, Service
->ErrorControl
);
203 /* Append service entry */
204 InsertTailList(&ServiceListHead
,
205 &Service
->ServiceListEntry
);
207 return(STATUS_SUCCESS
);
212 ScmCreateServiceDataBase(VOID
)
214 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
215 OBJECT_ATTRIBUTES ObjectAttributes
;
216 UNICODE_STRING ServicesKeyName
;
217 UNICODE_STRING SubKeyName
;
222 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
223 ULONG KeyInfoLength
= 0;
224 ULONG ReturnedLength
;
226 DPRINT("ScmCreateServiceDataBase() called\n");
228 /* Initialize basic variables */
229 InitializeListHead(&GroupListHead
);
230 InitializeListHead(&ServiceListHead
);
232 /* Build group order list */
233 RtlZeroMemory(&QueryTable
,
236 QueryTable
[0].Name
= L
"List";
237 QueryTable
[0].QueryRoutine
= CreateGroupListRoutine
;
239 Status
= RtlQueryRegistryValues(RTL_REGISTRY_CONTROL
,
240 L
"ServiceGroupOrder",
244 if (!NT_SUCCESS(Status
))
247 RtlInitUnicodeString(&ServicesKeyName
,
248 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
250 InitializeObjectAttributes(&ObjectAttributes
,
252 OBJ_CASE_INSENSITIVE
,
256 Status
= RtlpNtOpenKey(&ServicesKey
,
260 if (!NT_SUCCESS(Status
))
263 /* Allocate key info buffer */
264 KeyInfoLength
= sizeof(KEY_BASIC_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
);
265 KeyInfo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KeyInfoLength
);
268 NtClose(ServicesKey
);
269 return(STATUS_INSUFFICIENT_RESOURCES
);
275 Status
= NtEnumerateKey(ServicesKey
,
281 if (NT_SUCCESS(Status
))
283 if (KeyInfo
->NameLength
< MAX_PATH
* sizeof(WCHAR
))
286 SubKeyName
.Length
= KeyInfo
->NameLength
;
287 SubKeyName
.MaximumLength
= KeyInfo
->NameLength
+ sizeof(WCHAR
);
288 SubKeyName
.Buffer
= KeyInfo
->Name
;
289 SubKeyName
.Buffer
[SubKeyName
.Length
/ sizeof(WCHAR
)] = 0;
291 DPRINT("KeyName: '%wZ'\n", &SubKeyName
);
292 Status
= CreateServiceListEntry(&SubKeyName
);
296 if (!NT_SUCCESS(Status
))
302 HeapFree(GetProcessHeap(), 0, KeyInfo
);
303 NtClose(ServicesKey
);
305 DPRINT("ScmCreateServiceDataBase() done\n");
307 return(STATUS_SUCCESS
);
312 ScmCheckDriver(PSERVICE_GROUP Group
,
315 OBJECT_ATTRIBUTES ObjectAttributes
;
316 UNICODE_STRING DirName
;
320 DPRINT("ScmCheckDriver() called\n");
322 if (Service
->Type
== SERVICE_KERNEL_DRIVER
)
324 RtlInitUnicodeString(&DirName
,
329 RtlInitUnicodeString(&DirName
,
333 InitializeObjectAttributes(&ObjectAttributes
,
339 Status
= NtOpenDirectoryObject(&DirHandle
,
340 DIRECTORY_QUERY
| DIRECTORY_TRAVERSE
,
342 if (!NT_SUCCESS(Status
))
353 Status
= NtQueryDirectoryObject(DirHandle
,
360 if (!NT_SUCCESS(Status
))
367 if (NT_SUCCESS(Status
))
369 CurrentGroup
->ServicesRunning
= TRUE
;
370 CurrentService
->ServiceRunning
= TRUE
;
380 return(STATUS_SUCCESS
);
385 ScmGetBootAndSystemDriverState(VOID
)
387 PLIST_ENTRY GroupEntry
;
388 PLIST_ENTRY ServiceEntry
;
389 PSERVICE_GROUP CurrentGroup
;
390 PSERVICE CurrentService
;
393 DPRINT("ScmGetBootAndSystemDriverState() called\n");
395 GroupEntry
= GroupListHead
.Flink
;
396 while (GroupEntry
!= &GroupListHead
)
398 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
400 DPRINT("Checking group '%wZ'\n", &CurrentGroup
->GroupName
);
402 ServiceEntry
= ServiceListHead
.Flink
;
403 while (ServiceEntry
!= &ServiceListHead
)
405 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
407 if (CurrentService
->Start
== SERVICE_BOOT_START
||
408 CurrentService
->Start
== SERVICE_SYSTEM_START
)
411 DPRINT(" Checking service: %wZ\n", &CurrentService
->ServiceName
);
413 ScmCheckDriver(CurrentGroup
,
416 ServiceEntry
= ServiceEntry
->Flink
;
418 GroupEntry
= GroupEntry
->Flink
;
421 DPRINT("ScmGetBootAndSystemDriverState() done\n");
426 ScmStartService(PSERVICE Service
,
427 PSERVICE_GROUP Group
)
429 RTL_QUERY_REGISTRY_TABLE QueryTable
[3];
430 PROCESS_INFORMATION ProcessInformation
;
431 STARTUPINFOW StartupInfo
;
432 UNICODE_STRING ImagePath
;
437 DPRINT("ScmStartService() called\n");
439 if (Service
->Type
== SERVICE_KERNEL_DRIVER
||
440 Service
->Type
== SERVICE_FILE_SYSTEM_DRIVER
||
441 Service
->Type
== SERVICE_RECOGNIZER_DRIVER
)
444 DPRINT(" Path: %wZ\n", &Service
->RegistryPath
);
445 Status
= NtLoadDriver(&Service
->RegistryPath
);
449 RtlInitUnicodeString(&ImagePath
, NULL
);
451 /* Get service data */
452 RtlZeroMemory(&QueryTable
,
455 QueryTable
[0].Name
= L
"Type";
456 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
457 QueryTable
[0].EntryContext
= &Type
;
459 QueryTable
[1].Name
= L
"ImagePath";
460 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
461 QueryTable
[1].EntryContext
= &ImagePath
;
463 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
464 Service
->ServiceName
.Buffer
,
468 if (NT_SUCCESS(Status
))
470 DPRINT("ImagePath: '%S'\n", ImagePath
.Buffer
);
471 DPRINT("Type: %lx\n", Type
);
473 /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
475 StartupInfo
.cb
= sizeof(StartupInfo
);
476 StartupInfo
.lpReserved
= NULL
;
477 StartupInfo
.lpDesktop
= NULL
;
478 StartupInfo
.lpTitle
= NULL
;
479 StartupInfo
.dwFlags
= 0;
480 StartupInfo
.cbReserved2
= 0;
481 StartupInfo
.lpReserved2
= 0;
483 Result
= CreateProcessW(ImagePath
.Buffer
,
492 &ProcessInformation
);
494 RtlFreeUnicodeString(&ImagePath
);
498 /* FIXME: close control pipe */
500 DPRINT("Failed to start '%S'\n", Service
->ServiceName
.Buffer
);
501 Status
= STATUS_UNSUCCESSFUL
;
505 /* FIXME: connect control pipe */
511 if (NT_SUCCESS(Status
))
515 Group
->ServicesRunning
= TRUE
;
517 Service
->ServiceRunning
= TRUE
;
522 if (CurrentService
->ErrorControl
== 1)
527 else if (CurrentService
->ErrorControl
== 2)
529 if (IsLastKnownGood
== FALSE
)
531 /* Boot last known good configuration */
535 else if (CurrentService
->ErrorControl
== 3)
537 if (IsLastKnownGood
== FALSE
)
539 /* Boot last known good configuration */
551 return(STATUS_SUCCESS
);
556 ScmAutoStartServices(VOID
)
558 PLIST_ENTRY GroupEntry
;
559 PLIST_ENTRY ServiceEntry
;
560 PSERVICE_GROUP CurrentGroup
;
561 PSERVICE CurrentService
;
564 /* Clear 'ServiceVisited' flag */
565 ServiceEntry
= ServiceListHead
.Flink
;
566 while (ServiceEntry
!= &ServiceListHead
)
568 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
569 CurrentService
->ServiceVisited
= FALSE
;
570 ServiceEntry
= ServiceEntry
->Flink
;
573 /* Start all services which are members of an existing group */
574 GroupEntry
= GroupListHead
.Flink
;
575 while (GroupEntry
!= &GroupListHead
)
577 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
579 DPRINT("Group '%wZ'\n", &CurrentGroup
->GroupName
);
581 ServiceEntry
= ServiceListHead
.Flink
;
582 while (ServiceEntry
!= &ServiceListHead
)
584 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
586 if ((RtlEqualUnicodeString(&CurrentGroup
->GroupName
, &CurrentService
->ServiceGroup
, TRUE
)) &&
587 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
588 (CurrentService
->ServiceVisited
== FALSE
))
590 CurrentService
->ServiceVisited
= TRUE
;
591 ScmStartService(CurrentService
,
595 ServiceEntry
= ServiceEntry
->Flink
;
598 GroupEntry
= GroupEntry
->Flink
;
601 /* Start all services which are members of any non-existing group */
602 ServiceEntry
= ServiceListHead
.Flink
;
603 while (ServiceEntry
!= &ServiceListHead
)
605 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
607 if ((CurrentGroup
->GroupName
.Length
> 0) &&
608 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
609 (CurrentService
->ServiceVisited
== FALSE
))
611 CurrentService
->ServiceVisited
= TRUE
;
612 ScmStartService(CurrentService
,
616 ServiceEntry
= ServiceEntry
->Flink
;
619 /* Start all services which are not a member of any group */
620 ServiceEntry
= ServiceListHead
.Flink
;
621 while (ServiceEntry
!= &ServiceListHead
)
623 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
625 if ((CurrentGroup
->GroupName
.Length
== 0) &&
626 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
627 (CurrentService
->ServiceVisited
== FALSE
))
629 CurrentService
->ServiceVisited
= TRUE
;
630 ScmStartService(CurrentService
,
634 ServiceEntry
= ServiceEntry
->Flink
;
637 /* Clear 'ServiceVisited' flag again */
638 ServiceEntry
= ServiceListHead
.Flink
;
639 while (ServiceEntry
!= &ServiceListHead
)
641 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
642 CurrentService
->ServiceVisited
= FALSE
;
643 ServiceEntry
= ServiceEntry
->Flink
;