[NTVDM]
[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 #define NDEBUG
18 #include <debug.h>
19
20 /*
21 * Uncomment the line below to start services
22 * using the SERVICE_START_PENDING state.
23 */
24 #define USE_SERVICE_START_PENDING
25
26 /*
27 * Uncomment the line below to use asynchronous IO operations
28 * on the service control pipes.
29 */
30 // #define USE_ASYNCHRONOUS_IO
31
32
33 /* GLOBALS *******************************************************************/
34
35 LIST_ENTRY ImageListHead;
36 LIST_ENTRY ServiceListHead;
37
38 static RTL_RESOURCE DatabaseLock;
39 static DWORD ResumeCount = 1;
40
41 /* The critical section synchronizes service control requests */
42 static CRITICAL_SECTION ControlServiceCriticalSection;
43 static DWORD PipeTimeout = 30000; /* 30 Seconds */
44
45
46 /* FUNCTIONS *****************************************************************/
47
48 static DWORD
49 ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage)
50 {
51 WCHAR szControlPipeName[MAX_PATH + 1];
52 HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
53 DWORD ServiceCurrent = 0;
54 DWORD KeyDisposition;
55 DWORD dwKeySize;
56 DWORD dwError;
57
58 /* Get the service number */
59 /* TODO: Create registry entry with correct write access */
60 dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
61 L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
62 REG_OPTION_VOLATILE,
63 KEY_WRITE | KEY_READ,
64 NULL,
65 &hServiceCurrentKey,
66 &KeyDisposition);
67 if (dwError != ERROR_SUCCESS)
68 {
69 DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
70 return dwError;
71 }
72
73 if (KeyDisposition == REG_OPENED_EXISTING_KEY)
74 {
75 dwKeySize = sizeof(DWORD);
76 dwError = RegQueryValueExW(hServiceCurrentKey,
77 L"", 0, NULL, (BYTE*)&ServiceCurrent, &dwKeySize);
78
79 if (dwError != ERROR_SUCCESS)
80 {
81 RegCloseKey(hServiceCurrentKey);
82 DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
83 return dwError;
84 }
85
86 ServiceCurrent++;
87 }
88
89 dwError = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
90
91 RegCloseKey(hServiceCurrentKey);
92
93 if (dwError != ERROR_SUCCESS)
94 {
95 DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
96 return dwError;
97 }
98
99 /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
100 swprintf(szControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
101
102 DPRINT("PipeName: %S\n", szControlPipeName);
103
104 pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
105 #ifdef USE_ASYNCHRONOUS_IO
106 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
107 #else
108 PIPE_ACCESS_DUPLEX,
109 #endif
110 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
111 100,
112 8000,
113 4,
114 PipeTimeout,
115 NULL);
116 DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
117 if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
118 {
119 DPRINT1("Failed to create control pipe!\n");
120 return GetLastError();
121 }
122
123 return ERROR_SUCCESS;
124 }
125
126
127 static PSERVICE_IMAGE
128 ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
129 {
130 PLIST_ENTRY ImageEntry;
131 PSERVICE_IMAGE CurrentImage;
132
133 DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
134
135 ImageEntry = ImageListHead.Flink;
136 while (ImageEntry != &ImageListHead)
137 {
138 CurrentImage = CONTAINING_RECORD(ImageEntry,
139 SERVICE_IMAGE,
140 ImageListEntry);
141 if (_wcsicmp(CurrentImage->szImagePath, lpImagePath) == 0)
142 {
143 DPRINT("Found image: '%S'\n", CurrentImage->szImagePath);
144 return CurrentImage;
145 }
146
147 ImageEntry = ImageEntry->Flink;
148 }
149
150 DPRINT("Couldn't find a matching image\n");
151
152 return NULL;
153
154 }
155
156
157 static DWORD
158 ScmCreateOrReferenceServiceImage(PSERVICE pService)
159 {
160 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
161 UNICODE_STRING ImagePath;
162 PSERVICE_IMAGE pServiceImage = NULL;
163 NTSTATUS Status;
164 DWORD dwError = ERROR_SUCCESS;
165
166 DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
167
168 RtlInitUnicodeString(&ImagePath, NULL);
169
170 /* Get service data */
171 RtlZeroMemory(&QueryTable,
172 sizeof(QueryTable));
173
174 QueryTable[0].Name = L"ImagePath";
175 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
176 QueryTable[0].EntryContext = &ImagePath;
177
178 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
179 pService->lpServiceName,
180 QueryTable,
181 NULL,
182 NULL);
183 if (!NT_SUCCESS(Status))
184 {
185 DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
186 return RtlNtStatusToDosError(Status);
187 }
188
189 DPRINT("ImagePath: '%wZ'\n", &ImagePath);
190
191 pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
192 if (pServiceImage == NULL)
193 {
194 /* Create a new service image */
195 pServiceImage = HeapAlloc(GetProcessHeap(),
196 HEAP_ZERO_MEMORY,
197 FIELD_OFFSET(SERVICE_IMAGE, szImagePath[ImagePath.Length / sizeof(WCHAR) + 1]));
198 if (pServiceImage == NULL)
199 {
200 dwError = ERROR_NOT_ENOUGH_MEMORY;
201 goto done;
202 }
203
204 pServiceImage->dwImageRunCount = 1;
205 pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
206
207 /* Set the image path */
208 wcscpy(pServiceImage->szImagePath,
209 ImagePath.Buffer);
210
211 RtlFreeUnicodeString(&ImagePath);
212
213 /* Create the control pipe */
214 dwError = ScmCreateNewControlPipe(pServiceImage);
215 if (dwError != ERROR_SUCCESS)
216 {
217 HeapFree(GetProcessHeap(), 0, pServiceImage);
218 goto done;
219 }
220
221 /* FIXME: Add more initialization code here */
222
223
224 /* Append service record */
225 InsertTailList(&ImageListHead,
226 &pServiceImage->ImageListEntry);
227 }
228 else
229 {
230 /* Increment the run counter */
231 pServiceImage->dwImageRunCount++;
232 }
233
234 DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
235
236 /* Link the service image to the service */
237 pService->lpImage = pServiceImage;
238
239 done:;
240 RtlFreeUnicodeString(&ImagePath);
241
242 DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
243
244 return dwError;
245 }
246
247
248 static VOID
249 ScmDereferenceServiceImage(PSERVICE_IMAGE pServiceImage)
250 {
251 DPRINT1("ScmDereferenceServiceImage() called\n");
252
253 pServiceImage->dwImageRunCount--;
254
255 if (pServiceImage->dwImageRunCount == 0)
256 {
257 DPRINT1("dwImageRunCount == 0\n");
258
259 /* FIXME: Terminate the process */
260
261 /* Remove the service image from the list */
262 RemoveEntryList(&pServiceImage->ImageListEntry);
263
264 /* Close the control pipe */
265 if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
266 CloseHandle(pServiceImage->hControlPipe);
267
268 /* Release the service image */
269 HeapFree(GetProcessHeap(), 0, pServiceImage);
270 }
271 }
272
273
274 PSERVICE
275 ScmGetServiceEntryByName(LPCWSTR lpServiceName)
276 {
277 PLIST_ENTRY ServiceEntry;
278 PSERVICE CurrentService;
279
280 DPRINT("ScmGetServiceEntryByName() called\n");
281
282 ServiceEntry = ServiceListHead.Flink;
283 while (ServiceEntry != &ServiceListHead)
284 {
285 CurrentService = CONTAINING_RECORD(ServiceEntry,
286 SERVICE,
287 ServiceListEntry);
288 if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
289 {
290 DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
291 return CurrentService;
292 }
293
294 ServiceEntry = ServiceEntry->Flink;
295 }
296
297 DPRINT("Couldn't find a matching service\n");
298
299 return NULL;
300 }
301
302
303 PSERVICE
304 ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
305 {
306 PLIST_ENTRY ServiceEntry;
307 PSERVICE CurrentService;
308
309 DPRINT("ScmGetServiceEntryByDisplayName() called\n");
310
311 ServiceEntry = ServiceListHead.Flink;
312 while (ServiceEntry != &ServiceListHead)
313 {
314 CurrentService = CONTAINING_RECORD(ServiceEntry,
315 SERVICE,
316 ServiceListEntry);
317 if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
318 {
319 DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
320 return CurrentService;
321 }
322
323 ServiceEntry = ServiceEntry->Flink;
324 }
325
326 DPRINT("Couldn't find a matching service\n");
327
328 return NULL;
329 }
330
331
332 PSERVICE
333 ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
334 {
335 PLIST_ENTRY ServiceEntry;
336 PSERVICE CurrentService;
337
338 DPRINT("ScmGetServiceEntryByResumeCount() called\n");
339
340 ServiceEntry = ServiceListHead.Flink;
341 while (ServiceEntry != &ServiceListHead)
342 {
343 CurrentService = CONTAINING_RECORD(ServiceEntry,
344 SERVICE,
345 ServiceListEntry);
346 if (CurrentService->dwResumeCount > dwResumeCount)
347 {
348 DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
349 return CurrentService;
350 }
351
352 ServiceEntry = ServiceEntry->Flink;
353 }
354
355 DPRINT("Couldn't find a matching service\n");
356
357 return NULL;
358 }
359
360
361 DWORD
362 ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
363 PSERVICE* lpServiceRecord)
364 {
365 PSERVICE lpService = NULL;
366
367 DPRINT("Service: '%S'\n", lpServiceName);
368
369 /* Allocate service entry */
370 lpService = HeapAlloc(GetProcessHeap(),
371 HEAP_ZERO_MEMORY,
372 FIELD_OFFSET(SERVICE, szServiceName[wcslen(lpServiceName) + 1]));
373 if (lpService == NULL)
374 return ERROR_NOT_ENOUGH_MEMORY;
375
376 *lpServiceRecord = lpService;
377
378 /* Copy service name */
379 wcscpy(lpService->szServiceName, lpServiceName);
380 lpService->lpServiceName = lpService->szServiceName;
381 lpService->lpDisplayName = lpService->lpServiceName;
382
383 /* Set the resume count */
384 lpService->dwResumeCount = ResumeCount++;
385
386 /* Append service record */
387 InsertTailList(&ServiceListHead,
388 &lpService->ServiceListEntry);
389
390 /* Initialize the service status */
391 lpService->Status.dwCurrentState = SERVICE_STOPPED;
392 lpService->Status.dwControlsAccepted = 0;
393 lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
394 lpService->Status.dwServiceSpecificExitCode = 0;
395 lpService->Status.dwCheckPoint = 0;
396 lpService->Status.dwWaitHint = 2000; /* 2 seconds */
397
398 return ERROR_SUCCESS;
399 }
400
401
402 VOID
403 ScmDeleteServiceRecord(PSERVICE lpService)
404 {
405 DPRINT("Deleting Service %S\n", lpService->lpServiceName);
406
407 /* Delete the display name */
408 if (lpService->lpDisplayName != NULL &&
409 lpService->lpDisplayName != lpService->lpServiceName)
410 HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
411
412 /* Dereference the service image */
413 if (lpService->lpImage)
414 ScmDereferenceServiceImage(lpService->lpImage);
415
416 /* Decrement the group reference counter */
417 if (lpService->lpGroup)
418 lpService->lpGroup->dwRefCount--;
419
420 /* FIXME: SecurityDescriptor */
421
422
423 /* Remove the Service from the List */
424 RemoveEntryList(&lpService->ServiceListEntry);
425
426 DPRINT("Deleted Service %S\n", lpService->lpServiceName);
427
428 /* Delete the service record */
429 HeapFree(GetProcessHeap(), 0, lpService);
430
431 DPRINT("Done\n");
432 }
433
434
435 static DWORD
436 CreateServiceListEntry(LPCWSTR lpServiceName,
437 HKEY hServiceKey)
438 {
439 PSERVICE lpService = NULL;
440 LPWSTR lpDisplayName = NULL;
441 LPWSTR lpGroup = NULL;
442 DWORD dwSize;
443 DWORD dwError;
444 DWORD dwServiceType;
445 DWORD dwStartType;
446 DWORD dwErrorControl;
447 DWORD dwTagId;
448
449 DPRINT("Service: '%S'\n", lpServiceName);
450 if (*lpServiceName == L'{')
451 return ERROR_SUCCESS;
452
453 dwSize = sizeof(DWORD);
454 dwError = RegQueryValueExW(hServiceKey,
455 L"Type",
456 NULL,
457 NULL,
458 (LPBYTE)&dwServiceType,
459 &dwSize);
460 if (dwError != ERROR_SUCCESS)
461 return ERROR_SUCCESS;
462
463 if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
464 ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
465 (dwServiceType != SERVICE_KERNEL_DRIVER) &&
466 (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
467 return ERROR_SUCCESS;
468
469 DPRINT("Service type: %lx\n", dwServiceType);
470
471 dwSize = sizeof(DWORD);
472 dwError = RegQueryValueExW(hServiceKey,
473 L"Start",
474 NULL,
475 NULL,
476 (LPBYTE)&dwStartType,
477 &dwSize);
478 if (dwError != ERROR_SUCCESS)
479 return ERROR_SUCCESS;
480
481 DPRINT("Start type: %lx\n", dwStartType);
482
483 dwSize = sizeof(DWORD);
484 dwError = RegQueryValueExW(hServiceKey,
485 L"ErrorControl",
486 NULL,
487 NULL,
488 (LPBYTE)&dwErrorControl,
489 &dwSize);
490 if (dwError != ERROR_SUCCESS)
491 return ERROR_SUCCESS;
492
493 DPRINT("Error control: %lx\n", dwErrorControl);
494
495 dwError = RegQueryValueExW(hServiceKey,
496 L"Tag",
497 NULL,
498 NULL,
499 (LPBYTE)&dwTagId,
500 &dwSize);
501 if (dwError != ERROR_SUCCESS)
502 dwTagId = 0;
503
504 DPRINT("Tag: %lx\n", dwTagId);
505
506 dwError = ScmReadString(hServiceKey,
507 L"Group",
508 &lpGroup);
509 if (dwError != ERROR_SUCCESS)
510 lpGroup = NULL;
511
512 DPRINT("Group: %S\n", lpGroup);
513
514 dwError = ScmReadString(hServiceKey,
515 L"DisplayName",
516 &lpDisplayName);
517 if (dwError != ERROR_SUCCESS)
518 lpDisplayName = NULL;
519
520 DPRINT("Display name: %S\n", lpDisplayName);
521
522 dwError = ScmCreateNewServiceRecord(lpServiceName,
523 &lpService);
524 if (dwError != ERROR_SUCCESS)
525 goto done;
526
527 lpService->Status.dwServiceType = dwServiceType;
528 lpService->dwStartType = dwStartType;
529 lpService->dwErrorControl = dwErrorControl;
530 lpService->dwTag = dwTagId;
531
532 if (lpGroup != NULL)
533 {
534 dwError = ScmSetServiceGroup(lpService, lpGroup);
535 if (dwError != ERROR_SUCCESS)
536 goto done;
537 }
538
539 if (lpDisplayName != NULL)
540 {
541 lpService->lpDisplayName = lpDisplayName;
542 lpDisplayName = NULL;
543 }
544
545 DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
546 if (lpService->lpGroup != NULL)
547 {
548 DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
549 }
550 DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
551 lpService->dwStartType,
552 lpService->Status.dwServiceType,
553 lpService->dwTag,
554 lpService->dwErrorControl);
555
556 if (ScmIsDeleteFlagSet(hServiceKey))
557 lpService->bDeleted = TRUE;
558
559 done:;
560 if (lpGroup != NULL)
561 HeapFree(GetProcessHeap(), 0, lpGroup);
562
563 if (lpDisplayName != NULL)
564 HeapFree(GetProcessHeap(), 0, lpDisplayName);
565
566 if (lpService != NULL)
567 {
568 if (lpService->lpImage != NULL)
569 ScmDereferenceServiceImage(lpService->lpImage);
570 }
571
572 return dwError;
573 }
574
575
576 DWORD
577 ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
578 {
579 DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
580 WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
581 HKEY hSubKey = 0;
582
583 dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
584 if (!dwRet)
585 {
586 /* Find the maximum subkey length so that we can allocate a buffer */
587 dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
588 &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
589 if (!dwRet)
590 {
591 dwMaxSubkeyLen++;
592 if (dwMaxSubkeyLen > sizeof(szNameBuf) / sizeof(WCHAR))
593 {
594 /* Name too big: alloc a buffer for it */
595 lpszName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxSubkeyLen * sizeof(WCHAR));
596 }
597
598 if (!lpszName)
599 dwRet = ERROR_NOT_ENOUGH_MEMORY;
600 else
601 {
602 while (dwRet == ERROR_SUCCESS)
603 {
604 dwSize = dwMaxSubkeyLen;
605 dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
606 if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
607 dwRet = ScmDeleteRegKey(hSubKey, lpszName);
608 }
609 if (dwRet == ERROR_NO_MORE_ITEMS)
610 dwRet = ERROR_SUCCESS;
611
612 if (lpszName != szNameBuf)
613 HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
614 }
615 }
616
617 RegCloseKey(hSubKey);
618 if (!dwRet)
619 dwRet = RegDeleteKeyW(hKey, lpszSubKey);
620 }
621 return dwRet;
622 }
623
624
625 VOID
626 ScmDeleteMarkedServices(VOID)
627 {
628 PLIST_ENTRY ServiceEntry;
629 PSERVICE CurrentService;
630 HKEY hServicesKey;
631 DWORD dwError;
632
633 ServiceEntry = ServiceListHead.Flink;
634 while (ServiceEntry != &ServiceListHead)
635 {
636 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
637
638 ServiceEntry = ServiceEntry->Flink;
639
640 if (CurrentService->bDeleted == TRUE)
641 {
642 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
643 L"System\\CurrentControlSet\\Services",
644 0,
645 DELETE,
646 &hServicesKey);
647 if (dwError == ERROR_SUCCESS)
648 {
649 dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
650 RegCloseKey(hServicesKey);
651 if (dwError == ERROR_SUCCESS)
652 {
653 RemoveEntryList(&CurrentService->ServiceListEntry);
654 HeapFree(GetProcessHeap(), 0, CurrentService);
655 }
656 }
657
658 if (dwError != ERROR_SUCCESS)
659 DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
660 }
661 }
662 }
663
664
665 DWORD
666 ScmCreateServiceDatabase(VOID)
667 {
668 WCHAR szSubKey[MAX_PATH];
669 HKEY hServicesKey;
670 HKEY hServiceKey;
671 DWORD dwSubKey;
672 DWORD dwSubKeyLength;
673 FILETIME ftLastChanged;
674 DWORD dwError;
675
676 DPRINT("ScmCreateServiceDatabase() called\n");
677
678 dwError = ScmCreateGroupList();
679 if (dwError != ERROR_SUCCESS)
680 return dwError;
681
682 /* Initialize basic variables */
683 InitializeListHead(&ImageListHead);
684 InitializeListHead(&ServiceListHead);
685
686 /* Initialize the database lock */
687 RtlInitializeResource(&DatabaseLock);
688
689 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
690 L"System\\CurrentControlSet\\Services",
691 0,
692 KEY_READ,
693 &hServicesKey);
694 if (dwError != ERROR_SUCCESS)
695 return dwError;
696
697 dwSubKey = 0;
698 for (;;)
699 {
700 dwSubKeyLength = MAX_PATH;
701 dwError = RegEnumKeyExW(hServicesKey,
702 dwSubKey,
703 szSubKey,
704 &dwSubKeyLength,
705 NULL,
706 NULL,
707 NULL,
708 &ftLastChanged);
709 if (dwError == ERROR_SUCCESS &&
710 szSubKey[0] != L'{')
711 {
712 DPRINT("SubKeyName: '%S'\n", szSubKey);
713
714 dwError = RegOpenKeyExW(hServicesKey,
715 szSubKey,
716 0,
717 KEY_READ,
718 &hServiceKey);
719 if (dwError == ERROR_SUCCESS)
720 {
721 dwError = CreateServiceListEntry(szSubKey,
722 hServiceKey);
723
724 RegCloseKey(hServiceKey);
725 }
726 }
727
728 if (dwError != ERROR_SUCCESS)
729 break;
730
731 dwSubKey++;
732 }
733
734 RegCloseKey(hServicesKey);
735
736 /* Wait for the LSA server */
737 ScmWaitForLsa();
738
739 /* Delete services that are marked for delete */
740 ScmDeleteMarkedServices();
741
742 DPRINT("ScmCreateServiceDatabase() done\n");
743
744 return ERROR_SUCCESS;
745 }
746
747
748 VOID
749 ScmShutdownServiceDatabase(VOID)
750 {
751 DPRINT("ScmShutdownServiceDatabase() called\n");
752
753 ScmDeleteMarkedServices();
754 RtlDeleteResource(&DatabaseLock);
755
756 DPRINT("ScmShutdownServiceDatabase() done\n");
757 }
758
759
760 static NTSTATUS
761 ScmCheckDriver(PSERVICE Service)
762 {
763 OBJECT_ATTRIBUTES ObjectAttributes;
764 UNICODE_STRING DirName;
765 HANDLE DirHandle;
766 NTSTATUS Status;
767 POBJECT_DIRECTORY_INFORMATION DirInfo;
768 ULONG BufferLength;
769 ULONG DataLength;
770 ULONG Index;
771
772 DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
773
774 if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
775 {
776 RtlInitUnicodeString(&DirName, L"\\Driver");
777 }
778 else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
779 {
780 ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
781 RtlInitUnicodeString(&DirName, L"\\FileSystem");
782 }
783
784 InitializeObjectAttributes(&ObjectAttributes,
785 &DirName,
786 0,
787 NULL,
788 NULL);
789
790 Status = NtOpenDirectoryObject(&DirHandle,
791 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
792 &ObjectAttributes);
793 if (!NT_SUCCESS(Status))
794 {
795 return Status;
796 }
797
798 BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
799 2 * MAX_PATH * sizeof(WCHAR);
800 DirInfo = HeapAlloc(GetProcessHeap(),
801 HEAP_ZERO_MEMORY,
802 BufferLength);
803
804 Index = 0;
805 while (TRUE)
806 {
807 Status = NtQueryDirectoryObject(DirHandle,
808 DirInfo,
809 BufferLength,
810 TRUE,
811 FALSE,
812 &Index,
813 &DataLength);
814 if (Status == STATUS_NO_MORE_ENTRIES)
815 {
816 /* FIXME: Add current service to 'failed service' list */
817 DPRINT("Service '%S' failed\n", Service->lpServiceName);
818 break;
819 }
820
821 if (!NT_SUCCESS(Status))
822 break;
823
824 DPRINT("Comparing: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
825
826 if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
827 {
828 DPRINT("Found: '%S' '%wZ'\n",
829 Service->lpServiceName, &DirInfo->Name);
830
831 /* Mark service as 'running' */
832 Service->Status.dwCurrentState = SERVICE_RUNNING;
833
834 /* Mark the service group as 'running' */
835 if (Service->lpGroup != NULL)
836 {
837 Service->lpGroup->ServicesRunning = TRUE;
838 }
839
840 break;
841 }
842 }
843
844 HeapFree(GetProcessHeap(),
845 0,
846 DirInfo);
847 NtClose(DirHandle);
848
849 return STATUS_SUCCESS;
850 }
851
852
853 VOID
854 ScmGetBootAndSystemDriverState(VOID)
855 {
856 PLIST_ENTRY ServiceEntry;
857 PSERVICE CurrentService;
858
859 DPRINT("ScmGetBootAndSystemDriverState() called\n");
860
861 ServiceEntry = ServiceListHead.Flink;
862 while (ServiceEntry != &ServiceListHead)
863 {
864 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
865
866 if (CurrentService->dwStartType == SERVICE_BOOT_START ||
867 CurrentService->dwStartType == SERVICE_SYSTEM_START)
868 {
869 /* Check driver */
870 DPRINT(" Checking service: %S\n", CurrentService->lpServiceName);
871
872 ScmCheckDriver(CurrentService);
873 }
874
875 ServiceEntry = ServiceEntry->Flink;
876 }
877
878 DPRINT("ScmGetBootAndSystemDriverState() done\n");
879 }
880
881
882 DWORD
883 ScmControlService(PSERVICE Service,
884 DWORD dwControl)
885 {
886 PSCM_CONTROL_PACKET ControlPacket;
887 SCM_REPLY_PACKET ReplyPacket;
888
889 DWORD dwWriteCount = 0;
890 DWORD dwReadCount = 0;
891 DWORD PacketSize;
892 PWSTR Ptr;
893 DWORD dwError = ERROR_SUCCESS;
894 BOOL bResult;
895 #ifdef USE_ASYNCHRONOUS_IO
896 OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
897 #endif
898
899 DPRINT("ScmControlService() called\n");
900
901 /* Acquire the service control critical section, to synchronize requests */
902 EnterCriticalSection(&ControlServiceCriticalSection);
903
904 /* Calculate the total length of the start command line */
905 PacketSize = sizeof(SCM_CONTROL_PACKET);
906 PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
907
908 ControlPacket = HeapAlloc(GetProcessHeap(),
909 HEAP_ZERO_MEMORY,
910 PacketSize);
911 if (ControlPacket == NULL)
912 {
913 LeaveCriticalSection(&ControlServiceCriticalSection);
914 return ERROR_NOT_ENOUGH_MEMORY;
915 }
916
917 ControlPacket->dwSize = PacketSize;
918 ControlPacket->dwControl = dwControl;
919 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
920
921 ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
922
923 Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
924 wcscpy(Ptr, Service->lpServiceName);
925
926 ControlPacket->dwArgumentsCount = 0;
927 ControlPacket->dwArgumentsOffset = 0;
928
929 #ifdef USE_ASYNCHRONOUS_IO
930 bResult = WriteFile(Service->lpImage->hControlPipe,
931 ControlPacket,
932 PacketSize,
933 &dwWriteCount,
934 &Overlapped);
935 if (bResult == FALSE)
936 {
937 DPRINT1("WriteFile() returned FALSE\n");
938
939 dwError = GetLastError();
940 if (dwError == ERROR_IO_PENDING)
941 {
942 DPRINT1("dwError: ERROR_IO_PENDING\n");
943
944 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
945 PipeTimeout);
946 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
947
948 if (dwError == WAIT_TIMEOUT)
949 {
950 bResult = CancelIo(Service->lpImage->hControlPipe);
951 if (bResult == FALSE)
952 {
953 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
954 }
955
956 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
957 goto Done;
958 }
959 else if (dwError == WAIT_OBJECT_0)
960 {
961 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
962 &Overlapped,
963 &dwWriteCount,
964 TRUE);
965 if (bResult == FALSE)
966 {
967 dwError = GetLastError();
968 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
969
970 goto Done;
971 }
972 }
973 }
974 else
975 {
976 DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
977 goto Done;
978 }
979 }
980
981 /* Read the reply */
982 Overlapped.hEvent = (HANDLE) NULL;
983
984 bResult = ReadFile(Service->lpImage->hControlPipe,
985 &ReplyPacket,
986 sizeof(SCM_REPLY_PACKET),
987 &dwReadCount,
988 &Overlapped);
989 if (bResult == FALSE)
990 {
991 DPRINT1("ReadFile() returned FALSE\n");
992
993 dwError = GetLastError();
994 if (dwError == ERROR_IO_PENDING)
995 {
996 DPRINT1("dwError: ERROR_IO_PENDING\n");
997
998 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
999 PipeTimeout);
1000 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1001
1002 if (dwError == WAIT_TIMEOUT)
1003 {
1004 bResult = CancelIo(Service->lpImage->hControlPipe);
1005 if (bResult == FALSE)
1006 {
1007 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1008 }
1009
1010 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1011 goto Done;
1012 }
1013 else if (dwError == WAIT_OBJECT_0)
1014 {
1015 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1016 &Overlapped,
1017 &dwReadCount,
1018 TRUE);
1019 if (bResult == FALSE)
1020 {
1021 dwError = GetLastError();
1022 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1023
1024 goto Done;
1025 }
1026 }
1027 }
1028 else
1029 {
1030 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1031 goto Done;
1032 }
1033 }
1034
1035 #else
1036 /* Send the control packet */
1037 bResult = WriteFile(Service->lpImage->hControlPipe,
1038 ControlPacket,
1039 PacketSize,
1040 &dwWriteCount,
1041 NULL);
1042 if (bResult == FALSE)
1043 {
1044 dwError = GetLastError();
1045 DPRINT("WriteFile() failed (Error %lu)\n", dwError);
1046
1047 if ((dwError == ERROR_GEN_FAILURE) &&
1048 (dwControl == SERVICE_CONTROL_STOP))
1049 {
1050 /* Service is already terminated */
1051 Service->Status.dwCurrentState = SERVICE_STOPPED;
1052 Service->Status.dwControlsAccepted = 0;
1053 Service->Status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
1054 dwError = ERROR_SUCCESS;
1055 }
1056 goto Done;
1057 }
1058
1059 /* Read the reply */
1060 bResult = ReadFile(Service->lpImage->hControlPipe,
1061 &ReplyPacket,
1062 sizeof(SCM_REPLY_PACKET),
1063 &dwReadCount,
1064 NULL);
1065 if (bResult == FALSE)
1066 {
1067 dwError = GetLastError();
1068 DPRINT("ReadFile() failed (Error %lu)\n", dwError);
1069 }
1070 #endif
1071
1072 Done:
1073 /* Release the contol packet */
1074 HeapFree(GetProcessHeap(),
1075 0,
1076 ControlPacket);
1077
1078 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1079 {
1080 dwError = ReplyPacket.dwError;
1081 }
1082
1083 if (dwError == ERROR_SUCCESS &&
1084 dwControl == SERVICE_CONTROL_STOP)
1085 {
1086 ScmDereferenceServiceImage(Service->lpImage);
1087 }
1088
1089 LeaveCriticalSection(&ControlServiceCriticalSection);
1090
1091 DPRINT("ScmControlService() done\n");
1092
1093 return dwError;
1094 }
1095
1096
1097 static DWORD
1098 ScmSendStartCommand(PSERVICE Service,
1099 DWORD argc,
1100 LPWSTR* argv)
1101 {
1102 PSCM_CONTROL_PACKET ControlPacket;
1103 SCM_REPLY_PACKET ReplyPacket;
1104 DWORD PacketSize;
1105 PWSTR Ptr;
1106 DWORD dwWriteCount = 0;
1107 DWORD dwReadCount = 0;
1108 DWORD dwError = ERROR_SUCCESS;
1109 DWORD i;
1110 PWSTR *pOffPtr;
1111 PWSTR pArgPtr;
1112 BOOL bResult;
1113 #ifdef USE_ASYNCHRONOUS_IO
1114 OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
1115 #endif
1116
1117 DPRINT("ScmSendStartCommand() called\n");
1118
1119 /* Calculate the total length of the start command line */
1120 PacketSize = sizeof(SCM_CONTROL_PACKET) +
1121 (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
1122
1123 /* Calculate the required packet size for the start arguments */
1124 if (argc > 0 && argv != NULL)
1125 {
1126 PacketSize = ALIGN_UP(PacketSize, LPWSTR);
1127
1128 DPRINT("Argc: %lu\n", argc);
1129 for (i = 0; i < argc; i++)
1130 {
1131 DPRINT("Argv[%lu]: %S\n", i, argv[i]);
1132 PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR));
1133 }
1134 }
1135
1136 /* Allocate a control packet */
1137 ControlPacket = HeapAlloc(GetProcessHeap(),
1138 HEAP_ZERO_MEMORY,
1139 PacketSize);
1140 if (ControlPacket == NULL)
1141 return ERROR_NOT_ENOUGH_MEMORY;
1142
1143 ControlPacket->dwSize = PacketSize;
1144 ControlPacket->dwControl = (Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
1145 ? SERVICE_CONTROL_START_OWN
1146 : SERVICE_CONTROL_START_SHARE;
1147 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
1148 ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1149
1150 Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
1151 wcscpy(Ptr, Service->lpServiceName);
1152
1153 ControlPacket->dwArgumentsCount = 0;
1154 ControlPacket->dwArgumentsOffset = 0;
1155
1156 /* Copy argument list */
1157 if (argc > 0 && argv != NULL)
1158 {
1159 Ptr += wcslen(Service->lpServiceName) + 1;
1160 pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
1161 pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
1162
1163 ControlPacket->dwArgumentsCount = argc;
1164 ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
1165
1166 DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
1167 DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
1168
1169 for (i = 0; i < argc; i++)
1170 {
1171 wcscpy(pArgPtr, argv[i]);
1172 *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
1173 DPRINT("offset: %p\n", *pOffPtr);
1174
1175 pArgPtr += wcslen(argv[i]) + 1;
1176 pOffPtr++;
1177 }
1178 }
1179
1180 #ifdef USE_ASYNCHRONOUS_IO
1181 bResult = WriteFile(Service->lpImage->hControlPipe,
1182 ControlPacket,
1183 PacketSize,
1184 &dwWriteCount,
1185 &Overlapped);
1186 if (bResult == FALSE)
1187 {
1188 DPRINT1("WriteFile() returned FALSE\n");
1189
1190 dwError = GetLastError();
1191 if (dwError == ERROR_IO_PENDING)
1192 {
1193 DPRINT1("dwError: ERROR_IO_PENDING\n");
1194
1195 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1196 PipeTimeout);
1197 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1198
1199 if (dwError == WAIT_TIMEOUT)
1200 {
1201 bResult = CancelIo(Service->lpImage->hControlPipe);
1202 if (bResult == FALSE)
1203 {
1204 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1205 }
1206
1207 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1208 goto Done;
1209 }
1210 else if (dwError == WAIT_OBJECT_0)
1211 {
1212 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1213 &Overlapped,
1214 &dwWriteCount,
1215 TRUE);
1216 if (bResult == FALSE)
1217 {
1218 dwError = GetLastError();
1219 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1220
1221 goto Done;
1222 }
1223 }
1224 }
1225 else
1226 {
1227 DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
1228 goto Done;
1229 }
1230 }
1231
1232 /* Read the reply */
1233 Overlapped.hEvent = (HANDLE) NULL;
1234
1235 bResult = ReadFile(Service->lpImage->hControlPipe,
1236 &ReplyPacket,
1237 sizeof(SCM_REPLY_PACKET),
1238 &dwReadCount,
1239 &Overlapped);
1240 if (bResult == FALSE)
1241 {
1242 DPRINT1("ReadFile() returned FALSE\n");
1243
1244 dwError = GetLastError();
1245 if (dwError == ERROR_IO_PENDING)
1246 {
1247 DPRINT1("dwError: ERROR_IO_PENDING\n");
1248
1249 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1250 PipeTimeout);
1251 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1252
1253 if (dwError == WAIT_TIMEOUT)
1254 {
1255 bResult = CancelIo(Service->lpImage->hControlPipe);
1256 if (bResult == FALSE)
1257 {
1258 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1259 }
1260
1261 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1262 goto Done;
1263 }
1264 else if (dwError == WAIT_OBJECT_0)
1265 {
1266 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1267 &Overlapped,
1268 &dwReadCount,
1269 TRUE);
1270 if (bResult == FALSE)
1271 {
1272 dwError = GetLastError();
1273 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1274
1275 goto Done;
1276 }
1277 }
1278 }
1279 else
1280 {
1281 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1282 goto Done;
1283 }
1284 }
1285
1286 #else
1287 /* Send the start command */
1288 bResult = WriteFile(Service->lpImage->hControlPipe,
1289 ControlPacket,
1290 PacketSize,
1291 &dwWriteCount,
1292 NULL);
1293 if (bResult == FALSE)
1294 {
1295 dwError = GetLastError();
1296 DPRINT("WriteFile() failed (Error %lu)\n", dwError);
1297 goto Done;
1298 }
1299
1300 /* Read the reply */
1301 bResult = ReadFile(Service->lpImage->hControlPipe,
1302 &ReplyPacket,
1303 sizeof(SCM_REPLY_PACKET),
1304 &dwReadCount,
1305 NULL);
1306 if (bResult == FALSE)
1307 {
1308 dwError = GetLastError();
1309 DPRINT("ReadFile() failed (Error %lu)\n", dwError);
1310 }
1311 #endif
1312
1313 Done:
1314 /* Release the contol packet */
1315 HeapFree(GetProcessHeap(),
1316 0,
1317 ControlPacket);
1318
1319 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1320 {
1321 dwError = ReplyPacket.dwError;
1322 }
1323
1324 DPRINT("ScmSendStartCommand() done\n");
1325
1326 return dwError;
1327 }
1328
1329
1330 static DWORD
1331 ScmWaitForServiceConnect(PSERVICE Service)
1332 {
1333 DWORD dwRead = 0;
1334 DWORD dwProcessId = 0;
1335 DWORD dwError = ERROR_SUCCESS;
1336 BOOL bResult;
1337 #ifdef USE_ASYNCHRONOUS_IO
1338 OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
1339 #endif
1340
1341 DPRINT("ScmWaitForServiceConnect()\n");
1342
1343 #ifdef USE_ASYNCHRONOUS_IO
1344 Overlapped.hEvent = (HANDLE)NULL;
1345
1346 bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
1347 &Overlapped);
1348 if (bResult == FALSE)
1349 {
1350 DPRINT("ConnectNamedPipe() returned FALSE\n");
1351
1352 dwError = GetLastError();
1353 if (dwError == ERROR_IO_PENDING)
1354 {
1355 DPRINT("dwError: ERROR_IO_PENDING\n");
1356
1357 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1358 PipeTimeout);
1359 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1360
1361 if (dwError == WAIT_TIMEOUT)
1362 {
1363 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1364
1365 bResult = CancelIo(Service->lpImage->hControlPipe);
1366 if (bResult == FALSE)
1367 {
1368 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1369 }
1370
1371 return ERROR_SERVICE_REQUEST_TIMEOUT;
1372 }
1373 else if (dwError == WAIT_OBJECT_0)
1374 {
1375 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1376 &Overlapped,
1377 &dwRead,
1378 TRUE);
1379 if (bResult == FALSE)
1380 {
1381 dwError = GetLastError();
1382 DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
1383
1384 return dwError;
1385 }
1386 }
1387 }
1388 else if (dwError != ERROR_PIPE_CONNECTED)
1389 {
1390 DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
1391 return dwError;
1392 }
1393 }
1394
1395 DPRINT("Control pipe connected!\n");
1396
1397 Overlapped.hEvent = (HANDLE) NULL;
1398
1399 /* Read the process id from pipe */
1400 bResult = ReadFile(Service->lpImage->hControlPipe,
1401 (LPVOID)&dwProcessId,
1402 sizeof(DWORD),
1403 &dwRead,
1404 &Overlapped);
1405 if (bResult == FALSE)
1406 {
1407 DPRINT("ReadFile() returned FALSE\n");
1408
1409 dwError = GetLastError();
1410 if (dwError == ERROR_IO_PENDING)
1411 {
1412 DPRINT("dwError: ERROR_IO_PENDING\n");
1413
1414 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1415 PipeTimeout);
1416 if (dwError == WAIT_TIMEOUT)
1417 {
1418 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1419
1420 bResult = CancelIo(Service->lpImage->hControlPipe);
1421 if (bResult == FALSE)
1422 {
1423 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1424 }
1425
1426 return ERROR_SERVICE_REQUEST_TIMEOUT;
1427 }
1428 else if (dwError == WAIT_OBJECT_0)
1429 {
1430 DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
1431
1432 DPRINT("Process Id: %lu\n", dwProcessId);
1433
1434 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1435 &Overlapped,
1436 &dwRead,
1437 TRUE);
1438 if (bResult == FALSE)
1439 {
1440 dwError = GetLastError();
1441 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1442
1443 return dwError;
1444 }
1445 }
1446 else
1447 {
1448 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1449 }
1450 }
1451 else
1452 {
1453 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1454 return dwError;
1455 }
1456 }
1457
1458 DPRINT1("ScmWaitForServiceConnect() done\n");
1459
1460 return ERROR_SUCCESS;
1461 #else
1462
1463 /* Connect control pipe */
1464 if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
1465 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
1466 {
1467 DPRINT("Control pipe connected!\n");
1468
1469 /* Read SERVICE_STATUS_HANDLE from pipe */
1470 bResult = ReadFile(Service->lpImage->hControlPipe,
1471 (LPVOID)&dwProcessId,
1472 sizeof(DWORD),
1473 &dwRead,
1474 NULL);
1475 if (bResult == FALSE)
1476 {
1477 dwError = GetLastError();
1478 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
1479 dwError);
1480 }
1481 else
1482 {
1483 dwError = ERROR_SUCCESS;
1484 DPRINT("Read control pipe successfully\n");
1485 }
1486 }
1487 else
1488 {
1489 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1490 }
1491
1492 return dwError;
1493 #endif
1494 }
1495
1496
1497 static DWORD
1498 ScmStartUserModeService(PSERVICE Service,
1499 DWORD argc,
1500 LPWSTR* argv)
1501 {
1502 PROCESS_INFORMATION ProcessInformation;
1503 STARTUPINFOW StartupInfo;
1504 BOOL Result;
1505 DWORD dwError = ERROR_SUCCESS;
1506
1507 DPRINT("ScmStartUserModeService(%p)\n", Service);
1508
1509 /* If the image is already running ... */
1510 if (Service->lpImage->dwImageRunCount > 1)
1511 {
1512 /* ... just send a start command */
1513 return ScmSendStartCommand(Service, argc, argv);
1514 }
1515
1516 /* Otherwise start its process */
1517 ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1518 StartupInfo.cb = sizeof(StartupInfo);
1519 ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1520
1521 Result = CreateProcessW(NULL,
1522 Service->lpImage->szImagePath,
1523 NULL,
1524 NULL,
1525 FALSE,
1526 DETACHED_PROCESS | CREATE_SUSPENDED,
1527 NULL,
1528 NULL,
1529 &StartupInfo,
1530 &ProcessInformation);
1531 if (!Result)
1532 {
1533 dwError = GetLastError();
1534 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
1535 return dwError;
1536 }
1537
1538 DPRINT("Process Id: %lu Handle %p\n",
1539 ProcessInformation.dwProcessId,
1540 ProcessInformation.hProcess);
1541 DPRINT("Thread Id: %lu Handle %p\n",
1542 ProcessInformation.dwThreadId,
1543 ProcessInformation.hThread);
1544
1545 /* Get process handle and id */
1546 Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1547
1548 /* Resume Thread */
1549 ResumeThread(ProcessInformation.hThread);
1550
1551 /* Connect control pipe */
1552 dwError = ScmWaitForServiceConnect(Service);
1553 if (dwError == ERROR_SUCCESS)
1554 {
1555 /* Send start command */
1556 dwError = ScmSendStartCommand(Service, argc, argv);
1557 }
1558 else
1559 {
1560 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1561 Service->lpImage->dwProcessId = 0;
1562 }
1563
1564 /* Close thread and process handle */
1565 CloseHandle(ProcessInformation.hThread);
1566 CloseHandle(ProcessInformation.hProcess);
1567
1568 return dwError;
1569 }
1570
1571
1572 static DWORD
1573 ScmLoadService(PSERVICE Service,
1574 DWORD argc,
1575 LPWSTR* argv)
1576 {
1577 PSERVICE_GROUP Group = Service->lpGroup;
1578 DWORD dwError = ERROR_SUCCESS;
1579 LPCWSTR ErrorLogStrings[2];
1580 WCHAR szErrorBuffer[32];
1581
1582 DPRINT("ScmLoadService() called\n");
1583 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1584
1585 if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1586 {
1587 DPRINT("Service %S is already running!\n", Service->lpServiceName);
1588 return ERROR_SERVICE_ALREADY_RUNNING;
1589 }
1590
1591 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1592
1593 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1594 {
1595 /* Load driver */
1596 dwError = ScmLoadDriver(Service);
1597 if (dwError == ERROR_SUCCESS)
1598 {
1599 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1600 Service->Status.dwCurrentState = SERVICE_RUNNING;
1601 }
1602 }
1603 else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1604 {
1605 /* Start user-mode service */
1606 dwError = ScmCreateOrReferenceServiceImage(Service);
1607 if (dwError == ERROR_SUCCESS)
1608 {
1609 dwError = ScmStartUserModeService(Service, argc, argv);
1610 if (dwError == ERROR_SUCCESS)
1611 {
1612 #ifdef USE_SERVICE_START_PENDING
1613 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1614 #else
1615 Service->Status.dwCurrentState = SERVICE_RUNNING;
1616 #endif
1617 }
1618 else
1619 {
1620 ScmDereferenceServiceImage(Service->lpImage);
1621 Service->lpImage = NULL;
1622 }
1623 }
1624 }
1625
1626 DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1627
1628 if (dwError == ERROR_SUCCESS)
1629 {
1630 if (Group != NULL)
1631 {
1632 Group->ServicesRunning = TRUE;
1633 }
1634 }
1635 else
1636 {
1637 if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1638 {
1639 /* Log a failed service start */
1640 swprintf(szErrorBuffer, L"%lu", dwError);
1641 ErrorLogStrings[0] = Service->lpServiceName;
1642 ErrorLogStrings[1] = szErrorBuffer;
1643 ScmLogError(EVENT_SERVICE_START_FAILED,
1644 2,
1645 ErrorLogStrings);
1646 }
1647
1648 #if 0
1649 switch (Service->dwErrorControl)
1650 {
1651 case SERVICE_ERROR_SEVERE:
1652 if (IsLastKnownGood == FALSE)
1653 {
1654 /* FIXME: Boot last known good configuration */
1655 }
1656 break;
1657
1658 case SERVICE_ERROR_CRITICAL:
1659 if (IsLastKnownGood == FALSE)
1660 {
1661 /* FIXME: Boot last known good configuration */
1662 }
1663 else
1664 {
1665 /* FIXME: BSOD! */
1666 }
1667 break;
1668 }
1669 #endif
1670 }
1671
1672 return dwError;
1673 }
1674
1675
1676 DWORD
1677 ScmStartService(PSERVICE Service,
1678 DWORD argc,
1679 LPWSTR* argv)
1680 {
1681 DWORD dwError = ERROR_SUCCESS;
1682 SC_RPC_LOCK Lock = NULL;
1683
1684 DPRINT("ScmStartService() called\n");
1685 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1686
1687 /* Acquire the service control critical section, to synchronize starts */
1688 EnterCriticalSection(&ControlServiceCriticalSection);
1689
1690 /*
1691 * Acquire the user service start lock while the service is starting, if
1692 * needed (i.e. if we are not starting it during the initialization phase).
1693 * If we don't success, bail out.
1694 */
1695 if (!ScmInitialize)
1696 {
1697 dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
1698 if (dwError != ERROR_SUCCESS) goto done;
1699 }
1700
1701 /* Really start the service */
1702 dwError = ScmLoadService(Service, argc, argv);
1703
1704 /* Release the service start lock, if needed, and the critical section */
1705 if (Lock) ScmReleaseServiceStartLock(&Lock);
1706
1707 done:
1708 LeaveCriticalSection(&ControlServiceCriticalSection);
1709
1710 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1711
1712 return dwError;
1713 }
1714
1715
1716 VOID
1717 ScmAutoStartServices(VOID)
1718 {
1719 DWORD dwError = ERROR_SUCCESS;
1720 PLIST_ENTRY GroupEntry;
1721 PLIST_ENTRY ServiceEntry;
1722 PSERVICE_GROUP CurrentGroup;
1723 PSERVICE CurrentService;
1724 WCHAR szSafeBootServicePath[MAX_PATH];
1725 HKEY hKey;
1726 ULONG i;
1727
1728 /*
1729 * This function MUST be called ONLY at initialization time.
1730 * Therefore, no need to acquire the user service start lock.
1731 */
1732 ASSERT(ScmInitialize);
1733
1734 /* Acquire the service control critical section, to synchronize starts */
1735 EnterCriticalSection(&ControlServiceCriticalSection);
1736
1737 /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
1738 ServiceEntry = ServiceListHead.Flink;
1739 while (ServiceEntry != &ServiceListHead)
1740 {
1741 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1742
1743 /* Build the safe boot path */
1744 wcscpy(szSafeBootServicePath,
1745 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
1746
1747 switch (GetSystemMetrics(SM_CLEANBOOT))
1748 {
1749 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
1750 case 1:
1751 case 3:
1752 wcscat(szSafeBootServicePath, L"\\Minimal\\");
1753 break;
1754
1755 case 2:
1756 wcscat(szSafeBootServicePath, L"\\Network\\");
1757 break;
1758 }
1759
1760 if (GetSystemMetrics(SM_CLEANBOOT))
1761 {
1762 /* If key does not exist then do not assume safe mode */
1763 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1764 szSafeBootServicePath,
1765 0,
1766 KEY_READ,
1767 &hKey);
1768 if (dwError == ERROR_SUCCESS)
1769 {
1770 RegCloseKey(hKey);
1771
1772 /* Finish Safe Boot path off */
1773 wcsncat(szSafeBootServicePath,
1774 CurrentService->lpServiceName,
1775 MAX_PATH - wcslen(szSafeBootServicePath));
1776
1777 /* Check that the key is in the Safe Boot path */
1778 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1779 szSafeBootServicePath,
1780 0,
1781 KEY_READ,
1782 &hKey);
1783 if (dwError != ERROR_SUCCESS)
1784 {
1785 /* Mark service as visited so it is not auto-started */
1786 CurrentService->ServiceVisited = TRUE;
1787 }
1788 else
1789 {
1790 /* Must be auto-started in safe mode - mark as unvisited */
1791 RegCloseKey(hKey);
1792 CurrentService->ServiceVisited = FALSE;
1793 }
1794 }
1795 else
1796 {
1797 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
1798 CurrentService->ServiceVisited = FALSE;
1799 }
1800 }
1801
1802 ServiceEntry = ServiceEntry->Flink;
1803 }
1804
1805 /* Start all services which are members of an existing group */
1806 GroupEntry = GroupListHead.Flink;
1807 while (GroupEntry != &GroupListHead)
1808 {
1809 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1810
1811 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1812
1813 /* Start all services witch have a valid tag */
1814 for (i = 0; i < CurrentGroup->TagCount; i++)
1815 {
1816 ServiceEntry = ServiceListHead.Flink;
1817 while (ServiceEntry != &ServiceListHead)
1818 {
1819 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1820
1821 if ((CurrentService->lpGroup == CurrentGroup) &&
1822 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1823 (CurrentService->ServiceVisited == FALSE) &&
1824 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1825 {
1826 CurrentService->ServiceVisited = TRUE;
1827 ScmLoadService(CurrentService, 0, NULL);
1828 }
1829
1830 ServiceEntry = ServiceEntry->Flink;
1831 }
1832 }
1833
1834 /* Start all services which have an invalid tag or which do not have a tag */
1835 ServiceEntry = ServiceListHead.Flink;
1836 while (ServiceEntry != &ServiceListHead)
1837 {
1838 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1839
1840 if ((CurrentService->lpGroup == CurrentGroup) &&
1841 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1842 (CurrentService->ServiceVisited == FALSE))
1843 {
1844 CurrentService->ServiceVisited = TRUE;
1845 ScmLoadService(CurrentService, 0, NULL);
1846 }
1847
1848 ServiceEntry = ServiceEntry->Flink;
1849 }
1850
1851 GroupEntry = GroupEntry->Flink;
1852 }
1853
1854 /* Start all services which are members of any non-existing group */
1855 ServiceEntry = ServiceListHead.Flink;
1856 while (ServiceEntry != &ServiceListHead)
1857 {
1858 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1859
1860 if ((CurrentService->lpGroup != NULL) &&
1861 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1862 (CurrentService->ServiceVisited == FALSE))
1863 {
1864 CurrentService->ServiceVisited = TRUE;
1865 ScmLoadService(CurrentService, 0, NULL);
1866 }
1867
1868 ServiceEntry = ServiceEntry->Flink;
1869 }
1870
1871 /* Start all services which are not a member of any group */
1872 ServiceEntry = ServiceListHead.Flink;
1873 while (ServiceEntry != &ServiceListHead)
1874 {
1875 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1876
1877 if ((CurrentService->lpGroup == NULL) &&
1878 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1879 (CurrentService->ServiceVisited == FALSE))
1880 {
1881 CurrentService->ServiceVisited = TRUE;
1882 ScmLoadService(CurrentService, 0, NULL);
1883 }
1884
1885 ServiceEntry = ServiceEntry->Flink;
1886 }
1887
1888 /* Clear 'ServiceVisited' flag again */
1889 ServiceEntry = ServiceListHead.Flink;
1890 while (ServiceEntry != &ServiceListHead)
1891 {
1892 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1893 CurrentService->ServiceVisited = FALSE;
1894 ServiceEntry = ServiceEntry->Flink;
1895 }
1896
1897 /* Release the critical section */
1898 LeaveCriticalSection(&ControlServiceCriticalSection);
1899 }
1900
1901
1902 VOID
1903 ScmAutoShutdownServices(VOID)
1904 {
1905 PLIST_ENTRY ServiceEntry;
1906 PSERVICE CurrentService;
1907
1908 DPRINT("ScmAutoShutdownServices() called\n");
1909
1910 /* Lock the service database exclusively */
1911 ScmLockDatabaseExclusive();
1912
1913 ServiceEntry = ServiceListHead.Flink;
1914 while (ServiceEntry != &ServiceListHead)
1915 {
1916 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1917
1918 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
1919 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
1920 {
1921 /* shutdown service */
1922 DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
1923 ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
1924 }
1925
1926 ServiceEntry = ServiceEntry->Flink;
1927 }
1928
1929 /* Unlock the service database */
1930 ScmUnlockDatabase();
1931
1932 DPRINT("ScmAutoShutdownServices() done\n");
1933 }
1934
1935
1936 BOOL
1937 ScmLockDatabaseExclusive(VOID)
1938 {
1939 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
1940 }
1941
1942
1943 BOOL
1944 ScmLockDatabaseShared(VOID)
1945 {
1946 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
1947 }
1948
1949
1950 VOID
1951 ScmUnlockDatabase(VOID)
1952 {
1953 RtlReleaseResource(&DatabaseLock);
1954 }
1955
1956
1957 VOID
1958 ScmInitNamedPipeCriticalSection(VOID)
1959 {
1960 HKEY hKey;
1961 DWORD dwKeySize;
1962 DWORD dwError;
1963
1964 InitializeCriticalSection(&ControlServiceCriticalSection);
1965
1966 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1967 L"SYSTEM\\CurrentControlSet\\Control",
1968 0,
1969 KEY_READ,
1970 &hKey);
1971 if (dwError == ERROR_SUCCESS)
1972 {
1973 dwKeySize = sizeof(DWORD);
1974 RegQueryValueExW(hKey,
1975 L"ServicesPipeTimeout",
1976 0,
1977 NULL,
1978 (LPBYTE)&PipeTimeout,
1979 &dwKeySize);
1980
1981 RegCloseKey(hKey);
1982 }
1983 }
1984
1985
1986 VOID
1987 ScmDeleteNamedPipeCriticalSection(VOID)
1988 {
1989 DeleteCriticalSection(&ControlServiceCriticalSection);
1990 }
1991
1992 /* EOF */