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