[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 ScmSetServiceGroup(lpService, NULL);
420
421 /* FIXME: SecurityDescriptor */
422
423
424 /* Remove the Service from the List */
425 RemoveEntryList(&lpService->ServiceListEntry);
426
427 DPRINT("Deleted Service %S\n", lpService->lpServiceName);
428
429 /* Delete the service record */
430 HeapFree(GetProcessHeap(), 0, lpService);
431
432 DPRINT("Done\n");
433 }
434
435
436 static DWORD
437 CreateServiceListEntry(LPCWSTR lpServiceName,
438 HKEY hServiceKey)
439 {
440 PSERVICE lpService = NULL;
441 LPWSTR lpDisplayName = NULL;
442 LPWSTR lpGroup = NULL;
443 DWORD dwSize;
444 DWORD dwError;
445 DWORD dwServiceType;
446 DWORD dwStartType;
447 DWORD dwErrorControl;
448 DWORD dwTagId;
449
450 DPRINT("Service: '%S'\n", lpServiceName);
451 if (*lpServiceName == L'{')
452 return ERROR_SUCCESS;
453
454 dwSize = sizeof(DWORD);
455 dwError = RegQueryValueExW(hServiceKey,
456 L"Type",
457 NULL,
458 NULL,
459 (LPBYTE)&dwServiceType,
460 &dwSize);
461 if (dwError != ERROR_SUCCESS)
462 return ERROR_SUCCESS;
463
464 if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
465 ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
466 (dwServiceType != SERVICE_KERNEL_DRIVER) &&
467 (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
468 return ERROR_SUCCESS;
469
470 DPRINT("Service type: %lx\n", dwServiceType);
471
472 dwSize = sizeof(DWORD);
473 dwError = RegQueryValueExW(hServiceKey,
474 L"Start",
475 NULL,
476 NULL,
477 (LPBYTE)&dwStartType,
478 &dwSize);
479 if (dwError != ERROR_SUCCESS)
480 return ERROR_SUCCESS;
481
482 DPRINT("Start type: %lx\n", dwStartType);
483
484 dwSize = sizeof(DWORD);
485 dwError = RegQueryValueExW(hServiceKey,
486 L"ErrorControl",
487 NULL,
488 NULL,
489 (LPBYTE)&dwErrorControl,
490 &dwSize);
491 if (dwError != ERROR_SUCCESS)
492 return ERROR_SUCCESS;
493
494 DPRINT("Error control: %lx\n", dwErrorControl);
495
496 dwError = RegQueryValueExW(hServiceKey,
497 L"Tag",
498 NULL,
499 NULL,
500 (LPBYTE)&dwTagId,
501 &dwSize);
502 if (dwError != ERROR_SUCCESS)
503 dwTagId = 0;
504
505 DPRINT("Tag: %lx\n", dwTagId);
506
507 dwError = ScmReadString(hServiceKey,
508 L"Group",
509 &lpGroup);
510 if (dwError != ERROR_SUCCESS)
511 lpGroup = NULL;
512
513 DPRINT("Group: %S\n", lpGroup);
514
515 dwError = ScmReadString(hServiceKey,
516 L"DisplayName",
517 &lpDisplayName);
518 if (dwError != ERROR_SUCCESS)
519 lpDisplayName = NULL;
520
521 DPRINT("Display name: %S\n", lpDisplayName);
522
523 dwError = ScmCreateNewServiceRecord(lpServiceName,
524 &lpService);
525 if (dwError != ERROR_SUCCESS)
526 goto done;
527
528 lpService->Status.dwServiceType = dwServiceType;
529 lpService->dwStartType = dwStartType;
530 lpService->dwErrorControl = dwErrorControl;
531 lpService->dwTag = dwTagId;
532
533 if (lpGroup != NULL)
534 {
535 dwError = ScmSetServiceGroup(lpService, lpGroup);
536 if (dwError != ERROR_SUCCESS)
537 goto done;
538 }
539
540 if (lpDisplayName != NULL)
541 {
542 lpService->lpDisplayName = lpDisplayName;
543 lpDisplayName = NULL;
544 }
545
546 DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
547 if (lpService->lpGroup != NULL)
548 {
549 DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
550 }
551 DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
552 lpService->dwStartType,
553 lpService->Status.dwServiceType,
554 lpService->dwTag,
555 lpService->dwErrorControl);
556
557 if (ScmIsDeleteFlagSet(hServiceKey))
558 lpService->bDeleted = TRUE;
559
560 done:;
561 if (lpGroup != NULL)
562 HeapFree(GetProcessHeap(), 0, lpGroup);
563
564 if (lpDisplayName != NULL)
565 HeapFree(GetProcessHeap(), 0, lpDisplayName);
566
567 if (lpService != NULL)
568 {
569 if (lpService->lpImage != NULL)
570 ScmDereferenceServiceImage(lpService->lpImage);
571 }
572
573 return dwError;
574 }
575
576
577 DWORD
578 ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
579 {
580 DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
581 WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
582 HKEY hSubKey = 0;
583
584 dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
585 if (!dwRet)
586 {
587 /* Find the maximum subkey length so that we can allocate a buffer */
588 dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
589 &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
590 if (!dwRet)
591 {
592 dwMaxSubkeyLen++;
593 if (dwMaxSubkeyLen > sizeof(szNameBuf) / sizeof(WCHAR))
594 {
595 /* Name too big: alloc a buffer for it */
596 lpszName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxSubkeyLen * sizeof(WCHAR));
597 }
598
599 if (!lpszName)
600 dwRet = ERROR_NOT_ENOUGH_MEMORY;
601 else
602 {
603 while (dwRet == ERROR_SUCCESS)
604 {
605 dwSize = dwMaxSubkeyLen;
606 dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
607 if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
608 dwRet = ScmDeleteRegKey(hSubKey, lpszName);
609 }
610 if (dwRet == ERROR_NO_MORE_ITEMS)
611 dwRet = ERROR_SUCCESS;
612
613 if (lpszName != szNameBuf)
614 HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
615 }
616 }
617
618 RegCloseKey(hSubKey);
619 if (!dwRet)
620 dwRet = RegDeleteKeyW(hKey, lpszSubKey);
621 }
622 return dwRet;
623 }
624
625
626 VOID
627 ScmDeleteMarkedServices(VOID)
628 {
629 PLIST_ENTRY ServiceEntry;
630 PSERVICE CurrentService;
631 HKEY hServicesKey;
632 DWORD dwError;
633
634 ServiceEntry = ServiceListHead.Flink;
635 while (ServiceEntry != &ServiceListHead)
636 {
637 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
638
639 ServiceEntry = ServiceEntry->Flink;
640
641 if (CurrentService->bDeleted == TRUE)
642 {
643 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
644 L"System\\CurrentControlSet\\Services",
645 0,
646 DELETE,
647 &hServicesKey);
648 if (dwError == ERROR_SUCCESS)
649 {
650 dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
651 RegCloseKey(hServicesKey);
652 if (dwError == ERROR_SUCCESS)
653 {
654 RemoveEntryList(&CurrentService->ServiceListEntry);
655 HeapFree(GetProcessHeap(), 0, CurrentService);
656 }
657 }
658
659 if (dwError != ERROR_SUCCESS)
660 DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
661 }
662 }
663 }
664
665
666 DWORD
667 ScmCreateServiceDatabase(VOID)
668 {
669 WCHAR szSubKey[MAX_PATH];
670 HKEY hServicesKey;
671 HKEY hServiceKey;
672 DWORD dwSubKey;
673 DWORD dwSubKeyLength;
674 FILETIME ftLastChanged;
675 DWORD dwError;
676
677 DPRINT("ScmCreateServiceDatabase() called\n");
678
679 dwError = ScmCreateGroupList();
680 if (dwError != ERROR_SUCCESS)
681 return dwError;
682
683 /* Initialize basic variables */
684 InitializeListHead(&ImageListHead);
685 InitializeListHead(&ServiceListHead);
686
687 /* Initialize the database lock */
688 RtlInitializeResource(&DatabaseLock);
689
690 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
691 L"System\\CurrentControlSet\\Services",
692 0,
693 KEY_READ,
694 &hServicesKey);
695 if (dwError != ERROR_SUCCESS)
696 return dwError;
697
698 dwSubKey = 0;
699 for (;;)
700 {
701 dwSubKeyLength = MAX_PATH;
702 dwError = RegEnumKeyExW(hServicesKey,
703 dwSubKey,
704 szSubKey,
705 &dwSubKeyLength,
706 NULL,
707 NULL,
708 NULL,
709 &ftLastChanged);
710 if (dwError == ERROR_SUCCESS &&
711 szSubKey[0] != L'{')
712 {
713 DPRINT("SubKeyName: '%S'\n", szSubKey);
714
715 dwError = RegOpenKeyExW(hServicesKey,
716 szSubKey,
717 0,
718 KEY_READ,
719 &hServiceKey);
720 if (dwError == ERROR_SUCCESS)
721 {
722 dwError = CreateServiceListEntry(szSubKey,
723 hServiceKey);
724
725 RegCloseKey(hServiceKey);
726 }
727 }
728
729 if (dwError != ERROR_SUCCESS)
730 break;
731
732 dwSubKey++;
733 }
734
735 RegCloseKey(hServicesKey);
736
737 /* Wait for the LSA server */
738 ScmWaitForLsa();
739
740 /* Delete services that are marked for delete */
741 ScmDeleteMarkedServices();
742
743 DPRINT("ScmCreateServiceDatabase() done\n");
744
745 return ERROR_SUCCESS;
746 }
747
748
749 VOID
750 ScmShutdownServiceDatabase(VOID)
751 {
752 DPRINT("ScmShutdownServiceDatabase() called\n");
753
754 ScmDeleteMarkedServices();
755 RtlDeleteResource(&DatabaseLock);
756
757 DPRINT("ScmShutdownServiceDatabase() done\n");
758 }
759
760
761 static NTSTATUS
762 ScmCheckDriver(PSERVICE Service)
763 {
764 OBJECT_ATTRIBUTES ObjectAttributes;
765 UNICODE_STRING DirName;
766 HANDLE DirHandle;
767 NTSTATUS Status;
768 POBJECT_DIRECTORY_INFORMATION DirInfo;
769 ULONG BufferLength;
770 ULONG DataLength;
771 ULONG Index;
772
773 DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
774
775 if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
776 {
777 RtlInitUnicodeString(&DirName, L"\\Driver");
778 }
779 else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
780 {
781 ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
782 RtlInitUnicodeString(&DirName, L"\\FileSystem");
783 }
784
785 InitializeObjectAttributes(&ObjectAttributes,
786 &DirName,
787 0,
788 NULL,
789 NULL);
790
791 Status = NtOpenDirectoryObject(&DirHandle,
792 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
793 &ObjectAttributes);
794 if (!NT_SUCCESS(Status))
795 {
796 return Status;
797 }
798
799 BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
800 2 * MAX_PATH * sizeof(WCHAR);
801 DirInfo = HeapAlloc(GetProcessHeap(),
802 HEAP_ZERO_MEMORY,
803 BufferLength);
804
805 Index = 0;
806 while (TRUE)
807 {
808 Status = NtQueryDirectoryObject(DirHandle,
809 DirInfo,
810 BufferLength,
811 TRUE,
812 FALSE,
813 &Index,
814 &DataLength);
815 if (Status == STATUS_NO_MORE_ENTRIES)
816 {
817 /* FIXME: Add current service to 'failed service' list */
818 DPRINT("Service '%S' failed\n", Service->lpServiceName);
819 break;
820 }
821
822 if (!NT_SUCCESS(Status))
823 break;
824
825 DPRINT("Comparing: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
826
827 if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
828 {
829 DPRINT("Found: '%S' '%wZ'\n",
830 Service->lpServiceName, &DirInfo->Name);
831
832 /* Mark service as 'running' */
833 Service->Status.dwCurrentState = SERVICE_RUNNING;
834
835 /* Mark the service group as 'running' */
836 if (Service->lpGroup != NULL)
837 {
838 Service->lpGroup->ServicesRunning = TRUE;
839 }
840
841 break;
842 }
843 }
844
845 HeapFree(GetProcessHeap(),
846 0,
847 DirInfo);
848 NtClose(DirHandle);
849
850 return STATUS_SUCCESS;
851 }
852
853
854 VOID
855 ScmGetBootAndSystemDriverState(VOID)
856 {
857 PLIST_ENTRY ServiceEntry;
858 PSERVICE CurrentService;
859
860 DPRINT("ScmGetBootAndSystemDriverState() called\n");
861
862 ServiceEntry = ServiceListHead.Flink;
863 while (ServiceEntry != &ServiceListHead)
864 {
865 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
866
867 if (CurrentService->dwStartType == SERVICE_BOOT_START ||
868 CurrentService->dwStartType == SERVICE_SYSTEM_START)
869 {
870 /* Check driver */
871 DPRINT(" Checking service: %S\n", CurrentService->lpServiceName);
872
873 ScmCheckDriver(CurrentService);
874 }
875
876 ServiceEntry = ServiceEntry->Flink;
877 }
878
879 DPRINT("ScmGetBootAndSystemDriverState() done\n");
880 }
881
882
883 DWORD
884 ScmControlService(PSERVICE Service,
885 DWORD dwControl)
886 {
887 PSCM_CONTROL_PACKET ControlPacket;
888 SCM_REPLY_PACKET ReplyPacket;
889
890 DWORD dwWriteCount = 0;
891 DWORD dwReadCount = 0;
892 DWORD PacketSize;
893 PWSTR Ptr;
894 DWORD dwError = ERROR_SUCCESS;
895 BOOL bResult;
896 #ifdef USE_ASYNCHRONOUS_IO
897 OVERLAPPED Overlapped = {0};
898 #endif
899
900 DPRINT("ScmControlService() called\n");
901
902 /* Acquire the service control critical section, to synchronize requests */
903 EnterCriticalSection(&ControlServiceCriticalSection);
904
905 /* Calculate the total length of the start command line */
906 PacketSize = sizeof(SCM_CONTROL_PACKET);
907 PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
908
909 ControlPacket = HeapAlloc(GetProcessHeap(),
910 HEAP_ZERO_MEMORY,
911 PacketSize);
912 if (ControlPacket == NULL)
913 {
914 LeaveCriticalSection(&ControlServiceCriticalSection);
915 return ERROR_NOT_ENOUGH_MEMORY;
916 }
917
918 ControlPacket->dwSize = PacketSize;
919 ControlPacket->dwControl = dwControl;
920 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
921
922 ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
923
924 Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
925 wcscpy(Ptr, Service->lpServiceName);
926
927 ControlPacket->dwArgumentsCount = 0;
928 ControlPacket->dwArgumentsOffset = 0;
929
930 #ifdef USE_ASYNCHRONOUS_IO
931 bResult = WriteFile(Service->lpImage->hControlPipe,
932 ControlPacket,
933 PacketSize,
934 &dwWriteCount,
935 &Overlapped);
936 if (bResult == FALSE)
937 {
938 DPRINT("WriteFile() returned FALSE\n");
939
940 dwError = GetLastError();
941 if (dwError == ERROR_IO_PENDING)
942 {
943 DPRINT("dwError: ERROR_IO_PENDING\n");
944
945 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
946 PipeTimeout);
947 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
948
949 if (dwError == WAIT_TIMEOUT)
950 {
951 bResult = CancelIo(Service->lpImage->hControlPipe);
952 if (bResult == FALSE)
953 {
954 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
955 }
956
957 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
958 goto Done;
959 }
960 else if (dwError == WAIT_OBJECT_0)
961 {
962 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
963 &Overlapped,
964 &dwWriteCount,
965 TRUE);
966 if (bResult == FALSE)
967 {
968 dwError = GetLastError();
969 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
970
971 goto Done;
972 }
973 }
974 }
975 else
976 {
977 DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
978 goto Done;
979 }
980 }
981
982 /* Read the reply */
983 Overlapped.hEvent = (HANDLE) NULL;
984
985 bResult = ReadFile(Service->lpImage->hControlPipe,
986 &ReplyPacket,
987 sizeof(SCM_REPLY_PACKET),
988 &dwReadCount,
989 &Overlapped);
990 if (bResult == FALSE)
991 {
992 DPRINT("ReadFile() returned FALSE\n");
993
994 dwError = GetLastError();
995 if (dwError == ERROR_IO_PENDING)
996 {
997 DPRINT("dwError: ERROR_IO_PENDING\n");
998
999 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1000 PipeTimeout);
1001 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1002
1003 if (dwError == WAIT_TIMEOUT)
1004 {
1005 bResult = CancelIo(Service->lpImage->hControlPipe);
1006 if (bResult == FALSE)
1007 {
1008 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1009 }
1010
1011 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1012 goto Done;
1013 }
1014 else if (dwError == WAIT_OBJECT_0)
1015 {
1016 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1017 &Overlapped,
1018 &dwReadCount,
1019 TRUE);
1020 if (bResult == FALSE)
1021 {
1022 dwError = GetLastError();
1023 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1024
1025 goto Done;
1026 }
1027 }
1028 }
1029 else
1030 {
1031 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1032 goto Done;
1033 }
1034 }
1035
1036 #else
1037 /* Send the control packet */
1038 bResult = WriteFile(Service->lpImage->hControlPipe,
1039 ControlPacket,
1040 PacketSize,
1041 &dwWriteCount,
1042 NULL);
1043 if (bResult == FALSE)
1044 {
1045 dwError = GetLastError();
1046 DPRINT("WriteFile() failed (Error %lu)\n", dwError);
1047
1048 if ((dwError == ERROR_GEN_FAILURE) &&
1049 (dwControl == SERVICE_CONTROL_STOP))
1050 {
1051 /* Service is already terminated */
1052 Service->Status.dwCurrentState = SERVICE_STOPPED;
1053 Service->Status.dwControlsAccepted = 0;
1054 Service->Status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
1055 dwError = ERROR_SUCCESS;
1056 }
1057 goto Done;
1058 }
1059
1060 /* Read the reply */
1061 bResult = ReadFile(Service->lpImage->hControlPipe,
1062 &ReplyPacket,
1063 sizeof(SCM_REPLY_PACKET),
1064 &dwReadCount,
1065 NULL);
1066 if (bResult == FALSE)
1067 {
1068 dwError = GetLastError();
1069 DPRINT("ReadFile() failed (Error %lu)\n", dwError);
1070 }
1071 #endif
1072
1073 Done:
1074 /* Release the contol packet */
1075 HeapFree(GetProcessHeap(),
1076 0,
1077 ControlPacket);
1078
1079 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1080 {
1081 dwError = ReplyPacket.dwError;
1082 }
1083
1084 if (dwError == ERROR_SUCCESS &&
1085 dwControl == SERVICE_CONTROL_STOP)
1086 {
1087 ScmDereferenceServiceImage(Service->lpImage);
1088 }
1089
1090 LeaveCriticalSection(&ControlServiceCriticalSection);
1091
1092 DPRINT("ScmControlService() done\n");
1093
1094 return dwError;
1095 }
1096
1097
1098 static DWORD
1099 ScmSendStartCommand(PSERVICE Service,
1100 DWORD argc,
1101 LPWSTR* argv)
1102 {
1103 PSCM_CONTROL_PACKET ControlPacket;
1104 SCM_REPLY_PACKET ReplyPacket;
1105 DWORD PacketSize;
1106 PWSTR Ptr;
1107 DWORD dwWriteCount = 0;
1108 DWORD dwReadCount = 0;
1109 DWORD dwError = ERROR_SUCCESS;
1110 DWORD i;
1111 PWSTR *pOffPtr;
1112 PWSTR pArgPtr;
1113 BOOL bResult;
1114 #ifdef USE_ASYNCHRONOUS_IO
1115 OVERLAPPED Overlapped = {0};
1116 #endif
1117
1118 DPRINT("ScmSendStartCommand() called\n");
1119
1120 /* Calculate the total length of the start command line */
1121 PacketSize = sizeof(SCM_CONTROL_PACKET) +
1122 (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
1123
1124 /* Calculate the required packet size for the start arguments */
1125 if (argc > 0 && argv != NULL)
1126 {
1127 PacketSize = ALIGN_UP(PacketSize, LPWSTR);
1128
1129 DPRINT("Argc: %lu\n", argc);
1130 for (i = 0; i < argc; i++)
1131 {
1132 DPRINT("Argv[%lu]: %S\n", i, argv[i]);
1133 PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR));
1134 }
1135 }
1136
1137 /* Allocate a control packet */
1138 ControlPacket = HeapAlloc(GetProcessHeap(),
1139 HEAP_ZERO_MEMORY,
1140 PacketSize);
1141 if (ControlPacket == NULL)
1142 return ERROR_NOT_ENOUGH_MEMORY;
1143
1144 ControlPacket->dwSize = PacketSize;
1145 ControlPacket->dwControl = (Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
1146 ? SERVICE_CONTROL_START_OWN
1147 : SERVICE_CONTROL_START_SHARE;
1148 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
1149 ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1150
1151 Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
1152 wcscpy(Ptr, Service->lpServiceName);
1153
1154 ControlPacket->dwArgumentsCount = 0;
1155 ControlPacket->dwArgumentsOffset = 0;
1156
1157 /* Copy argument list */
1158 if (argc > 0 && argv != NULL)
1159 {
1160 Ptr += wcslen(Service->lpServiceName) + 1;
1161 pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
1162 pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
1163
1164 ControlPacket->dwArgumentsCount = argc;
1165 ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
1166
1167 DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
1168 DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
1169
1170 for (i = 0; i < argc; i++)
1171 {
1172 wcscpy(pArgPtr, argv[i]);
1173 *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
1174 DPRINT("offset: %p\n", *pOffPtr);
1175
1176 pArgPtr += wcslen(argv[i]) + 1;
1177 pOffPtr++;
1178 }
1179 }
1180
1181 #ifdef USE_ASYNCHRONOUS_IO
1182 bResult = WriteFile(Service->lpImage->hControlPipe,
1183 ControlPacket,
1184 PacketSize,
1185 &dwWriteCount,
1186 &Overlapped);
1187 if (bResult == FALSE)
1188 {
1189 DPRINT("WriteFile() returned FALSE\n");
1190
1191 dwError = GetLastError();
1192 if (dwError == ERROR_IO_PENDING)
1193 {
1194 DPRINT("dwError: ERROR_IO_PENDING\n");
1195
1196 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1197 PipeTimeout);
1198 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1199
1200 if (dwError == WAIT_TIMEOUT)
1201 {
1202 bResult = CancelIo(Service->lpImage->hControlPipe);
1203 if (bResult == FALSE)
1204 {
1205 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1206 }
1207
1208 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1209 goto Done;
1210 }
1211 else if (dwError == WAIT_OBJECT_0)
1212 {
1213 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1214 &Overlapped,
1215 &dwWriteCount,
1216 TRUE);
1217 if (bResult == FALSE)
1218 {
1219 dwError = GetLastError();
1220 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1221
1222 goto Done;
1223 }
1224 }
1225 }
1226 else
1227 {
1228 DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
1229 goto Done;
1230 }
1231 }
1232
1233 /* Read the reply */
1234 Overlapped.hEvent = (HANDLE) NULL;
1235
1236 bResult = ReadFile(Service->lpImage->hControlPipe,
1237 &ReplyPacket,
1238 sizeof(SCM_REPLY_PACKET),
1239 &dwReadCount,
1240 &Overlapped);
1241 if (bResult == FALSE)
1242 {
1243 DPRINT("ReadFile() returned FALSE\n");
1244
1245 dwError = GetLastError();
1246 if (dwError == ERROR_IO_PENDING)
1247 {
1248 DPRINT("dwError: ERROR_IO_PENDING\n");
1249
1250 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1251 PipeTimeout);
1252 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1253
1254 if (dwError == WAIT_TIMEOUT)
1255 {
1256 bResult = CancelIo(Service->lpImage->hControlPipe);
1257 if (bResult == FALSE)
1258 {
1259 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1260 }
1261
1262 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1263 goto Done;
1264 }
1265 else if (dwError == WAIT_OBJECT_0)
1266 {
1267 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1268 &Overlapped,
1269 &dwReadCount,
1270 TRUE);
1271 if (bResult == FALSE)
1272 {
1273 dwError = GetLastError();
1274 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1275
1276 goto Done;
1277 }
1278 }
1279 }
1280 else
1281 {
1282 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1283 goto Done;
1284 }
1285 }
1286
1287 #else
1288 /* Send the start command */
1289 bResult = WriteFile(Service->lpImage->hControlPipe,
1290 ControlPacket,
1291 PacketSize,
1292 &dwWriteCount,
1293 NULL);
1294 if (bResult == FALSE)
1295 {
1296 dwError = GetLastError();
1297 DPRINT("WriteFile() failed (Error %lu)\n", dwError);
1298 goto Done;
1299 }
1300
1301 /* Read the reply */
1302 bResult = ReadFile(Service->lpImage->hControlPipe,
1303 &ReplyPacket,
1304 sizeof(SCM_REPLY_PACKET),
1305 &dwReadCount,
1306 NULL);
1307 if (bResult == FALSE)
1308 {
1309 dwError = GetLastError();
1310 DPRINT("ReadFile() failed (Error %lu)\n", dwError);
1311 }
1312 #endif
1313
1314 Done:
1315 /* Release the contol packet */
1316 HeapFree(GetProcessHeap(),
1317 0,
1318 ControlPacket);
1319
1320 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1321 {
1322 dwError = ReplyPacket.dwError;
1323 }
1324
1325 DPRINT("ScmSendStartCommand() done\n");
1326
1327 return dwError;
1328 }
1329
1330
1331 static DWORD
1332 ScmWaitForServiceConnect(PSERVICE Service)
1333 {
1334 DWORD dwRead = 0;
1335 DWORD dwProcessId = 0;
1336 DWORD dwError = ERROR_SUCCESS;
1337 BOOL bResult;
1338 #ifdef USE_ASYNCHRONOUS_IO
1339 OVERLAPPED Overlapped = {0};
1340 #endif
1341 #if 0
1342 LPCWSTR lpLogStrings[3];
1343 WCHAR szBuffer1[20];
1344 WCHAR szBuffer2[20];
1345 #endif
1346
1347 DPRINT("ScmWaitForServiceConnect()\n");
1348
1349 #ifdef USE_ASYNCHRONOUS_IO
1350 Overlapped.hEvent = (HANDLE)NULL;
1351
1352 bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
1353 &Overlapped);
1354 if (bResult == FALSE)
1355 {
1356 DPRINT("ConnectNamedPipe() returned FALSE\n");
1357
1358 dwError = GetLastError();
1359 if (dwError == ERROR_IO_PENDING)
1360 {
1361 DPRINT("dwError: ERROR_IO_PENDING\n");
1362
1363 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1364 PipeTimeout);
1365 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1366
1367 if (dwError == WAIT_TIMEOUT)
1368 {
1369 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1370
1371 bResult = CancelIo(Service->lpImage->hControlPipe);
1372 if (bResult == FALSE)
1373 {
1374 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1375 }
1376
1377 #if 0
1378 _ultow(PipeTimeout, szBuffer1, 10);
1379 lpLogStrings[0] = Service->lpDisplayName;
1380 lpLogStrings[1] = szBuffer1;
1381
1382 ScmLogEvent(EVENT_CONNECTION_TIMEOUT,
1383 EVENTLOG_ERROR_TYPE,
1384 2,
1385 lpLogStrings);
1386 #endif
1387 DPRINT1("Log EVENT_CONNECTION_TIMEOUT by %S\n", Service->lpDisplayName);
1388
1389 return ERROR_SERVICE_REQUEST_TIMEOUT;
1390 }
1391 else if (dwError == WAIT_OBJECT_0)
1392 {
1393 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1394 &Overlapped,
1395 &dwRead,
1396 TRUE);
1397 if (bResult == FALSE)
1398 {
1399 dwError = GetLastError();
1400 DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
1401
1402 return dwError;
1403 }
1404 }
1405 }
1406 else if (dwError != ERROR_PIPE_CONNECTED)
1407 {
1408 DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
1409 return dwError;
1410 }
1411 }
1412
1413 DPRINT("Control pipe connected!\n");
1414
1415 Overlapped.hEvent = (HANDLE) NULL;
1416
1417 /* Read the process id from pipe */
1418 bResult = ReadFile(Service->lpImage->hControlPipe,
1419 (LPVOID)&dwProcessId,
1420 sizeof(DWORD),
1421 &dwRead,
1422 &Overlapped);
1423 if (bResult == FALSE)
1424 {
1425 DPRINT("ReadFile() returned FALSE\n");
1426
1427 dwError = GetLastError();
1428 if (dwError == ERROR_IO_PENDING)
1429 {
1430 DPRINT("dwError: ERROR_IO_PENDING\n");
1431
1432 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1433 PipeTimeout);
1434 if (dwError == WAIT_TIMEOUT)
1435 {
1436 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1437
1438 bResult = CancelIo(Service->lpImage->hControlPipe);
1439 if (bResult == FALSE)
1440 {
1441 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1442 }
1443
1444 #if 0
1445 _ultow(PipeTimeout, szBuffer1, 10);
1446 lpLogStrings[0] = szBuffer1;
1447
1448 ScmLogEvent(EVENT_READFILE_TIMEOUT,
1449 EVENTLOG_ERROR_TYPE,
1450 1,
1451 lpLogStrings);
1452 #endif
1453 DPRINT1("Log EVENT_READFILE_TIMEOUT by %S\n", Service->lpDisplayName);
1454
1455 return ERROR_SERVICE_REQUEST_TIMEOUT;
1456 }
1457 else if (dwError == WAIT_OBJECT_0)
1458 {
1459 DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
1460
1461 DPRINT("Process Id: %lu\n", dwProcessId);
1462
1463 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1464 &Overlapped,
1465 &dwRead,
1466 TRUE);
1467 if (bResult == FALSE)
1468 {
1469 dwError = GetLastError();
1470 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1471
1472 return dwError;
1473 }
1474 }
1475 else
1476 {
1477 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1478 }
1479 }
1480 else
1481 {
1482 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1483 return dwError;
1484 }
1485 }
1486
1487 if (dwProcessId != Service->lpImage->dwProcessId)
1488 {
1489 #if 0
1490 _ultow(Service->lpImage->dwProcessId, szBuffer1, 10);
1491 _ultow(dwProcessId, szBuffer2, 10);
1492
1493 lpLogStrings[0] = Service->lpDisplayName;
1494 lpLogStrings[1] = szBuffer1;
1495 lpLogStrings[2] = szBuffer2;
1496
1497 ScmLogEvent(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
1498 EVENTLOG_WARNING_TYPE,
1499 3,
1500 lpLogStrings);
1501 #endif
1502
1503 DPRINT1("Log EVENT_SERVICE_DIFFERENT_PID_CONNECTED by %S\n", Service->lpDisplayName);
1504 }
1505
1506 DPRINT("ScmWaitForServiceConnect() done\n");
1507
1508 return ERROR_SUCCESS;
1509 #else
1510
1511 /* Connect control pipe */
1512 if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
1513 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
1514 {
1515 DPRINT("Control pipe connected!\n");
1516
1517 /* Read SERVICE_STATUS_HANDLE from pipe */
1518 bResult = ReadFile(Service->lpImage->hControlPipe,
1519 (LPVOID)&dwProcessId,
1520 sizeof(DWORD),
1521 &dwRead,
1522 NULL);
1523 if (bResult == FALSE)
1524 {
1525 dwError = GetLastError();
1526 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
1527 dwError);
1528 }
1529 else
1530 {
1531 dwError = ERROR_SUCCESS;
1532 DPRINT("Read control pipe successfully\n");
1533 }
1534 }
1535 else
1536 {
1537 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1538 }
1539
1540 return dwError;
1541 #endif
1542 }
1543
1544
1545 static DWORD
1546 ScmStartUserModeService(PSERVICE Service,
1547 DWORD argc,
1548 LPWSTR* argv)
1549 {
1550 PROCESS_INFORMATION ProcessInformation;
1551 STARTUPINFOW StartupInfo;
1552 BOOL Result;
1553 DWORD dwError = ERROR_SUCCESS;
1554
1555 DPRINT("ScmStartUserModeService(%p)\n", Service);
1556
1557 /* If the image is already running ... */
1558 if (Service->lpImage->dwImageRunCount > 1)
1559 {
1560 /* ... just send a start command */
1561 return ScmSendStartCommand(Service, argc, argv);
1562 }
1563
1564 /* Otherwise start its process */
1565 ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1566 StartupInfo.cb = sizeof(StartupInfo);
1567 ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1568
1569 Result = CreateProcessW(NULL,
1570 Service->lpImage->szImagePath,
1571 NULL,
1572 NULL,
1573 FALSE,
1574 DETACHED_PROCESS | CREATE_SUSPENDED,
1575 NULL,
1576 NULL,
1577 &StartupInfo,
1578 &ProcessInformation);
1579 if (!Result)
1580 {
1581 dwError = GetLastError();
1582 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
1583 return dwError;
1584 }
1585
1586 DPRINT("Process Id: %lu Handle %p\n",
1587 ProcessInformation.dwProcessId,
1588 ProcessInformation.hProcess);
1589 DPRINT("Thread Id: %lu Handle %p\n",
1590 ProcessInformation.dwThreadId,
1591 ProcessInformation.hThread);
1592
1593 /* Get process handle and id */
1594 Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1595
1596 /* Resume Thread */
1597 ResumeThread(ProcessInformation.hThread);
1598
1599 /* Connect control pipe */
1600 dwError = ScmWaitForServiceConnect(Service);
1601 if (dwError == ERROR_SUCCESS)
1602 {
1603 /* Send start command */
1604 dwError = ScmSendStartCommand(Service, argc, argv);
1605 }
1606 else
1607 {
1608 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1609 Service->lpImage->dwProcessId = 0;
1610 }
1611
1612 /* Close thread and process handle */
1613 CloseHandle(ProcessInformation.hThread);
1614 CloseHandle(ProcessInformation.hProcess);
1615
1616 return dwError;
1617 }
1618
1619
1620 static DWORD
1621 ScmLoadService(PSERVICE Service,
1622 DWORD argc,
1623 LPWSTR* argv)
1624 {
1625 PSERVICE_GROUP Group = Service->lpGroup;
1626 DWORD dwError = ERROR_SUCCESS;
1627 LPCWSTR lpLogStrings[2];
1628 WCHAR szLogBuffer[80];
1629
1630 DPRINT("ScmLoadService() called\n");
1631 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1632
1633 if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1634 {
1635 DPRINT("Service %S is already running!\n", Service->lpServiceName);
1636 return ERROR_SERVICE_ALREADY_RUNNING;
1637 }
1638
1639 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1640
1641 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1642 {
1643 /* Load driver */
1644 dwError = ScmLoadDriver(Service);
1645 if (dwError == ERROR_SUCCESS)
1646 {
1647 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1648 Service->Status.dwCurrentState = SERVICE_RUNNING;
1649 }
1650 }
1651 else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1652 {
1653 /* Start user-mode service */
1654 dwError = ScmCreateOrReferenceServiceImage(Service);
1655 if (dwError == ERROR_SUCCESS)
1656 {
1657 dwError = ScmStartUserModeService(Service, argc, argv);
1658 if (dwError == ERROR_SUCCESS)
1659 {
1660 #ifdef USE_SERVICE_START_PENDING
1661 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1662 #else
1663 Service->Status.dwCurrentState = SERVICE_RUNNING;
1664 #endif
1665 }
1666 else
1667 {
1668 ScmDereferenceServiceImage(Service->lpImage);
1669 Service->lpImage = NULL;
1670 }
1671 }
1672 }
1673
1674 DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1675
1676 if (dwError == ERROR_SUCCESS)
1677 {
1678 if (Group != NULL)
1679 {
1680 Group->ServicesRunning = TRUE;
1681 }
1682
1683 /* Log a successful service start */
1684 LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
1685 lpLogStrings[0] = Service->lpDisplayName;
1686 lpLogStrings[1] = szLogBuffer;
1687
1688 ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
1689 EVENTLOG_INFORMATION_TYPE,
1690 2,
1691 lpLogStrings);
1692 }
1693 else
1694 {
1695 if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1696 {
1697 /* Log a failed service start */
1698 swprintf(szLogBuffer, L"%lu", dwError);
1699 lpLogStrings[0] = Service->lpServiceName;
1700 lpLogStrings[1] = szLogBuffer;
1701 ScmLogEvent(EVENT_SERVICE_START_FAILED,
1702 EVENTLOG_ERROR_TYPE,
1703 2,
1704 lpLogStrings);
1705 }
1706
1707 #if 0
1708 switch (Service->dwErrorControl)
1709 {
1710 case SERVICE_ERROR_SEVERE:
1711 if (IsLastKnownGood == FALSE)
1712 {
1713 /* FIXME: Boot last known good configuration */
1714 }
1715 break;
1716
1717 case SERVICE_ERROR_CRITICAL:
1718 if (IsLastKnownGood == FALSE)
1719 {
1720 /* FIXME: Boot last known good configuration */
1721 }
1722 else
1723 {
1724 /* FIXME: BSOD! */
1725 }
1726 break;
1727 }
1728 #endif
1729 }
1730
1731 return dwError;
1732 }
1733
1734
1735 DWORD
1736 ScmStartService(PSERVICE Service,
1737 DWORD argc,
1738 LPWSTR* argv)
1739 {
1740 DWORD dwError = ERROR_SUCCESS;
1741 SC_RPC_LOCK Lock = NULL;
1742
1743 DPRINT("ScmStartService() called\n");
1744 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1745
1746 /* Acquire the service control critical section, to synchronize starts */
1747 EnterCriticalSection(&ControlServiceCriticalSection);
1748
1749 /*
1750 * Acquire the user service start lock while the service is starting, if
1751 * needed (i.e. if we are not starting it during the initialization phase).
1752 * If we don't success, bail out.
1753 */
1754 if (!ScmInitialize)
1755 {
1756 dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
1757 if (dwError != ERROR_SUCCESS) goto done;
1758 }
1759
1760 /* Really start the service */
1761 dwError = ScmLoadService(Service, argc, argv);
1762
1763 /* Release the service start lock, if needed, and the critical section */
1764 if (Lock) ScmReleaseServiceStartLock(&Lock);
1765
1766 done:
1767 LeaveCriticalSection(&ControlServiceCriticalSection);
1768
1769 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1770
1771 return dwError;
1772 }
1773
1774
1775 VOID
1776 ScmAutoStartServices(VOID)
1777 {
1778 DWORD dwError;
1779 PLIST_ENTRY GroupEntry;
1780 PLIST_ENTRY ServiceEntry;
1781 PSERVICE_GROUP CurrentGroup;
1782 PSERVICE CurrentService;
1783 WCHAR szSafeBootServicePath[MAX_PATH];
1784 DWORD SafeBootEnabled;
1785 HKEY hKey;
1786 DWORD dwKeySize;
1787 ULONG i;
1788
1789 /*
1790 * This function MUST be called ONLY at initialization time.
1791 * Therefore, no need to acquire the user service start lock.
1792 */
1793 ASSERT(ScmInitialize);
1794
1795 /*
1796 * Retrieve the SafeBoot parameter.
1797 */
1798 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1799 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
1800 0,
1801 KEY_READ,
1802 &hKey);
1803 if (dwError == ERROR_SUCCESS)
1804 {
1805 dwKeySize = sizeof(SafeBootEnabled);
1806 dwError = RegQueryValueExW(hKey,
1807 L"OptionValue",
1808 0,
1809 NULL,
1810 (LPBYTE)&SafeBootEnabled,
1811 &dwKeySize);
1812 RegCloseKey(hKey);
1813 }
1814
1815 /* Default to Normal boot if the value doesn't exist */
1816 if (dwError != ERROR_SUCCESS)
1817 SafeBootEnabled = 0;
1818
1819 /* Acquire the service control critical section, to synchronize starts */
1820 EnterCriticalSection(&ControlServiceCriticalSection);
1821
1822 /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
1823 ServiceEntry = ServiceListHead.Flink;
1824 while (ServiceEntry != &ServiceListHead)
1825 {
1826 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1827
1828 /* Build the safe boot path */
1829 wcscpy(szSafeBootServicePath,
1830 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
1831
1832 switch (SafeBootEnabled)
1833 {
1834 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
1835 case 1:
1836 case 3:
1837 wcscat(szSafeBootServicePath, L"\\Minimal\\");
1838 break;
1839
1840 case 2:
1841 wcscat(szSafeBootServicePath, L"\\Network\\");
1842 break;
1843 }
1844
1845 if (SafeBootEnabled != 0)
1846 {
1847 /* If key does not exist then do not assume safe mode */
1848 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1849 szSafeBootServicePath,
1850 0,
1851 KEY_READ,
1852 &hKey);
1853 if (dwError == ERROR_SUCCESS)
1854 {
1855 RegCloseKey(hKey);
1856
1857 /* Finish Safe Boot path off */
1858 wcsncat(szSafeBootServicePath,
1859 CurrentService->lpServiceName,
1860 MAX_PATH - wcslen(szSafeBootServicePath));
1861
1862 /* Check that the key is in the Safe Boot path */
1863 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1864 szSafeBootServicePath,
1865 0,
1866 KEY_READ,
1867 &hKey);
1868 if (dwError != ERROR_SUCCESS)
1869 {
1870 /* Mark service as visited so it is not auto-started */
1871 CurrentService->ServiceVisited = TRUE;
1872 }
1873 else
1874 {
1875 /* Must be auto-started in safe mode - mark as unvisited */
1876 RegCloseKey(hKey);
1877 CurrentService->ServiceVisited = FALSE;
1878 }
1879 }
1880 else
1881 {
1882 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
1883 CurrentService->ServiceVisited = FALSE;
1884 }
1885 }
1886
1887 ServiceEntry = ServiceEntry->Flink;
1888 }
1889
1890 /* Start all services which are members of an existing group */
1891 GroupEntry = GroupListHead.Flink;
1892 while (GroupEntry != &GroupListHead)
1893 {
1894 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1895
1896 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1897
1898 /* Start all services witch have a valid tag */
1899 for (i = 0; i < CurrentGroup->TagCount; i++)
1900 {
1901 ServiceEntry = ServiceListHead.Flink;
1902 while (ServiceEntry != &ServiceListHead)
1903 {
1904 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1905
1906 if ((CurrentService->lpGroup == CurrentGroup) &&
1907 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1908 (CurrentService->ServiceVisited == FALSE) &&
1909 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1910 {
1911 CurrentService->ServiceVisited = TRUE;
1912 ScmLoadService(CurrentService, 0, NULL);
1913 }
1914
1915 ServiceEntry = ServiceEntry->Flink;
1916 }
1917 }
1918
1919 /* Start all services which have an invalid tag or which do not have a tag */
1920 ServiceEntry = ServiceListHead.Flink;
1921 while (ServiceEntry != &ServiceListHead)
1922 {
1923 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1924
1925 if ((CurrentService->lpGroup == CurrentGroup) &&
1926 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1927 (CurrentService->ServiceVisited == FALSE))
1928 {
1929 CurrentService->ServiceVisited = TRUE;
1930 ScmLoadService(CurrentService, 0, NULL);
1931 }
1932
1933 ServiceEntry = ServiceEntry->Flink;
1934 }
1935
1936 GroupEntry = GroupEntry->Flink;
1937 }
1938
1939 /* Start all services which are members of any non-existing group */
1940 ServiceEntry = ServiceListHead.Flink;
1941 while (ServiceEntry != &ServiceListHead)
1942 {
1943 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1944
1945 if ((CurrentService->lpGroup != NULL) &&
1946 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1947 (CurrentService->ServiceVisited == FALSE))
1948 {
1949 CurrentService->ServiceVisited = TRUE;
1950 ScmLoadService(CurrentService, 0, NULL);
1951 }
1952
1953 ServiceEntry = ServiceEntry->Flink;
1954 }
1955
1956 /* Start all services which are not a member of any group */
1957 ServiceEntry = ServiceListHead.Flink;
1958 while (ServiceEntry != &ServiceListHead)
1959 {
1960 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1961
1962 if ((CurrentService->lpGroup == NULL) &&
1963 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1964 (CurrentService->ServiceVisited == FALSE))
1965 {
1966 CurrentService->ServiceVisited = TRUE;
1967 ScmLoadService(CurrentService, 0, NULL);
1968 }
1969
1970 ServiceEntry = ServiceEntry->Flink;
1971 }
1972
1973 /* Clear 'ServiceVisited' flag again */
1974 ServiceEntry = ServiceListHead.Flink;
1975 while (ServiceEntry != &ServiceListHead)
1976 {
1977 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1978 CurrentService->ServiceVisited = FALSE;
1979 ServiceEntry = ServiceEntry->Flink;
1980 }
1981
1982 /* Release the critical section */
1983 LeaveCriticalSection(&ControlServiceCriticalSection);
1984 }
1985
1986
1987 VOID
1988 ScmAutoShutdownServices(VOID)
1989 {
1990 PLIST_ENTRY ServiceEntry;
1991 PSERVICE CurrentService;
1992
1993 DPRINT("ScmAutoShutdownServices() called\n");
1994
1995 /* Lock the service database exclusively */
1996 ScmLockDatabaseExclusive();
1997
1998 ServiceEntry = ServiceListHead.Flink;
1999 while (ServiceEntry != &ServiceListHead)
2000 {
2001 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2002
2003 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
2004 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
2005 {
2006 /* shutdown service */
2007 DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
2008 ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
2009 }
2010
2011 ServiceEntry = ServiceEntry->Flink;
2012 }
2013
2014 /* Unlock the service database */
2015 ScmUnlockDatabase();
2016
2017 DPRINT("ScmAutoShutdownServices() done\n");
2018 }
2019
2020
2021 BOOL
2022 ScmLockDatabaseExclusive(VOID)
2023 {
2024 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
2025 }
2026
2027
2028 BOOL
2029 ScmLockDatabaseShared(VOID)
2030 {
2031 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
2032 }
2033
2034
2035 VOID
2036 ScmUnlockDatabase(VOID)
2037 {
2038 RtlReleaseResource(&DatabaseLock);
2039 }
2040
2041
2042 VOID
2043 ScmInitNamedPipeCriticalSection(VOID)
2044 {
2045 HKEY hKey;
2046 DWORD dwKeySize;
2047 DWORD dwError;
2048
2049 InitializeCriticalSection(&ControlServiceCriticalSection);
2050
2051 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2052 L"SYSTEM\\CurrentControlSet\\Control",
2053 0,
2054 KEY_READ,
2055 &hKey);
2056 if (dwError == ERROR_SUCCESS)
2057 {
2058 dwKeySize = sizeof(PipeTimeout);
2059 RegQueryValueExW(hKey,
2060 L"ServicesPipeTimeout",
2061 0,
2062 NULL,
2063 (LPBYTE)&PipeTimeout,
2064 &dwKeySize);
2065 RegCloseKey(hKey);
2066 }
2067 }
2068
2069
2070 VOID
2071 ScmDeleteNamedPipeCriticalSection(VOID)
2072 {
2073 DeleteCriticalSection(&ControlServiceCriticalSection);
2074 }
2075
2076 /* EOF */