[SERVICES]
[reactos.git] / reactos / 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 else
1460 {
1461 if (dwProcessId != Service->lpImage->dwProcessId)
1462 {
1463 /*
1464 LPWSTR Strings[3]
1465
1466 ScmLogError(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
1467 3,
1468 lpStrings);
1469 */
1470 }
1471 }
1472
1473 DPRINT("ScmWaitForServiceConnect() done\n");
1474
1475 return ERROR_SUCCESS;
1476 #else
1477
1478 /* Connect control pipe */
1479 if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
1480 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
1481 {
1482 DPRINT("Control pipe connected!\n");
1483
1484 /* Read SERVICE_STATUS_HANDLE from pipe */
1485 bResult = ReadFile(Service->lpImage->hControlPipe,
1486 (LPVOID)&dwProcessId,
1487 sizeof(DWORD),
1488 &dwRead,
1489 NULL);
1490 if (bResult == FALSE)
1491 {
1492 dwError = GetLastError();
1493 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
1494 dwError);
1495 }
1496 else
1497 {
1498 dwError = ERROR_SUCCESS;
1499 DPRINT("Read control pipe successfully\n");
1500 }
1501 }
1502 else
1503 {
1504 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1505 }
1506
1507 return dwError;
1508 #endif
1509 }
1510
1511
1512 static DWORD
1513 ScmStartUserModeService(PSERVICE Service,
1514 DWORD argc,
1515 LPWSTR* argv)
1516 {
1517 PROCESS_INFORMATION ProcessInformation;
1518 STARTUPINFOW StartupInfo;
1519 BOOL Result;
1520 DWORD dwError = ERROR_SUCCESS;
1521
1522 DPRINT("ScmStartUserModeService(%p)\n", Service);
1523
1524 /* If the image is already running ... */
1525 if (Service->lpImage->dwImageRunCount > 1)
1526 {
1527 /* ... just send a start command */
1528 return ScmSendStartCommand(Service, argc, argv);
1529 }
1530
1531 /* Otherwise start its process */
1532 ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1533 StartupInfo.cb = sizeof(StartupInfo);
1534 ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1535
1536 Result = CreateProcessW(NULL,
1537 Service->lpImage->szImagePath,
1538 NULL,
1539 NULL,
1540 FALSE,
1541 DETACHED_PROCESS | CREATE_SUSPENDED,
1542 NULL,
1543 NULL,
1544 &StartupInfo,
1545 &ProcessInformation);
1546 if (!Result)
1547 {
1548 dwError = GetLastError();
1549 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
1550 return dwError;
1551 }
1552
1553 DPRINT("Process Id: %lu Handle %p\n",
1554 ProcessInformation.dwProcessId,
1555 ProcessInformation.hProcess);
1556 DPRINT("Thread Id: %lu Handle %p\n",
1557 ProcessInformation.dwThreadId,
1558 ProcessInformation.hThread);
1559
1560 /* Get process handle and id */
1561 Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1562
1563 /* Resume Thread */
1564 ResumeThread(ProcessInformation.hThread);
1565
1566 /* Connect control pipe */
1567 dwError = ScmWaitForServiceConnect(Service);
1568 if (dwError == ERROR_SUCCESS)
1569 {
1570 /* Send start command */
1571 dwError = ScmSendStartCommand(Service, argc, argv);
1572 }
1573 else
1574 {
1575 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1576 Service->lpImage->dwProcessId = 0;
1577 }
1578
1579 /* Close thread and process handle */
1580 CloseHandle(ProcessInformation.hThread);
1581 CloseHandle(ProcessInformation.hProcess);
1582
1583 return dwError;
1584 }
1585
1586
1587 static DWORD
1588 ScmLoadService(PSERVICE Service,
1589 DWORD argc,
1590 LPWSTR* argv)
1591 {
1592 PSERVICE_GROUP Group = Service->lpGroup;
1593 DWORD dwError = ERROR_SUCCESS;
1594 LPCWSTR lpErrorStrings[2];
1595 WCHAR szErrorBuffer[32];
1596
1597 DPRINT("ScmLoadService() called\n");
1598 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1599
1600 if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1601 {
1602 DPRINT("Service %S is already running!\n", Service->lpServiceName);
1603 return ERROR_SERVICE_ALREADY_RUNNING;
1604 }
1605
1606 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1607
1608 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1609 {
1610 /* Load driver */
1611 dwError = ScmLoadDriver(Service);
1612 if (dwError == ERROR_SUCCESS)
1613 {
1614 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1615 Service->Status.dwCurrentState = SERVICE_RUNNING;
1616 }
1617 }
1618 else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1619 {
1620 /* Start user-mode service */
1621 dwError = ScmCreateOrReferenceServiceImage(Service);
1622 if (dwError == ERROR_SUCCESS)
1623 {
1624 dwError = ScmStartUserModeService(Service, argc, argv);
1625 if (dwError == ERROR_SUCCESS)
1626 {
1627 #ifdef USE_SERVICE_START_PENDING
1628 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1629 #else
1630 Service->Status.dwCurrentState = SERVICE_RUNNING;
1631 #endif
1632 }
1633 else
1634 {
1635 ScmDereferenceServiceImage(Service->lpImage);
1636 Service->lpImage = NULL;
1637 }
1638 }
1639 }
1640
1641 DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1642
1643 if (dwError == ERROR_SUCCESS)
1644 {
1645 if (Group != NULL)
1646 {
1647 Group->ServicesRunning = TRUE;
1648 }
1649
1650 /* Log a successful service start */
1651 lpErrorStrings[0] = Service->lpDisplayName;
1652 lpErrorStrings[1] = L"start";
1653 ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
1654 EVENTLOG_INFORMATION_TYPE,
1655 2,
1656 lpErrorStrings);
1657 }
1658 else
1659 {
1660 if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1661 {
1662 /* Log a failed service start */
1663 swprintf(szErrorBuffer, L"%lu", dwError);
1664 lpErrorStrings[0] = Service->lpServiceName;
1665 lpErrorStrings[1] = szErrorBuffer;
1666 ScmLogEvent(EVENT_SERVICE_START_FAILED,
1667 EVENTLOG_ERROR_TYPE,
1668 2,
1669 lpErrorStrings);
1670 }
1671
1672 #if 0
1673 switch (Service->dwErrorControl)
1674 {
1675 case SERVICE_ERROR_SEVERE:
1676 if (IsLastKnownGood == FALSE)
1677 {
1678 /* FIXME: Boot last known good configuration */
1679 }
1680 break;
1681
1682 case SERVICE_ERROR_CRITICAL:
1683 if (IsLastKnownGood == FALSE)
1684 {
1685 /* FIXME: Boot last known good configuration */
1686 }
1687 else
1688 {
1689 /* FIXME: BSOD! */
1690 }
1691 break;
1692 }
1693 #endif
1694 }
1695
1696 return dwError;
1697 }
1698
1699
1700 DWORD
1701 ScmStartService(PSERVICE Service,
1702 DWORD argc,
1703 LPWSTR* argv)
1704 {
1705 DWORD dwError = ERROR_SUCCESS;
1706 SC_RPC_LOCK Lock = NULL;
1707
1708 DPRINT("ScmStartService() called\n");
1709 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1710
1711 /* Acquire the service control critical section, to synchronize starts */
1712 EnterCriticalSection(&ControlServiceCriticalSection);
1713
1714 /*
1715 * Acquire the user service start lock while the service is starting, if
1716 * needed (i.e. if we are not starting it during the initialization phase).
1717 * If we don't success, bail out.
1718 */
1719 if (!ScmInitialize)
1720 {
1721 dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
1722 if (dwError != ERROR_SUCCESS) goto done;
1723 }
1724
1725 /* Really start the service */
1726 dwError = ScmLoadService(Service, argc, argv);
1727
1728 /* Release the service start lock, if needed, and the critical section */
1729 if (Lock) ScmReleaseServiceStartLock(&Lock);
1730
1731 done:
1732 LeaveCriticalSection(&ControlServiceCriticalSection);
1733
1734 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1735
1736 return dwError;
1737 }
1738
1739
1740 VOID
1741 ScmAutoStartServices(VOID)
1742 {
1743 DWORD dwError = ERROR_SUCCESS;
1744 PLIST_ENTRY GroupEntry;
1745 PLIST_ENTRY ServiceEntry;
1746 PSERVICE_GROUP CurrentGroup;
1747 PSERVICE CurrentService;
1748 WCHAR szSafeBootServicePath[MAX_PATH];
1749 HKEY hKey;
1750 ULONG i;
1751
1752 /*
1753 * This function MUST be called ONLY at initialization time.
1754 * Therefore, no need to acquire the user service start lock.
1755 */
1756 ASSERT(ScmInitialize);
1757
1758 /* Acquire the service control critical section, to synchronize starts */
1759 EnterCriticalSection(&ControlServiceCriticalSection);
1760
1761 /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
1762 ServiceEntry = ServiceListHead.Flink;
1763 while (ServiceEntry != &ServiceListHead)
1764 {
1765 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1766
1767 /* Build the safe boot path */
1768 wcscpy(szSafeBootServicePath,
1769 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
1770
1771 switch (GetSystemMetrics(SM_CLEANBOOT))
1772 {
1773 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
1774 case 1:
1775 case 3:
1776 wcscat(szSafeBootServicePath, L"\\Minimal\\");
1777 break;
1778
1779 case 2:
1780 wcscat(szSafeBootServicePath, L"\\Network\\");
1781 break;
1782 }
1783
1784 if (GetSystemMetrics(SM_CLEANBOOT))
1785 {
1786 /* If key does not exist then do not assume safe mode */
1787 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1788 szSafeBootServicePath,
1789 0,
1790 KEY_READ,
1791 &hKey);
1792 if (dwError == ERROR_SUCCESS)
1793 {
1794 RegCloseKey(hKey);
1795
1796 /* Finish Safe Boot path off */
1797 wcsncat(szSafeBootServicePath,
1798 CurrentService->lpServiceName,
1799 MAX_PATH - wcslen(szSafeBootServicePath));
1800
1801 /* Check that the key is in the Safe Boot path */
1802 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1803 szSafeBootServicePath,
1804 0,
1805 KEY_READ,
1806 &hKey);
1807 if (dwError != ERROR_SUCCESS)
1808 {
1809 /* Mark service as visited so it is not auto-started */
1810 CurrentService->ServiceVisited = TRUE;
1811 }
1812 else
1813 {
1814 /* Must be auto-started in safe mode - mark as unvisited */
1815 RegCloseKey(hKey);
1816 CurrentService->ServiceVisited = FALSE;
1817 }
1818 }
1819 else
1820 {
1821 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
1822 CurrentService->ServiceVisited = FALSE;
1823 }
1824 }
1825
1826 ServiceEntry = ServiceEntry->Flink;
1827 }
1828
1829 /* Start all services which are members of an existing group */
1830 GroupEntry = GroupListHead.Flink;
1831 while (GroupEntry != &GroupListHead)
1832 {
1833 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1834
1835 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1836
1837 /* Start all services witch have a valid tag */
1838 for (i = 0; i < CurrentGroup->TagCount; i++)
1839 {
1840 ServiceEntry = ServiceListHead.Flink;
1841 while (ServiceEntry != &ServiceListHead)
1842 {
1843 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1844
1845 if ((CurrentService->lpGroup == CurrentGroup) &&
1846 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1847 (CurrentService->ServiceVisited == FALSE) &&
1848 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1849 {
1850 CurrentService->ServiceVisited = TRUE;
1851 ScmLoadService(CurrentService, 0, NULL);
1852 }
1853
1854 ServiceEntry = ServiceEntry->Flink;
1855 }
1856 }
1857
1858 /* Start all services which have an invalid tag or which do not have a tag */
1859 ServiceEntry = ServiceListHead.Flink;
1860 while (ServiceEntry != &ServiceListHead)
1861 {
1862 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1863
1864 if ((CurrentService->lpGroup == CurrentGroup) &&
1865 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1866 (CurrentService->ServiceVisited == FALSE))
1867 {
1868 CurrentService->ServiceVisited = TRUE;
1869 ScmLoadService(CurrentService, 0, NULL);
1870 }
1871
1872 ServiceEntry = ServiceEntry->Flink;
1873 }
1874
1875 GroupEntry = GroupEntry->Flink;
1876 }
1877
1878 /* Start all services which are members of any non-existing group */
1879 ServiceEntry = ServiceListHead.Flink;
1880 while (ServiceEntry != &ServiceListHead)
1881 {
1882 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1883
1884 if ((CurrentService->lpGroup != NULL) &&
1885 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1886 (CurrentService->ServiceVisited == FALSE))
1887 {
1888 CurrentService->ServiceVisited = TRUE;
1889 ScmLoadService(CurrentService, 0, NULL);
1890 }
1891
1892 ServiceEntry = ServiceEntry->Flink;
1893 }
1894
1895 /* Start all services which are not a member of any group */
1896 ServiceEntry = ServiceListHead.Flink;
1897 while (ServiceEntry != &ServiceListHead)
1898 {
1899 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1900
1901 if ((CurrentService->lpGroup == NULL) &&
1902 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1903 (CurrentService->ServiceVisited == FALSE))
1904 {
1905 CurrentService->ServiceVisited = TRUE;
1906 ScmLoadService(CurrentService, 0, NULL);
1907 }
1908
1909 ServiceEntry = ServiceEntry->Flink;
1910 }
1911
1912 /* Clear 'ServiceVisited' flag again */
1913 ServiceEntry = ServiceListHead.Flink;
1914 while (ServiceEntry != &ServiceListHead)
1915 {
1916 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1917 CurrentService->ServiceVisited = FALSE;
1918 ServiceEntry = ServiceEntry->Flink;
1919 }
1920
1921 /* Release the critical section */
1922 LeaveCriticalSection(&ControlServiceCriticalSection);
1923 }
1924
1925
1926 VOID
1927 ScmAutoShutdownServices(VOID)
1928 {
1929 PLIST_ENTRY ServiceEntry;
1930 PSERVICE CurrentService;
1931
1932 DPRINT("ScmAutoShutdownServices() called\n");
1933
1934 /* Lock the service database exclusively */
1935 ScmLockDatabaseExclusive();
1936
1937 ServiceEntry = ServiceListHead.Flink;
1938 while (ServiceEntry != &ServiceListHead)
1939 {
1940 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1941
1942 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
1943 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
1944 {
1945 /* shutdown service */
1946 DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
1947 ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
1948 }
1949
1950 ServiceEntry = ServiceEntry->Flink;
1951 }
1952
1953 /* Unlock the service database */
1954 ScmUnlockDatabase();
1955
1956 DPRINT("ScmAutoShutdownServices() done\n");
1957 }
1958
1959
1960 BOOL
1961 ScmLockDatabaseExclusive(VOID)
1962 {
1963 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
1964 }
1965
1966
1967 BOOL
1968 ScmLockDatabaseShared(VOID)
1969 {
1970 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
1971 }
1972
1973
1974 VOID
1975 ScmUnlockDatabase(VOID)
1976 {
1977 RtlReleaseResource(&DatabaseLock);
1978 }
1979
1980
1981 VOID
1982 ScmInitNamedPipeCriticalSection(VOID)
1983 {
1984 HKEY hKey;
1985 DWORD dwKeySize;
1986 DWORD dwError;
1987
1988 InitializeCriticalSection(&ControlServiceCriticalSection);
1989
1990 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1991 L"SYSTEM\\CurrentControlSet\\Control",
1992 0,
1993 KEY_READ,
1994 &hKey);
1995 if (dwError == ERROR_SUCCESS)
1996 {
1997 dwKeySize = sizeof(DWORD);
1998 RegQueryValueExW(hKey,
1999 L"ServicesPipeTimeout",
2000 0,
2001 NULL,
2002 (LPBYTE)&PipeTimeout,
2003 &dwKeySize);
2004
2005 RegCloseKey(hKey);
2006 }
2007 }
2008
2009
2010 VOID
2011 ScmDeleteNamedPipeCriticalSection(VOID)
2012 {
2013 DeleteCriticalSection(&ControlServiceCriticalSection);
2014 }
2015
2016 /* EOF */