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