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