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 *****************************************************************/
34 /* GLOBALS *******************************************************************/
36 LIST_ENTRY ServiceListHead
;
38 static RTL_RESOURCE DatabaseLock
;
39 static DWORD dwResumeCount
= 1;
42 /* FUNCTIONS *****************************************************************/
45 ScmGetServiceEntryByName(LPWSTR lpServiceName
)
47 PLIST_ENTRY ServiceEntry
;
48 PSERVICE CurrentService
;
50 DPRINT("ScmGetServiceEntryByName() called\n");
52 ServiceEntry
= ServiceListHead
.Flink
;
53 while (ServiceEntry
!= &ServiceListHead
)
55 CurrentService
= CONTAINING_RECORD(ServiceEntry
,
58 if (_wcsicmp(CurrentService
->lpServiceName
, lpServiceName
) == 0)
60 DPRINT("Found service: '%S'\n", CurrentService
->lpServiceName
);
61 return CurrentService
;
64 ServiceEntry
= ServiceEntry
->Flink
;
67 DPRINT("Couldn't find a matching service\n");
74 ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName
)
76 PLIST_ENTRY ServiceEntry
;
77 PSERVICE CurrentService
;
79 DPRINT("ScmGetServiceEntryByDisplayName() called\n");
81 ServiceEntry
= ServiceListHead
.Flink
;
82 while (ServiceEntry
!= &ServiceListHead
)
84 CurrentService
= CONTAINING_RECORD(ServiceEntry
,
87 if (_wcsicmp(CurrentService
->lpDisplayName
, lpDisplayName
) == 0)
89 DPRINT("Found service: '%S'\n", CurrentService
->lpDisplayName
);
90 return CurrentService
;
93 ServiceEntry
= ServiceEntry
->Flink
;
96 DPRINT("Couldn't find a matching service\n");
103 ScmGetServiceEntryByResumeCount(DWORD dwResumeCount
)
105 PLIST_ENTRY ServiceEntry
;
106 PSERVICE CurrentService
;
108 DPRINT("ScmGetServiceEntryByResumeCount() called\n");
110 ServiceEntry
= ServiceListHead
.Flink
;
111 while (ServiceEntry
!= &ServiceListHead
)
113 CurrentService
= CONTAINING_RECORD(ServiceEntry
,
116 if (CurrentService
->dwResumeCount
> dwResumeCount
)
118 DPRINT("Found service: '%S'\n", CurrentService
->lpDisplayName
);
119 return CurrentService
;
122 ServiceEntry
= ServiceEntry
->Flink
;
125 DPRINT("Couldn't find a matching service\n");
132 ScmCreateNewServiceRecord(LPWSTR lpServiceName
,
133 PSERVICE
*lpServiceRecord
)
135 PSERVICE lpService
= NULL
;
137 DPRINT("Service: '%S'\n", lpServiceName
);
139 /* Allocate service entry */
140 lpService
= HeapAlloc(GetProcessHeap(),
142 sizeof(SERVICE
) + ((wcslen(lpServiceName
) + 1) * sizeof(WCHAR
)));
143 if (lpService
== NULL
)
144 return ERROR_NOT_ENOUGH_MEMORY
;
146 *lpServiceRecord
= lpService
;
148 /* Copy service name */
149 wcscpy(lpService
->szServiceName
, lpServiceName
);
150 lpService
->lpServiceName
= lpService
->szServiceName
;
151 lpService
->lpDisplayName
= lpService
->lpServiceName
;
153 /* Set the resume count */
154 lpService
->dwResumeCount
= dwResumeCount
++;
156 /* Append service entry */
157 InsertTailList(&ServiceListHead
,
158 &lpService
->ServiceListEntry
);
160 lpService
->Status
.dwCurrentState
= SERVICE_STOPPED
;
161 lpService
->Status
.dwControlsAccepted
= 0;
162 lpService
->Status
.dwWin32ExitCode
= ERROR_SERVICE_NEVER_STARTED
;
163 lpService
->Status
.dwServiceSpecificExitCode
= 0;
164 lpService
->Status
.dwCheckPoint
= 0;
165 lpService
->Status
.dwWaitHint
= 2000; /* 2 seconds */
167 return ERROR_SUCCESS
;
172 CreateServiceListEntry(LPWSTR lpServiceName
,
175 PSERVICE lpService
= NULL
;
176 LPWSTR lpDisplayName
= NULL
;
177 LPWSTR lpGroup
= NULL
;
182 DWORD dwErrorControl
;
185 DPRINT("Service: '%S'\n", lpServiceName
);
186 if (*lpServiceName
== L
'{')
187 return ERROR_SUCCESS
;
189 dwSize
= sizeof(DWORD
);
190 dwError
= RegQueryValueExW(hServiceKey
,
194 (LPBYTE
)&dwServiceType
,
196 if (dwError
!= ERROR_SUCCESS
)
197 return ERROR_SUCCESS
;
199 if (((dwServiceType
& ~SERVICE_INTERACTIVE_PROCESS
) != SERVICE_WIN32_OWN_PROCESS
) &&
200 ((dwServiceType
& ~SERVICE_INTERACTIVE_PROCESS
) != SERVICE_WIN32_SHARE_PROCESS
) &&
201 (dwServiceType
!= SERVICE_KERNEL_DRIVER
) &&
202 (dwServiceType
!= SERVICE_FILE_SYSTEM_DRIVER
))
203 return ERROR_SUCCESS
;
205 DPRINT("Service type: %lx\n", dwServiceType
);
207 dwSize
= sizeof(DWORD
);
208 dwError
= RegQueryValueExW(hServiceKey
,
212 (LPBYTE
)&dwStartType
,
214 if (dwError
!= ERROR_SUCCESS
)
215 return ERROR_SUCCESS
;
217 DPRINT("Start type: %lx\n", dwStartType
);
219 dwSize
= sizeof(DWORD
);
220 dwError
= RegQueryValueExW(hServiceKey
,
224 (LPBYTE
)&dwErrorControl
,
226 if (dwError
!= ERROR_SUCCESS
)
227 return ERROR_SUCCESS
;
229 DPRINT("Error control: %lx\n", dwErrorControl
);
231 dwError
= RegQueryValueExW(hServiceKey
,
237 if (dwError
!= ERROR_SUCCESS
)
240 DPRINT("Tag: %lx\n", dwTagId
);
242 dwError
= ScmReadString(hServiceKey
,
245 if (dwError
!= ERROR_SUCCESS
)
248 DPRINT("Group: %S\n", lpGroup
);
250 dwError
= ScmReadString(hServiceKey
,
253 if (dwError
!= ERROR_SUCCESS
)
254 lpDisplayName
= NULL
;
256 DPRINT("Display name: %S\n", lpDisplayName
);
258 dwError
= ScmCreateNewServiceRecord(lpServiceName
,
260 if (dwError
!= ERROR_SUCCESS
)
263 lpService
->Status
.dwServiceType
= dwServiceType
;
264 lpService
->dwStartType
= dwStartType
;
265 lpService
->dwErrorControl
= dwErrorControl
;
266 lpService
->dwTag
= dwTagId
;
270 dwError
= ScmSetServiceGroup(lpService
, lpGroup
);
271 if (dwError
!= ERROR_SUCCESS
)
275 if (lpDisplayName
!= NULL
)
277 lpService
->lpDisplayName
= lpDisplayName
;
278 lpDisplayName
= NULL
;
281 DPRINT("ServiceName: '%S'\n", lpService
->lpServiceName
);
282 DPRINT("Group: '%S'\n", lpService
->lpGroup
->lpGroupName
);
283 DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
284 lpService
->dwStartType
,
285 lpService
->Status
.dwServiceType
,
287 lpService
->dwErrorControl
);
289 if (ScmIsDeleteFlagSet(hServiceKey
))
290 lpService
->bDeleted
= TRUE
;
294 HeapFree(GetProcessHeap(), 0, lpGroup
);
296 if (lpDisplayName
!= NULL
)
297 HeapFree(GetProcessHeap(), 0, lpDisplayName
);
304 ScmDeleteMarkedServices(VOID
)
306 PLIST_ENTRY ServiceEntry
;
307 PSERVICE CurrentService
;
309 ServiceEntry
= ServiceListHead
.Flink
;
310 while (ServiceEntry
!= &ServiceListHead
)
312 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
314 ServiceEntry
= ServiceEntry
->Flink
;
316 if (CurrentService
->bDeleted
== TRUE
)
318 DPRINT1("Delete service: %S\n", CurrentService
->lpServiceName
);
320 /* FIXME: Delete the registry keys */
322 /* FIXME: Delete the service record from the list */
330 ScmCreateServiceDatabase(VOID
)
332 WCHAR szSubKey
[MAX_PATH
];
336 DWORD dwSubKeyLength
;
337 FILETIME ftLastChanged
;
340 DPRINT("ScmCreateServiceDatabase() called\n");
342 dwError
= ScmCreateGroupList();
343 if (dwError
!= ERROR_SUCCESS
)
346 /* Initialize basic variables */
347 InitializeListHead(&ServiceListHead
);
349 /* Initialize the database lock */
350 RtlInitializeResource(&DatabaseLock
);
352 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
353 L
"System\\CurrentControlSet\\Services",
357 if (dwError
!= ERROR_SUCCESS
)
363 dwSubKeyLength
= MAX_PATH
;
364 dwError
= RegEnumKeyExW(hServicesKey
,
372 if (dwError
== ERROR_SUCCESS
&&
375 DPRINT("SubKeyName: '%S'\n", szSubKey
);
377 dwError
= RegOpenKeyExW(hServicesKey
,
382 if (dwError
== ERROR_SUCCESS
)
384 dwError
= CreateServiceListEntry(szSubKey
,
387 RegCloseKey(hServiceKey
);
391 if (dwError
!= ERROR_SUCCESS
)
397 RegCloseKey(hServicesKey
);
399 /* Delete services that are marked for delete */
400 ScmDeleteMarkedServices();
402 DPRINT("ScmCreateServiceDatabase() done\n");
404 return ERROR_SUCCESS
;
409 ScmCheckDriver(PSERVICE Service
)
411 OBJECT_ATTRIBUTES ObjectAttributes
;
412 UNICODE_STRING DirName
;
415 POBJECT_DIRECTORY_INFORMATION DirInfo
;
420 DPRINT("ScmCheckDriver(%S) called\n", Service
->lpServiceName
);
422 if (Service
->Status
.dwServiceType
== SERVICE_KERNEL_DRIVER
)
424 RtlInitUnicodeString(&DirName
,
429 RtlInitUnicodeString(&DirName
,
433 InitializeObjectAttributes(&ObjectAttributes
,
439 Status
= NtOpenDirectoryObject(&DirHandle
,
440 DIRECTORY_QUERY
| DIRECTORY_TRAVERSE
,
442 if (!NT_SUCCESS(Status
))
447 BufferLength
= sizeof(OBJECT_DIRECTORY_INFORMATION
) +
448 2 * MAX_PATH
* sizeof(WCHAR
);
449 DirInfo
= HeapAlloc(GetProcessHeap(),
456 Status
= NtQueryDirectoryObject(DirHandle
,
463 if (Status
== STATUS_NO_MORE_ENTRIES
)
465 /* FIXME: Add current service to 'failed service' list */
466 DPRINT("Service '%S' failed\n", Service
->lpServiceName
);
470 if (!NT_SUCCESS(Status
))
473 DPRINT("Comparing: '%S' '%wZ'\n", Service
->lpServiceName
, &DirInfo
->ObjectName
);
475 if (_wcsicmp(Service
->lpServiceName
, DirInfo
->ObjectName
.Buffer
) == 0)
477 DPRINT("Found: '%S' '%wZ'\n",
478 Service
->lpServiceName
, &DirInfo
->ObjectName
);
480 /* Mark service as 'running' */
481 Service
->Status
.dwCurrentState
= SERVICE_RUNNING
;
483 /* Mark the service group as 'running' */
484 if (Service
->lpGroup
!= NULL
)
486 Service
->lpGroup
->ServicesRunning
= TRUE
;
493 HeapFree(GetProcessHeap(),
498 return STATUS_SUCCESS
;
503 ScmGetBootAndSystemDriverState(VOID
)
505 PLIST_ENTRY ServiceEntry
;
506 PSERVICE CurrentService
;
508 DPRINT("ScmGetBootAndSystemDriverState() called\n");
510 ServiceEntry
= ServiceListHead
.Flink
;
511 while (ServiceEntry
!= &ServiceListHead
)
513 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
515 if (CurrentService
->dwStartType
== SERVICE_BOOT_START
||
516 CurrentService
->dwStartType
== SERVICE_SYSTEM_START
)
519 DPRINT(" Checking service: %S\n", CurrentService
->lpServiceName
);
521 ScmCheckDriver(CurrentService
);
524 ServiceEntry
= ServiceEntry
->Flink
;
527 DPRINT("ScmGetBootAndSystemDriverState() done\n");
532 ScmSendStartCommand(PSERVICE Service
, LPWSTR Arguments
)
534 PSCM_START_PACKET StartPacket
;
542 DPRINT("ScmSendStartCommand() called\n");
544 /* Calculate the total length of the start command line */
545 TotalLength
= wcslen(Service
->lpServiceName
) + 1;
547 if (Arguments
!= NULL
)
552 Length
= wcslen(Ptr
) + 1;
553 TotalLength
+= Length
;
560 /* Allocate start command packet */
561 StartPacket
= HeapAlloc(GetProcessHeap(),
563 sizeof(SCM_START_PACKET
) + (TotalLength
- 1) * sizeof(WCHAR
));
564 if (StartPacket
== NULL
)
565 return STATUS_INSUFFICIENT_RESOURCES
;
567 StartPacket
->Command
= SCM_START_COMMAND
;
568 StartPacket
->Size
= TotalLength
;
569 Ptr
= &StartPacket
->Arguments
[0];
570 wcscpy(Ptr
, Service
->lpServiceName
);
571 Ptr
+= (wcslen(Service
->lpServiceName
) + 1);
573 /* FIXME: Copy argument list */
577 /* Send the start command */
578 WriteFile(Service
->ControlPipeHandle
,
580 sizeof(SCM_START_PACKET
) + (TotalLength
- 1) * sizeof(WCHAR
),
584 /* FIXME: Read the reply */
586 HeapFree(GetProcessHeap(),
590 DPRINT("ScmSendStartCommand() done\n");
592 return STATUS_SUCCESS
;
597 ScmStartUserModeService(PSERVICE Service
)
599 RTL_QUERY_REGISTRY_TABLE QueryTable
[3];
600 PROCESS_INFORMATION ProcessInformation
;
601 STARTUPINFOW StartupInfo
;
602 UNICODE_STRING ImagePath
;
607 RtlInitUnicodeString(&ImagePath
, NULL
);
609 /* Get service data */
610 RtlZeroMemory(&QueryTable
,
613 QueryTable
[0].Name
= L
"Type";
614 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
615 QueryTable
[0].EntryContext
= &Type
;
617 QueryTable
[1].Name
= L
"ImagePath";
618 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
619 QueryTable
[1].EntryContext
= &ImagePath
;
621 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
622 Service
->lpServiceName
,
626 if (!NT_SUCCESS(Status
))
628 DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status
);
631 DPRINT("ImagePath: '%S'\n", ImagePath
.Buffer
);
632 DPRINT("Type: %lx\n", Type
);
634 /* Create '\\.\pipe\net\NtControlPipe' instance */
635 Service
->ControlPipeHandle
= CreateNamedPipeW(L
"\\\\.\\pipe\\net\\NtControlPipe",
637 PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT
,
643 DPRINT("CreateNamedPipeW() done\n");
644 if (Service
->ControlPipeHandle
== INVALID_HANDLE_VALUE
)
646 DPRINT1("Failed to create control pipe!\n");
647 return STATUS_UNSUCCESSFUL
;
650 StartupInfo
.cb
= sizeof(StartupInfo
);
651 StartupInfo
.lpReserved
= NULL
;
652 StartupInfo
.lpDesktop
= NULL
;
653 StartupInfo
.lpTitle
= NULL
;
654 StartupInfo
.dwFlags
= 0;
655 StartupInfo
.cbReserved2
= 0;
656 StartupInfo
.lpReserved2
= 0;
658 Result
= CreateProcessW(ImagePath
.Buffer
,
663 DETACHED_PROCESS
| CREATE_SUSPENDED
,
667 &ProcessInformation
);
668 RtlFreeUnicodeString(&ImagePath
);
672 /* Close control pipe */
673 CloseHandle(Service
->ControlPipeHandle
);
674 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
676 DPRINT1("Starting '%S' failed!\n", Service
->lpServiceName
);
677 return STATUS_UNSUCCESSFUL
;
680 DPRINT("Process Id: %lu Handle %lx\n",
681 ProcessInformation
.dwProcessId
,
682 ProcessInformation
.hProcess
);
683 DPRINT("Thread Id: %lu Handle %lx\n",
684 ProcessInformation
.dwThreadId
,
685 ProcessInformation
.hThread
);
687 /* Get process and thread ids */
688 Service
->ProcessId
= ProcessInformation
.dwProcessId
;
689 Service
->ThreadId
= ProcessInformation
.dwThreadId
;
692 ResumeThread(ProcessInformation
.hThread
);
694 /* Connect control pipe */
695 if (ConnectNamedPipe(Service
->ControlPipeHandle
, NULL
))
697 DWORD dwProcessId
= 0;
700 DPRINT("Control pipe connected!\n");
702 /* Read thread id from pipe */
703 if (!ReadFile(Service
->ControlPipeHandle
,
704 (LPVOID
)&dwProcessId
,
709 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
711 Status
= STATUS_UNSUCCESSFUL
;
715 DPRINT("Received process id %lu\n", dwProcessId
);
717 /* FIXME: Send start command */
719 Status
= STATUS_SUCCESS
;
724 DPRINT("Connecting control pipe failed!\n");
726 /* Close control pipe */
727 CloseHandle(Service
->ControlPipeHandle
);
728 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
729 Service
->ProcessId
= 0;
730 Service
->ThreadId
= 0;
731 Status
= STATUS_UNSUCCESSFUL
;
734 ScmSendStartCommand(Service
, NULL
);
736 /* Close process and thread handle */
737 CloseHandle(ProcessInformation
.hThread
);
738 CloseHandle(ProcessInformation
.hProcess
);
745 ScmStartService(PSERVICE Service
,
746 PSERVICE_GROUP Group
)
750 DPRINT("ScmStartService() called\n");
752 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
753 DPRINT("Service->Type: %lu\n", Service
->Status
.dwServiceType
);
755 if (Service
->Status
.dwServiceType
& SERVICE_DRIVER
)
758 Status
= ScmLoadDriver(Service
);
759 if (Status
== STATUS_SUCCESS
)
760 Service
->Status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
764 /* Start user-mode service */
765 Status
= ScmStartUserModeService(Service
);
768 DPRINT("ScmStartService() done (Status %lx)\n", Status
);
770 if (NT_SUCCESS(Status
))
774 Group
->ServicesRunning
= TRUE
;
776 Service
->Status
.dwCurrentState
= SERVICE_RUNNING
;
781 switch (Service
->ErrorControl
)
783 case SERVICE_ERROR_NORMAL
:
784 /* FIXME: Log error */
787 case SERVICE_ERROR_SEVERE
:
788 if (IsLastKnownGood
== FALSE
)
790 /* FIXME: Boot last known good configuration */
794 case SERVICE_ERROR_CRITICAL
:
795 if (IsLastKnownGood
== FALSE
)
797 /* FIXME: Boot last known good configuration */
813 ScmAutoStartServices(VOID
)
815 PLIST_ENTRY GroupEntry
;
816 PLIST_ENTRY ServiceEntry
;
817 PSERVICE_GROUP CurrentGroup
;
818 PSERVICE CurrentService
;
821 /* Clear 'ServiceVisited' flag */
822 ServiceEntry
= ServiceListHead
.Flink
;
823 while (ServiceEntry
!= &ServiceListHead
)
825 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
826 CurrentService
->ServiceVisited
= FALSE
;
827 ServiceEntry
= ServiceEntry
->Flink
;
830 /* Start all services which are members of an existing group */
831 GroupEntry
= GroupListHead
.Flink
;
832 while (GroupEntry
!= &GroupListHead
)
834 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
836 DPRINT("Group '%S'\n", CurrentGroup
->lpGroupName
);
838 /* Start all services witch have a valid tag */
839 for (i
= 0; i
< CurrentGroup
->TagCount
; i
++)
841 ServiceEntry
= ServiceListHead
.Flink
;
842 while (ServiceEntry
!= &ServiceListHead
)
844 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
846 if ((CurrentService
->lpGroup
== CurrentGroup
) &&
847 (CurrentService
->dwStartType
== SERVICE_AUTO_START
) &&
848 (CurrentService
->ServiceVisited
== FALSE
) &&
849 (CurrentService
->dwTag
== CurrentGroup
->TagArray
[i
]))
851 CurrentService
->ServiceVisited
= TRUE
;
852 ScmStartService(CurrentService
,
856 ServiceEntry
= ServiceEntry
->Flink
;
860 /* Start all services which have an invalid tag or which do not have a tag */
861 ServiceEntry
= ServiceListHead
.Flink
;
862 while (ServiceEntry
!= &ServiceListHead
)
864 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
866 if ((CurrentService
->lpGroup
== CurrentGroup
) &&
867 (CurrentService
->dwStartType
== SERVICE_AUTO_START
) &&
868 (CurrentService
->ServiceVisited
== FALSE
))
870 CurrentService
->ServiceVisited
= TRUE
;
871 ScmStartService(CurrentService
,
875 ServiceEntry
= ServiceEntry
->Flink
;
878 GroupEntry
= GroupEntry
->Flink
;
881 /* Start all services which are members of any non-existing group */
882 ServiceEntry
= ServiceListHead
.Flink
;
883 while (ServiceEntry
!= &ServiceListHead
)
885 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
887 if ((CurrentService
->lpGroup
!= NULL
) &&
888 (CurrentService
->dwStartType
== SERVICE_AUTO_START
) &&
889 (CurrentService
->ServiceVisited
== FALSE
))
891 CurrentService
->ServiceVisited
= TRUE
;
892 ScmStartService(CurrentService
,
896 ServiceEntry
= ServiceEntry
->Flink
;
899 /* Start all services which are not a member of any group */
900 ServiceEntry
= ServiceListHead
.Flink
;
901 while (ServiceEntry
!= &ServiceListHead
)
903 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
905 if ((CurrentService
->lpGroup
== NULL
) &&
906 (CurrentService
->dwStartType
== SERVICE_AUTO_START
) &&
907 (CurrentService
->ServiceVisited
== FALSE
))
909 CurrentService
->ServiceVisited
= TRUE
;
910 ScmStartService(CurrentService
,
914 ServiceEntry
= ServiceEntry
->Flink
;
917 /* Clear 'ServiceVisited' flag again */
918 ServiceEntry
= ServiceListHead
.Flink
;
919 while (ServiceEntry
!= &ServiceListHead
)
921 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
922 CurrentService
->ServiceVisited
= FALSE
;
923 ServiceEntry
= ServiceEntry
->Flink
;