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