[SERVICES] Implement ScmGenerateServiceTag and call it on service creation
[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
1435 /* Copy the start command line */
1436 ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1437 Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
1438 wcscpy(Ptr, Service->lpServiceName);
1439
1440 ControlPacket->dwArgumentsCount = 0;
1441 ControlPacket->dwArgumentsOffset = 0;
1442
1443 /* Copy the argument vector */
1444 if (argc > 0 && argv != NULL)
1445 {
1446 Ptr += wcslen(Service->lpServiceName) + 1;
1447 pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
1448 pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
1449
1450 ControlPacket->dwArgumentsCount = argc;
1451 ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
1452
1453 DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
1454 DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
1455
1456 for (i = 0; i < argc; i++)
1457 {
1458 wcscpy(pArgPtr, argv[i]);
1459 pOffPtr[i] = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
1460 DPRINT("offset[%lu]: %p\n", i, pOffPtr[i]);
1461 pArgPtr += wcslen(argv[i]) + 1;
1462 }
1463 }
1464
1465 bResult = WriteFile(Service->lpImage->hControlPipe,
1466 ControlPacket,
1467 PacketSize,
1468 &dwWriteCount,
1469 &Overlapped);
1470 if (bResult == FALSE)
1471 {
1472 DPRINT("WriteFile() returned FALSE\n");
1473
1474 dwError = GetLastError();
1475 if (dwError == ERROR_IO_PENDING)
1476 {
1477 DPRINT("dwError: ERROR_IO_PENDING\n");
1478
1479 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1480 PipeTimeout);
1481 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1482
1483 if (dwError == WAIT_TIMEOUT)
1484 {
1485 bResult = CancelIo(Service->lpImage->hControlPipe);
1486 if (bResult == FALSE)
1487 {
1488 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1489 }
1490
1491 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1492 goto Done;
1493 }
1494 else if (dwError == WAIT_OBJECT_0)
1495 {
1496 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1497 &Overlapped,
1498 &dwWriteCount,
1499 TRUE);
1500 if (bResult == FALSE)
1501 {
1502 dwError = GetLastError();
1503 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1504
1505 goto Done;
1506 }
1507 }
1508 }
1509 else
1510 {
1511 DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
1512 goto Done;
1513 }
1514 }
1515
1516 /* Read the reply */
1517 Overlapped.hEvent = (HANDLE) NULL;
1518
1519 bResult = ReadFile(Service->lpImage->hControlPipe,
1520 &ReplyPacket,
1521 sizeof(SCM_REPLY_PACKET),
1522 &dwReadCount,
1523 &Overlapped);
1524 if (bResult == FALSE)
1525 {
1526 DPRINT("ReadFile() returned FALSE\n");
1527
1528 dwError = GetLastError();
1529 if (dwError == ERROR_IO_PENDING)
1530 {
1531 DPRINT("dwError: ERROR_IO_PENDING\n");
1532
1533 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1534 PipeTimeout);
1535 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1536
1537 if (dwError == WAIT_TIMEOUT)
1538 {
1539 bResult = CancelIo(Service->lpImage->hControlPipe);
1540 if (bResult == FALSE)
1541 {
1542 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1543 }
1544
1545 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1546 goto Done;
1547 }
1548 else if (dwError == WAIT_OBJECT_0)
1549 {
1550 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1551 &Overlapped,
1552 &dwReadCount,
1553 TRUE);
1554 if (bResult == FALSE)
1555 {
1556 dwError = GetLastError();
1557 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1558
1559 goto Done;
1560 }
1561 }
1562 }
1563 else
1564 {
1565 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1566 goto Done;
1567 }
1568 }
1569
1570 Done:
1571 /* Release the control packet */
1572 HeapFree(GetProcessHeap(),
1573 0,
1574 ControlPacket);
1575
1576 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1577 {
1578 dwError = ReplyPacket.dwError;
1579 }
1580
1581 DPRINT("ScmSendStartCommand() done\n");
1582
1583 return dwError;
1584 }
1585
1586
1587 static DWORD
1588 ScmWaitForServiceConnect(PSERVICE Service)
1589 {
1590 DWORD dwRead = 0;
1591 DWORD dwProcessId = 0;
1592 DWORD dwError = ERROR_SUCCESS;
1593 BOOL bResult;
1594 OVERLAPPED Overlapped = {0};
1595 #if 0
1596 LPCWSTR lpLogStrings[3];
1597 WCHAR szBuffer1[20];
1598 WCHAR szBuffer2[20];
1599 #endif
1600
1601 DPRINT("ScmWaitForServiceConnect()\n");
1602
1603 Overlapped.hEvent = (HANDLE)NULL;
1604
1605 bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
1606 &Overlapped);
1607 if (bResult == FALSE)
1608 {
1609 DPRINT("ConnectNamedPipe() returned FALSE\n");
1610
1611 dwError = GetLastError();
1612 if (dwError == ERROR_IO_PENDING)
1613 {
1614 DPRINT("dwError: ERROR_IO_PENDING\n");
1615
1616 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1617 PipeTimeout);
1618 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1619
1620 if (dwError == WAIT_TIMEOUT)
1621 {
1622 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1623
1624 bResult = CancelIo(Service->lpImage->hControlPipe);
1625 if (bResult == FALSE)
1626 {
1627 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1628 }
1629
1630 #if 0
1631 _ultow(PipeTimeout, szBuffer1, 10);
1632 lpLogStrings[0] = Service->lpDisplayName;
1633 lpLogStrings[1] = szBuffer1;
1634
1635 ScmLogEvent(EVENT_CONNECTION_TIMEOUT,
1636 EVENTLOG_ERROR_TYPE,
1637 2,
1638 lpLogStrings);
1639 #endif
1640 DPRINT1("Log EVENT_CONNECTION_TIMEOUT by %S\n", Service->lpDisplayName);
1641
1642 return ERROR_SERVICE_REQUEST_TIMEOUT;
1643 }
1644 else if (dwError == WAIT_OBJECT_0)
1645 {
1646 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1647 &Overlapped,
1648 &dwRead,
1649 TRUE);
1650 if (bResult == FALSE)
1651 {
1652 dwError = GetLastError();
1653 DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
1654
1655 return dwError;
1656 }
1657 }
1658 }
1659 else if (dwError != ERROR_PIPE_CONNECTED)
1660 {
1661 DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
1662 return dwError;
1663 }
1664 }
1665
1666 DPRINT("Control pipe connected!\n");
1667
1668 Overlapped.hEvent = (HANDLE) NULL;
1669
1670 /* Read the process id from pipe */
1671 bResult = ReadFile(Service->lpImage->hControlPipe,
1672 (LPVOID)&dwProcessId,
1673 sizeof(DWORD),
1674 &dwRead,
1675 &Overlapped);
1676 if (bResult == FALSE)
1677 {
1678 DPRINT("ReadFile() returned FALSE\n");
1679
1680 dwError = GetLastError();
1681 if (dwError == ERROR_IO_PENDING)
1682 {
1683 DPRINT("dwError: ERROR_IO_PENDING\n");
1684
1685 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1686 PipeTimeout);
1687 if (dwError == WAIT_TIMEOUT)
1688 {
1689 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1690
1691 bResult = CancelIo(Service->lpImage->hControlPipe);
1692 if (bResult == FALSE)
1693 {
1694 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1695 }
1696
1697 #if 0
1698 _ultow(PipeTimeout, szBuffer1, 10);
1699 lpLogStrings[0] = szBuffer1;
1700
1701 ScmLogEvent(EVENT_READFILE_TIMEOUT,
1702 EVENTLOG_ERROR_TYPE,
1703 1,
1704 lpLogStrings);
1705 #endif
1706 DPRINT1("Log EVENT_READFILE_TIMEOUT by %S\n", Service->lpDisplayName);
1707
1708 return ERROR_SERVICE_REQUEST_TIMEOUT;
1709 }
1710 else if (dwError == WAIT_OBJECT_0)
1711 {
1712 DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
1713
1714 DPRINT("Process Id: %lu\n", dwProcessId);
1715
1716 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1717 &Overlapped,
1718 &dwRead,
1719 TRUE);
1720 if (bResult == FALSE)
1721 {
1722 dwError = GetLastError();
1723 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1724
1725 return dwError;
1726 }
1727 }
1728 else
1729 {
1730 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1731 }
1732 }
1733 else
1734 {
1735 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1736 return dwError;
1737 }
1738 }
1739
1740 if (dwProcessId != Service->lpImage->dwProcessId)
1741 {
1742 #if 0
1743 _ultow(Service->lpImage->dwProcessId, szBuffer1, 10);
1744 _ultow(dwProcessId, szBuffer2, 10);
1745
1746 lpLogStrings[0] = Service->lpDisplayName;
1747 lpLogStrings[1] = szBuffer1;
1748 lpLogStrings[2] = szBuffer2;
1749
1750 ScmLogEvent(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
1751 EVENTLOG_WARNING_TYPE,
1752 3,
1753 lpLogStrings);
1754 #endif
1755
1756 DPRINT1("Log EVENT_SERVICE_DIFFERENT_PID_CONNECTED by %S\n", Service->lpDisplayName);
1757 }
1758
1759 DPRINT("ScmWaitForServiceConnect() done\n");
1760
1761 return ERROR_SUCCESS;
1762 }
1763
1764
1765 static DWORD
1766 ScmStartUserModeService(PSERVICE Service,
1767 DWORD argc,
1768 LPWSTR* argv)
1769 {
1770 PROCESS_INFORMATION ProcessInformation;
1771 STARTUPINFOW StartupInfo;
1772 LPVOID lpEnvironment;
1773 BOOL Result;
1774 DWORD dwError = ERROR_SUCCESS;
1775
1776 DPRINT("ScmStartUserModeService(%p)\n", Service);
1777
1778 /* If the image is already running ... */
1779 if (Service->lpImage->dwImageRunCount > 1)
1780 {
1781 /* ... just send a start command */
1782 return ScmSendStartCommand(Service, argc, argv);
1783 }
1784
1785 /* Otherwise start its process */
1786 ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1787 StartupInfo.cb = sizeof(StartupInfo);
1788 ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1789
1790 if (Service->lpImage->hToken)
1791 {
1792 /* User token: Run the service under the user account */
1793
1794 if (!CreateEnvironmentBlock(&lpEnvironment, Service->lpImage->hToken, FALSE))
1795 {
1796 /* We failed, run the service with the current environment */
1797 DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
1798 GetLastError(), Service->lpServiceName);
1799 lpEnvironment = NULL;
1800 }
1801
1802 /* Impersonate the new user */
1803 Result = ImpersonateLoggedOnUser(Service->lpImage->hToken);
1804 if (Result)
1805 {
1806 /* Launch the process in the user's logon session */
1807 Result = CreateProcessAsUserW(Service->lpImage->hToken,
1808 NULL,
1809 Service->lpImage->pszImagePath,
1810 NULL,
1811 NULL,
1812 FALSE,
1813 CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1814 lpEnvironment,
1815 NULL,
1816 &StartupInfo,
1817 &ProcessInformation);
1818 if (!Result)
1819 dwError = GetLastError();
1820
1821 /* Revert the impersonation */
1822 RevertToSelf();
1823 }
1824 else
1825 {
1826 dwError = GetLastError();
1827 DPRINT1("ImpersonateLoggedOnUser() failed with error %d\n", dwError);
1828 }
1829 }
1830 else
1831 {
1832 /* No user token: Run the service under the LocalSystem account */
1833
1834 if (!CreateEnvironmentBlock(&lpEnvironment, NULL, TRUE))
1835 {
1836 /* We failed, run the service with the current environment */
1837 DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
1838 GetLastError(), Service->lpServiceName);
1839 lpEnvironment = NULL;
1840 }
1841
1842 /* Use the interactive desktop if the service is interactive */
1843 if ((NoInteractiveServices == 0) &&
1844 (Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
1845 {
1846 StartupInfo.dwFlags |= STARTF_INHERITDESKTOP;
1847 StartupInfo.lpDesktop = L"WinSta0\\Default";
1848 }
1849
1850 Result = CreateProcessW(NULL,
1851 Service->lpImage->pszImagePath,
1852 NULL,
1853 NULL,
1854 FALSE,
1855 CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1856 lpEnvironment,
1857 NULL,
1858 &StartupInfo,
1859 &ProcessInformation);
1860 if (!Result)
1861 dwError = GetLastError();
1862 }
1863
1864 if (lpEnvironment)
1865 DestroyEnvironmentBlock(lpEnvironment);
1866
1867 if (!Result)
1868 {
1869 DPRINT1("Starting '%S' failed with error %d\n",
1870 Service->lpServiceName, dwError);
1871 return dwError;
1872 }
1873
1874 DPRINT("Process Id: %lu Handle %p\n",
1875 ProcessInformation.dwProcessId,
1876 ProcessInformation.hProcess);
1877 DPRINT("Thread Id: %lu Handle %p\n",
1878 ProcessInformation.dwThreadId,
1879 ProcessInformation.hThread);
1880
1881 /* Get the process handle and ID */
1882 Service->lpImage->hProcess = ProcessInformation.hProcess;
1883 Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1884
1885 /* Resume the main thread and close its handle */
1886 ResumeThread(ProcessInformation.hThread);
1887 CloseHandle(ProcessInformation.hThread);
1888
1889 /* Connect control pipe */
1890 dwError = ScmWaitForServiceConnect(Service);
1891 if (dwError != ERROR_SUCCESS)
1892 {
1893 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1894 Service->lpImage->dwProcessId = 0;
1895 return dwError;
1896 }
1897
1898 /* Send the start command */
1899 return ScmSendStartCommand(Service, argc, argv);
1900 }
1901
1902
1903 static DWORD
1904 ScmLoadService(PSERVICE Service,
1905 DWORD argc,
1906 LPWSTR* argv)
1907 {
1908 PSERVICE_GROUP Group = Service->lpGroup;
1909 DWORD dwError = ERROR_SUCCESS;
1910 LPCWSTR lpLogStrings[2];
1911 WCHAR szLogBuffer[80];
1912
1913 DPRINT("ScmLoadService() called\n");
1914 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1915
1916 if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1917 {
1918 DPRINT("Service %S is already running!\n", Service->lpServiceName);
1919 return ERROR_SERVICE_ALREADY_RUNNING;
1920 }
1921
1922 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1923
1924 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1925 {
1926 /* Start the driver */
1927 dwError = ScmStartDriver(Service);
1928 }
1929 else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1930 {
1931 /* Start user-mode service */
1932 dwError = ScmCreateOrReferenceServiceImage(Service);
1933 if (dwError == ERROR_SUCCESS)
1934 {
1935 dwError = ScmStartUserModeService(Service, argc, argv);
1936 if (dwError == ERROR_SUCCESS)
1937 {
1938 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1939 Service->Status.dwControlsAccepted = 0;
1940 }
1941 else
1942 {
1943 Service->lpImage->dwImageRunCount--;
1944 if (Service->lpImage->dwImageRunCount == 0)
1945 {
1946 ScmRemoveServiceImage(Service->lpImage);
1947 Service->lpImage = NULL;
1948 }
1949 }
1950 }
1951 }
1952
1953 DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1954
1955 if (dwError == ERROR_SUCCESS)
1956 {
1957 if (Group != NULL)
1958 {
1959 Group->ServicesRunning = TRUE;
1960 }
1961
1962 /* Log a successful service start */
1963 LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
1964 lpLogStrings[0] = Service->lpDisplayName;
1965 lpLogStrings[1] = szLogBuffer;
1966
1967 ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
1968 EVENTLOG_INFORMATION_TYPE,
1969 2,
1970 lpLogStrings);
1971 }
1972 else
1973 {
1974 if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1975 {
1976 /* Log a failed service start */
1977 StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
1978 L"%lu", dwError);
1979 lpLogStrings[0] = Service->lpServiceName;
1980 lpLogStrings[1] = szLogBuffer;
1981 ScmLogEvent(EVENT_SERVICE_START_FAILED,
1982 EVENTLOG_ERROR_TYPE,
1983 2,
1984 lpLogStrings);
1985 }
1986
1987 #if 0
1988 switch (Service->dwErrorControl)
1989 {
1990 case SERVICE_ERROR_SEVERE:
1991 if (IsLastKnownGood == FALSE)
1992 {
1993 /* FIXME: Boot last known good configuration */
1994 }
1995 break;
1996
1997 case SERVICE_ERROR_CRITICAL:
1998 if (IsLastKnownGood == FALSE)
1999 {
2000 /* FIXME: Boot last known good configuration */
2001 }
2002 else
2003 {
2004 /* FIXME: BSOD! */
2005 }
2006 break;
2007 }
2008 #endif
2009 }
2010
2011 return dwError;
2012 }
2013
2014
2015 DWORD
2016 ScmStartService(PSERVICE Service,
2017 DWORD argc,
2018 LPWSTR* argv)
2019 {
2020 DWORD dwError = ERROR_SUCCESS;
2021 SC_RPC_LOCK Lock = NULL;
2022
2023 DPRINT("ScmStartService() called\n");
2024 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
2025
2026 /* Acquire the service control critical section, to synchronize starts */
2027 EnterCriticalSection(&ControlServiceCriticalSection);
2028
2029 /*
2030 * Acquire the user service start lock while the service is starting, if
2031 * needed (i.e. if we are not starting it during the initialization phase).
2032 * If we don't success, bail out.
2033 */
2034 if (!ScmInitialize)
2035 {
2036 dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
2037 if (dwError != ERROR_SUCCESS) goto done;
2038 }
2039
2040 /* Really start the service */
2041 dwError = ScmLoadService(Service, argc, argv);
2042
2043 /* Release the service start lock, if needed, and the critical section */
2044 if (Lock) ScmReleaseServiceStartLock(&Lock);
2045
2046 done:
2047 LeaveCriticalSection(&ControlServiceCriticalSection);
2048
2049 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
2050
2051 return dwError;
2052 }
2053
2054
2055 VOID
2056 ScmAutoStartServices(VOID)
2057 {
2058 DWORD dwError;
2059 PLIST_ENTRY GroupEntry;
2060 PLIST_ENTRY ServiceEntry;
2061 PSERVICE_GROUP CurrentGroup;
2062 PSERVICE CurrentService;
2063 WCHAR szSafeBootServicePath[MAX_PATH];
2064 DWORD SafeBootEnabled;
2065 HKEY hKey;
2066 DWORD dwKeySize;
2067 ULONG i;
2068
2069 /*
2070 * This function MUST be called ONLY at initialization time.
2071 * Therefore, no need to acquire the user service start lock.
2072 */
2073 ASSERT(ScmInitialize);
2074
2075 /* Retrieve the SafeBoot parameter */
2076 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2077 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
2078 0,
2079 KEY_READ,
2080 &hKey);
2081 if (dwError == ERROR_SUCCESS)
2082 {
2083 dwKeySize = sizeof(SafeBootEnabled);
2084 dwError = RegQueryValueExW(hKey,
2085 L"OptionValue",
2086 0,
2087 NULL,
2088 (LPBYTE)&SafeBootEnabled,
2089 &dwKeySize);
2090 RegCloseKey(hKey);
2091 }
2092
2093 /* Default to Normal boot if the value doesn't exist */
2094 if (dwError != ERROR_SUCCESS)
2095 SafeBootEnabled = 0;
2096
2097 /* Acquire the service control critical section, to synchronize starts */
2098 EnterCriticalSection(&ControlServiceCriticalSection);
2099
2100 /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
2101 ServiceEntry = ServiceListHead.Flink;
2102 while (ServiceEntry != &ServiceListHead)
2103 {
2104 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2105
2106 /* Build the safe boot path */
2107 StringCchCopyW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2108 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
2109
2110 switch (SafeBootEnabled)
2111 {
2112 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
2113 case 1:
2114 case 3:
2115 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2116 L"\\Minimal\\");
2117 break;
2118
2119 case 2:
2120 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2121 L"\\Network\\");
2122 break;
2123 }
2124
2125 if (SafeBootEnabled != 0)
2126 {
2127 /* If key does not exist then do not assume safe mode */
2128 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2129 szSafeBootServicePath,
2130 0,
2131 KEY_READ,
2132 &hKey);
2133 if (dwError == ERROR_SUCCESS)
2134 {
2135 RegCloseKey(hKey);
2136
2137 /* Finish Safe Boot path off */
2138 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2139 CurrentService->lpServiceName);
2140
2141 /* Check that the key is in the Safe Boot path */
2142 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2143 szSafeBootServicePath,
2144 0,
2145 KEY_READ,
2146 &hKey);
2147 if (dwError != ERROR_SUCCESS)
2148 {
2149 /* Mark service as visited so it is not auto-started */
2150 CurrentService->ServiceVisited = TRUE;
2151 }
2152 else
2153 {
2154 /* Must be auto-started in safe mode - mark as unvisited */
2155 RegCloseKey(hKey);
2156 CurrentService->ServiceVisited = FALSE;
2157 }
2158 }
2159 else
2160 {
2161 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
2162 CurrentService->ServiceVisited = FALSE;
2163 }
2164 }
2165
2166 ServiceEntry = ServiceEntry->Flink;
2167 }
2168
2169 /* Start all services which are members of an existing group */
2170 GroupEntry = GroupListHead.Flink;
2171 while (GroupEntry != &GroupListHead)
2172 {
2173 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
2174
2175 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
2176
2177 /* Start all services witch have a valid tag */
2178 for (i = 0; i < CurrentGroup->TagCount; i++)
2179 {
2180 ServiceEntry = ServiceListHead.Flink;
2181 while (ServiceEntry != &ServiceListHead)
2182 {
2183 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2184
2185 if ((CurrentService->lpGroup == CurrentGroup) &&
2186 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2187 (CurrentService->ServiceVisited == FALSE) &&
2188 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
2189 {
2190 CurrentService->ServiceVisited = TRUE;
2191 ScmLoadService(CurrentService, 0, NULL);
2192 }
2193
2194 ServiceEntry = ServiceEntry->Flink;
2195 }
2196 }
2197
2198 /* Start all services which have an invalid tag or which do not have a tag */
2199 ServiceEntry = ServiceListHead.Flink;
2200 while (ServiceEntry != &ServiceListHead)
2201 {
2202 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2203
2204 if ((CurrentService->lpGroup == CurrentGroup) &&
2205 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2206 (CurrentService->ServiceVisited == FALSE))
2207 {
2208 CurrentService->ServiceVisited = TRUE;
2209 ScmLoadService(CurrentService, 0, NULL);
2210 }
2211
2212 ServiceEntry = ServiceEntry->Flink;
2213 }
2214
2215 GroupEntry = GroupEntry->Flink;
2216 }
2217
2218 /* Start all services which are members of any non-existing group */
2219 ServiceEntry = ServiceListHead.Flink;
2220 while (ServiceEntry != &ServiceListHead)
2221 {
2222 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2223
2224 if ((CurrentService->lpGroup != NULL) &&
2225 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2226 (CurrentService->ServiceVisited == FALSE))
2227 {
2228 CurrentService->ServiceVisited = TRUE;
2229 ScmLoadService(CurrentService, 0, NULL);
2230 }
2231
2232 ServiceEntry = ServiceEntry->Flink;
2233 }
2234
2235 /* Start all services which are not a member of any group */
2236 ServiceEntry = ServiceListHead.Flink;
2237 while (ServiceEntry != &ServiceListHead)
2238 {
2239 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2240
2241 if ((CurrentService->lpGroup == NULL) &&
2242 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2243 (CurrentService->ServiceVisited == FALSE))
2244 {
2245 CurrentService->ServiceVisited = TRUE;
2246 ScmLoadService(CurrentService, 0, NULL);
2247 }
2248
2249 ServiceEntry = ServiceEntry->Flink;
2250 }
2251
2252 /* Clear 'ServiceVisited' flag again */
2253 ServiceEntry = ServiceListHead.Flink;
2254 while (ServiceEntry != &ServiceListHead)
2255 {
2256 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2257 CurrentService->ServiceVisited = FALSE;
2258 ServiceEntry = ServiceEntry->Flink;
2259 }
2260
2261 /* Release the critical section */
2262 LeaveCriticalSection(&ControlServiceCriticalSection);
2263 }
2264
2265
2266 VOID
2267 ScmAutoShutdownServices(VOID)
2268 {
2269 PLIST_ENTRY ServiceEntry;
2270 PSERVICE CurrentService;
2271
2272 DPRINT("ScmAutoShutdownServices() called\n");
2273
2274 /* Lock the service database exclusively */
2275 ScmLockDatabaseExclusive();
2276
2277 ServiceEntry = ServiceListHead.Flink;
2278 while (ServiceEntry != &ServiceListHead)
2279 {
2280 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2281
2282 if ((CurrentService->Status.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) &&
2283 (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
2284 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING))
2285 {
2286 /* Send the shutdown notification */
2287 DPRINT("Shutdown service: %S\n", CurrentService->lpServiceName);
2288 ScmControlService(CurrentService->lpImage->hControlPipe,
2289 CurrentService->lpServiceName,
2290 (SERVICE_STATUS_HANDLE)CurrentService,
2291 SERVICE_CONTROL_SHUTDOWN);
2292 }
2293
2294 ServiceEntry = ServiceEntry->Flink;
2295 }
2296
2297 /* Unlock the service database */
2298 ScmUnlockDatabase();
2299
2300 DPRINT("ScmAutoShutdownServices() done\n");
2301 }
2302
2303
2304 BOOL
2305 ScmLockDatabaseExclusive(VOID)
2306 {
2307 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
2308 }
2309
2310
2311 BOOL
2312 ScmLockDatabaseShared(VOID)
2313 {
2314 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
2315 }
2316
2317
2318 VOID
2319 ScmUnlockDatabase(VOID)
2320 {
2321 RtlReleaseResource(&DatabaseLock);
2322 }
2323
2324
2325 VOID
2326 ScmInitNamedPipeCriticalSection(VOID)
2327 {
2328 HKEY hKey;
2329 DWORD dwKeySize;
2330 DWORD dwError;
2331
2332 InitializeCriticalSection(&ControlServiceCriticalSection);
2333
2334 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2335 L"SYSTEM\\CurrentControlSet\\Control",
2336 0,
2337 KEY_READ,
2338 &hKey);
2339 if (dwError == ERROR_SUCCESS)
2340 {
2341 dwKeySize = sizeof(PipeTimeout);
2342 RegQueryValueExW(hKey,
2343 L"ServicesPipeTimeout",
2344 0,
2345 NULL,
2346 (LPBYTE)&PipeTimeout,
2347 &dwKeySize);
2348 RegCloseKey(hKey);
2349 }
2350 }
2351
2352
2353 VOID
2354 ScmDeleteNamedPipeCriticalSection(VOID)
2355 {
2356 DeleteCriticalSection(&ControlServiceCriticalSection);
2357 }
2358
2359 /* EOF */