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