xref: /reactos/base/system/services/database.c (revision 45b08ed3)
1 /*
2  * PROJECT:     ReactOS Service Control Manager
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/system/services/database.c
5  * PURPOSE:     Database control interface
6  * COPYRIGHT:   Copyright 2002-2006 Eric Kohl
7  *              Copyright 2006 Herv� Poussineau <hpoussin@reactos.org>
8  *              Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
9  *                             Gregor Brunmar <gregor.brunmar@home.se>
10  *
11  */
12 
13 /* INCLUDES *****************************************************************/
14 
15 #include "services.h"
16 
17 #include <userenv.h>
18 #include <strsafe.h>
19 
20 #define NDEBUG
21 #include <debug.h>
22 
23 
24 /* GLOBALS *******************************************************************/
25 
26 LIST_ENTRY ImageListHead;
27 LIST_ENTRY ServiceListHead;
28 
29 static RTL_RESOURCE DatabaseLock;
30 static DWORD ResumeCount = 1;
31 static DWORD NoInteractiveServices = 0;
32 
33 /* The critical section synchronizes service control requests */
34 static CRITICAL_SECTION ControlServiceCriticalSection;
35 static DWORD PipeTimeout = 30000; /* 30 Seconds */
36 
37 
38 /* FUNCTIONS *****************************************************************/
39 
40 static DWORD
41 ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage)
42 {
43     WCHAR szControlPipeName[MAX_PATH + 1];
44     HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
45     DWORD ServiceCurrent = 0;
46     DWORD KeyDisposition;
47     DWORD dwKeySize;
48     DWORD dwError;
49 
50     /* Get the service number */
51     /* TODO: Create registry entry with correct write access */
52     dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
53                               L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
54                               REG_OPTION_VOLATILE,
55                               KEY_WRITE | KEY_READ,
56                               NULL,
57                               &hServiceCurrentKey,
58                               &KeyDisposition);
59     if (dwError != ERROR_SUCCESS)
60     {
61         DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
62         return dwError;
63     }
64 
65     if (KeyDisposition == REG_OPENED_EXISTING_KEY)
66     {
67         dwKeySize = sizeof(DWORD);
68         dwError = RegQueryValueExW(hServiceCurrentKey,
69                                    L"", 0, NULL, (BYTE*)&ServiceCurrent, &dwKeySize);
70 
71         if (dwError != ERROR_SUCCESS)
72         {
73             RegCloseKey(hServiceCurrentKey);
74             DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
75             return dwError;
76         }
77 
78         ServiceCurrent++;
79     }
80 
81     dwError = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
82 
83     RegCloseKey(hServiceCurrentKey);
84 
85     if (dwError != ERROR_SUCCESS)
86     {
87         DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
88         return dwError;
89     }
90 
91     /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
92     StringCchPrintfW(szControlPipeName, ARRAYSIZE(szControlPipeName),
93                      L"\\\\.\\pipe\\net\\NtControlPipe%lu", ServiceCurrent);
94 
95     DPRINT("PipeName: %S\n", szControlPipeName);
96 
97     pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
98                                                    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
99                                                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
100                                                    100,
101                                                    8000,
102                                                    4,
103                                                    PipeTimeout,
104                                                    NULL);
105     DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
106     if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
107     {
108         DPRINT1("Failed to create control pipe!\n");
109         return GetLastError();
110     }
111 
112     return ERROR_SUCCESS;
113 }
114 
115 
116 static PSERVICE_IMAGE
117 ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
118 {
119     PLIST_ENTRY ImageEntry;
120     PSERVICE_IMAGE CurrentImage;
121 
122     DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
123 
124     ImageEntry = ImageListHead.Flink;
125     while (ImageEntry != &ImageListHead)
126     {
127         CurrentImage = CONTAINING_RECORD(ImageEntry,
128                                          SERVICE_IMAGE,
129                                          ImageListEntry);
130         if (_wcsicmp(CurrentImage->pszImagePath, lpImagePath) == 0)
131         {
132             DPRINT("Found image: '%S'\n", CurrentImage->pszImagePath);
133             return CurrentImage;
134         }
135 
136         ImageEntry = ImageEntry->Flink;
137     }
138 
139     DPRINT("Couldn't find a matching image\n");
140 
141     return NULL;
142 
143 }
144 
145 
146 static
147 BOOL
148 ScmIsSameServiceAccount(
149     _In_ PCWSTR pszAccountName1,
150     _In_ PCWSTR pszAccountName2)
151 {
152     if (pszAccountName1 == NULL && pszAccountName2 == NULL)
153         return TRUE;
154 
155     if (pszAccountName1 == NULL && pszAccountName2 && wcscmp(pszAccountName2, L"LocalSystem") == 0)
156         return TRUE;
157 
158     if (pszAccountName2 == NULL && pszAccountName1 && wcscmp(pszAccountName1, L"LocalSystem") == 0)
159         return TRUE;
160 
161     if (pszAccountName1 && pszAccountName2 && wcscmp(pszAccountName1, pszAccountName2) == 0)
162         return TRUE;
163 
164     return FALSE;
165 }
166 
167 
168 static
169 BOOL
170 ScmIsLocalSystemAccount(
171     _In_ PCWSTR pszAccountName)
172 {
173     if (pszAccountName == NULL ||
174         wcscmp(pszAccountName, L"LocalSystem") == 0)
175         return TRUE;
176 
177     return FALSE;
178 }
179 
180 
181 static
182 DWORD
183 ScmLogonService(
184     IN PSERVICE pService,
185     IN PSERVICE_IMAGE pImage)
186 {
187 #if 0
188     PROFILEINFOW ProfileInfo;
189     PWSTR pszUserName = NULL;
190     PWSTR pszDomainName = NULL;
191     PWSTR pszPassword = NULL;
192     PWSTR ptr;
193     DWORD dwError = ERROR_SUCCESS;
194 #endif
195 
196     DPRINT("ScmLogonService(%p %p)\n", pService, pImage);
197 
198     DPRINT("Service %S\n", pService->lpServiceName);
199 
200     if (ScmIsLocalSystemAccount(pImage->pszAccountName))
201         return ERROR_SUCCESS;
202 
203     // FIXME: Always assume LocalSystem
204     return ERROR_SUCCESS;
205 
206 #if 0
207     /* Get the user and domain names */
208     ptr = wcschr(pImage->pszAccountName, L'\\');
209     if (ptr != NULL)
210     {
211         *ptr = L'\0';
212 
213         pszUserName = ptr + 1;
214         pszDomainName = pImage->pszAccountName;
215     }
216     else
217     {
218         pszUserName = pImage->pszAccountName;
219         pszDomainName = NULL;
220     }
221 
222     /* Build the service 'password' */
223     pszPassword = HeapAlloc(GetProcessHeap(),
224                             HEAP_ZERO_MEMORY,
225                             (wcslen(pService->lpServiceName) + 5) * sizeof(WCHAR));
226     if (pszPassword == NULL)
227     {
228         dwError = ERROR_NOT_ENOUGH_MEMORY;
229         goto done;
230     }
231 
232     wcscpy(pszPassword, L"_SC_");
233     wcscat(pszPassword, pService->lpServiceName);
234 
235     DPRINT("Domain: %S  User: %S  Password: %S\n", pszDomainName, pszUserName, pszPassword);
236 
237     /* Service logon */
238     if (!LogonUserW(pszUserName,
239                     pszDomainName,
240                     pszPassword,
241                     LOGON32_LOGON_SERVICE,
242                     LOGON32_PROVIDER_DEFAULT,
243                     &pImage->hToken))
244     {
245         dwError = GetLastError();
246         DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
247         goto done;
248     }
249 
250     // FIXME: Call LoadUserProfileW to be able to initialize a per-user
251     // environment block, with user-specific environment variables as
252     // %USERNAME%, %USERPROFILE%, and %ALLUSERSPROFILE% correctly initialized!!
253 
254     /* Load the user profile, so that the per-user environment variables can be initialized */
255     ZeroMemory(&ProfileInfo, sizeof(ProfileInfo));
256     ProfileInfo.dwSize = sizeof(ProfileInfo);
257     ProfileInfo.dwFlags = PI_NOUI;
258     ProfileInfo.lpUserName = pszUserName;
259     // ProfileInfo.lpProfilePath = NULL;
260     // ProfileInfo.lpDefaultPath = NULL;
261     // ProfileInfo.lpServerName = NULL;
262     // ProfileInfo.lpPolicyPath = NULL;
263     // ProfileInfo.hProfile = NULL;
264 
265     if (!LoadUserProfileW(pImage->hToken, &ProfileInfo))
266     {
267         dwError = GetLastError();
268         DPRINT1("LoadUserProfileW() failed (Error %lu)\n", dwError);
269         goto done;
270     }
271 
272     pImage->hProfile = ProfileInfo.hProfile;
273 
274 done:
275     if (pszPassword != NULL)
276         HeapFree(GetProcessHeap(), 0, pszPassword);
277 
278     if (ptr != NULL)
279         *ptr = L'\\';
280 
281     return dwError;
282 #endif
283 }
284 
285 
286 static DWORD
287 ScmCreateOrReferenceServiceImage(PSERVICE pService)
288 {
289     RTL_QUERY_REGISTRY_TABLE QueryTable[3];
290     UNICODE_STRING ImagePath;
291     UNICODE_STRING ObjectName;
292     PSERVICE_IMAGE pServiceImage = NULL;
293     NTSTATUS Status;
294     DWORD dwError = ERROR_SUCCESS;
295     DWORD dwRecordSize;
296     LPWSTR pString;
297 
298     DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
299 
300     RtlInitUnicodeString(&ImagePath, NULL);
301     RtlInitUnicodeString(&ObjectName, NULL);
302 
303     /* Get service data */
304     RtlZeroMemory(&QueryTable,
305                   sizeof(QueryTable));
306 
307     QueryTable[0].Name = L"ImagePath";
308     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
309     QueryTable[0].EntryContext = &ImagePath;
310     QueryTable[1].Name = L"ObjectName";
311     QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
312     QueryTable[1].EntryContext = &ObjectName;
313 
314     Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
315                                     pService->lpServiceName,
316                                     QueryTable,
317                                     NULL,
318                                     NULL);
319     if (!NT_SUCCESS(Status))
320     {
321         DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
322         return RtlNtStatusToDosError(Status);
323     }
324 
325     DPRINT("ImagePath: '%wZ'\n", &ImagePath);
326     DPRINT("ObjectName: '%wZ'\n", &ObjectName);
327 
328     pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
329     if (pServiceImage == NULL)
330     {
331         dwRecordSize = sizeof(SERVICE_IMAGE) +
332                        ImagePath.Length + sizeof(WCHAR) +
333                        ((ObjectName.Length != 0) ? (ObjectName.Length + sizeof(WCHAR)) : 0);
334 
335         /* Create a new service image */
336         pServiceImage = HeapAlloc(GetProcessHeap(),
337                                   HEAP_ZERO_MEMORY,
338                                   dwRecordSize);
339         if (pServiceImage == NULL)
340         {
341             dwError = ERROR_NOT_ENOUGH_MEMORY;
342             goto done;
343         }
344 
345         pServiceImage->dwImageRunCount = 1;
346         pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
347         pServiceImage->hProcess = INVALID_HANDLE_VALUE;
348 
349         pString = (PWSTR)((INT_PTR)pServiceImage + sizeof(SERVICE_IMAGE));
350 
351         /* Set the image path */
352         pServiceImage->pszImagePath = pString;
353         wcscpy(pServiceImage->pszImagePath,
354                ImagePath.Buffer);
355 
356         /* Set the account name */
357         if (ObjectName.Length > 0)
358         {
359             pString = pString + wcslen(pString) + 1;
360 
361             pServiceImage->pszAccountName = pString;
362             wcscpy(pServiceImage->pszAccountName,
363                    ObjectName.Buffer);
364         }
365 
366         /* Service logon */
367         dwError = ScmLogonService(pService, pServiceImage);
368         if (dwError != ERROR_SUCCESS)
369         {
370             DPRINT1("ScmLogonService() failed (Error %lu)\n", dwError);
371 
372             /* Release the service image */
373             HeapFree(GetProcessHeap(), 0, pServiceImage);
374 
375             goto done;
376         }
377 
378         /* Create the control pipe */
379         dwError = ScmCreateNewControlPipe(pServiceImage);
380         if (dwError != ERROR_SUCCESS)
381         {
382             DPRINT1("ScmCreateNewControlPipe() failed (Error %lu)\n", dwError);
383 
384             /* Unload the user profile */
385             if (pServiceImage->hProfile != NULL)
386                 UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
387 
388             /* Close the logon token */
389             if (pServiceImage->hToken != NULL)
390                 CloseHandle(pServiceImage->hToken);
391 
392             /* Release the service image */
393             HeapFree(GetProcessHeap(), 0, pServiceImage);
394 
395             goto done;
396         }
397 
398         /* FIXME: Add more initialization code here */
399 
400 
401         /* Append service record */
402         InsertTailList(&ImageListHead,
403                        &pServiceImage->ImageListEntry);
404     }
405     else
406     {
407 //        if ((lpService->Status.dwServiceType & SERVICE_WIN32_SHARE_PROCESS) == 0)
408 
409         /* Fail if services in an image use different accounts */
410         if (!ScmIsSameServiceAccount(pServiceImage->pszAccountName, ObjectName.Buffer))
411         {
412             dwError = ERROR_DIFFERENT_SERVICE_ACCOUNT;
413             goto done;
414         }
415 
416         /* Increment the run counter */
417         pServiceImage->dwImageRunCount++;
418     }
419 
420     DPRINT("pServiceImage->pszImagePath: %S\n", pServiceImage->pszImagePath);
421     DPRINT("pServiceImage->pszAccountName: %S\n", pServiceImage->pszAccountName);
422     DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
423 
424     /* Link the service image to the service */
425     pService->lpImage = pServiceImage;
426 
427 done:
428     RtlFreeUnicodeString(&ObjectName);
429     RtlFreeUnicodeString(&ImagePath);
430 
431     DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
432 
433     return dwError;
434 }
435 
436 
437 VOID
438 ScmRemoveServiceImage(PSERVICE_IMAGE pServiceImage)
439 {
440     DPRINT1("ScmRemoveServiceImage() called\n");
441 
442     /* FIXME: Terminate the process */
443 
444     /* Remove the service image from the list */
445     RemoveEntryList(&pServiceImage->ImageListEntry);
446 
447     /* Close the process handle */
448     if (pServiceImage->hProcess != INVALID_HANDLE_VALUE)
449         CloseHandle(pServiceImage->hProcess);
450 
451     /* Close the control pipe */
452     if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
453         CloseHandle(pServiceImage->hControlPipe);
454 
455     /* Unload the user profile */
456     if (pServiceImage->hProfile != NULL)
457         UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
458 
459     /* Close the logon token */
460     if (pServiceImage->hToken != NULL)
461         CloseHandle(pServiceImage->hToken);
462 
463     /* Release the service image */
464     HeapFree(GetProcessHeap(), 0, pServiceImage);
465 }
466 
467 
468 PSERVICE
469 ScmGetServiceEntryByName(LPCWSTR lpServiceName)
470 {
471     PLIST_ENTRY ServiceEntry;
472     PSERVICE CurrentService;
473 
474     DPRINT("ScmGetServiceEntryByName() called\n");
475 
476     ServiceEntry = ServiceListHead.Flink;
477     while (ServiceEntry != &ServiceListHead)
478     {
479         CurrentService = CONTAINING_RECORD(ServiceEntry,
480                                            SERVICE,
481                                            ServiceListEntry);
482         if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
483         {
484             DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
485             return CurrentService;
486         }
487 
488         ServiceEntry = ServiceEntry->Flink;
489     }
490 
491     DPRINT("Couldn't find a matching service\n");
492 
493     return NULL;
494 }
495 
496 
497 PSERVICE
498 ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
499 {
500     PLIST_ENTRY ServiceEntry;
501     PSERVICE CurrentService;
502 
503     DPRINT("ScmGetServiceEntryByDisplayName() called\n");
504 
505     ServiceEntry = ServiceListHead.Flink;
506     while (ServiceEntry != &ServiceListHead)
507     {
508         CurrentService = CONTAINING_RECORD(ServiceEntry,
509                                            SERVICE,
510                                            ServiceListEntry);
511         if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
512         {
513             DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
514             return CurrentService;
515         }
516 
517         ServiceEntry = ServiceEntry->Flink;
518     }
519 
520     DPRINT("Couldn't find a matching service\n");
521 
522     return NULL;
523 }
524 
525 
526 PSERVICE
527 ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
528 {
529     PLIST_ENTRY ServiceEntry;
530     PSERVICE CurrentService;
531 
532     DPRINT("ScmGetServiceEntryByResumeCount() called\n");
533 
534     ServiceEntry = ServiceListHead.Flink;
535     while (ServiceEntry != &ServiceListHead)
536     {
537         CurrentService = CONTAINING_RECORD(ServiceEntry,
538                                            SERVICE,
539                                            ServiceListEntry);
540         if (CurrentService->dwResumeCount > dwResumeCount)
541         {
542             DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
543             return CurrentService;
544         }
545 
546         ServiceEntry = ServiceEntry->Flink;
547     }
548 
549     DPRINT("Couldn't find a matching service\n");
550 
551     return NULL;
552 }
553 
554 
555 DWORD
556 ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
557                           PSERVICE *lpServiceRecord,
558                           DWORD dwServiceType,
559                           DWORD dwStartType)
560 {
561     PSERVICE lpService = NULL;
562 
563     DPRINT("Service: '%S'\n", lpServiceName);
564 
565     /* Allocate service entry */
566     lpService = HeapAlloc(GetProcessHeap(),
567                           HEAP_ZERO_MEMORY,
568                           FIELD_OFFSET(SERVICE, szServiceName[wcslen(lpServiceName) + 1]));
569     if (lpService == NULL)
570         return ERROR_NOT_ENOUGH_MEMORY;
571 
572     *lpServiceRecord = lpService;
573 
574     /* Copy service name */
575     wcscpy(lpService->szServiceName, lpServiceName);
576     lpService->lpServiceName = lpService->szServiceName;
577     lpService->lpDisplayName = lpService->lpServiceName;
578 
579     /* Set the start type */
580     lpService->dwStartType = dwStartType;
581 
582     /* Set the resume count */
583     lpService->dwResumeCount = ResumeCount++;
584 
585     /* Append service record */
586     InsertTailList(&ServiceListHead,
587                    &lpService->ServiceListEntry);
588 
589     /* Initialize the service status */
590     lpService->Status.dwServiceType = dwServiceType;
591     lpService->Status.dwCurrentState = SERVICE_STOPPED;
592     lpService->Status.dwControlsAccepted = 0;
593     lpService->Status.dwWin32ExitCode =
594         (dwStartType == SERVICE_DISABLED) ? ERROR_SERVICE_DISABLED : ERROR_SERVICE_NEVER_STARTED;
595     lpService->Status.dwServiceSpecificExitCode = 0;
596     lpService->Status.dwCheckPoint = 0;
597     lpService->Status.dwWaitHint =
598         (dwServiceType & SERVICE_DRIVER) ? 0 : 2000; /* 2 seconds */
599 
600     return ERROR_SUCCESS;
601 }
602 
603 
604 VOID
605 ScmDeleteServiceRecord(PSERVICE lpService)
606 {
607     DPRINT("Deleting Service %S\n", lpService->lpServiceName);
608 
609     /* Delete the display name */
610     if (lpService->lpDisplayName != NULL &&
611         lpService->lpDisplayName != lpService->lpServiceName)
612         HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
613 
614     /* Dereference the service image */
615     if (lpService->lpImage)
616     {
617         lpService->lpImage->dwImageRunCount--;
618 
619         if (lpService->lpImage->dwImageRunCount == 0)
620         {
621             ScmRemoveServiceImage(lpService->lpImage);
622             lpService->lpImage = NULL;
623         }
624     }
625 
626     /* Decrement the group reference counter */
627     ScmSetServiceGroup(lpService, NULL);
628 
629     /* Release the SecurityDescriptor */
630     if (lpService->pSecurityDescriptor != NULL)
631         HeapFree(GetProcessHeap(), 0, lpService->pSecurityDescriptor);
632 
633     /* Remove the Service from the List */
634     RemoveEntryList(&lpService->ServiceListEntry);
635 
636     DPRINT("Deleted Service %S\n", lpService->lpServiceName);
637 
638     /* Delete the service record */
639     HeapFree(GetProcessHeap(), 0, lpService);
640 
641     DPRINT("Done\n");
642 }
643 
644 
645 static DWORD
646 CreateServiceListEntry(LPCWSTR lpServiceName,
647                        HKEY hServiceKey)
648 {
649     PSERVICE lpService = NULL;
650     LPWSTR lpDisplayName = NULL;
651     LPWSTR lpGroup = NULL;
652     DWORD dwSize;
653     DWORD dwError;
654     DWORD dwServiceType;
655     DWORD dwStartType;
656     DWORD dwErrorControl;
657     DWORD dwTagId;
658 
659     DPRINT("Service: '%S'\n", lpServiceName);
660     if (*lpServiceName == L'{')
661         return ERROR_SUCCESS;
662 
663     dwSize = sizeof(DWORD);
664     dwError = RegQueryValueExW(hServiceKey,
665                                L"Type",
666                                NULL,
667                                NULL,
668                                (LPBYTE)&dwServiceType,
669                                &dwSize);
670     if (dwError != ERROR_SUCCESS)
671         return ERROR_SUCCESS;
672 
673     if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
674         ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
675         (dwServiceType != SERVICE_KERNEL_DRIVER) &&
676         (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
677         return ERROR_SUCCESS;
678 
679     DPRINT("Service type: %lx\n", dwServiceType);
680 
681     dwSize = sizeof(DWORD);
682     dwError = RegQueryValueExW(hServiceKey,
683                                L"Start",
684                                NULL,
685                                NULL,
686                                (LPBYTE)&dwStartType,
687                                &dwSize);
688     if (dwError != ERROR_SUCCESS)
689         return ERROR_SUCCESS;
690 
691     DPRINT("Start type: %lx\n", dwStartType);
692 
693     dwSize = sizeof(DWORD);
694     dwError = RegQueryValueExW(hServiceKey,
695                                L"ErrorControl",
696                                NULL,
697                                NULL,
698                                (LPBYTE)&dwErrorControl,
699                                &dwSize);
700     if (dwError != ERROR_SUCCESS)
701         return ERROR_SUCCESS;
702 
703     DPRINT("Error control: %lx\n", dwErrorControl);
704 
705     dwError = RegQueryValueExW(hServiceKey,
706                                L"Tag",
707                                NULL,
708                                NULL,
709                                (LPBYTE)&dwTagId,
710                                &dwSize);
711     if (dwError != ERROR_SUCCESS)
712         dwTagId = 0;
713 
714     DPRINT("Tag: %lx\n", dwTagId);
715 
716     dwError = ScmReadString(hServiceKey,
717                             L"Group",
718                             &lpGroup);
719     if (dwError != ERROR_SUCCESS)
720         lpGroup = NULL;
721 
722     DPRINT("Group: %S\n", lpGroup);
723 
724     dwError = ScmReadString(hServiceKey,
725                             L"DisplayName",
726                             &lpDisplayName);
727     if (dwError != ERROR_SUCCESS)
728         lpDisplayName = NULL;
729 
730     DPRINT("Display name: %S\n", lpDisplayName);
731 
732     dwError = ScmCreateNewServiceRecord(lpServiceName,
733                                         &lpService,
734                                         dwServiceType,
735                                         dwStartType);
736     if (dwError != ERROR_SUCCESS)
737         goto done;
738 
739     lpService->dwErrorControl = dwErrorControl;
740     lpService->dwTag = dwTagId;
741 
742     if (lpGroup != NULL)
743     {
744         dwError = ScmSetServiceGroup(lpService, lpGroup);
745         if (dwError != ERROR_SUCCESS)
746             goto done;
747     }
748 
749     if (lpDisplayName != NULL)
750     {
751         lpService->lpDisplayName = lpDisplayName;
752         lpDisplayName = NULL;
753     }
754 
755     DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
756     if (lpService->lpGroup != NULL)
757     {
758         DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
759     }
760     DPRINT("Start %lx  Type %lx  Tag %lx  ErrorControl %lx\n",
761            lpService->dwStartType,
762            lpService->Status.dwServiceType,
763            lpService->dwTag,
764            lpService->dwErrorControl);
765 
766     if (ScmIsDeleteFlagSet(hServiceKey))
767         lpService->bDeleted = TRUE;
768 
769     if (lpService->Status.dwServiceType & SERVICE_WIN32)
770     {
771         dwError = ScmReadSecurityDescriptor(hServiceKey,
772                                             &lpService->pSecurityDescriptor);
773         if (dwError != ERROR_SUCCESS)
774             goto done;
775 
776         /* Assing the default security descriptor if the security descriptor cannot be read */
777         if (lpService->pSecurityDescriptor == NULL)
778         {
779             DPRINT("No security descriptor found! Assign default security descriptor!\n");
780             dwError = ScmCreateDefaultServiceSD(&lpService->pSecurityDescriptor);
781             if (dwError != ERROR_SUCCESS)
782                 goto done;
783 
784             dwError = ScmWriteSecurityDescriptor(hServiceKey,
785                                                  lpService->pSecurityDescriptor);
786             if (dwError != ERROR_SUCCESS)
787                 goto done;
788         }
789     }
790 
791 done:
792     if (lpGroup != NULL)
793         HeapFree(GetProcessHeap(), 0, lpGroup);
794 
795     if (lpDisplayName != NULL)
796         HeapFree(GetProcessHeap(), 0, lpDisplayName);
797 
798     if (lpService != NULL)
799     {
800         ASSERT(lpService->lpImage == NULL);
801     }
802 
803     return dwError;
804 }
805 
806 
807 VOID
808 ScmDeleteMarkedServices(VOID)
809 {
810     PLIST_ENTRY ServiceEntry;
811     PSERVICE CurrentService;
812     HKEY hServicesKey;
813     DWORD dwError;
814 
815     ServiceEntry = ServiceListHead.Flink;
816     while (ServiceEntry != &ServiceListHead)
817     {
818         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
819 
820         ServiceEntry = ServiceEntry->Flink;
821 
822         if (CurrentService->bDeleted != FALSE)
823         {
824             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
825                                     L"System\\CurrentControlSet\\Services",
826                                     0,
827                                     DELETE,
828                                     &hServicesKey);
829             if (dwError == ERROR_SUCCESS)
830             {
831                 dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
832                 RegCloseKey(hServicesKey);
833                 if (dwError == ERROR_SUCCESS)
834                 {
835                     RemoveEntryList(&CurrentService->ServiceListEntry);
836                     HeapFree(GetProcessHeap(), 0, CurrentService);
837                 }
838             }
839 
840             if (dwError != ERROR_SUCCESS)
841                 DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
842         }
843     }
844 }
845 
846 
847 static
848 VOID
849 ScmGetNoInteractiveServicesValue(VOID)
850 {
851     HKEY hKey;
852     DWORD dwKeySize;
853     LONG lError;
854 
855     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
856                            L"SYSTEM\\CurrentControlSet\\Control\\Windows",
857                            0,
858                            KEY_READ,
859                            &hKey);
860     if (lError == ERROR_SUCCESS)
861     {
862         dwKeySize = sizeof(NoInteractiveServices);
863         lError = RegQueryValueExW(hKey,
864                                   L"NoInteractiveServices",
865                                   0,
866                                   NULL,
867                                   (LPBYTE)&NoInteractiveServices,
868                                   &dwKeySize);
869         RegCloseKey(hKey);
870     }
871 }
872 
873 
874 DWORD
875 ScmCreateServiceDatabase(VOID)
876 {
877     WCHAR szSubKey[MAX_PATH];
878     HKEY hServicesKey;
879     HKEY hServiceKey;
880     DWORD dwSubKey;
881     DWORD dwSubKeyLength;
882     FILETIME ftLastChanged;
883     DWORD dwError;
884 
885     DPRINT("ScmCreateServiceDatabase() called\n");
886 
887     /* Retrieve the NoInteractiveServies value */
888     ScmGetNoInteractiveServicesValue();
889 
890     /* Create the service group list */
891     dwError = ScmCreateGroupList();
892     if (dwError != ERROR_SUCCESS)
893         return dwError;
894 
895     /* Initialize image and service lists */
896     InitializeListHead(&ImageListHead);
897     InitializeListHead(&ServiceListHead);
898 
899     /* Initialize the database lock */
900     RtlInitializeResource(&DatabaseLock);
901 
902     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
903                             L"System\\CurrentControlSet\\Services",
904                             0,
905                             KEY_READ,
906                             &hServicesKey);
907     if (dwError != ERROR_SUCCESS)
908         return dwError;
909 
910     dwSubKey = 0;
911     for (;;)
912     {
913         dwSubKeyLength = MAX_PATH;
914         dwError = RegEnumKeyExW(hServicesKey,
915                                 dwSubKey,
916                                 szSubKey,
917                                 &dwSubKeyLength,
918                                 NULL,
919                                 NULL,
920                                 NULL,
921                                 &ftLastChanged);
922         if (dwError == ERROR_SUCCESS &&
923             szSubKey[0] != L'{')
924         {
925             DPRINT("SubKeyName: '%S'\n", szSubKey);
926 
927             dwError = RegOpenKeyExW(hServicesKey,
928                                     szSubKey,
929                                     0,
930                                     KEY_READ,
931                                     &hServiceKey);
932             if (dwError == ERROR_SUCCESS)
933             {
934                 dwError = CreateServiceListEntry(szSubKey,
935                                                  hServiceKey);
936 
937                 RegCloseKey(hServiceKey);
938             }
939         }
940 
941         if (dwError != ERROR_SUCCESS)
942             break;
943 
944         dwSubKey++;
945     }
946 
947     RegCloseKey(hServicesKey);
948 
949     /* Wait for the LSA server */
950     ScmWaitForLsa();
951 
952     /* Delete services that are marked for delete */
953     ScmDeleteMarkedServices();
954 
955     DPRINT("ScmCreateServiceDatabase() done\n");
956 
957     return ERROR_SUCCESS;
958 }
959 
960 
961 VOID
962 ScmShutdownServiceDatabase(VOID)
963 {
964     DPRINT("ScmShutdownServiceDatabase() called\n");
965 
966     ScmDeleteMarkedServices();
967     RtlDeleteResource(&DatabaseLock);
968 
969     DPRINT("ScmShutdownServiceDatabase() done\n");
970 }
971 
972 
973 static NTSTATUS
974 ScmCheckDriver(PSERVICE Service)
975 {
976     OBJECT_ATTRIBUTES ObjectAttributes;
977     UNICODE_STRING DirName;
978     HANDLE DirHandle;
979     NTSTATUS Status;
980     POBJECT_DIRECTORY_INFORMATION DirInfo;
981     ULONG BufferLength;
982     ULONG DataLength;
983     ULONG Index;
984 
985     DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
986 
987     if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
988     {
989         RtlInitUnicodeString(&DirName, L"\\Driver");
990     }
991     else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
992     {
993         ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
994         RtlInitUnicodeString(&DirName, L"\\FileSystem");
995     }
996 
997     InitializeObjectAttributes(&ObjectAttributes,
998                                &DirName,
999                                0,
1000                                NULL,
1001                                NULL);
1002 
1003     Status = NtOpenDirectoryObject(&DirHandle,
1004                                    DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
1005                                    &ObjectAttributes);
1006     if (!NT_SUCCESS(Status))
1007     {
1008         return Status;
1009     }
1010 
1011     BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
1012                        2 * MAX_PATH * sizeof(WCHAR);
1013     DirInfo = HeapAlloc(GetProcessHeap(),
1014                         HEAP_ZERO_MEMORY,
1015                         BufferLength);
1016 
1017     Index = 0;
1018     while (TRUE)
1019     {
1020         Status = NtQueryDirectoryObject(DirHandle,
1021                                         DirInfo,
1022                                         BufferLength,
1023                                         TRUE,
1024                                         FALSE,
1025                                         &Index,
1026                                         &DataLength);
1027         if (Status == STATUS_NO_MORE_ENTRIES)
1028         {
1029             /* FIXME: Add current service to 'failed service' list */
1030             DPRINT("Service '%S' failed\n", Service->lpServiceName);
1031             break;
1032         }
1033 
1034         if (!NT_SUCCESS(Status))
1035             break;
1036 
1037         DPRINT("Comparing: '%S'  '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
1038 
1039         if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
1040         {
1041             DPRINT("Found: '%S'  '%wZ'\n",
1042                    Service->lpServiceName, &DirInfo->Name);
1043 
1044             /* Mark service as 'running' */
1045             Service->Status.dwCurrentState = SERVICE_RUNNING;
1046             Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1047             Service->Status.dwWin32ExitCode = ERROR_SUCCESS;
1048             Service->Status.dwServiceSpecificExitCode = 0;
1049             Service->Status.dwCheckPoint = 0;
1050             Service->Status.dwWaitHint = 0;
1051 
1052             /* Mark the service group as 'running' */
1053             if (Service->lpGroup != NULL)
1054             {
1055                 Service->lpGroup->ServicesRunning = TRUE;
1056             }
1057 
1058             break;
1059         }
1060     }
1061 
1062     HeapFree(GetProcessHeap(),
1063              0,
1064              DirInfo);
1065     NtClose(DirHandle);
1066 
1067     return STATUS_SUCCESS;
1068 }
1069 
1070 
1071 VOID
1072 ScmGetBootAndSystemDriverState(VOID)
1073 {
1074     PLIST_ENTRY ServiceEntry;
1075     PSERVICE CurrentService;
1076 
1077     DPRINT("ScmGetBootAndSystemDriverState() called\n");
1078 
1079     ServiceEntry = ServiceListHead.Flink;
1080     while (ServiceEntry != &ServiceListHead)
1081     {
1082         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1083 
1084         if (CurrentService->dwStartType == SERVICE_BOOT_START ||
1085             CurrentService->dwStartType == SERVICE_SYSTEM_START)
1086         {
1087             /* Check driver */
1088             DPRINT("  Checking service: %S\n", CurrentService->lpServiceName);
1089 
1090             ScmCheckDriver(CurrentService);
1091         }
1092 
1093         ServiceEntry = ServiceEntry->Flink;
1094     }
1095 
1096     DPRINT("ScmGetBootAndSystemDriverState() done\n");
1097 }
1098 
1099 
1100 DWORD
1101 ScmControlService(HANDLE hControlPipe,
1102                   PWSTR pServiceName,
1103                   SERVICE_STATUS_HANDLE hServiceStatus,
1104                   DWORD dwControl)
1105 {
1106     PSCM_CONTROL_PACKET ControlPacket;
1107     SCM_REPLY_PACKET ReplyPacket;
1108 
1109     DWORD dwWriteCount = 0;
1110     DWORD dwReadCount = 0;
1111     DWORD PacketSize;
1112     PWSTR Ptr;
1113     DWORD dwError = ERROR_SUCCESS;
1114     BOOL bResult;
1115     OVERLAPPED Overlapped = {0};
1116 
1117     DPRINT("ScmControlService() called\n");
1118 
1119     /* Acquire the service control critical section, to synchronize requests */
1120     EnterCriticalSection(&ControlServiceCriticalSection);
1121 
1122     /* Calculate the total length of the start command line */
1123     PacketSize = sizeof(SCM_CONTROL_PACKET);
1124     PacketSize += (DWORD)((wcslen(pServiceName) + 1) * sizeof(WCHAR));
1125 
1126     ControlPacket = HeapAlloc(GetProcessHeap(),
1127                               HEAP_ZERO_MEMORY,
1128                               PacketSize);
1129     if (ControlPacket == NULL)
1130     {
1131         LeaveCriticalSection(&ControlServiceCriticalSection);
1132         return ERROR_NOT_ENOUGH_MEMORY;
1133     }
1134 
1135     ControlPacket->dwSize = PacketSize;
1136     ControlPacket->dwControl = dwControl;
1137     ControlPacket->hServiceStatus = hServiceStatus;
1138 
1139     ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1140 
1141     Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
1142     wcscpy(Ptr, pServiceName);
1143 
1144     ControlPacket->dwArgumentsCount = 0;
1145     ControlPacket->dwArgumentsOffset = 0;
1146 
1147     bResult = WriteFile(hControlPipe,
1148                         ControlPacket,
1149                         PacketSize,
1150                         &dwWriteCount,
1151                         &Overlapped);
1152     if (bResult == FALSE)
1153     {
1154         DPRINT("WriteFile() returned FALSE\n");
1155 
1156         dwError = GetLastError();
1157         if (dwError == ERROR_IO_PENDING)
1158         {
1159             DPRINT("dwError: ERROR_IO_PENDING\n");
1160 
1161             dwError = WaitForSingleObject(hControlPipe,
1162                                           PipeTimeout);
1163             DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1164 
1165             if (dwError == WAIT_TIMEOUT)
1166             {
1167                 bResult = CancelIo(hControlPipe);
1168                 if (bResult == FALSE)
1169                 {
1170                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1171                 }
1172 
1173                 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1174                 goto Done;
1175             }
1176             else if (dwError == WAIT_OBJECT_0)
1177             {
1178                 bResult = GetOverlappedResult(hControlPipe,
1179                                               &Overlapped,
1180                                               &dwWriteCount,
1181                                               TRUE);
1182                 if (bResult == FALSE)
1183                 {
1184                     dwError = GetLastError();
1185                     DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1186 
1187                     goto Done;
1188                 }
1189             }
1190         }
1191         else
1192         {
1193             DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
1194             goto Done;
1195         }
1196     }
1197 
1198     /* Read the reply */
1199     Overlapped.hEvent = (HANDLE) NULL;
1200 
1201     bResult = ReadFile(hControlPipe,
1202                        &ReplyPacket,
1203                        sizeof(SCM_REPLY_PACKET),
1204                        &dwReadCount,
1205                        &Overlapped);
1206     if (bResult == FALSE)
1207     {
1208         DPRINT("ReadFile() returned FALSE\n");
1209 
1210         dwError = GetLastError();
1211         if (dwError == ERROR_IO_PENDING)
1212         {
1213             DPRINT("dwError: ERROR_IO_PENDING\n");
1214 
1215             dwError = WaitForSingleObject(hControlPipe,
1216                                           PipeTimeout);
1217             DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1218 
1219             if (dwError == WAIT_TIMEOUT)
1220             {
1221                 bResult = CancelIo(hControlPipe);
1222                 if (bResult == FALSE)
1223                 {
1224                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1225                 }
1226 
1227                 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1228                 goto Done;
1229             }
1230             else if (dwError == WAIT_OBJECT_0)
1231             {
1232                 bResult = GetOverlappedResult(hControlPipe,
1233                                               &Overlapped,
1234                                               &dwReadCount,
1235                                               TRUE);
1236                 if (bResult == FALSE)
1237                 {
1238                     dwError = GetLastError();
1239                     DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1240 
1241                     goto Done;
1242                 }
1243             }
1244         }
1245         else
1246         {
1247             DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1248             goto Done;
1249         }
1250     }
1251 
1252 Done:
1253     /* Release the control packet */
1254     HeapFree(GetProcessHeap(),
1255              0,
1256              ControlPacket);
1257 
1258     if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1259     {
1260         dwError = ReplyPacket.dwError;
1261     }
1262 
1263     LeaveCriticalSection(&ControlServiceCriticalSection);
1264 
1265     DPRINT("ScmControlService() done\n");
1266 
1267     return dwError;
1268 }
1269 
1270 
1271 static DWORD
1272 ScmSendStartCommand(PSERVICE Service,
1273                     DWORD argc,
1274                     LPWSTR* argv)
1275 {
1276     DWORD dwError = ERROR_SUCCESS;
1277     PSCM_CONTROL_PACKET ControlPacket;
1278     SCM_REPLY_PACKET ReplyPacket;
1279     DWORD PacketSize;
1280     DWORD i;
1281     PWSTR Ptr;
1282     PWSTR *pOffPtr;
1283     PWSTR pArgPtr;
1284     BOOL bResult;
1285     DWORD dwWriteCount = 0;
1286     DWORD dwReadCount = 0;
1287     OVERLAPPED Overlapped = {0};
1288 
1289     DPRINT("ScmSendStartCommand() called\n");
1290 
1291     /* Calculate the total length of the start command line */
1292     PacketSize = sizeof(SCM_CONTROL_PACKET);
1293     PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
1294 
1295     /*
1296      * Calculate the required packet size for the start argument vector 'argv',
1297      * composed of the list of pointer offsets, followed by UNICODE strings.
1298      * The strings are stored continuously after the vector of offsets, with
1299      * the offsets being relative to the beginning of the vector, as in the
1300      * following layout (with N == argc):
1301      *     [argOff(0)]...[argOff(N-1)][str(0)]...[str(N-1)] .
1302      */
1303     if (argc > 0 && argv != NULL)
1304     {
1305         PacketSize = ALIGN_UP(PacketSize, PWSTR);
1306         PacketSize += (argc * sizeof(PWSTR));
1307 
1308         DPRINT("Argc: %lu\n", argc);
1309         for (i = 0; i < argc; i++)
1310         {
1311             DPRINT("Argv[%lu]: %S\n", i, argv[i]);
1312             PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR));
1313         }
1314     }
1315 
1316     /* Allocate a control packet */
1317     ControlPacket = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PacketSize);
1318     if (ControlPacket == NULL)
1319         return ERROR_NOT_ENOUGH_MEMORY;
1320 
1321     ControlPacket->dwSize = PacketSize;
1322     ControlPacket->dwControl = (Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
1323                                ? SERVICE_CONTROL_START_OWN
1324                                : SERVICE_CONTROL_START_SHARE;
1325     ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
1326 
1327     /* Copy the start command line */
1328     ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1329     Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
1330     wcscpy(Ptr, Service->lpServiceName);
1331 
1332     ControlPacket->dwArgumentsCount  = 0;
1333     ControlPacket->dwArgumentsOffset = 0;
1334 
1335     /* Copy the argument vector */
1336     if (argc > 0 && argv != NULL)
1337     {
1338         Ptr += wcslen(Service->lpServiceName) + 1;
1339         pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
1340         pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
1341 
1342         ControlPacket->dwArgumentsCount  = argc;
1343         ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
1344 
1345         DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
1346         DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
1347 
1348         for (i = 0; i < argc; i++)
1349         {
1350             wcscpy(pArgPtr, argv[i]);
1351             pOffPtr[i] = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
1352             DPRINT("offset[%lu]: %p\n", i, pOffPtr[i]);
1353             pArgPtr += wcslen(argv[i]) + 1;
1354         }
1355     }
1356 
1357     bResult = WriteFile(Service->lpImage->hControlPipe,
1358                         ControlPacket,
1359                         PacketSize,
1360                         &dwWriteCount,
1361                         &Overlapped);
1362     if (bResult == FALSE)
1363     {
1364         DPRINT("WriteFile() returned FALSE\n");
1365 
1366         dwError = GetLastError();
1367         if (dwError == ERROR_IO_PENDING)
1368         {
1369             DPRINT("dwError: ERROR_IO_PENDING\n");
1370 
1371             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1372                                           PipeTimeout);
1373             DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1374 
1375             if (dwError == WAIT_TIMEOUT)
1376             {
1377                 bResult = CancelIo(Service->lpImage->hControlPipe);
1378                 if (bResult == FALSE)
1379                 {
1380                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1381                 }
1382 
1383                 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1384                 goto Done;
1385             }
1386             else if (dwError == WAIT_OBJECT_0)
1387             {
1388                 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1389                                               &Overlapped,
1390                                               &dwWriteCount,
1391                                               TRUE);
1392                 if (bResult == FALSE)
1393                 {
1394                     dwError = GetLastError();
1395                     DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1396 
1397                     goto Done;
1398                 }
1399             }
1400         }
1401         else
1402         {
1403             DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
1404             goto Done;
1405         }
1406     }
1407 
1408     /* Read the reply */
1409     Overlapped.hEvent = (HANDLE) NULL;
1410 
1411     bResult = ReadFile(Service->lpImage->hControlPipe,
1412                        &ReplyPacket,
1413                        sizeof(SCM_REPLY_PACKET),
1414                        &dwReadCount,
1415                        &Overlapped);
1416     if (bResult == FALSE)
1417     {
1418         DPRINT("ReadFile() returned FALSE\n");
1419 
1420         dwError = GetLastError();
1421         if (dwError == ERROR_IO_PENDING)
1422         {
1423             DPRINT("dwError: ERROR_IO_PENDING\n");
1424 
1425             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1426                                           PipeTimeout);
1427             DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1428 
1429             if (dwError == WAIT_TIMEOUT)
1430             {
1431                 bResult = CancelIo(Service->lpImage->hControlPipe);
1432                 if (bResult == FALSE)
1433                 {
1434                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1435                 }
1436 
1437                 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1438                 goto Done;
1439             }
1440             else if (dwError == WAIT_OBJECT_0)
1441             {
1442                 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1443                                               &Overlapped,
1444                                               &dwReadCount,
1445                                               TRUE);
1446                 if (bResult == FALSE)
1447                 {
1448                     dwError = GetLastError();
1449                     DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1450 
1451                     goto Done;
1452                 }
1453             }
1454         }
1455         else
1456         {
1457             DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1458             goto Done;
1459         }
1460     }
1461 
1462 Done:
1463     /* Release the control packet */
1464     HeapFree(GetProcessHeap(),
1465              0,
1466              ControlPacket);
1467 
1468     if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1469     {
1470         dwError = ReplyPacket.dwError;
1471     }
1472 
1473     DPRINT("ScmSendStartCommand() done\n");
1474 
1475     return dwError;
1476 }
1477 
1478 
1479 static DWORD
1480 ScmWaitForServiceConnect(PSERVICE Service)
1481 {
1482     DWORD dwRead = 0;
1483     DWORD dwProcessId = 0;
1484     DWORD dwError = ERROR_SUCCESS;
1485     BOOL bResult;
1486     OVERLAPPED Overlapped = {0};
1487 #if 0
1488     LPCWSTR lpLogStrings[3];
1489     WCHAR szBuffer1[20];
1490     WCHAR szBuffer2[20];
1491 #endif
1492 
1493     DPRINT("ScmWaitForServiceConnect()\n");
1494 
1495     Overlapped.hEvent = (HANDLE)NULL;
1496 
1497     bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
1498                                &Overlapped);
1499     if (bResult == FALSE)
1500     {
1501         DPRINT("ConnectNamedPipe() returned FALSE\n");
1502 
1503         dwError = GetLastError();
1504         if (dwError == ERROR_IO_PENDING)
1505         {
1506             DPRINT("dwError: ERROR_IO_PENDING\n");
1507 
1508             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1509                                           PipeTimeout);
1510             DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1511 
1512             if (dwError == WAIT_TIMEOUT)
1513             {
1514                 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1515 
1516                 bResult = CancelIo(Service->lpImage->hControlPipe);
1517                 if (bResult == FALSE)
1518                 {
1519                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1520                 }
1521 
1522 #if 0
1523                 _ultow(PipeTimeout, szBuffer1, 10);
1524                 lpLogStrings[0] = Service->lpDisplayName;
1525                 lpLogStrings[1] = szBuffer1;
1526 
1527                 ScmLogEvent(EVENT_CONNECTION_TIMEOUT,
1528                             EVENTLOG_ERROR_TYPE,
1529                             2,
1530                             lpLogStrings);
1531 #endif
1532                 DPRINT1("Log EVENT_CONNECTION_TIMEOUT by %S\n", Service->lpDisplayName);
1533 
1534                 return ERROR_SERVICE_REQUEST_TIMEOUT;
1535             }
1536             else if (dwError == WAIT_OBJECT_0)
1537             {
1538                 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1539                                               &Overlapped,
1540                                               &dwRead,
1541                                               TRUE);
1542                 if (bResult == FALSE)
1543                 {
1544                     dwError = GetLastError();
1545                     DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
1546 
1547                     return dwError;
1548                 }
1549             }
1550         }
1551         else if (dwError != ERROR_PIPE_CONNECTED)
1552         {
1553             DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
1554             return dwError;
1555         }
1556     }
1557 
1558     DPRINT("Control pipe connected!\n");
1559 
1560     Overlapped.hEvent = (HANDLE) NULL;
1561 
1562     /* Read the process id from pipe */
1563     bResult = ReadFile(Service->lpImage->hControlPipe,
1564                        (LPVOID)&dwProcessId,
1565                        sizeof(DWORD),
1566                        &dwRead,
1567                        &Overlapped);
1568     if (bResult == FALSE)
1569     {
1570         DPRINT("ReadFile() returned FALSE\n");
1571 
1572         dwError = GetLastError();
1573         if (dwError == ERROR_IO_PENDING)
1574         {
1575             DPRINT("dwError: ERROR_IO_PENDING\n");
1576 
1577             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1578                                           PipeTimeout);
1579             if (dwError == WAIT_TIMEOUT)
1580             {
1581                 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1582 
1583                 bResult = CancelIo(Service->lpImage->hControlPipe);
1584                 if (bResult == FALSE)
1585                 {
1586                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1587                 }
1588 
1589 #if 0
1590                 _ultow(PipeTimeout, szBuffer1, 10);
1591                 lpLogStrings[0] = szBuffer1;
1592 
1593                 ScmLogEvent(EVENT_READFILE_TIMEOUT,
1594                             EVENTLOG_ERROR_TYPE,
1595                             1,
1596                             lpLogStrings);
1597 #endif
1598                 DPRINT1("Log EVENT_READFILE_TIMEOUT by %S\n", Service->lpDisplayName);
1599 
1600                 return ERROR_SERVICE_REQUEST_TIMEOUT;
1601             }
1602             else if (dwError == WAIT_OBJECT_0)
1603             {
1604                 DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
1605 
1606                 DPRINT("Process Id: %lu\n", dwProcessId);
1607 
1608                 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1609                                               &Overlapped,
1610                                               &dwRead,
1611                                               TRUE);
1612                 if (bResult == FALSE)
1613                 {
1614                     dwError = GetLastError();
1615                     DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1616 
1617                     return dwError;
1618                 }
1619             }
1620             else
1621             {
1622                 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1623             }
1624         }
1625         else
1626         {
1627             DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1628             return dwError;
1629         }
1630     }
1631 
1632     if (dwProcessId != Service->lpImage->dwProcessId)
1633     {
1634 #if 0
1635         _ultow(Service->lpImage->dwProcessId, szBuffer1, 10);
1636         _ultow(dwProcessId, szBuffer2, 10);
1637 
1638         lpLogStrings[0] = Service->lpDisplayName;
1639         lpLogStrings[1] = szBuffer1;
1640         lpLogStrings[2] = szBuffer2;
1641 
1642         ScmLogEvent(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
1643                     EVENTLOG_WARNING_TYPE,
1644                     3,
1645                     lpLogStrings);
1646 #endif
1647 
1648         DPRINT1("Log EVENT_SERVICE_DIFFERENT_PID_CONNECTED by %S\n", Service->lpDisplayName);
1649     }
1650 
1651     DPRINT("ScmWaitForServiceConnect() done\n");
1652 
1653     return ERROR_SUCCESS;
1654 }
1655 
1656 
1657 static DWORD
1658 ScmStartUserModeService(PSERVICE Service,
1659                         DWORD argc,
1660                         LPWSTR* argv)
1661 {
1662     PROCESS_INFORMATION ProcessInformation;
1663     STARTUPINFOW StartupInfo;
1664     LPVOID lpEnvironment;
1665     BOOL Result;
1666     DWORD dwError = ERROR_SUCCESS;
1667 
1668     DPRINT("ScmStartUserModeService(%p)\n", Service);
1669 
1670     /* If the image is already running ... */
1671     if (Service->lpImage->dwImageRunCount > 1)
1672     {
1673         /* ... just send a start command */
1674         return ScmSendStartCommand(Service, argc, argv);
1675     }
1676 
1677     /* Otherwise start its process */
1678     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1679     StartupInfo.cb = sizeof(StartupInfo);
1680     ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1681 
1682     /* Use the interactive desktop if the service is interactive */
1683     if ((NoInteractiveServices == 0) &&
1684         (Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
1685         StartupInfo.lpDesktop = L"WinSta0\\Default";
1686 
1687     if (Service->lpImage->hToken)
1688     {
1689         /* User token: Run the service under the user account */
1690 
1691         if (!CreateEnvironmentBlock(&lpEnvironment, Service->lpImage->hToken, FALSE))
1692         {
1693             /* We failed, run the service with the current environment */
1694             DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
1695                     GetLastError(), Service->lpServiceName);
1696             lpEnvironment = NULL;
1697         }
1698 
1699         /* Impersonate the new user */
1700         Result = ImpersonateLoggedOnUser(Service->lpImage->hToken);
1701         if (Result)
1702         {
1703             /* Launch the process in the user's logon session */
1704             Result = CreateProcessAsUserW(Service->lpImage->hToken,
1705                                           NULL,
1706                                           Service->lpImage->pszImagePath,
1707                                           NULL,
1708                                           NULL,
1709                                           FALSE,
1710                                           CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1711                                           lpEnvironment,
1712                                           NULL,
1713                                           &StartupInfo,
1714                                           &ProcessInformation);
1715             if (!Result)
1716                 dwError = GetLastError();
1717 
1718             /* Revert the impersonation */
1719             RevertToSelf();
1720         }
1721         else
1722         {
1723             dwError = GetLastError();
1724             DPRINT1("ImpersonateLoggedOnUser() failed with error %d\n", dwError);
1725         }
1726     }
1727     else
1728     {
1729         /* No user token: Run the service under the LocalSystem account */
1730 
1731         if (!CreateEnvironmentBlock(&lpEnvironment, NULL, TRUE))
1732         {
1733             /* We failed, run the service with the current environment */
1734             DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
1735                     GetLastError(), Service->lpServiceName);
1736             lpEnvironment = NULL;
1737         }
1738 
1739         Result = CreateProcessW(NULL,
1740                                 Service->lpImage->pszImagePath,
1741                                 NULL,
1742                                 NULL,
1743                                 FALSE,
1744                                 CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1745                                 lpEnvironment,
1746                                 NULL,
1747                                 &StartupInfo,
1748                                 &ProcessInformation);
1749         if (!Result)
1750             dwError = GetLastError();
1751     }
1752 
1753     if (lpEnvironment)
1754         DestroyEnvironmentBlock(lpEnvironment);
1755 
1756     if (!Result)
1757     {
1758         DPRINT1("Starting '%S' failed with error %d\n",
1759                 Service->lpServiceName, dwError);
1760         return dwError;
1761     }
1762 
1763     DPRINT("Process Id: %lu  Handle %p\n",
1764            ProcessInformation.dwProcessId,
1765            ProcessInformation.hProcess);
1766     DPRINT("Thread Id: %lu  Handle %p\n",
1767            ProcessInformation.dwThreadId,
1768            ProcessInformation.hThread);
1769 
1770     /* Get the process handle and ID */
1771     Service->lpImage->hProcess = ProcessInformation.hProcess;
1772     Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1773 
1774     /* Resume the main thread and close its handle */
1775     ResumeThread(ProcessInformation.hThread);
1776     CloseHandle(ProcessInformation.hThread);
1777 
1778     /* Connect control pipe */
1779     dwError = ScmWaitForServiceConnect(Service);
1780     if (dwError != ERROR_SUCCESS)
1781     {
1782         DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1783         Service->lpImage->dwProcessId = 0;
1784         return dwError;
1785     }
1786 
1787     /* Send the start command */
1788     return ScmSendStartCommand(Service, argc, argv);
1789 }
1790 
1791 
1792 static DWORD
1793 ScmLoadService(PSERVICE Service,
1794                DWORD argc,
1795                LPWSTR* argv)
1796 {
1797     PSERVICE_GROUP Group = Service->lpGroup;
1798     DWORD dwError = ERROR_SUCCESS;
1799     LPCWSTR lpLogStrings[2];
1800     WCHAR szLogBuffer[80];
1801 
1802     DPRINT("ScmLoadService() called\n");
1803     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1804 
1805     if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1806     {
1807         DPRINT("Service %S is already running!\n", Service->lpServiceName);
1808         return ERROR_SERVICE_ALREADY_RUNNING;
1809     }
1810 
1811     DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1812 
1813     if (Service->Status.dwServiceType & SERVICE_DRIVER)
1814     {
1815         /* Start the driver */
1816         dwError = ScmStartDriver(Service);
1817     }
1818     else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1819     {
1820         /* Start user-mode service */
1821         dwError = ScmCreateOrReferenceServiceImage(Service);
1822         if (dwError == ERROR_SUCCESS)
1823         {
1824             dwError = ScmStartUserModeService(Service, argc, argv);
1825             if (dwError == ERROR_SUCCESS)
1826             {
1827                 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1828                 Service->Status.dwControlsAccepted = 0;
1829             }
1830             else
1831             {
1832                 Service->lpImage->dwImageRunCount--;
1833                 if (Service->lpImage->dwImageRunCount == 0)
1834                 {
1835                     ScmRemoveServiceImage(Service->lpImage);
1836                     Service->lpImage = NULL;
1837                 }
1838             }
1839         }
1840     }
1841 
1842     DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1843 
1844     if (dwError == ERROR_SUCCESS)
1845     {
1846         if (Group != NULL)
1847         {
1848             Group->ServicesRunning = TRUE;
1849         }
1850 
1851         /* Log a successful service start */
1852         LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
1853         lpLogStrings[0] = Service->lpDisplayName;
1854         lpLogStrings[1] = szLogBuffer;
1855 
1856         ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
1857                     EVENTLOG_INFORMATION_TYPE,
1858                     2,
1859                     lpLogStrings);
1860     }
1861     else
1862     {
1863         if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1864         {
1865             /* Log a failed service start */
1866             StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
1867                              L"%lu", dwError);
1868             lpLogStrings[0] = Service->lpServiceName;
1869             lpLogStrings[1] = szLogBuffer;
1870             ScmLogEvent(EVENT_SERVICE_START_FAILED,
1871                         EVENTLOG_ERROR_TYPE,
1872                         2,
1873                         lpLogStrings);
1874         }
1875 
1876 #if 0
1877         switch (Service->dwErrorControl)
1878         {
1879             case SERVICE_ERROR_SEVERE:
1880                 if (IsLastKnownGood == FALSE)
1881                 {
1882                     /* FIXME: Boot last known good configuration */
1883                 }
1884                 break;
1885 
1886             case SERVICE_ERROR_CRITICAL:
1887                 if (IsLastKnownGood == FALSE)
1888                 {
1889                     /* FIXME: Boot last known good configuration */
1890                 }
1891                 else
1892                 {
1893                     /* FIXME: BSOD! */
1894                 }
1895                 break;
1896         }
1897 #endif
1898     }
1899 
1900     return dwError;
1901 }
1902 
1903 
1904 DWORD
1905 ScmStartService(PSERVICE Service,
1906                 DWORD argc,
1907                 LPWSTR* argv)
1908 {
1909     DWORD dwError = ERROR_SUCCESS;
1910     SC_RPC_LOCK Lock = NULL;
1911 
1912     DPRINT("ScmStartService() called\n");
1913     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1914 
1915     /* Acquire the service control critical section, to synchronize starts */
1916     EnterCriticalSection(&ControlServiceCriticalSection);
1917 
1918     /*
1919      * Acquire the user service start lock while the service is starting, if
1920      * needed (i.e. if we are not starting it during the initialization phase).
1921      * If we don't success, bail out.
1922      */
1923     if (!ScmInitialize)
1924     {
1925         dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
1926         if (dwError != ERROR_SUCCESS) goto done;
1927     }
1928 
1929     /* Really start the service */
1930     dwError = ScmLoadService(Service, argc, argv);
1931 
1932     /* Release the service start lock, if needed, and the critical section */
1933     if (Lock) ScmReleaseServiceStartLock(&Lock);
1934 
1935 done:
1936     LeaveCriticalSection(&ControlServiceCriticalSection);
1937 
1938     DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1939 
1940     return dwError;
1941 }
1942 
1943 
1944 VOID
1945 ScmAutoStartServices(VOID)
1946 {
1947     DWORD dwError;
1948     PLIST_ENTRY GroupEntry;
1949     PLIST_ENTRY ServiceEntry;
1950     PSERVICE_GROUP CurrentGroup;
1951     PSERVICE CurrentService;
1952     WCHAR szSafeBootServicePath[MAX_PATH];
1953     DWORD SafeBootEnabled;
1954     HKEY hKey;
1955     DWORD dwKeySize;
1956     ULONG i;
1957 
1958     /*
1959      * This function MUST be called ONLY at initialization time.
1960      * Therefore, no need to acquire the user service start lock.
1961      */
1962     ASSERT(ScmInitialize);
1963 
1964     /* Retrieve the SafeBoot parameter */
1965     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1966                             L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
1967                             0,
1968                             KEY_READ,
1969                             &hKey);
1970     if (dwError == ERROR_SUCCESS)
1971     {
1972         dwKeySize = sizeof(SafeBootEnabled);
1973         dwError = RegQueryValueExW(hKey,
1974                                    L"OptionValue",
1975                                    0,
1976                                    NULL,
1977                                    (LPBYTE)&SafeBootEnabled,
1978                                    &dwKeySize);
1979         RegCloseKey(hKey);
1980     }
1981 
1982     /* Default to Normal boot if the value doesn't exist */
1983     if (dwError != ERROR_SUCCESS)
1984         SafeBootEnabled = 0;
1985 
1986     /* Acquire the service control critical section, to synchronize starts */
1987     EnterCriticalSection(&ControlServiceCriticalSection);
1988 
1989     /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
1990     ServiceEntry = ServiceListHead.Flink;
1991     while (ServiceEntry != &ServiceListHead)
1992     {
1993         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1994 
1995         /* Build the safe boot path */
1996         StringCchCopyW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
1997                        L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
1998 
1999         switch (SafeBootEnabled)
2000         {
2001             /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
2002             case 1:
2003             case 3:
2004                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2005                               L"\\Minimal\\");
2006                 break;
2007 
2008             case 2:
2009                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2010                               L"\\Network\\");
2011                 break;
2012         }
2013 
2014         if (SafeBootEnabled != 0)
2015         {
2016             /* If key does not exist then do not assume safe mode */
2017             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2018                                     szSafeBootServicePath,
2019                                     0,
2020                                     KEY_READ,
2021                                     &hKey);
2022             if (dwError == ERROR_SUCCESS)
2023             {
2024                 RegCloseKey(hKey);
2025 
2026                 /* Finish Safe Boot path off */
2027                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2028                               CurrentService->lpServiceName);
2029 
2030                 /* Check that the key is in the Safe Boot path */
2031                 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2032                                         szSafeBootServicePath,
2033                                         0,
2034                                         KEY_READ,
2035                                         &hKey);
2036                 if (dwError != ERROR_SUCCESS)
2037                 {
2038                     /* Mark service as visited so it is not auto-started */
2039                     CurrentService->ServiceVisited = TRUE;
2040                 }
2041                 else
2042                 {
2043                     /* Must be auto-started in safe mode - mark as unvisited */
2044                     RegCloseKey(hKey);
2045                     CurrentService->ServiceVisited = FALSE;
2046                 }
2047             }
2048             else
2049             {
2050                 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
2051                 CurrentService->ServiceVisited = FALSE;
2052             }
2053         }
2054 
2055         ServiceEntry = ServiceEntry->Flink;
2056     }
2057 
2058     /* Start all services which are members of an existing group */
2059     GroupEntry = GroupListHead.Flink;
2060     while (GroupEntry != &GroupListHead)
2061     {
2062         CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
2063 
2064         DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
2065 
2066         /* Start all services witch have a valid tag */
2067         for (i = 0; i < CurrentGroup->TagCount; i++)
2068         {
2069             ServiceEntry = ServiceListHead.Flink;
2070             while (ServiceEntry != &ServiceListHead)
2071             {
2072                 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2073 
2074                 if ((CurrentService->lpGroup == CurrentGroup) &&
2075                     (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2076                     (CurrentService->ServiceVisited == FALSE) &&
2077                     (CurrentService->dwTag == CurrentGroup->TagArray[i]))
2078                 {
2079                     CurrentService->ServiceVisited = TRUE;
2080                     ScmLoadService(CurrentService, 0, NULL);
2081                 }
2082 
2083                 ServiceEntry = ServiceEntry->Flink;
2084              }
2085         }
2086 
2087         /* Start all services which have an invalid tag or which do not have a tag */
2088         ServiceEntry = ServiceListHead.Flink;
2089         while (ServiceEntry != &ServiceListHead)
2090         {
2091             CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2092 
2093             if ((CurrentService->lpGroup == CurrentGroup) &&
2094                 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2095                 (CurrentService->ServiceVisited == FALSE))
2096             {
2097                 CurrentService->ServiceVisited = TRUE;
2098                 ScmLoadService(CurrentService, 0, NULL);
2099             }
2100 
2101             ServiceEntry = ServiceEntry->Flink;
2102         }
2103 
2104         GroupEntry = GroupEntry->Flink;
2105     }
2106 
2107     /* Start all services which are members of any non-existing group */
2108     ServiceEntry = ServiceListHead.Flink;
2109     while (ServiceEntry != &ServiceListHead)
2110     {
2111         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2112 
2113         if ((CurrentService->lpGroup != NULL) &&
2114             (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2115             (CurrentService->ServiceVisited == FALSE))
2116         {
2117             CurrentService->ServiceVisited = TRUE;
2118             ScmLoadService(CurrentService, 0, NULL);
2119         }
2120 
2121         ServiceEntry = ServiceEntry->Flink;
2122     }
2123 
2124     /* Start all services which are not a member of any group */
2125     ServiceEntry = ServiceListHead.Flink;
2126     while (ServiceEntry != &ServiceListHead)
2127     {
2128         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2129 
2130         if ((CurrentService->lpGroup == NULL) &&
2131             (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2132             (CurrentService->ServiceVisited == FALSE))
2133         {
2134             CurrentService->ServiceVisited = TRUE;
2135             ScmLoadService(CurrentService, 0, NULL);
2136         }
2137 
2138         ServiceEntry = ServiceEntry->Flink;
2139     }
2140 
2141     /* Clear 'ServiceVisited' flag again */
2142     ServiceEntry = ServiceListHead.Flink;
2143     while (ServiceEntry != &ServiceListHead)
2144     {
2145         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2146         CurrentService->ServiceVisited = FALSE;
2147         ServiceEntry = ServiceEntry->Flink;
2148     }
2149 
2150     /* Release the critical section */
2151     LeaveCriticalSection(&ControlServiceCriticalSection);
2152 }
2153 
2154 
2155 VOID
2156 ScmAutoShutdownServices(VOID)
2157 {
2158     PLIST_ENTRY ServiceEntry;
2159     PSERVICE CurrentService;
2160 
2161     DPRINT("ScmAutoShutdownServices() called\n");
2162 
2163     /* Lock the service database exclusively */
2164     ScmLockDatabaseExclusive();
2165 
2166     ServiceEntry = ServiceListHead.Flink;
2167     while (ServiceEntry != &ServiceListHead)
2168     {
2169         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2170 
2171         if ((CurrentService->Status.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) &&
2172             (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
2173              CurrentService->Status.dwCurrentState == SERVICE_START_PENDING))
2174         {
2175             /* Send the shutdown notification */
2176             DPRINT("Shutdown service: %S\n", CurrentService->lpServiceName);
2177             ScmControlService(CurrentService->lpImage->hControlPipe,
2178                               CurrentService->lpServiceName,
2179                               (SERVICE_STATUS_HANDLE)CurrentService,
2180                               SERVICE_CONTROL_SHUTDOWN);
2181         }
2182 
2183         ServiceEntry = ServiceEntry->Flink;
2184     }
2185 
2186     /* Unlock the service database */
2187     ScmUnlockDatabase();
2188 
2189     DPRINT("ScmAutoShutdownServices() done\n");
2190 }
2191 
2192 
2193 BOOL
2194 ScmLockDatabaseExclusive(VOID)
2195 {
2196     return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
2197 }
2198 
2199 
2200 BOOL
2201 ScmLockDatabaseShared(VOID)
2202 {
2203     return RtlAcquireResourceShared(&DatabaseLock, TRUE);
2204 }
2205 
2206 
2207 VOID
2208 ScmUnlockDatabase(VOID)
2209 {
2210     RtlReleaseResource(&DatabaseLock);
2211 }
2212 
2213 
2214 VOID
2215 ScmInitNamedPipeCriticalSection(VOID)
2216 {
2217     HKEY hKey;
2218     DWORD dwKeySize;
2219     DWORD dwError;
2220 
2221     InitializeCriticalSection(&ControlServiceCriticalSection);
2222 
2223     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2224                             L"SYSTEM\\CurrentControlSet\\Control",
2225                             0,
2226                             KEY_READ,
2227                             &hKey);
2228    if (dwError == ERROR_SUCCESS)
2229    {
2230         dwKeySize = sizeof(PipeTimeout);
2231         RegQueryValueExW(hKey,
2232                          L"ServicesPipeTimeout",
2233                          0,
2234                          NULL,
2235                          (LPBYTE)&PipeTimeout,
2236                          &dwKeySize);
2237        RegCloseKey(hKey);
2238    }
2239 }
2240 
2241 
2242 VOID
2243 ScmDeleteNamedPipeCriticalSection(VOID)
2244 {
2245     DeleteCriticalSection(&ControlServiceCriticalSection);
2246 }
2247 
2248 /* EOF */
2249