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