xref: /reactos/base/system/services/database.c (revision f4d29a74)
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 #include <reactos/undocuser.h>
21 
22 #define NDEBUG
23 #include <debug.h>
24 
25 
26 /* GLOBALS *******************************************************************/
27 
28 LIST_ENTRY ImageListHead;
29 LIST_ENTRY ServiceListHead;
30 
31 static RTL_RESOURCE DatabaseLock;
32 static DWORD ResumeCount = 1;
33 static DWORD NoInteractiveServices = 0;
34 
35 /* The critical section synchronizes service control requests */
36 static CRITICAL_SECTION ControlServiceCriticalSection;
37 static DWORD PipeTimeout = 30000; /* 30 Seconds */
38 
39 
40 /* FUNCTIONS *****************************************************************/
41 
42 static DWORD
43 ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage)
44 {
45     WCHAR szControlPipeName[MAX_PATH + 1];
46     HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
47     DWORD ServiceCurrent = 0;
48     DWORD KeyDisposition;
49     DWORD dwKeySize;
50     DWORD dwError;
51 
52     /* Get the service number */
53     /* TODO: Create registry entry with correct write access */
54     dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
55                               L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
56                               REG_OPTION_VOLATILE,
57                               KEY_WRITE | KEY_READ,
58                               NULL,
59                               &hServiceCurrentKey,
60                               &KeyDisposition);
61     if (dwError != ERROR_SUCCESS)
62     {
63         DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
64         return dwError;
65     }
66 
67     if (KeyDisposition == REG_OPENED_EXISTING_KEY)
68     {
69         dwKeySize = sizeof(DWORD);
70         dwError = RegQueryValueExW(hServiceCurrentKey,
71                                    L"", 0, NULL, (BYTE*)&ServiceCurrent, &dwKeySize);
72 
73         if (dwError != ERROR_SUCCESS)
74         {
75             RegCloseKey(hServiceCurrentKey);
76             DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
77             return dwError;
78         }
79 
80         ServiceCurrent++;
81     }
82 
83     dwError = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
84 
85     RegCloseKey(hServiceCurrentKey);
86 
87     if (dwError != ERROR_SUCCESS)
88     {
89         DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
90         return dwError;
91     }
92 
93     /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
94     StringCchPrintfW(szControlPipeName, ARRAYSIZE(szControlPipeName),
95                      L"\\\\.\\pipe\\net\\NtControlPipe%lu", ServiceCurrent);
96 
97     DPRINT("PipeName: %S\n", szControlPipeName);
98 
99     pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
100                                                    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
101                                                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
102                                                    100,
103                                                    8000,
104                                                    4,
105                                                    PipeTimeout,
106                                                    NULL);
107     DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
108     if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
109     {
110         DPRINT1("Failed to create control pipe!\n");
111         return GetLastError();
112     }
113 
114     return ERROR_SUCCESS;
115 }
116 
117 
118 static PSERVICE_IMAGE
119 ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
120 {
121     PLIST_ENTRY ImageEntry;
122     PSERVICE_IMAGE CurrentImage;
123 
124     DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
125 
126     ImageEntry = ImageListHead.Flink;
127     while (ImageEntry != &ImageListHead)
128     {
129         CurrentImage = CONTAINING_RECORD(ImageEntry,
130                                          SERVICE_IMAGE,
131                                          ImageListEntry);
132         if (_wcsicmp(CurrentImage->pszImagePath, lpImagePath) == 0)
133         {
134             DPRINT("Found image: '%S'\n", CurrentImage->pszImagePath);
135             return CurrentImage;
136         }
137 
138         ImageEntry = ImageEntry->Flink;
139     }
140 
141     DPRINT("Couldn't find a matching image\n");
142 
143     return NULL;
144 
145 }
146 
147 
148 static
149 BOOL
150 ScmIsSameServiceAccount(
151     _In_ PCWSTR pszAccountName1,
152     _In_ PCWSTR pszAccountName2)
153 {
154     if (pszAccountName1 == NULL &&
155         pszAccountName2 == NULL)
156         return TRUE;
157 
158     if (pszAccountName1 == NULL &&
159         pszAccountName2 != NULL &&
160         _wcsicmp(pszAccountName2, L"LocalSystem") == 0)
161         return TRUE;
162 
163     if (pszAccountName1 != NULL &&
164         pszAccountName2 == NULL &&
165         _wcsicmp(pszAccountName1, L"LocalSystem") == 0)
166         return TRUE;
167 
168     if (pszAccountName1 != NULL &&
169         pszAccountName2 != NULL &&
170         _wcsicmp(pszAccountName1, pszAccountName2) == 0)
171         return TRUE;
172 
173     return FALSE;
174 }
175 
176 
177 static
178 BOOL
179 ScmIsLocalSystemAccount(
180     _In_ PCWSTR pszAccountName)
181 {
182     if (pszAccountName == NULL ||
183         _wcsicmp(pszAccountName, L"LocalSystem") == 0)
184         return TRUE;
185 
186     return FALSE;
187 }
188 
189 
190 static
191 DWORD
192 ScmLogonService(
193     IN PSERVICE pService,
194     IN PSERVICE_IMAGE pImage)
195 {
196     DWORD dwError = ERROR_SUCCESS;
197     PROFILEINFOW ProfileInfo;
198     PWSTR pszUserName = NULL;
199     PWSTR pszDomainName = NULL;
200     PWSTR pszPassword = NULL;
201     PWSTR ptr;
202 
203     DPRINT("ScmLogonService(%p %p)\n", pService, pImage);
204     DPRINT("Service %S\n", pService->lpServiceName);
205 
206     if (ScmIsLocalSystemAccount(pImage->pszAccountName))
207         return ERROR_SUCCESS;
208 
209     /* Get the user and domain names */
210     ptr = wcschr(pImage->pszAccountName, L'\\');
211     if (ptr != NULL)
212     {
213         *ptr = L'\0';
214         pszUserName = ptr + 1;
215         pszDomainName = pImage->pszAccountName;
216     }
217     else
218     {
219         // ERROR_INVALID_SERVICE_ACCOUNT
220         pszUserName = pImage->pszAccountName;
221         pszDomainName = NULL;
222     }
223 
224     /* Build the service 'password' */
225     pszPassword = HeapAlloc(GetProcessHeap(),
226                             HEAP_ZERO_MEMORY,
227                             (wcslen(pService->lpServiceName) + 5) * sizeof(WCHAR));
228     if (pszPassword == NULL)
229     {
230         dwError = ERROR_NOT_ENOUGH_MEMORY;
231         goto done;
232     }
233 
234     wcscpy(pszPassword, L"_SC_");
235     wcscat(pszPassword, pService->lpServiceName);
236 
237     DPRINT("Domain: %S  User: %S  Password: %S\n", pszDomainName, pszUserName, pszPassword);
238 
239     /* Do the service logon */
240     if (!LogonUserW(pszUserName,
241                     pszDomainName,
242                     pszPassword,
243                     LOGON32_LOGON_SERVICE,
244                     LOGON32_PROVIDER_DEFAULT,
245                     &pImage->hToken))
246     {
247         dwError = GetLastError();
248         DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
249 
250         /* Normalize the returned error */
251         dwError = ERROR_SERVICE_LOGON_FAILED;
252         goto done;
253     }
254 
255     /* Load the user profile; the per-user environment variables are thus correctly initialized */
256     ZeroMemory(&ProfileInfo, sizeof(ProfileInfo));
257     ProfileInfo.dwSize = sizeof(ProfileInfo);
258     ProfileInfo.dwFlags = PI_NOUI;
259     ProfileInfo.lpUserName = pszUserName;
260     // ProfileInfo.lpProfilePath = NULL;
261     // ProfileInfo.lpDefaultPath = NULL;
262     // ProfileInfo.lpServerName = NULL;
263     // ProfileInfo.lpPolicyPath = NULL;
264     // ProfileInfo.hProfile = NULL;
265 
266     if (!LoadUserProfileW(pImage->hToken, &ProfileInfo))
267     {
268         dwError = GetLastError();
269         DPRINT1("LoadUserProfileW() failed (Error %lu)\n", dwError);
270         goto done;
271     }
272 
273     pImage->hProfile = ProfileInfo.hProfile;
274 
275 done:
276     if (pszPassword != NULL)
277         HeapFree(GetProcessHeap(), 0, pszPassword);
278 
279     if (ptr != NULL)
280         *ptr = L'\\';
281 
282     return dwError;
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     if (Service->lpImage->hToken)
1683     {
1684         /* User token: Run the service under the user account */
1685 
1686         if (!CreateEnvironmentBlock(&lpEnvironment, Service->lpImage->hToken, FALSE))
1687         {
1688             /* We failed, run the service with the current environment */
1689             DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
1690                     GetLastError(), Service->lpServiceName);
1691             lpEnvironment = NULL;
1692         }
1693 
1694         /* Impersonate the new user */
1695         Result = ImpersonateLoggedOnUser(Service->lpImage->hToken);
1696         if (Result)
1697         {
1698             /* Launch the process in the user's logon session */
1699             Result = CreateProcessAsUserW(Service->lpImage->hToken,
1700                                           NULL,
1701                                           Service->lpImage->pszImagePath,
1702                                           NULL,
1703                                           NULL,
1704                                           FALSE,
1705                                           CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1706                                           lpEnvironment,
1707                                           NULL,
1708                                           &StartupInfo,
1709                                           &ProcessInformation);
1710             if (!Result)
1711                 dwError = GetLastError();
1712 
1713             /* Revert the impersonation */
1714             RevertToSelf();
1715         }
1716         else
1717         {
1718             dwError = GetLastError();
1719             DPRINT1("ImpersonateLoggedOnUser() failed with error %d\n", dwError);
1720         }
1721     }
1722     else
1723     {
1724         /* No user token: Run the service under the LocalSystem account */
1725 
1726         if (!CreateEnvironmentBlock(&lpEnvironment, NULL, TRUE))
1727         {
1728             /* We failed, run the service with the current environment */
1729             DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
1730                     GetLastError(), Service->lpServiceName);
1731             lpEnvironment = NULL;
1732         }
1733 
1734         /* Use the interactive desktop if the service is interactive */
1735         if ((NoInteractiveServices == 0) &&
1736             (Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
1737         {
1738             StartupInfo.dwFlags |= STARTF_INHERITDESKTOP;
1739             StartupInfo.lpDesktop = L"WinSta0\\Default";
1740         }
1741 
1742         Result = CreateProcessW(NULL,
1743                                 Service->lpImage->pszImagePath,
1744                                 NULL,
1745                                 NULL,
1746                                 FALSE,
1747                                 CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1748                                 lpEnvironment,
1749                                 NULL,
1750                                 &StartupInfo,
1751                                 &ProcessInformation);
1752         if (!Result)
1753             dwError = GetLastError();
1754     }
1755 
1756     if (lpEnvironment)
1757         DestroyEnvironmentBlock(lpEnvironment);
1758 
1759     if (!Result)
1760     {
1761         DPRINT1("Starting '%S' failed with error %d\n",
1762                 Service->lpServiceName, dwError);
1763         return dwError;
1764     }
1765 
1766     DPRINT("Process Id: %lu  Handle %p\n",
1767            ProcessInformation.dwProcessId,
1768            ProcessInformation.hProcess);
1769     DPRINT("Thread Id: %lu  Handle %p\n",
1770            ProcessInformation.dwThreadId,
1771            ProcessInformation.hThread);
1772 
1773     /* Get the process handle and ID */
1774     Service->lpImage->hProcess = ProcessInformation.hProcess;
1775     Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1776 
1777     /* Resume the main thread and close its handle */
1778     ResumeThread(ProcessInformation.hThread);
1779     CloseHandle(ProcessInformation.hThread);
1780 
1781     /* Connect control pipe */
1782     dwError = ScmWaitForServiceConnect(Service);
1783     if (dwError != ERROR_SUCCESS)
1784     {
1785         DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1786         Service->lpImage->dwProcessId = 0;
1787         return dwError;
1788     }
1789 
1790     /* Send the start command */
1791     return ScmSendStartCommand(Service, argc, argv);
1792 }
1793 
1794 
1795 static DWORD
1796 ScmLoadService(PSERVICE Service,
1797                DWORD argc,
1798                LPWSTR* argv)
1799 {
1800     PSERVICE_GROUP Group = Service->lpGroup;
1801     DWORD dwError = ERROR_SUCCESS;
1802     LPCWSTR lpLogStrings[2];
1803     WCHAR szLogBuffer[80];
1804 
1805     DPRINT("ScmLoadService() called\n");
1806     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1807 
1808     if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1809     {
1810         DPRINT("Service %S is already running!\n", Service->lpServiceName);
1811         return ERROR_SERVICE_ALREADY_RUNNING;
1812     }
1813 
1814     DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1815 
1816     if (Service->Status.dwServiceType & SERVICE_DRIVER)
1817     {
1818         /* Start the driver */
1819         dwError = ScmStartDriver(Service);
1820     }
1821     else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1822     {
1823         /* Start user-mode service */
1824         dwError = ScmCreateOrReferenceServiceImage(Service);
1825         if (dwError == ERROR_SUCCESS)
1826         {
1827             dwError = ScmStartUserModeService(Service, argc, argv);
1828             if (dwError == ERROR_SUCCESS)
1829             {
1830                 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1831                 Service->Status.dwControlsAccepted = 0;
1832             }
1833             else
1834             {
1835                 Service->lpImage->dwImageRunCount--;
1836                 if (Service->lpImage->dwImageRunCount == 0)
1837                 {
1838                     ScmRemoveServiceImage(Service->lpImage);
1839                     Service->lpImage = NULL;
1840                 }
1841             }
1842         }
1843     }
1844 
1845     DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1846 
1847     if (dwError == ERROR_SUCCESS)
1848     {
1849         if (Group != NULL)
1850         {
1851             Group->ServicesRunning = TRUE;
1852         }
1853 
1854         /* Log a successful service start */
1855         LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
1856         lpLogStrings[0] = Service->lpDisplayName;
1857         lpLogStrings[1] = szLogBuffer;
1858 
1859         ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
1860                     EVENTLOG_INFORMATION_TYPE,
1861                     2,
1862                     lpLogStrings);
1863     }
1864     else
1865     {
1866         if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1867         {
1868             /* Log a failed service start */
1869             StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
1870                              L"%lu", dwError);
1871             lpLogStrings[0] = Service->lpServiceName;
1872             lpLogStrings[1] = szLogBuffer;
1873             ScmLogEvent(EVENT_SERVICE_START_FAILED,
1874                         EVENTLOG_ERROR_TYPE,
1875                         2,
1876                         lpLogStrings);
1877         }
1878 
1879 #if 0
1880         switch (Service->dwErrorControl)
1881         {
1882             case SERVICE_ERROR_SEVERE:
1883                 if (IsLastKnownGood == FALSE)
1884                 {
1885                     /* FIXME: Boot last known good configuration */
1886                 }
1887                 break;
1888 
1889             case SERVICE_ERROR_CRITICAL:
1890                 if (IsLastKnownGood == FALSE)
1891                 {
1892                     /* FIXME: Boot last known good configuration */
1893                 }
1894                 else
1895                 {
1896                     /* FIXME: BSOD! */
1897                 }
1898                 break;
1899         }
1900 #endif
1901     }
1902 
1903     return dwError;
1904 }
1905 
1906 
1907 DWORD
1908 ScmStartService(PSERVICE Service,
1909                 DWORD argc,
1910                 LPWSTR* argv)
1911 {
1912     DWORD dwError = ERROR_SUCCESS;
1913     SC_RPC_LOCK Lock = NULL;
1914 
1915     DPRINT("ScmStartService() called\n");
1916     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1917 
1918     /* Acquire the service control critical section, to synchronize starts */
1919     EnterCriticalSection(&ControlServiceCriticalSection);
1920 
1921     /*
1922      * Acquire the user service start lock while the service is starting, if
1923      * needed (i.e. if we are not starting it during the initialization phase).
1924      * If we don't success, bail out.
1925      */
1926     if (!ScmInitialize)
1927     {
1928         dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
1929         if (dwError != ERROR_SUCCESS) goto done;
1930     }
1931 
1932     /* Really start the service */
1933     dwError = ScmLoadService(Service, argc, argv);
1934 
1935     /* Release the service start lock, if needed, and the critical section */
1936     if (Lock) ScmReleaseServiceStartLock(&Lock);
1937 
1938 done:
1939     LeaveCriticalSection(&ControlServiceCriticalSection);
1940 
1941     DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1942 
1943     return dwError;
1944 }
1945 
1946 
1947 VOID
1948 ScmAutoStartServices(VOID)
1949 {
1950     DWORD dwError;
1951     PLIST_ENTRY GroupEntry;
1952     PLIST_ENTRY ServiceEntry;
1953     PSERVICE_GROUP CurrentGroup;
1954     PSERVICE CurrentService;
1955     WCHAR szSafeBootServicePath[MAX_PATH];
1956     DWORD SafeBootEnabled;
1957     HKEY hKey;
1958     DWORD dwKeySize;
1959     ULONG i;
1960 
1961     /*
1962      * This function MUST be called ONLY at initialization time.
1963      * Therefore, no need to acquire the user service start lock.
1964      */
1965     ASSERT(ScmInitialize);
1966 
1967     /* Retrieve the SafeBoot parameter */
1968     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1969                             L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
1970                             0,
1971                             KEY_READ,
1972                             &hKey);
1973     if (dwError == ERROR_SUCCESS)
1974     {
1975         dwKeySize = sizeof(SafeBootEnabled);
1976         dwError = RegQueryValueExW(hKey,
1977                                    L"OptionValue",
1978                                    0,
1979                                    NULL,
1980                                    (LPBYTE)&SafeBootEnabled,
1981                                    &dwKeySize);
1982         RegCloseKey(hKey);
1983     }
1984 
1985     /* Default to Normal boot if the value doesn't exist */
1986     if (dwError != ERROR_SUCCESS)
1987         SafeBootEnabled = 0;
1988 
1989     /* Acquire the service control critical section, to synchronize starts */
1990     EnterCriticalSection(&ControlServiceCriticalSection);
1991 
1992     /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
1993     ServiceEntry = ServiceListHead.Flink;
1994     while (ServiceEntry != &ServiceListHead)
1995     {
1996         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1997 
1998         /* Build the safe boot path */
1999         StringCchCopyW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2000                        L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
2001 
2002         switch (SafeBootEnabled)
2003         {
2004             /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
2005             case 1:
2006             case 3:
2007                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2008                               L"\\Minimal\\");
2009                 break;
2010 
2011             case 2:
2012                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2013                               L"\\Network\\");
2014                 break;
2015         }
2016 
2017         if (SafeBootEnabled != 0)
2018         {
2019             /* If key does not exist then do not assume safe mode */
2020             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2021                                     szSafeBootServicePath,
2022                                     0,
2023                                     KEY_READ,
2024                                     &hKey);
2025             if (dwError == ERROR_SUCCESS)
2026             {
2027                 RegCloseKey(hKey);
2028 
2029                 /* Finish Safe Boot path off */
2030                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2031                               CurrentService->lpServiceName);
2032 
2033                 /* Check that the key is in the Safe Boot path */
2034                 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2035                                         szSafeBootServicePath,
2036                                         0,
2037                                         KEY_READ,
2038                                         &hKey);
2039                 if (dwError != ERROR_SUCCESS)
2040                 {
2041                     /* Mark service as visited so it is not auto-started */
2042                     CurrentService->ServiceVisited = TRUE;
2043                 }
2044                 else
2045                 {
2046                     /* Must be auto-started in safe mode - mark as unvisited */
2047                     RegCloseKey(hKey);
2048                     CurrentService->ServiceVisited = FALSE;
2049                 }
2050             }
2051             else
2052             {
2053                 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
2054                 CurrentService->ServiceVisited = FALSE;
2055             }
2056         }
2057 
2058         ServiceEntry = ServiceEntry->Flink;
2059     }
2060 
2061     /* Start all services which are members of an existing group */
2062     GroupEntry = GroupListHead.Flink;
2063     while (GroupEntry != &GroupListHead)
2064     {
2065         CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
2066 
2067         DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
2068 
2069         /* Start all services witch have a valid tag */
2070         for (i = 0; i < CurrentGroup->TagCount; i++)
2071         {
2072             ServiceEntry = ServiceListHead.Flink;
2073             while (ServiceEntry != &ServiceListHead)
2074             {
2075                 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2076 
2077                 if ((CurrentService->lpGroup == CurrentGroup) &&
2078                     (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2079                     (CurrentService->ServiceVisited == FALSE) &&
2080                     (CurrentService->dwTag == CurrentGroup->TagArray[i]))
2081                 {
2082                     CurrentService->ServiceVisited = TRUE;
2083                     ScmLoadService(CurrentService, 0, NULL);
2084                 }
2085 
2086                 ServiceEntry = ServiceEntry->Flink;
2087              }
2088         }
2089 
2090         /* Start all services which have an invalid tag or which do not have a tag */
2091         ServiceEntry = ServiceListHead.Flink;
2092         while (ServiceEntry != &ServiceListHead)
2093         {
2094             CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2095 
2096             if ((CurrentService->lpGroup == CurrentGroup) &&
2097                 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2098                 (CurrentService->ServiceVisited == FALSE))
2099             {
2100                 CurrentService->ServiceVisited = TRUE;
2101                 ScmLoadService(CurrentService, 0, NULL);
2102             }
2103 
2104             ServiceEntry = ServiceEntry->Flink;
2105         }
2106 
2107         GroupEntry = GroupEntry->Flink;
2108     }
2109 
2110     /* Start all services which are members of any non-existing group */
2111     ServiceEntry = ServiceListHead.Flink;
2112     while (ServiceEntry != &ServiceListHead)
2113     {
2114         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2115 
2116         if ((CurrentService->lpGroup != NULL) &&
2117             (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2118             (CurrentService->ServiceVisited == FALSE))
2119         {
2120             CurrentService->ServiceVisited = TRUE;
2121             ScmLoadService(CurrentService, 0, NULL);
2122         }
2123 
2124         ServiceEntry = ServiceEntry->Flink;
2125     }
2126 
2127     /* Start all services which are not a member of any group */
2128     ServiceEntry = ServiceListHead.Flink;
2129     while (ServiceEntry != &ServiceListHead)
2130     {
2131         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2132 
2133         if ((CurrentService->lpGroup == NULL) &&
2134             (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2135             (CurrentService->ServiceVisited == FALSE))
2136         {
2137             CurrentService->ServiceVisited = TRUE;
2138             ScmLoadService(CurrentService, 0, NULL);
2139         }
2140 
2141         ServiceEntry = ServiceEntry->Flink;
2142     }
2143 
2144     /* Clear 'ServiceVisited' flag again */
2145     ServiceEntry = ServiceListHead.Flink;
2146     while (ServiceEntry != &ServiceListHead)
2147     {
2148         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2149         CurrentService->ServiceVisited = FALSE;
2150         ServiceEntry = ServiceEntry->Flink;
2151     }
2152 
2153     /* Release the critical section */
2154     LeaveCriticalSection(&ControlServiceCriticalSection);
2155 }
2156 
2157 
2158 VOID
2159 ScmAutoShutdownServices(VOID)
2160 {
2161     PLIST_ENTRY ServiceEntry;
2162     PSERVICE CurrentService;
2163 
2164     DPRINT("ScmAutoShutdownServices() called\n");
2165 
2166     /* Lock the service database exclusively */
2167     ScmLockDatabaseExclusive();
2168 
2169     ServiceEntry = ServiceListHead.Flink;
2170     while (ServiceEntry != &ServiceListHead)
2171     {
2172         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2173 
2174         if ((CurrentService->Status.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) &&
2175             (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
2176              CurrentService->Status.dwCurrentState == SERVICE_START_PENDING))
2177         {
2178             /* Send the shutdown notification */
2179             DPRINT("Shutdown service: %S\n", CurrentService->lpServiceName);
2180             ScmControlService(CurrentService->lpImage->hControlPipe,
2181                               CurrentService->lpServiceName,
2182                               (SERVICE_STATUS_HANDLE)CurrentService,
2183                               SERVICE_CONTROL_SHUTDOWN);
2184         }
2185 
2186         ServiceEntry = ServiceEntry->Flink;
2187     }
2188 
2189     /* Unlock the service database */
2190     ScmUnlockDatabase();
2191 
2192     DPRINT("ScmAutoShutdownServices() done\n");
2193 }
2194 
2195 
2196 BOOL
2197 ScmLockDatabaseExclusive(VOID)
2198 {
2199     return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
2200 }
2201 
2202 
2203 BOOL
2204 ScmLockDatabaseShared(VOID)
2205 {
2206     return RtlAcquireResourceShared(&DatabaseLock, TRUE);
2207 }
2208 
2209 
2210 VOID
2211 ScmUnlockDatabase(VOID)
2212 {
2213     RtlReleaseResource(&DatabaseLock);
2214 }
2215 
2216 
2217 VOID
2218 ScmInitNamedPipeCriticalSection(VOID)
2219 {
2220     HKEY hKey;
2221     DWORD dwKeySize;
2222     DWORD dwError;
2223 
2224     InitializeCriticalSection(&ControlServiceCriticalSection);
2225 
2226     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2227                             L"SYSTEM\\CurrentControlSet\\Control",
2228                             0,
2229                             KEY_READ,
2230                             &hKey);
2231    if (dwError == ERROR_SUCCESS)
2232    {
2233         dwKeySize = sizeof(PipeTimeout);
2234         RegQueryValueExW(hKey,
2235                          L"ServicesPipeTimeout",
2236                          0,
2237                          NULL,
2238                          (LPBYTE)&PipeTimeout,
2239                          &dwKeySize);
2240        RegCloseKey(hKey);
2241    }
2242 }
2243 
2244 
2245 VOID
2246 ScmDeleteNamedPipeCriticalSection(VOID)
2247 {
2248     DeleteCriticalSection(&ControlServiceCriticalSection);
2249 }
2250 
2251 /* EOF */
2252