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
30 #include <rosrtl/string.h>
35 #include <services/services.h>
42 /* TYPES *********************************************************************/
44 typedef struct _SERVICE_GROUP
46 LIST_ENTRY GroupListEntry
;
47 UNICODE_STRING GroupName
;
49 BOOLEAN ServicesRunning
;
53 } SERVICE_GROUP
, *PSERVICE_GROUP
;
56 typedef struct _SERVICE
58 LIST_ENTRY ServiceListEntry
;
59 UNICODE_STRING ServiceName
;
60 UNICODE_STRING RegistryPath
;
61 UNICODE_STRING ServiceGroup
;
68 BOOLEAN ServiceRunning
;
69 BOOLEAN ServiceVisited
;
71 HANDLE ControlPipeHandle
;
77 /* GLOBALS *******************************************************************/
79 LIST_ENTRY GroupListHead
;
80 LIST_ENTRY ServiceListHead
;
83 /* FUNCTIONS *****************************************************************/
85 static NTSTATUS STDCALL
86 CreateGroupOrderListRoutine(PWSTR ValueName
,
95 DPRINT("IopGetGroupOrderList(%S, %x, %x, %x, %x, %x)\n",
96 ValueName
, ValueType
, ValueData
, ValueLength
, Context
, EntryContext
);
98 if (ValueType
== REG_BINARY
&&
100 ValueLength
>= sizeof(DWORD
) &&
101 ValueLength
>= (*(PULONG
)ValueData
+ 1) * sizeof(DWORD
))
103 Group
= (PSERVICE_GROUP
)Context
;
104 Group
->TagCount
= ((PULONG
)ValueData
)[0];
105 if (Group
->TagCount
> 0)
107 if (ValueLength
>= (Group
->TagCount
+ 1) * sizeof(DWORD
))
109 Group
->TagArray
= (PULONG
)HeapAlloc(GetProcessHeap(),
111 Group
->TagCount
* sizeof(DWORD
));
112 if (Group
->TagArray
== NULL
)
115 return STATUS_INSUFFICIENT_RESOURCES
;
117 RtlCopyMemory(Group
->TagArray
,
118 (PULONG
)ValueData
+ 1,
119 Group
->TagCount
* sizeof(DWORD
));
124 return STATUS_UNSUCCESSFUL
;
128 return STATUS_SUCCESS
;
132 static NTSTATUS STDCALL
133 CreateGroupListRoutine(PWSTR ValueName
,
140 PSERVICE_GROUP Group
;
141 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
144 if (ValueType
== REG_SZ
)
146 DPRINT("Data: '%S'\n", (PWCHAR
)ValueData
);
148 Group
= (PSERVICE_GROUP
)HeapAlloc(GetProcessHeap(),
150 sizeof(SERVICE_GROUP
));
153 return STATUS_INSUFFICIENT_RESOURCES
;
156 if (!RtlCreateUnicodeString(&Group
->GroupName
,
159 return STATUS_INSUFFICIENT_RESOURCES
;
162 RtlZeroMemory(&QueryTable
, sizeof(QueryTable
));
163 QueryTable
[0].Name
= (PWSTR
)ValueData
;
164 QueryTable
[0].QueryRoutine
= CreateGroupOrderListRoutine
;
166 Status
= RtlQueryRegistryValues(RTL_REGISTRY_CONTROL
,
171 DPRINT("%x %d %S\n", Status
, Group
->TagCount
, (PWSTR
)ValueData
);
174 InsertTailList(&GroupListHead
,
175 &Group
->GroupListEntry
);
178 return STATUS_SUCCESS
;
182 static NTSTATUS STDCALL
183 CreateServiceListEntry(PUNICODE_STRING ServiceName
)
185 RTL_QUERY_REGISTRY_TABLE QueryTable
[6];
186 PSERVICE Service
= NULL
;
189 DPRINT("Service: '%wZ'\n", ServiceName
);
191 /* Allocate service entry */
192 Service
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
196 return STATUS_INSUFFICIENT_RESOURCES
;
199 /* Copy service name */
200 Service
->ServiceName
.Length
= ServiceName
->Length
;
201 Service
->ServiceName
.MaximumLength
= ServiceName
->Length
+ sizeof(WCHAR
);
202 Service
->ServiceName
.Buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
203 Service
->ServiceName
.MaximumLength
);
204 if (Service
->ServiceName
.Buffer
== NULL
)
206 HeapFree(GetProcessHeap(), 0, Service
);
207 return STATUS_INSUFFICIENT_RESOURCES
;
210 RtlCopyMemory(Service
->ServiceName
.Buffer
,
212 ServiceName
->Length
);
213 Service
->ServiceName
.Buffer
[ServiceName
->Length
/ sizeof(WCHAR
)] = 0;
215 /* Build registry path */
216 Service
->RegistryPath
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
217 Service
->RegistryPath
.Buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
218 MAX_PATH
* sizeof(WCHAR
));
219 if (Service
->ServiceName
.Buffer
== NULL
)
221 HeapFree(GetProcessHeap(), 0, Service
->ServiceName
.Buffer
);
222 HeapFree(GetProcessHeap(), 0, Service
);
223 return STATUS_INSUFFICIENT_RESOURCES
;
226 wcscpy(Service
->RegistryPath
.Buffer
,
227 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
228 wcscat(Service
->RegistryPath
.Buffer
,
229 Service
->ServiceName
.Buffer
);
230 Service
->RegistryPath
.Length
= wcslen(Service
->RegistryPath
.Buffer
) * sizeof(WCHAR
);
232 /* Get service data */
233 RtlZeroMemory(&QueryTable
,
236 QueryTable
[0].Name
= L
"Start";
237 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
238 QueryTable
[0].EntryContext
= &Service
->Start
;
240 QueryTable
[1].Name
= L
"Type";
241 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
242 QueryTable
[1].EntryContext
= &Service
->Type
;
244 QueryTable
[2].Name
= L
"ErrorControl";
245 QueryTable
[2].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
246 QueryTable
[2].EntryContext
= &Service
->ErrorControl
;
248 QueryTable
[3].Name
= L
"Group";
249 QueryTable
[3].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
250 QueryTable
[3].EntryContext
= &Service
->ServiceGroup
;
252 QueryTable
[4].Name
= L
"Tag";
253 QueryTable
[4].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
254 QueryTable
[4].EntryContext
= &Service
->Tag
;
256 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
261 if (!NT_SUCCESS(Status
))
263 PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status
);
264 RtlFreeUnicodeString(&Service
->RegistryPath
);
265 RtlFreeUnicodeString(&Service
->ServiceName
);
266 HeapFree(GetProcessHeap(), 0, Service
);
270 DPRINT("ServiceName: '%wZ'\n", &Service
->ServiceName
);
271 DPRINT("RegistryPath: '%wZ'\n", &Service
->RegistryPath
);
272 DPRINT("ServiceGroup: '%wZ'\n", &Service
->ServiceGroup
);
273 DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
274 Service
->Start
, Service
->Type
, Service
->Tag
, Service
->ErrorControl
);
276 /* Append service entry */
277 InsertTailList(&ServiceListHead
,
278 &Service
->ServiceListEntry
);
280 return STATUS_SUCCESS
;
285 ScmCreateServiceDataBase(VOID
)
287 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
288 OBJECT_ATTRIBUTES ObjectAttributes
;
289 UNICODE_STRING ServicesKeyName
;
290 UNICODE_STRING SubKeyName
;
295 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
296 ULONG KeyInfoLength
= 0;
297 ULONG ReturnedLength
;
299 DPRINT("ScmCreateServiceDataBase() called\n");
301 /* Initialize basic variables */
302 InitializeListHead(&GroupListHead
);
303 InitializeListHead(&ServiceListHead
);
305 /* Build group order list */
306 RtlZeroMemory(&QueryTable
,
309 QueryTable
[0].Name
= L
"List";
310 QueryTable
[0].QueryRoutine
= CreateGroupListRoutine
;
312 Status
= RtlQueryRegistryValues(RTL_REGISTRY_CONTROL
,
313 L
"ServiceGroupOrder",
317 if (!NT_SUCCESS(Status
))
320 RtlRosInitUnicodeStringFromLiteral(&ServicesKeyName
,
321 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
323 InitializeObjectAttributes(&ObjectAttributes
,
325 OBJ_CASE_INSENSITIVE
,
329 Status
= RtlpNtOpenKey(&ServicesKey
,
333 if (!NT_SUCCESS(Status
))
336 /* Allocate key info buffer */
337 KeyInfoLength
= sizeof(KEY_BASIC_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
);
338 KeyInfo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KeyInfoLength
);
341 NtClose(ServicesKey
);
342 return STATUS_INSUFFICIENT_RESOURCES
;
348 Status
= NtEnumerateKey(ServicesKey
,
354 if (NT_SUCCESS(Status
))
356 if (KeyInfo
->NameLength
< MAX_PATH
* sizeof(WCHAR
))
359 SubKeyName
.Length
= KeyInfo
->NameLength
;
360 SubKeyName
.MaximumLength
= KeyInfo
->NameLength
+ sizeof(WCHAR
);
361 SubKeyName
.Buffer
= KeyInfo
->Name
;
362 SubKeyName
.Buffer
[SubKeyName
.Length
/ sizeof(WCHAR
)] = 0;
364 DPRINT("KeyName: '%wZ'\n", &SubKeyName
);
365 Status
= CreateServiceListEntry(&SubKeyName
);
367 /* Ignore services without proper registry. */
368 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
370 Status
= STATUS_SUCCESS
;
375 if (!NT_SUCCESS(Status
))
381 HeapFree(GetProcessHeap(), 0, KeyInfo
);
382 NtClose(ServicesKey
);
384 DPRINT("ScmCreateServiceDataBase() done\n");
386 return STATUS_SUCCESS
;
391 ScmCheckDriver(PSERVICE Service
)
393 OBJECT_ATTRIBUTES ObjectAttributes
;
394 UNICODE_STRING DirName
;
397 POBJECT_DIRECTORY_INFORMATION DirInfo
;
401 PLIST_ENTRY GroupEntry
;
402 PSERVICE_GROUP CurrentGroup
;
404 DPRINT("ScmCheckDriver(%wZ) called\n", &Service
->ServiceName
);
406 if (Service
->Type
== SERVICE_KERNEL_DRIVER
)
408 RtlRosInitUnicodeStringFromLiteral(&DirName
,
413 RtlRosInitUnicodeStringFromLiteral(&DirName
,
417 InitializeObjectAttributes(&ObjectAttributes
,
423 Status
= NtOpenDirectoryObject(&DirHandle
,
424 DIRECTORY_QUERY
| DIRECTORY_TRAVERSE
,
426 if (!NT_SUCCESS(Status
))
431 BufferLength
= sizeof(OBJECT_DIRECTORY_INFORMATION
) +
432 2 * MAX_PATH
* sizeof(WCHAR
);
433 DirInfo
= HeapAlloc(GetProcessHeap(),
440 Status
= NtQueryDirectoryObject(DirHandle
,
447 if (Status
== STATUS_NO_MORE_ENTRIES
)
449 /* FIXME: Add current service to 'failed service' list */
450 DPRINT("Service '%wZ' failed\n", &Service
->ServiceName
);
454 if (!NT_SUCCESS(Status
))
457 DPRINT("Comparing: '%wZ' '%wZ'\n", &Service
->ServiceName
, &DirInfo
->ObjectName
);
459 if (RtlEqualUnicodeString(&Service
->ServiceName
, &DirInfo
->ObjectName
, TRUE
))
461 DPRINT("Found: '%wZ' '%wZ'\n", &Service
->ServiceName
, &DirInfo
->ObjectName
);
463 /* Mark service as 'running' */
464 Service
->ServiceRunning
= TRUE
;
466 /* Find the driver's group and mark it as 'running' */
467 if (Service
->ServiceGroup
.Buffer
!= NULL
)
469 GroupEntry
= GroupListHead
.Flink
;
470 while (GroupEntry
!= &GroupListHead
)
472 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
474 DPRINT("Checking group '%wZ'\n", &CurrentGroup
->GroupName
);
475 if (RtlEqualUnicodeString(&Service
->ServiceGroup
, &CurrentGroup
->GroupName
, TRUE
))
477 CurrentGroup
->ServicesRunning
= TRUE
;
480 GroupEntry
= GroupEntry
->Flink
;
487 HeapFree(GetProcessHeap(),
492 return STATUS_SUCCESS
;
497 ScmGetBootAndSystemDriverState(VOID
)
499 PLIST_ENTRY ServiceEntry
;
500 PSERVICE CurrentService
;
502 DPRINT("ScmGetBootAndSystemDriverState() called\n");
504 ServiceEntry
= ServiceListHead
.Flink
;
505 while (ServiceEntry
!= &ServiceListHead
)
507 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
509 if (CurrentService
->Start
== SERVICE_BOOT_START
||
510 CurrentService
->Start
== SERVICE_SYSTEM_START
)
513 DPRINT(" Checking service: %wZ\n", &CurrentService
->ServiceName
);
515 ScmCheckDriver(CurrentService
);
517 ServiceEntry
= ServiceEntry
->Flink
;
520 DPRINT("ScmGetBootAndSystemDriverState() done\n");
525 ScmSendStartCommand(PSERVICE Service
, LPWSTR Arguments
)
527 PSCM_START_PACKET StartPacket
;
535 DPRINT("ScmSendStartCommand() called\n");
537 /* Calculate the total length of the start command line */
538 TotalLength
= wcslen(Service
->ServiceName
.Buffer
) + 1;
540 if (Arguments
!= NULL
)
545 Length
= wcslen(Ptr
) + 1;
546 TotalLength
+= Length
;
553 /* Allocate start command packet */
554 StartPacket
= HeapAlloc(GetProcessHeap(),
556 sizeof(SCM_START_PACKET
) + (TotalLength
- 1) * sizeof(WCHAR
));
557 if (StartPacket
== NULL
)
558 return STATUS_INSUFFICIENT_RESOURCES
;
560 StartPacket
->Command
= SCM_START_COMMAND
;
561 StartPacket
->Size
= TotalLength
;
562 Ptr
= &StartPacket
->Arguments
[0];
563 wcscpy(Ptr
, Service
->ServiceName
.Buffer
);
564 Ptr
+= (wcslen(Service
->ServiceName
.Buffer
) + 1);
566 /* FIXME: Copy argument list */
570 /* Send the start command */
571 WriteFile(Service
->ControlPipeHandle
,
573 sizeof(SCM_START_PACKET
) + (TotalLength
- 1) * sizeof(WCHAR
),
577 /* FIXME: Read the reply */
579 HeapFree(GetProcessHeap(),
583 DPRINT("ScmSendStartCommand() done\n");
585 return STATUS_SUCCESS
;
590 ScmStartUserModeService(PSERVICE Service
)
592 RTL_QUERY_REGISTRY_TABLE QueryTable
[3];
593 PROCESS_INFORMATION ProcessInformation
;
594 STARTUPINFOW StartupInfo
;
595 UNICODE_STRING ImagePath
;
600 RtlInitUnicodeString(&ImagePath
, NULL
);
602 /* Get service data */
603 RtlZeroMemory(&QueryTable
,
606 QueryTable
[0].Name
= L
"Type";
607 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
608 QueryTable
[0].EntryContext
= &Type
;
610 QueryTable
[1].Name
= L
"ImagePath";
611 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
612 QueryTable
[1].EntryContext
= &ImagePath
;
614 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
615 Service
->ServiceName
.Buffer
,
619 if (!NT_SUCCESS(Status
))
621 DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status
);
624 DPRINT("ImagePath: '%S'\n", ImagePath
.Buffer
);
625 DPRINT("Type: %lx\n", Type
);
627 /* Create '\\.\pipe\net\NtControlPipe' instance */
628 Service
->ControlPipeHandle
= CreateNamedPipeW(L
"\\\\.\\pipe\\net\\NtControlPipe",
630 PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT
,
636 DPRINT("CreateNamedPipeW() done\n");
637 if (Service
->ControlPipeHandle
== INVALID_HANDLE_VALUE
)
639 DPRINT1("Failed to create control pipe!\n");
640 return STATUS_UNSUCCESSFUL
;
643 StartupInfo
.cb
= sizeof(StartupInfo
);
644 StartupInfo
.lpReserved
= NULL
;
645 StartupInfo
.lpDesktop
= NULL
;
646 StartupInfo
.lpTitle
= NULL
;
647 StartupInfo
.dwFlags
= 0;
648 StartupInfo
.cbReserved2
= 0;
649 StartupInfo
.lpReserved2
= 0;
651 Result
= CreateProcessW(ImagePath
.Buffer
,
656 DETACHED_PROCESS
| CREATE_SUSPENDED
,
660 &ProcessInformation
);
661 RtlFreeUnicodeString(&ImagePath
);
665 /* Close control pipe */
666 CloseHandle(Service
->ControlPipeHandle
);
667 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
669 DPRINT1("Starting '%S' failed!\n", Service
->ServiceName
.Buffer
);
670 return STATUS_UNSUCCESSFUL
;
673 DPRINT("Process Id: %lu Handle %lx\n",
674 ProcessInformation
.dwProcessId
,
675 ProcessInformation
.hProcess
);
676 DPRINT("Thread Id: %lu Handle %lx\n",
677 ProcessInformation
.dwThreadId
,
678 ProcessInformation
.hThread
);
680 /* Get process and thread ids */
681 Service
->ProcessId
= ProcessInformation
.dwProcessId
;
682 Service
->ThreadId
= ProcessInformation
.dwThreadId
;
685 ResumeThread(ProcessInformation
.hThread
);
687 /* Connect control pipe */
688 if (ConnectNamedPipe(Service
->ControlPipeHandle
, NULL
))
690 DWORD dwProcessId
= 0;
693 DPRINT("Control pipe connected!\n");
695 /* Read thread id from pipe */
696 if (!ReadFile(Service
->ControlPipeHandle
,
697 (LPVOID
)&dwProcessId
,
702 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
704 Status
= STATUS_UNSUCCESSFUL
;
708 DPRINT("Received process id %lu\n", dwProcessId
);
710 /* FIXME: Send start command */
712 Status
= STATUS_SUCCESS
;
717 DPRINT("Connecting control pipe failed!\n");
719 /* Close control pipe */
720 CloseHandle(Service
->ControlPipeHandle
);
721 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
722 Service
->ProcessId
= 0;
723 Service
->ThreadId
= 0;
724 Status
= STATUS_UNSUCCESSFUL
;
727 ScmSendStartCommand(Service
, NULL
);
729 /* Close process and thread handle */
730 CloseHandle(ProcessInformation
.hThread
);
731 CloseHandle(ProcessInformation
.hProcess
);
738 ScmStartService(PSERVICE Service
,
739 PSERVICE_GROUP Group
)
743 DPRINT("ScmStartService() called\n");
745 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
746 DPRINT("Service->Type: %u\n", Service
->Type
);
748 if (Service
->Type
== SERVICE_KERNEL_DRIVER
||
749 Service
->Type
== SERVICE_FILE_SYSTEM_DRIVER
||
750 Service
->Type
== SERVICE_RECOGNIZER_DRIVER
)
753 DPRINT(" Path: %wZ\n", &Service
->RegistryPath
);
754 Status
= NtLoadDriver(&Service
->RegistryPath
);
758 /* Start user-mode service */
759 Status
= ScmStartUserModeService(Service
);
762 DPRINT("ScmStartService() done (Status %lx)\n", Status
);
764 if (NT_SUCCESS(Status
))
768 Group
->ServicesRunning
= TRUE
;
770 Service
->ServiceRunning
= TRUE
;
775 switch (Service
->ErrorControl
)
777 case SERVICE_ERROR_NORMAL
:
778 /* FIXME: Log error */
781 case SERVICE_ERROR_SEVERE
:
782 if (IsLastKnownGood
== FALSE
)
784 /* FIXME: Boot last known good configuration */
788 case SERVICE_ERROR_CRITICAL
:
789 if (IsLastKnownGood
== FALSE
)
791 /* FIXME: Boot last known good configuration */
807 ScmAutoStartServices(VOID
)
809 PLIST_ENTRY GroupEntry
;
810 PLIST_ENTRY ServiceEntry
;
811 PSERVICE_GROUP CurrentGroup
;
812 PSERVICE CurrentService
;
815 /* Clear 'ServiceVisited' flag */
816 ServiceEntry
= ServiceListHead
.Flink
;
817 while (ServiceEntry
!= &ServiceListHead
)
819 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
820 CurrentService
->ServiceVisited
= FALSE
;
821 ServiceEntry
= ServiceEntry
->Flink
;
824 /* Start all services which are members of an existing group */
825 GroupEntry
= GroupListHead
.Flink
;
826 while (GroupEntry
!= &GroupListHead
)
828 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
830 DPRINT("Group '%wZ'\n", &CurrentGroup
->GroupName
);
832 /* Start all services witch have a valid tag */
833 for (i
= 0; i
< CurrentGroup
->TagCount
; i
++)
835 ServiceEntry
= ServiceListHead
.Flink
;
836 while (ServiceEntry
!= &ServiceListHead
)
838 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
840 if ((RtlEqualUnicodeString(&CurrentGroup
->GroupName
, &CurrentService
->ServiceGroup
, TRUE
)) &&
841 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
842 (CurrentService
->ServiceVisited
== FALSE
) &&
843 (CurrentService
->Tag
== CurrentGroup
->TagArray
[i
]))
845 CurrentService
->ServiceVisited
= TRUE
;
846 ScmStartService(CurrentService
,
850 ServiceEntry
= ServiceEntry
->Flink
;
854 /* Start all services which have an invalid tag or which do not have a tag */
855 ServiceEntry
= ServiceListHead
.Flink
;
856 while (ServiceEntry
!= &ServiceListHead
)
858 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
860 if ((RtlEqualUnicodeString(&CurrentGroup
->GroupName
, &CurrentService
->ServiceGroup
, TRUE
)) &&
861 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
862 (CurrentService
->ServiceVisited
== FALSE
))
864 CurrentService
->ServiceVisited
= TRUE
;
865 ScmStartService(CurrentService
,
869 ServiceEntry
= ServiceEntry
->Flink
;
872 GroupEntry
= GroupEntry
->Flink
;
875 /* Start all services which are members of any non-existing group */
876 ServiceEntry
= ServiceListHead
.Flink
;
877 while (ServiceEntry
!= &ServiceListHead
)
879 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
881 if ((CurrentService
->ServiceGroup
.Length
!= 0) &&
882 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
883 (CurrentService
->ServiceVisited
== FALSE
))
885 CurrentService
->ServiceVisited
= TRUE
;
886 ScmStartService(CurrentService
,
890 ServiceEntry
= ServiceEntry
->Flink
;
893 /* Start all services which are not a member of any group */
894 ServiceEntry
= ServiceListHead
.Flink
;
895 while (ServiceEntry
!= &ServiceListHead
)
897 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
899 if ((CurrentService
->ServiceGroup
.Length
== 0) &&
900 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
901 (CurrentService
->ServiceVisited
== FALSE
))
903 CurrentService
->ServiceVisited
= TRUE
;
904 ScmStartService(CurrentService
,
908 ServiceEntry
= ServiceEntry
->Flink
;
911 /* Clear 'ServiceVisited' flag again */
912 ServiceEntry
= ServiceListHead
.Flink
;
913 while (ServiceEntry
!= &ServiceListHead
)
915 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
916 CurrentService
->ServiceVisited
= FALSE
;
917 ServiceEntry
= ServiceEntry
->Flink
;