Improved driver loading sequence with respect to the group order.
[reactos.git] / reactos / subsys / system / services / database.c
1 /* $Id: database.c,v 1.3 2002/06/17 15:47:32 ekohl Exp $
2 *
3 * service control manager
4 *
5 * ReactOS Operating System
6 *
7 * --------------------------------------------------------------------
8 *
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.
13 *
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.
18 *
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,
22 * MA 02139, USA.
23 *
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #define NTOS_MODE_USER
29 #include <ntos.h>
30
31 #include <windows.h>
32 #include <tchar.h>
33
34 #include "services.h"
35
36 #define NDEBUG
37 #include <debug.h>
38
39
40 /* TYPES *********************************************************************/
41
42 typedef struct _SERVICE_GROUP
43 {
44 LIST_ENTRY GroupListEntry;
45 UNICODE_STRING GroupName;
46
47 BOOLEAN ServicesRunning;
48
49 } SERVICE_GROUP, *PSERVICE_GROUP;
50
51
52 typedef struct _SERVICE
53 {
54 LIST_ENTRY ServiceListEntry;
55 UNICODE_STRING ServiceName;
56 UNICODE_STRING RegistryPath;
57 UNICODE_STRING ServiceGroup;
58
59 ULONG Start;
60 ULONG Type;
61 ULONG ErrorControl;
62 ULONG Tag;
63
64 BOOLEAN ServiceRunning;
65 BOOLEAN ServiceVisited;
66
67 } SERVICE, *PSERVICE;
68
69
70 /* GLOBALS *******************************************************************/
71
72 LIST_ENTRY GroupListHead = {NULL, NULL};
73 LIST_ENTRY ServiceListHead = {NULL, NULL};
74
75
76 /* FUNCTIONS *****************************************************************/
77
78 static NTSTATUS STDCALL
79 CreateGroupListRoutine(PWSTR ValueName,
80 ULONG ValueType,
81 PVOID ValueData,
82 ULONG ValueLength,
83 PVOID Context,
84 PVOID EntryContext)
85 {
86 PSERVICE_GROUP Group;
87
88 if (ValueType == REG_SZ)
89 {
90 DPRINT("Data: '%S'\n", (PWCHAR)ValueData);
91
92 Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),
93 HEAP_ZERO_MEMORY,
94 sizeof(SERVICE_GROUP));
95 if (Group == NULL)
96 {
97 return(STATUS_INSUFFICIENT_RESOURCES);
98 }
99
100 if (!RtlCreateUnicodeString(&Group->GroupName,
101 (PWSTR)ValueData))
102 {
103 return(STATUS_INSUFFICIENT_RESOURCES);
104 }
105
106
107 InsertTailList(&GroupListHead,
108 &Group->GroupListEntry);
109 }
110
111 return(STATUS_SUCCESS);
112 }
113
114
115 static NTSTATUS STDCALL
116 CreateServiceListEntry(PUNICODE_STRING ServiceName)
117 {
118 RTL_QUERY_REGISTRY_TABLE QueryTable[6];
119 PSERVICE Service = NULL;
120 NTSTATUS Status;
121
122 DPRINT("Service: '%wZ'\n", ServiceName);
123
124 /* Allocate service entry */
125 Service = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
126 sizeof(SERVICE));
127 if (Service == NULL)
128 {
129 return(STATUS_INSUFFICIENT_RESOURCES);
130 }
131
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)
138 {
139 HeapFree(GetProcessHeap(), 0, Service);
140 return(STATUS_INSUFFICIENT_RESOURCES);
141 }
142 RtlCopyMemory(Service->ServiceName.Buffer,
143 ServiceName->Buffer,
144 ServiceName->Length);
145 Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
146
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)
152 {
153 HeapFree(GetProcessHeap(), 0, Service->ServiceName.Buffer);
154 HeapFree(GetProcessHeap(), 0, Service);
155 return(STATUS_INSUFFICIENT_RESOURCES);
156 }
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);
162
163 /* Get service data */
164 RtlZeroMemory(&QueryTable,
165 sizeof(QueryTable));
166
167 QueryTable[0].Name = L"Start";
168 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
169 QueryTable[0].EntryContext = &Service->Start;
170
171 QueryTable[1].Name = L"Type";
172 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
173 QueryTable[1].EntryContext = &Service->Type;
174
175 QueryTable[2].Name = L"ErrorControl";
176 QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
177 QueryTable[2].EntryContext = &Service->ErrorControl;
178
179 QueryTable[3].Name = L"Group";
180 QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
181 QueryTable[3].EntryContext = &Service->ServiceGroup;
182
183 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
184 ServiceName->Buffer,
185 QueryTable,
186 NULL,
187 NULL);
188 if (!NT_SUCCESS(Status))
189 {
190 PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
191 RtlFreeUnicodeString(&Service->RegistryPath);
192 RtlFreeUnicodeString(&Service->ServiceName);
193 HeapFree(GetProcessHeap(), 0, Service);
194 return(Status);
195 }
196
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);
202
203 /* Append service entry */
204 InsertTailList(&ServiceListHead,
205 &Service->ServiceListEntry);
206
207 return(STATUS_SUCCESS);
208 }
209
210
211 NTSTATUS
212 ScmCreateServiceDataBase(VOID)
213 {
214 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
215 OBJECT_ATTRIBUTES ObjectAttributes;
216 UNICODE_STRING ServicesKeyName;
217 UNICODE_STRING SubKeyName;
218 HKEY ServicesKey;
219 ULONG Index;
220 NTSTATUS Status;
221
222 PKEY_BASIC_INFORMATION KeyInfo = NULL;
223 ULONG KeyInfoLength = 0;
224 ULONG ReturnedLength;
225
226 DPRINT("ScmCreateServiceDataBase() called\n");
227
228 /* Initialize basic variables */
229 InitializeListHead(&GroupListHead);
230 InitializeListHead(&ServiceListHead);
231
232 /* Build group order list */
233 RtlZeroMemory(&QueryTable,
234 sizeof(QueryTable));
235
236 QueryTable[0].Name = L"List";
237 QueryTable[0].QueryRoutine = CreateGroupListRoutine;
238
239 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
240 L"ServiceGroupOrder",
241 QueryTable,
242 NULL,
243 NULL);
244 if (!NT_SUCCESS(Status))
245 return(Status);
246
247 RtlInitUnicodeString(&ServicesKeyName,
248 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
249
250 InitializeObjectAttributes(&ObjectAttributes,
251 &ServicesKeyName,
252 OBJ_CASE_INSENSITIVE,
253 NULL,
254 NULL);
255
256 Status = RtlpNtOpenKey(&ServicesKey,
257 0x10001,
258 &ObjectAttributes,
259 0);
260 if (!NT_SUCCESS(Status))
261 return(Status);
262
263 /* Allocate key info buffer */
264 KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
265 KeyInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KeyInfoLength);
266 if (KeyInfo == NULL)
267 {
268 NtClose(ServicesKey);
269 return(STATUS_INSUFFICIENT_RESOURCES);
270 }
271
272 Index = 0;
273 while (TRUE)
274 {
275 Status = NtEnumerateKey(ServicesKey,
276 Index,
277 KeyBasicInformation,
278 KeyInfo,
279 KeyInfoLength,
280 &ReturnedLength);
281 if (NT_SUCCESS(Status))
282 {
283 if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
284 {
285
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;
290
291 DPRINT("KeyName: '%wZ'\n", &SubKeyName);
292 Status = CreateServiceListEntry(&SubKeyName);
293 }
294 }
295
296 if (!NT_SUCCESS(Status))
297 break;
298
299 Index++;
300 }
301
302 HeapFree(GetProcessHeap(), 0, KeyInfo);
303 NtClose(ServicesKey);
304
305 DPRINT("ScmCreateServiceDataBase() done\n");
306
307 return(STATUS_SUCCESS);
308 }
309
310
311 static NTSTATUS
312 ScmCheckDriver(PSERVICE_GROUP Group,
313 PSERVICE Service)
314 {
315 OBJECT_ATTRIBUTES ObjectAttributes;
316 UNICODE_STRING DirName;
317 HANDLE DirHandle;
318 NTSTATUS Status;
319
320 DPRINT("ScmCheckDriver() called\n");
321
322 if (Service->Type == SERVICE_KERNEL_DRIVER)
323 {
324 RtlInitUnicodeString(&DirName,
325 L"\\Driver");
326 }
327 else
328 {
329 RtlInitUnicodeString(&DirName,
330 L"\\FileSystem");
331 }
332
333 InitializeObjectAttributes(&ObjectAttributes,
334 &DirName,
335 0,
336 NULL,
337 NULL);
338
339 Status = NtOpenDirectoryObject(&DirHandle,
340 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
341 &ObjectAttributes);
342 if (!NT_SUCCESS(Status))
343 {
344 return(Status);
345 }
346
347 #if 0
348 Index = 0;
349 while (TRUE)
350 {
351
352
353 Status = NtQueryDirectoryObject(DirHandle,
354 &DirInfo,
355 BufferLength,
356 TRUE,
357 FALSE,
358 &Index,
359 &DataLength);
360 if (!NT_SUCCESS(Status))
361 {
362 NtClose(DirHandle);
363 return(Status);
364 }
365
366 #if 0
367 if (NT_SUCCESS(Status))
368 {
369 CurrentGroup->ServicesRunning = TRUE;
370 CurrentService->ServiceRunning = TRUE;
371 }
372 #endif
373
374
375 }
376 #endif
377
378 NtClose(DirHandle);
379
380 return(STATUS_SUCCESS);
381 }
382
383
384 VOID
385 ScmGetBootAndSystemDriverState(VOID)
386 {
387 PLIST_ENTRY GroupEntry;
388 PLIST_ENTRY ServiceEntry;
389 PSERVICE_GROUP CurrentGroup;
390 PSERVICE CurrentService;
391 NTSTATUS Status;
392
393 DPRINT("ScmGetBootAndSystemDriverState() called\n");
394
395 GroupEntry = GroupListHead.Flink;
396 while (GroupEntry != &GroupListHead)
397 {
398 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
399
400 DPRINT("Checking group '%wZ'\n", &CurrentGroup->GroupName);
401
402 ServiceEntry = ServiceListHead.Flink;
403 while (ServiceEntry != &ServiceListHead)
404 {
405 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
406
407 if (CurrentService->Start == SERVICE_BOOT_START ||
408 CurrentService->Start == SERVICE_SYSTEM_START)
409 {
410 /* Check driver */
411 DPRINT(" Checking service: %wZ\n", &CurrentService->ServiceName);
412
413 ScmCheckDriver(CurrentGroup,
414 CurrentService);
415 }
416 ServiceEntry = ServiceEntry->Flink;
417 }
418 GroupEntry = GroupEntry->Flink;
419 }
420
421 DPRINT("ScmGetBootAndSystemDriverState() done\n");
422 }
423
424
425 static NTSTATUS
426 ScmStartService(PSERVICE Service,
427 PSERVICE_GROUP Group)
428 {
429 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
430 PROCESS_INFORMATION ProcessInformation;
431 STARTUPINFOW StartupInfo;
432 UNICODE_STRING ImagePath;
433 NTSTATUS Status;
434 ULONG Type;
435 BOOL Result;
436
437 DPRINT("ScmStartService() called\n");
438
439 if (Service->Type == SERVICE_KERNEL_DRIVER ||
440 Service->Type == SERVICE_FILE_SYSTEM_DRIVER ||
441 Service->Type == SERVICE_RECOGNIZER_DRIVER)
442 {
443 /* Load driver */
444 DPRINT(" Path: %wZ\n", &Service->RegistryPath);
445 Status = NtLoadDriver(&Service->RegistryPath);
446 }
447 else
448 {
449 RtlInitUnicodeString(&ImagePath, NULL);
450
451 /* Get service data */
452 RtlZeroMemory(&QueryTable,
453 sizeof(QueryTable));
454
455 QueryTable[0].Name = L"Type";
456 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
457 QueryTable[0].EntryContext = &Type;
458
459 QueryTable[1].Name = L"ImagePath";
460 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
461 QueryTable[1].EntryContext = &ImagePath;
462
463 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
464 Service->ServiceName.Buffer,
465 QueryTable,
466 NULL,
467 NULL);
468 if (NT_SUCCESS(Status))
469 {
470 DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
471 DPRINT("Type: %lx\n", Type);
472
473 /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
474
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;
482
483 Result = CreateProcessW(ImagePath.Buffer,
484 NULL,
485 NULL,
486 NULL,
487 FALSE,
488 DETACHED_PROCESS,
489 NULL,
490 NULL,
491 &StartupInfo,
492 &ProcessInformation);
493
494 RtlFreeUnicodeString(&ImagePath);
495
496 if (!Result)
497 {
498 /* FIXME: close control pipe */
499
500 DPRINT("Failed to start '%S'\n", Service->ServiceName.Buffer);
501 Status = STATUS_UNSUCCESSFUL;
502 }
503 else
504 {
505 /* FIXME: connect control pipe */
506
507 }
508 }
509 }
510
511 if (NT_SUCCESS(Status))
512 {
513 if (Group != NULL)
514 {
515 Group->ServicesRunning = TRUE;
516 }
517 Service->ServiceRunning = TRUE;
518 }
519 #if 0
520 else
521 {
522 if (CurrentService->ErrorControl == 1)
523 {
524 /* Log error */
525
526 }
527 else if (CurrentService->ErrorControl == 2)
528 {
529 if (IsLastKnownGood == FALSE)
530 {
531 /* Boot last known good configuration */
532
533 }
534 }
535 else if (CurrentService->ErrorControl == 3)
536 {
537 if (IsLastKnownGood == FALSE)
538 {
539 /* Boot last known good configuration */
540
541 }
542 else
543 {
544 /* BSOD! */
545
546 }
547 }
548 }
549 #endif
550
551 return(STATUS_SUCCESS);
552 }
553
554
555 VOID
556 ScmAutoStartServices(VOID)
557 {
558 PLIST_ENTRY GroupEntry;
559 PLIST_ENTRY ServiceEntry;
560 PSERVICE_GROUP CurrentGroup;
561 PSERVICE CurrentService;
562 NTSTATUS Status;
563
564 /* Clear 'ServiceVisited' flag */
565 ServiceEntry = ServiceListHead.Flink;
566 while (ServiceEntry != &ServiceListHead)
567 {
568 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
569 CurrentService->ServiceVisited = FALSE;
570 ServiceEntry = ServiceEntry->Flink;
571 }
572
573 /* Start all services which are members of an existing group */
574 GroupEntry = GroupListHead.Flink;
575 while (GroupEntry != &GroupListHead)
576 {
577 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
578
579 DPRINT("Group '%wZ'\n", &CurrentGroup->GroupName);
580
581 ServiceEntry = ServiceListHead.Flink;
582 while (ServiceEntry != &ServiceListHead)
583 {
584 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
585
586 if ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
587 (CurrentService->Start == SERVICE_AUTO_START) &&
588 (CurrentService->ServiceVisited == FALSE))
589 {
590 CurrentService->ServiceVisited = TRUE;
591 ScmStartService(CurrentService,
592 CurrentGroup);
593 }
594
595 ServiceEntry = ServiceEntry->Flink;
596 }
597
598 GroupEntry = GroupEntry->Flink;
599 }
600
601 /* Start all services which are members of any non-existing group */
602 ServiceEntry = ServiceListHead.Flink;
603 while (ServiceEntry != &ServiceListHead)
604 {
605 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
606
607 if ((CurrentGroup->GroupName.Length > 0) &&
608 (CurrentService->Start == SERVICE_AUTO_START) &&
609 (CurrentService->ServiceVisited == FALSE))
610 {
611 CurrentService->ServiceVisited = TRUE;
612 ScmStartService(CurrentService,
613 NULL);
614 }
615
616 ServiceEntry = ServiceEntry->Flink;
617 }
618
619 /* Start all services which are not a member of any group */
620 ServiceEntry = ServiceListHead.Flink;
621 while (ServiceEntry != &ServiceListHead)
622 {
623 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
624
625 if ((CurrentGroup->GroupName.Length == 0) &&
626 (CurrentService->Start == SERVICE_AUTO_START) &&
627 (CurrentService->ServiceVisited == FALSE))
628 {
629 CurrentService->ServiceVisited = TRUE;
630 ScmStartService(CurrentService,
631 NULL);
632 }
633
634 ServiceEntry = ServiceEntry->Flink;
635 }
636
637 /* Clear 'ServiceVisited' flag again */
638 ServiceEntry = ServiceListHead.Flink;
639 while (ServiceEntry != &ServiceListHead)
640 {
641 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
642 CurrentService->ServiceVisited = FALSE;
643 ServiceEntry = ServiceEntry->Flink;
644 }
645 }
646
647 /* EOF */