[SERVICES]
[reactos.git] / reactos / base / system / services / database.c
1 /*
2 * PROJECT: ReactOS Service Control Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/system/services/database.c
5 * PURPOSE: Database control interface
6 * COPYRIGHT: Copyright 2002-2006 Eric Kohl
7 * Copyright 2006 Hervé Poussineau <hpoussin@reactos.org>
8 * Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
9 * Gregor Brunmar <gregor.brunmar@home.se>
10 *
11 */
12
13 /* INCLUDES *****************************************************************/
14
15 #include "services.h"
16
17 #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(), 0, 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 += (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 PacketSize += (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 += (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_CONTROL_START;
1144 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
1145 ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1146
1147 Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
1148 wcscpy(Ptr, Service->lpServiceName);
1149
1150 ControlPacket->dwArgumentsCount = 0;
1151 ControlPacket->dwArgumentsOffset = 0;
1152
1153 /* Copy argument list */
1154 if (argc > 0 && argv != NULL)
1155 {
1156 Ptr += wcslen(Service->lpServiceName) + 1;
1157 pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
1158 pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
1159
1160 ControlPacket->dwArgumentsCount = argc;
1161 ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
1162
1163 DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
1164 DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
1165
1166 for (i = 0; i < argc; i++)
1167 {
1168 wcscpy(pArgPtr, argv[i]);
1169 *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
1170 DPRINT("offset: %p\n", *pOffPtr);
1171
1172 pArgPtr += wcslen(argv[i]) + 1;
1173 pOffPtr++;
1174 }
1175 }
1176
1177 #ifdef USE_ASYNCHRONOUS_IO
1178 bResult = WriteFile(Service->lpImage->hControlPipe,
1179 ControlPacket,
1180 PacketSize,
1181 &dwWriteCount,
1182 &Overlapped);
1183 if (bResult == FALSE)
1184 {
1185 DPRINT1("WriteFile() returned FALSE\n");
1186
1187 dwError = GetLastError();
1188 if (dwError == ERROR_IO_PENDING)
1189 {
1190 DPRINT1("dwError: ERROR_IO_PENDING\n");
1191
1192 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1193 dwPipeTimeout);
1194 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1195
1196 if (dwError == WAIT_TIMEOUT)
1197 {
1198 bResult = CancelIo(Service->lpImage->hControlPipe);
1199 if (bResult == FALSE)
1200 {
1201 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1202 }
1203
1204 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1205 goto Done;
1206 }
1207 else if (dwError == ERROR_SUCCESS)
1208 {
1209 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1210 &Overlapped,
1211 &dwWriteCount,
1212 TRUE);
1213 if (bResult == FALSE)
1214 {
1215 dwError = GetLastError();
1216 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1217
1218 goto Done;
1219 }
1220 }
1221 }
1222 else
1223 {
1224 DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
1225 goto Done;
1226 }
1227 }
1228
1229 /* Read the reply */
1230 Overlapped.hEvent = (HANDLE) NULL;
1231
1232 bResult = ReadFile(Service->lpImage->hControlPipe,
1233 &ReplyPacket,
1234 sizeof(SCM_REPLY_PACKET),
1235 &dwReadCount,
1236 &Overlapped);
1237 if (bResult == FALSE)
1238 {
1239 DPRINT1("ReadFile() returned FALSE\n");
1240
1241 dwError = GetLastError();
1242 if (dwError == ERROR_IO_PENDING)
1243 {
1244 DPRINT1("dwError: ERROR_IO_PENDING\n");
1245
1246 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1247 dwPipeTimeout);
1248 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1249
1250 if (dwError == WAIT_TIMEOUT)
1251 {
1252 bResult = CancelIo(Service->lpImage->hControlPipe);
1253 if (bResult == FALSE)
1254 {
1255 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1256 }
1257
1258 dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1259 goto Done;
1260 }
1261 else if (dwError == ERROR_SUCCESS)
1262 {
1263 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1264 &Overlapped,
1265 &dwReadCount,
1266 TRUE);
1267 if (bResult == FALSE)
1268 {
1269 dwError = GetLastError();
1270 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1271
1272 goto Done;
1273 }
1274 }
1275 }
1276 else
1277 {
1278 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1279 goto Done;
1280 }
1281 }
1282
1283 #else
1284 /* Send the start command */
1285 bResult = WriteFile(Service->lpImage->hControlPipe,
1286 ControlPacket,
1287 PacketSize,
1288 &dwWriteCount,
1289 NULL);
1290 if (bResult == FALSE)
1291 {
1292 dwError = GetLastError();
1293 DPRINT("WriteFile() failed (Error %lu)\n", dwError);
1294 goto Done;
1295 }
1296
1297 /* Read the reply */
1298 bResult = ReadFile(Service->lpImage->hControlPipe,
1299 &ReplyPacket,
1300 sizeof(SCM_REPLY_PACKET),
1301 &dwReadCount,
1302 NULL);
1303 if (bResult == FALSE)
1304 {
1305 dwError = GetLastError();
1306 DPRINT("ReadFile() failed (Error %lu)\n", dwError);
1307 }
1308 #endif
1309
1310 Done:
1311 /* Release the contol packet */
1312 HeapFree(GetProcessHeap(),
1313 0,
1314 ControlPacket);
1315
1316 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
1317 {
1318 dwError = ReplyPacket.dwError;
1319 }
1320
1321 DPRINT("ScmSendStartCommand() done\n");
1322
1323 return dwError;
1324 }
1325
1326
1327 static DWORD
1328 ScmWaitForServiceConnect(PSERVICE Service)
1329 {
1330 DWORD dwRead = 0;
1331 DWORD dwProcessId = 0;
1332 DWORD dwError = ERROR_SUCCESS;
1333 BOOL bResult;
1334 #ifdef USE_ASYNCHRONOUS_IO
1335 OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
1336 #endif
1337
1338 DPRINT("ScmWaitForServiceConnect()\n");
1339
1340 #ifdef USE_ASYNCHRONOUS_IO
1341 Overlapped.hEvent = (HANDLE)NULL;
1342
1343 bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
1344 &Overlapped);
1345 if (bResult == FALSE)
1346 {
1347 DPRINT("ConnectNamedPipe() returned FALSE\n");
1348
1349 dwError = GetLastError();
1350 if (dwError == ERROR_IO_PENDING)
1351 {
1352 DPRINT("dwError: ERROR_IO_PENDING\n");
1353
1354 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1355 dwPipeTimeout);
1356 DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1357
1358 if (dwError == WAIT_TIMEOUT)
1359 {
1360 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1361
1362 bResult = CancelIo(Service->lpImage->hControlPipe);
1363 if (bResult == FALSE)
1364 {
1365 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1366 }
1367
1368 return ERROR_SERVICE_REQUEST_TIMEOUT;
1369 }
1370 else if (dwError == WAIT_OBJECT_0)
1371 {
1372 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1373 &Overlapped,
1374 &dwRead,
1375 TRUE);
1376 if (bResult == FALSE)
1377 {
1378 dwError = GetLastError();
1379 DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
1380
1381 return dwError;
1382 }
1383 }
1384 }
1385 else if (dwError != ERROR_PIPE_CONNECTED)
1386 {
1387 DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
1388 return dwError;
1389 }
1390 }
1391
1392 DPRINT("Control pipe connected!\n");
1393
1394 Overlapped.hEvent = (HANDLE) NULL;
1395
1396 /* Read the process id from pipe */
1397 bResult = ReadFile(Service->lpImage->hControlPipe,
1398 (LPVOID)&dwProcessId,
1399 sizeof(DWORD),
1400 &dwRead,
1401 &Overlapped);
1402 if (bResult == FALSE)
1403 {
1404 DPRINT("ReadFile() returned FALSE\n");
1405
1406 dwError = GetLastError();
1407 if (dwError == ERROR_IO_PENDING)
1408 {
1409 DPRINT("dwError: ERROR_IO_PENDING\n");
1410
1411 dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1412 dwPipeTimeout);
1413 if (dwError == WAIT_TIMEOUT)
1414 {
1415 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1416
1417 bResult = CancelIo(Service->lpImage->hControlPipe);
1418 if (bResult == FALSE)
1419 {
1420 DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1421 }
1422
1423 return ERROR_SERVICE_REQUEST_TIMEOUT;
1424 }
1425 else if (dwError == ERROR_SUCCESS)
1426 {
1427 DPRINT("WaitForSingleObject() returned ERROR_SUCCESS\n");
1428
1429 DPRINT("Process Id: %lu\n", dwProcessId);
1430
1431 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1432 &Overlapped,
1433 &dwRead,
1434 TRUE);
1435 if (bResult == FALSE)
1436 {
1437 dwError = GetLastError();
1438 DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1439
1440 return dwError;
1441 }
1442 }
1443 else
1444 {
1445 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1446 }
1447 }
1448 else
1449 {
1450 DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1451 return dwError;
1452 }
1453 }
1454
1455 DPRINT1("ScmWaitForServiceConnect() done\n");
1456
1457 return ERROR_SUCCESS;
1458 #else
1459
1460 /* Connect control pipe */
1461 if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
1462 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
1463 {
1464 DPRINT("Control pipe connected!\n");
1465
1466 /* Read SERVICE_STATUS_HANDLE from pipe */
1467 bResult = ReadFile(Service->lpImage->hControlPipe,
1468 (LPVOID)&dwProcessId,
1469 sizeof(DWORD),
1470 &dwRead,
1471 NULL);
1472 if (bResult == FALSE)
1473 {
1474 dwError = GetLastError();
1475 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
1476 dwError);
1477 }
1478 else
1479 {
1480 dwError = ERROR_SUCCESS;
1481 DPRINT("Read control pipe successfully\n");
1482 }
1483 }
1484 else
1485 {
1486 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1487 }
1488
1489 return dwError;
1490 #endif
1491 }
1492
1493
1494 static DWORD
1495 ScmStartUserModeService(PSERVICE Service,
1496 DWORD argc,
1497 LPWSTR *argv)
1498 {
1499 PROCESS_INFORMATION ProcessInformation;
1500 STARTUPINFOW StartupInfo;
1501 BOOL Result;
1502 DWORD dwError = ERROR_SUCCESS;
1503
1504 DPRINT("ScmStartUserModeService(%p)\n", Service);
1505
1506 /* If the image is already running ... */
1507 if (Service->lpImage->dwImageRunCount > 1)
1508 {
1509 /* ... just send a start command */
1510 return ScmSendStartCommand(Service, argc, argv);
1511 }
1512
1513 StartupInfo.cb = sizeof(StartupInfo);
1514 StartupInfo.lpReserved = NULL;
1515 StartupInfo.lpDesktop = NULL;
1516 StartupInfo.lpTitle = NULL;
1517 StartupInfo.dwFlags = 0;
1518 StartupInfo.cbReserved2 = 0;
1519 StartupInfo.lpReserved2 = 0;
1520
1521 Result = CreateProcessW(NULL,
1522 Service->lpImage->szImagePath,
1523 NULL,
1524 NULL,
1525 FALSE,
1526 DETACHED_PROCESS | CREATE_SUSPENDED,
1527 NULL,
1528 NULL,
1529 &StartupInfo,
1530 &ProcessInformation);
1531 if (!Result)
1532 {
1533 dwError = GetLastError();
1534 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
1535 return dwError;
1536 }
1537
1538 DPRINT("Process Id: %lu Handle %p\n",
1539 ProcessInformation.dwProcessId,
1540 ProcessInformation.hProcess);
1541 DPRINT("Thread Id: %lu Handle %p\n",
1542 ProcessInformation.dwThreadId,
1543 ProcessInformation.hThread);
1544
1545 /* Get process handle and id */
1546 Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1547
1548 /* Resume Thread */
1549 ResumeThread(ProcessInformation.hThread);
1550
1551 /* Connect control pipe */
1552 dwError = ScmWaitForServiceConnect(Service);
1553 if (dwError == ERROR_SUCCESS)
1554 {
1555 /* Send start command */
1556 dwError = ScmSendStartCommand(Service,
1557 argc,
1558 argv);
1559 }
1560 else
1561 {
1562 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1563 Service->lpImage->dwProcessId = 0;
1564 }
1565
1566 /* Close thread and process handle */
1567 CloseHandle(ProcessInformation.hThread);
1568 CloseHandle(ProcessInformation.hProcess);
1569
1570 return dwError;
1571 }
1572
1573
1574 DWORD
1575 ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
1576 {
1577 PSERVICE_GROUP Group = Service->lpGroup;
1578 DWORD dwError = ERROR_SUCCESS;
1579 LPCWSTR ErrorLogStrings[2];
1580 WCHAR szErrorBuffer[32];
1581
1582 DPRINT("ScmStartService() called\n");
1583
1584 DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1585
1586 EnterCriticalSection(&ControlServiceCriticalSection);
1587
1588 if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1589 {
1590 DPRINT("Service %S is already running!\n", Service->lpServiceName);
1591 LeaveCriticalSection(&ControlServiceCriticalSection);
1592 return ERROR_SERVICE_ALREADY_RUNNING;
1593 }
1594
1595 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1596
1597 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1598 {
1599 /* Load driver */
1600 dwError = ScmLoadDriver(Service);
1601 if (dwError == ERROR_SUCCESS)
1602 {
1603 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1604 Service->Status.dwCurrentState = SERVICE_RUNNING;
1605 }
1606 }
1607 else
1608 {
1609 /* Start user-mode service */
1610 dwError = ScmCreateOrReferenceServiceImage(Service);
1611 if (dwError == ERROR_SUCCESS)
1612 {
1613 dwError = ScmStartUserModeService(Service, argc, argv);
1614 if (dwError == ERROR_SUCCESS)
1615 {
1616 #ifdef USE_SERVICE_START_PENDING
1617 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1618 #else
1619 Service->Status.dwCurrentState = SERVICE_RUNNING;
1620 #endif
1621 }
1622 else
1623 {
1624 ScmDereferenceServiceImage(Service->lpImage);
1625 Service->lpImage = NULL;
1626 }
1627 }
1628 }
1629
1630 LeaveCriticalSection(&ControlServiceCriticalSection);
1631
1632 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1633
1634 if (dwError == ERROR_SUCCESS)
1635 {
1636 if (Group != NULL)
1637 {
1638 Group->ServicesRunning = TRUE;
1639 }
1640 }
1641 else
1642 {
1643 if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1644 {
1645 /* Log a failed service start */
1646 swprintf(szErrorBuffer, L"%lu", dwError);
1647 ErrorLogStrings[0] = Service->lpServiceName;
1648 ErrorLogStrings[1] = szErrorBuffer;
1649 ScmLogError(EVENT_SERVICE_START_FAILED,
1650 2,
1651 ErrorLogStrings);
1652 }
1653
1654 #if 0
1655 switch (Service->dwErrorControl)
1656 {
1657 case SERVICE_ERROR_SEVERE:
1658 if (IsLastKnownGood == FALSE)
1659 {
1660 /* FIXME: Boot last known good configuration */
1661 }
1662 break;
1663
1664 case SERVICE_ERROR_CRITICAL:
1665 if (IsLastKnownGood == FALSE)
1666 {
1667 /* FIXME: Boot last known good configuration */
1668 }
1669 else
1670 {
1671 /* FIXME: BSOD! */
1672 }
1673 break;
1674 }
1675 #endif
1676 }
1677
1678 return dwError;
1679 }
1680
1681
1682 VOID
1683 ScmAutoStartServices(VOID)
1684 {
1685 PLIST_ENTRY GroupEntry;
1686 PLIST_ENTRY ServiceEntry;
1687 PSERVICE_GROUP CurrentGroup;
1688 PSERVICE CurrentService;
1689 WCHAR szSafeBootServicePath[MAX_PATH];
1690 DWORD dwError;
1691 HKEY hKey;
1692 ULONG i;
1693
1694 /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
1695 ServiceEntry = ServiceListHead.Flink;
1696 while (ServiceEntry != &ServiceListHead)
1697 {
1698 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1699
1700 /* Build the safe boot path */
1701 wcscpy(szSafeBootServicePath,
1702 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
1703
1704 switch (GetSystemMetrics(SM_CLEANBOOT))
1705 {
1706 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
1707 case 1:
1708 case 3:
1709 wcscat(szSafeBootServicePath, L"\\Minimal\\");
1710 break;
1711
1712 case 2:
1713 wcscat(szSafeBootServicePath, L"\\Network\\");
1714 break;
1715 }
1716
1717 if (GetSystemMetrics(SM_CLEANBOOT))
1718 {
1719 /* If key does not exist then do not assume safe mode */
1720 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1721 szSafeBootServicePath,
1722 0,
1723 KEY_READ,
1724 &hKey);
1725 if (dwError == ERROR_SUCCESS)
1726 {
1727 RegCloseKey(hKey);
1728
1729 /* Finish Safe Boot path off */
1730 wcsncat(szSafeBootServicePath,
1731 CurrentService->lpServiceName,
1732 MAX_PATH - wcslen(szSafeBootServicePath));
1733
1734 /* Check that the key is in the Safe Boot path */
1735 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1736 szSafeBootServicePath,
1737 0,
1738 KEY_READ,
1739 &hKey);
1740 if (dwError != ERROR_SUCCESS)
1741 {
1742 /* Mark service as visited so it is not auto-started */
1743 CurrentService->ServiceVisited = TRUE;
1744 }
1745 else
1746 {
1747 /* Must be auto-started in safe mode - mark as unvisited */
1748 RegCloseKey(hKey);
1749 CurrentService->ServiceVisited = FALSE;
1750 }
1751 }
1752 else
1753 {
1754 DPRINT1("WARNING: Could not open the associated Safe Boot key!");
1755 CurrentService->ServiceVisited = FALSE;
1756 }
1757 }
1758
1759 ServiceEntry = ServiceEntry->Flink;
1760 }
1761
1762 /* Start all services which are members of an existing group */
1763 GroupEntry = GroupListHead.Flink;
1764 while (GroupEntry != &GroupListHead)
1765 {
1766 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1767
1768 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1769
1770 /* Start all services witch have a valid tag */
1771 for (i = 0; i < CurrentGroup->TagCount; i++)
1772 {
1773 ServiceEntry = ServiceListHead.Flink;
1774 while (ServiceEntry != &ServiceListHead)
1775 {
1776 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1777
1778 if ((CurrentService->lpGroup == CurrentGroup) &&
1779 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1780 (CurrentService->ServiceVisited == FALSE) &&
1781 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1782 {
1783 CurrentService->ServiceVisited = TRUE;
1784 ScmStartService(CurrentService, 0, NULL);
1785 }
1786
1787 ServiceEntry = ServiceEntry->Flink;
1788 }
1789 }
1790
1791 /* Start all services which have an invalid tag or which do not have a tag */
1792 ServiceEntry = ServiceListHead.Flink;
1793 while (ServiceEntry != &ServiceListHead)
1794 {
1795 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1796
1797 if ((CurrentService->lpGroup == CurrentGroup) &&
1798 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1799 (CurrentService->ServiceVisited == FALSE))
1800 {
1801 CurrentService->ServiceVisited = TRUE;
1802 ScmStartService(CurrentService, 0, NULL);
1803 }
1804
1805 ServiceEntry = ServiceEntry->Flink;
1806 }
1807
1808 GroupEntry = GroupEntry->Flink;
1809 }
1810
1811 /* Start all services which are members of any non-existing group */
1812 ServiceEntry = ServiceListHead.Flink;
1813 while (ServiceEntry != &ServiceListHead)
1814 {
1815 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1816
1817 if ((CurrentService->lpGroup != NULL) &&
1818 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1819 (CurrentService->ServiceVisited == FALSE))
1820 {
1821 CurrentService->ServiceVisited = TRUE;
1822 ScmStartService(CurrentService, 0, NULL);
1823 }
1824
1825 ServiceEntry = ServiceEntry->Flink;
1826 }
1827
1828 /* Start all services which are not a member of any group */
1829 ServiceEntry = ServiceListHead.Flink;
1830 while (ServiceEntry != &ServiceListHead)
1831 {
1832 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1833
1834 if ((CurrentService->lpGroup == NULL) &&
1835 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1836 (CurrentService->ServiceVisited == FALSE))
1837 {
1838 CurrentService->ServiceVisited = TRUE;
1839 ScmStartService(CurrentService, 0, NULL);
1840 }
1841
1842 ServiceEntry = ServiceEntry->Flink;
1843 }
1844
1845 /* Clear 'ServiceVisited' flag again */
1846 ServiceEntry = ServiceListHead.Flink;
1847 while (ServiceEntry != &ServiceListHead)
1848 {
1849 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1850 CurrentService->ServiceVisited = FALSE;
1851 ServiceEntry = ServiceEntry->Flink;
1852 }
1853 }
1854
1855
1856 VOID
1857 ScmAutoShutdownServices(VOID)
1858 {
1859 PLIST_ENTRY ServiceEntry;
1860 PSERVICE CurrentService;
1861
1862 DPRINT("ScmAutoShutdownServices() called\n");
1863
1864 /* Lock the service database exclusively */
1865 ScmLockDatabaseExclusive();
1866
1867 ServiceEntry = ServiceListHead.Flink;
1868 while (ServiceEntry != &ServiceListHead)
1869 {
1870 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1871
1872 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
1873 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
1874 {
1875 /* shutdown service */
1876 DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
1877 ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
1878 }
1879
1880 ServiceEntry = ServiceEntry->Flink;
1881 }
1882
1883 /* Unlock the service database */
1884 ScmUnlockDatabase();
1885
1886 DPRINT("ScmAutoShutdownServices() done\n");
1887 }
1888
1889
1890 BOOL
1891 ScmLockDatabaseExclusive(VOID)
1892 {
1893 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
1894 }
1895
1896
1897 BOOL
1898 ScmLockDatabaseShared(VOID)
1899 {
1900 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
1901 }
1902
1903
1904 VOID
1905 ScmUnlockDatabase(VOID)
1906 {
1907 RtlReleaseResource(&DatabaseLock);
1908 }
1909
1910
1911 VOID
1912 ScmInitNamedPipeCriticalSection(VOID)
1913 {
1914 HKEY hKey;
1915 DWORD dwKeySize;
1916 DWORD dwError;
1917
1918 InitializeCriticalSection(&ControlServiceCriticalSection);
1919
1920 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1921 L"SYSTEM\\CurrentControlSet\\Control",
1922 0,
1923 KEY_READ,
1924 &hKey);
1925 if (dwError == ERROR_SUCCESS)
1926 {
1927 dwKeySize = sizeof(DWORD);
1928 RegQueryValueExW(hKey,
1929 L"ServicesPipeTimeout",
1930 0,
1931 NULL,
1932 (LPBYTE)&dwPipeTimeout,
1933 &dwKeySize);
1934
1935 RegCloseKey(hKey);
1936 }
1937 }
1938
1939
1940 VOID
1941 ScmDeleteNamedPipeCriticalSection(VOID)
1942 {
1943 DeleteCriticalSection(&ControlServiceCriticalSection);
1944 }
1945
1946 /* EOF */