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