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