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