- Initial hacky implementation of some SCM calls needed by coLinux.
[reactos.git] / reactos / subsys / system / services / database.c
1 /* $Id: database.c,v 1.15 2004/04/12 17:14:54 navaraf 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 #include <rosrtl/string.h>
31
32 #include <windows.h>
33 #include <tchar.h>
34
35 #include "services.h"
36
37 #define NDEBUG
38 #include <debug.h>
39
40
41 /* GLOBALS *******************************************************************/
42
43 LIST_ENTRY GroupListHead;
44 LIST_ENTRY ServiceListHead;
45
46
47 /* FUNCTIONS *****************************************************************/
48
49 static NTSTATUS STDCALL
50 CreateGroupListRoutine(PWSTR ValueName,
51 ULONG ValueType,
52 PVOID ValueData,
53 ULONG ValueLength,
54 PVOID Context,
55 PVOID EntryContext)
56 {
57 PSERVICE_GROUP Group;
58
59 if (ValueType == REG_SZ)
60 {
61 DPRINT("Data: '%S'\n", (PWCHAR)ValueData);
62
63 Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),
64 HEAP_ZERO_MEMORY,
65 sizeof(SERVICE_GROUP));
66 if (Group == NULL)
67 {
68 return(STATUS_INSUFFICIENT_RESOURCES);
69 }
70
71 if (!RtlCreateUnicodeString(&Group->GroupName,
72 (PWSTR)ValueData))
73 {
74 return(STATUS_INSUFFICIENT_RESOURCES);
75 }
76
77
78 InsertTailList(&GroupListHead,
79 &Group->GroupListEntry);
80 }
81
82 return(STATUS_SUCCESS);
83 }
84
85
86 PSERVICE FASTCALL
87 ScmCreateServiceListEntry(PUNICODE_STRING ServiceName)
88 {
89 RTL_QUERY_REGISTRY_TABLE QueryTable[6];
90 PSERVICE Service = NULL;
91 NTSTATUS Status;
92
93 DPRINT("Service: '%wZ'\n", ServiceName);
94
95 /* Allocate service entry */
96 Service = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
97 sizeof(SERVICE));
98 if (Service == NULL)
99 {
100 return NULL;
101 }
102
103 /* Copy service name */
104 Service->ServiceName.Length = ServiceName->Length;
105 Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
106 Service->ServiceName.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
107 Service->ServiceName.MaximumLength);
108 if (Service->ServiceName.Buffer == NULL)
109 {
110 HeapFree(GetProcessHeap(), 0, Service);
111 return NULL;
112 }
113 RtlCopyMemory(Service->ServiceName.Buffer,
114 ServiceName->Buffer,
115 ServiceName->Length);
116 Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
117
118 /* Build registry path */
119 Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
120 Service->RegistryPath.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
121 MAX_PATH * sizeof(WCHAR));
122 if (Service->ServiceName.Buffer == NULL)
123 {
124 HeapFree(GetProcessHeap(), 0, Service->ServiceName.Buffer);
125 HeapFree(GetProcessHeap(), 0, Service);
126 return NULL;
127 }
128 wcscpy(Service->RegistryPath.Buffer,
129 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
130 wcscat(Service->RegistryPath.Buffer,
131 Service->ServiceName.Buffer);
132 Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
133
134 /* Get service data */
135 RtlZeroMemory(&QueryTable,
136 sizeof(QueryTable));
137
138 QueryTable[0].Name = L"Start";
139 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
140 QueryTable[0].EntryContext = &Service->Start;
141
142 QueryTable[1].Name = L"Type";
143 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
144 QueryTable[1].EntryContext = &Service->Type;
145
146 QueryTable[2].Name = L"ErrorControl";
147 QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
148 QueryTable[2].EntryContext = &Service->ErrorControl;
149
150 QueryTable[3].Name = L"Group";
151 QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
152 QueryTable[3].EntryContext = &Service->ServiceGroup;
153
154 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
155 ServiceName->Buffer,
156 QueryTable,
157 NULL,
158 NULL);
159 if (!NT_SUCCESS(Status))
160 {
161 PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
162 RtlFreeUnicodeString(&Service->RegistryPath);
163 RtlFreeUnicodeString(&Service->ServiceName);
164 HeapFree(GetProcessHeap(), 0, Service);
165 return NULL;
166 }
167
168 DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
169 DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
170 DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
171 DPRINT("Start %lx Type %lx ErrorControl %lx\n",
172 Service->Start, Service->Type, Service->ErrorControl);
173
174 /* Append service entry */
175 InsertTailList(&ServiceListHead,
176 &Service->ServiceListEntry);
177
178 return Service;
179 }
180
181
182 NTSTATUS
183 ScmCreateServiceDataBase(VOID)
184 {
185 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
186 OBJECT_ATTRIBUTES ObjectAttributes;
187 UNICODE_STRING ServicesKeyName;
188 UNICODE_STRING SubKeyName;
189 HKEY ServicesKey;
190 ULONG Index;
191 NTSTATUS Status;
192
193 PKEY_BASIC_INFORMATION KeyInfo = NULL;
194 ULONG KeyInfoLength = 0;
195 ULONG ReturnedLength;
196
197 DPRINT("ScmCreateServiceDataBase() called\n");
198
199 /* Initialize basic variables */
200 InitializeListHead(&GroupListHead);
201 InitializeListHead(&ServiceListHead);
202
203 /* Build group order list */
204 RtlZeroMemory(&QueryTable,
205 sizeof(QueryTable));
206
207 QueryTable[0].Name = L"List";
208 QueryTable[0].QueryRoutine = CreateGroupListRoutine;
209
210 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
211 L"ServiceGroupOrder",
212 QueryTable,
213 NULL,
214 NULL);
215 if (!NT_SUCCESS(Status))
216 return(Status);
217
218 RtlRosInitUnicodeStringFromLiteral(&ServicesKeyName,
219 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
220
221 InitializeObjectAttributes(&ObjectAttributes,
222 &ServicesKeyName,
223 OBJ_CASE_INSENSITIVE,
224 NULL,
225 NULL);
226
227 Status = RtlpNtOpenKey(&ServicesKey,
228 0x10001,
229 &ObjectAttributes,
230 0);
231 if (!NT_SUCCESS(Status))
232 return(Status);
233
234 /* Allocate key info buffer */
235 KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
236 KeyInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KeyInfoLength);
237 if (KeyInfo == NULL)
238 {
239 NtClose(ServicesKey);
240 return(STATUS_INSUFFICIENT_RESOURCES);
241 }
242
243 Index = 0;
244 while (TRUE)
245 {
246 Status = NtEnumerateKey(ServicesKey,
247 Index,
248 KeyBasicInformation,
249 KeyInfo,
250 KeyInfoLength,
251 &ReturnedLength);
252 if (NT_SUCCESS(Status))
253 {
254 if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
255 {
256
257 SubKeyName.Length = KeyInfo->NameLength;
258 SubKeyName.MaximumLength = KeyInfo->NameLength + sizeof(WCHAR);
259 SubKeyName.Buffer = KeyInfo->Name;
260 SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
261
262 DPRINT("KeyName: '%wZ'\n", &SubKeyName);
263 if (ScmCreateServiceListEntry(&SubKeyName) == NULL)
264 {
265 return STATUS_INSUFFICIENT_RESOURCES;
266 }
267 }
268 }
269
270 if (!NT_SUCCESS(Status))
271 break;
272
273 Index++;
274 }
275
276 HeapFree(GetProcessHeap(), 0, KeyInfo);
277 NtClose(ServicesKey);
278
279 DPRINT("ScmCreateServiceDataBase() done\n");
280
281 return(STATUS_SUCCESS);
282 }
283
284
285 static NTSTATUS
286 ScmCheckDriver(PSERVICE Service)
287 {
288 OBJECT_ATTRIBUTES ObjectAttributes;
289 UNICODE_STRING DirName;
290 HANDLE DirHandle;
291 NTSTATUS Status;
292 PDIRECTORY_BASIC_INFORMATION DirInfo;
293 ULONG BufferLength;
294 ULONG DataLength;
295 ULONG Index;
296 PLIST_ENTRY GroupEntry;
297 PSERVICE_GROUP CurrentGroup;
298
299 DPRINT("ScmCheckDriver(%wZ) called\n", &Service->ServiceName);
300
301 if (Service->Type == SERVICE_KERNEL_DRIVER)
302 {
303 RtlRosInitUnicodeStringFromLiteral(&DirName,
304 L"\\Driver");
305 }
306 else
307 {
308 RtlRosInitUnicodeStringFromLiteral(&DirName,
309 L"\\FileSystem");
310 }
311
312 InitializeObjectAttributes(&ObjectAttributes,
313 &DirName,
314 0,
315 NULL,
316 NULL);
317
318 Status = NtOpenDirectoryObject(&DirHandle,
319 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
320 &ObjectAttributes);
321 if (!NT_SUCCESS(Status))
322 {
323 return(Status);
324 }
325
326 BufferLength = sizeof(DIRECTORY_BASIC_INFORMATION) +
327 2 * MAX_PATH * sizeof(WCHAR);
328 DirInfo = HeapAlloc(GetProcessHeap(),
329 HEAP_ZERO_MEMORY,
330 BufferLength);
331
332 Index = 0;
333 while (TRUE)
334 {
335 Status = NtQueryDirectoryObject(DirHandle,
336 DirInfo,
337 BufferLength,
338 TRUE,
339 FALSE,
340 &Index,
341 &DataLength);
342 if (Status == STATUS_NO_MORE_ENTRIES)
343 {
344 /* FIXME: Add current service to 'failed service' list */
345 DPRINT("Service '%wZ' failed\n", &Service->ServiceName);
346 break;
347 }
348
349 if (!NT_SUCCESS(Status))
350 break;
351
352 DPRINT("Comparing: '%wZ' '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
353
354 if (RtlEqualUnicodeString(&Service->ServiceName, &DirInfo->ObjectName, TRUE))
355 {
356 DPRINT("Found: '%wZ' '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
357
358 /* Mark service as 'running' */
359 Service->ServiceRunning = TRUE;
360
361 /* Find the driver's group and mark it as 'running' */
362 if (Service->ServiceGroup.Buffer != NULL)
363 {
364 GroupEntry = GroupListHead.Flink;
365 while (GroupEntry != &GroupListHead)
366 {
367 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
368
369 DPRINT("Checking group '%wZ'\n", &CurrentGroup->GroupName);
370 if (RtlEqualUnicodeString(&Service->ServiceGroup, &CurrentGroup->GroupName, TRUE))
371 {
372 CurrentGroup->ServicesRunning = TRUE;
373 }
374
375 GroupEntry = GroupEntry->Flink;
376 }
377 }
378 break;
379 }
380 }
381
382 HeapFree(GetProcessHeap(),
383 0,
384 DirInfo);
385 NtClose(DirHandle);
386
387 return(STATUS_SUCCESS);
388 }
389
390
391 VOID
392 ScmGetBootAndSystemDriverState(VOID)
393 {
394 PLIST_ENTRY ServiceEntry;
395 PSERVICE CurrentService;
396
397 DPRINT("ScmGetBootAndSystemDriverState() called\n");
398
399 ServiceEntry = ServiceListHead.Flink;
400 while (ServiceEntry != &ServiceListHead)
401 {
402 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
403
404 if (CurrentService->Start == SERVICE_BOOT_START ||
405 CurrentService->Start == SERVICE_SYSTEM_START)
406 {
407 /* Check driver */
408 DPRINT(" Checking service: %wZ\n", &CurrentService->ServiceName);
409
410 ScmCheckDriver(CurrentService);
411 }
412 ServiceEntry = ServiceEntry->Flink;
413 }
414
415 DPRINT("ScmGetBootAndSystemDriverState() done\n");
416 }
417
418
419 NTSTATUS FASTCALL
420 ScmStartService(PSERVICE Service,
421 PSERVICE_GROUP Group)
422 {
423 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
424 PROCESS_INFORMATION ProcessInformation;
425 STARTUPINFOW StartupInfo;
426 UNICODE_STRING ImagePath;
427 NTSTATUS Status;
428 ULONG Type;
429 BOOL Result;
430
431 DPRINT("ScmStartService() called\n");
432
433 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
434 DPRINT("Service->Type: %u\n", Service->Type);
435
436 if (Service->Type == SERVICE_KERNEL_DRIVER ||
437 Service->Type == SERVICE_FILE_SYSTEM_DRIVER ||
438 Service->Type == SERVICE_RECOGNIZER_DRIVER)
439 {
440 /* Load driver */
441 DPRINT(" Path: %wZ\n", &Service->RegistryPath);
442 Status = NtLoadDriver(&Service->RegistryPath);
443 }
444 else
445 {
446 RtlInitUnicodeString(&ImagePath, NULL);
447
448 /* Get service data */
449 RtlZeroMemory(&QueryTable,
450 sizeof(QueryTable));
451
452 QueryTable[0].Name = L"Type";
453 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
454 QueryTable[0].EntryContext = &Type;
455
456 QueryTable[1].Name = L"ImagePath";
457 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
458 QueryTable[1].EntryContext = &ImagePath;
459
460 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
461 Service->ServiceName.Buffer,
462 QueryTable,
463 NULL,
464 NULL);
465 if (NT_SUCCESS(Status))
466 {
467 DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
468 DPRINT("Type: %lx\n", Type);
469
470 /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
471 Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
472 PIPE_ACCESS_DUPLEX,
473 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
474 100,
475 8000,
476 4,
477 30000,
478 NULL);
479 DPRINT("CreateNamedPipeW() done\n");
480 if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
481 {
482 DPRINT1("Failed to create control pipe!\n");
483 Status = STATUS_UNSUCCESSFUL;
484 goto Done;
485 }
486
487 StartupInfo.cb = sizeof(StartupInfo);
488 StartupInfo.lpReserved = NULL;
489 StartupInfo.lpDesktop = NULL;
490 StartupInfo.lpTitle = NULL;
491 StartupInfo.dwFlags = 0;
492 StartupInfo.cbReserved2 = 0;
493 StartupInfo.lpReserved2 = 0;
494
495 Result = CreateProcessW(ImagePath.Buffer,
496 NULL,
497 NULL,
498 NULL,
499 FALSE,
500 DETACHED_PROCESS | CREATE_SUSPENDED,
501 NULL,
502 NULL,
503 &StartupInfo,
504 &ProcessInformation);
505 RtlFreeUnicodeString(&ImagePath);
506
507 if (!Result)
508 {
509 /* Close control pipe */
510 CloseHandle(Service->ControlPipeHandle);
511 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
512
513 DPRINT("Starting '%S' failed!\n", Service->ServiceName.Buffer);
514 Status = STATUS_UNSUCCESSFUL;
515 }
516 else
517 {
518 DPRINT("Process Id: %lu Handle %lx\n",
519 ProcessInformation.dwProcessId,
520 ProcessInformation.hProcess);
521 DPRINT("Thread Id: %lu Handle %lx\n",
522 ProcessInformation.dwThreadId,
523 ProcessInformation.hThread);
524
525 /* Get process and thread ids */
526 Service->ProcessId = ProcessInformation.dwProcessId;
527 Service->ThreadId = ProcessInformation.dwThreadId;
528
529 /* Resume Thread */
530 ResumeThread(ProcessInformation.hThread);
531
532 /* FIXME: connect control pipe */
533 if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
534 {
535 DPRINT("Control pipe connected!\n");
536 Status = STATUS_SUCCESS;
537 }
538 else
539 {
540 DPRINT1("Connecting control pipe failed!\n");
541
542 /* Close control pipe */
543 CloseHandle(Service->ControlPipeHandle);
544 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
545 Service->ProcessId = 0;
546 Service->ThreadId = 0;
547 Status = STATUS_UNSUCCESSFUL;
548 }
549
550 /* Close process and thread handle */
551 CloseHandle(ProcessInformation.hThread);
552 CloseHandle(ProcessInformation.hProcess);
553 }
554 }
555 }
556
557 Done:
558 if (NT_SUCCESS(Status))
559 {
560 if (Group != NULL)
561 {
562 Group->ServicesRunning = TRUE;
563 }
564 Service->ServiceRunning = TRUE;
565 }
566 #if 0
567 else
568 {
569 if (CurrentService->ErrorControl == 1)
570 {
571 /* Log error */
572
573 }
574 else if (CurrentService->ErrorControl == 2)
575 {
576 if (IsLastKnownGood == FALSE)
577 {
578 /* Boot last known good configuration */
579
580 }
581 }
582 else if (CurrentService->ErrorControl == 3)
583 {
584 if (IsLastKnownGood == FALSE)
585 {
586 /* Boot last known good configuration */
587
588 }
589 else
590 {
591 /* BSOD! */
592
593 }
594 }
595 }
596 #endif
597
598 return Status; //(STATUS_SUCCESS);
599 }
600
601
602 VOID
603 ScmAutoStartServices(VOID)
604 {
605 PLIST_ENTRY GroupEntry;
606 PLIST_ENTRY ServiceEntry;
607 PSERVICE_GROUP CurrentGroup;
608 PSERVICE CurrentService;
609
610 /* Clear 'ServiceVisited' flag */
611 ServiceEntry = ServiceListHead.Flink;
612 while (ServiceEntry != &ServiceListHead)
613 {
614 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
615 CurrentService->ServiceVisited = FALSE;
616 ServiceEntry = ServiceEntry->Flink;
617 }
618
619 /* Start all services which are members of an existing group */
620 GroupEntry = GroupListHead.Flink;
621 while (GroupEntry != &GroupListHead)
622 {
623 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
624
625 DPRINT("Group '%wZ'\n", &CurrentGroup->GroupName);
626
627 ServiceEntry = ServiceListHead.Flink;
628 while (ServiceEntry != &ServiceListHead)
629 {
630 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
631
632 if ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
633 (CurrentService->Start == SERVICE_AUTO_START) &&
634 (CurrentService->ServiceVisited == FALSE))
635 {
636 CurrentService->ServiceVisited = TRUE;
637 ScmStartService(CurrentService,
638 CurrentGroup);
639 }
640
641 ServiceEntry = ServiceEntry->Flink;
642 }
643
644 GroupEntry = GroupEntry->Flink;
645 }
646
647 /* Start all services which are members of any non-existing group */
648 ServiceEntry = ServiceListHead.Flink;
649 while (ServiceEntry != &ServiceListHead)
650 {
651 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
652
653 if ((CurrentGroup->GroupName.Length > 0) &&
654 (CurrentService->Start == SERVICE_AUTO_START) &&
655 (CurrentService->ServiceVisited == FALSE))
656 {
657 CurrentService->ServiceVisited = TRUE;
658 ScmStartService(CurrentService,
659 NULL);
660 }
661
662 ServiceEntry = ServiceEntry->Flink;
663 }
664
665 /* Start all services which are not a member of any group */
666 ServiceEntry = ServiceListHead.Flink;
667 while (ServiceEntry != &ServiceListHead)
668 {
669 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
670
671 if ((CurrentGroup->GroupName.Length == 0) &&
672 (CurrentService->Start == SERVICE_AUTO_START) &&
673 (CurrentService->ServiceVisited == FALSE))
674 {
675 CurrentService->ServiceVisited = TRUE;
676 ScmStartService(CurrentService,
677 NULL);
678 }
679
680 ServiceEntry = ServiceEntry->Flink;
681 }
682
683 /* Clear 'ServiceVisited' flag again */
684 ServiceEntry = ServiceListHead.Flink;
685 while (ServiceEntry != &ServiceListHead)
686 {
687 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
688 CurrentService->ServiceVisited = FALSE;
689 ServiceEntry = ServiceEntry->Flink;
690 }
691 }
692
693 /*
694 * FIXME: Doesn't work!!!
695 */
696 PSERVICE FASTCALL
697 ScmFindService(PUNICODE_STRING ServiceName)
698 {
699 PSERVICE CurrentService;
700 PLIST_ENTRY ServiceEntry;
701
702 ServiceEntry = ServiceListHead.Flink;
703 while (ServiceEntry != &ServiceListHead)
704 {
705 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
706 if (!RtlCompareUnicodeString(ServiceName, &CurrentService->ServiceName, TRUE))
707 {
708 return CurrentService;
709 }
710 ServiceEntry = ServiceEntry->Flink;
711 }
712
713 return NULL;
714 }
715
716 /* EOF */