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