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