- Delete the service key along with any subkeys
[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 /* GLOBALS *******************************************************************/
22
23 LIST_ENTRY ServiceListHead;
24
25 static RTL_RESOURCE DatabaseLock;
26 static DWORD dwResumeCount = 1;
27
28
29 /* FUNCTIONS *****************************************************************/
30
31
32 PSERVICE
33 ScmGetServiceEntryByName(LPWSTR lpServiceName)
34 {
35 PLIST_ENTRY ServiceEntry;
36 PSERVICE CurrentService;
37
38 DPRINT("ScmGetServiceEntryByName() called\n");
39
40 ServiceEntry = ServiceListHead.Flink;
41 while (ServiceEntry != &ServiceListHead)
42 {
43 CurrentService = CONTAINING_RECORD(ServiceEntry,
44 SERVICE,
45 ServiceListEntry);
46 if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
47 {
48 DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
49 return CurrentService;
50 }
51
52 ServiceEntry = ServiceEntry->Flink;
53 }
54
55 DPRINT("Couldn't find a matching service\n");
56
57 return NULL;
58 }
59
60
61 PSERVICE
62 ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName)
63 {
64 PLIST_ENTRY ServiceEntry;
65 PSERVICE CurrentService;
66
67 DPRINT("ScmGetServiceEntryByDisplayName() called\n");
68
69 ServiceEntry = ServiceListHead.Flink;
70 while (ServiceEntry != &ServiceListHead)
71 {
72 CurrentService = CONTAINING_RECORD(ServiceEntry,
73 SERVICE,
74 ServiceListEntry);
75 if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
76 {
77 DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
78 return CurrentService;
79 }
80
81 ServiceEntry = ServiceEntry->Flink;
82 }
83
84 DPRINT("Couldn't find a matching service\n");
85
86 return NULL;
87 }
88
89
90 PSERVICE
91 ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
92 {
93 PLIST_ENTRY ServiceEntry;
94 PSERVICE CurrentService;
95
96 DPRINT("ScmGetServiceEntryByResumeCount() called\n");
97
98 ServiceEntry = ServiceListHead.Flink;
99 while (ServiceEntry != &ServiceListHead)
100 {
101 CurrentService = CONTAINING_RECORD(ServiceEntry,
102 SERVICE,
103 ServiceListEntry);
104 if (CurrentService->dwResumeCount > dwResumeCount)
105 {
106 DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
107 return CurrentService;
108 }
109
110 ServiceEntry = ServiceEntry->Flink;
111 }
112
113 DPRINT("Couldn't find a matching service\n");
114
115 return NULL;
116 }
117
118
119 PSERVICE
120 ScmGetServiceEntryByClientHandle(ULONG Handle)
121 {
122 PLIST_ENTRY ServiceEntry;
123 PSERVICE CurrentService;
124
125 DPRINT("ScmGetServiceEntryByClientHandle() called\n");
126 DPRINT("looking for %lu\n", Handle);
127
128 ServiceEntry = ServiceListHead.Flink;
129 while (ServiceEntry != &ServiceListHead)
130 {
131 CurrentService = CONTAINING_RECORD(ServiceEntry,
132 SERVICE,
133 ServiceListEntry);
134
135 if (CurrentService->hClient == Handle)
136 {
137 DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
138 return CurrentService;
139 }
140
141 ServiceEntry = ServiceEntry->Flink;
142 }
143
144 DPRINT("Couldn't find a matching service\n");
145
146 return NULL;
147 }
148
149
150 DWORD
151 ScmCreateNewServiceRecord(LPWSTR lpServiceName,
152 PSERVICE *lpServiceRecord)
153 {
154 PSERVICE lpService = NULL;
155
156 DPRINT("Service: '%S'\n", lpServiceName);
157
158 /* Allocate service entry */
159 lpService = (SERVICE*) HeapAlloc(GetProcessHeap(),
160 HEAP_ZERO_MEMORY,
161 sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR)));
162 if (lpService == NULL)
163 return ERROR_NOT_ENOUGH_MEMORY;
164
165 *lpServiceRecord = lpService;
166
167 /* Copy service name */
168 wcscpy(lpService->szServiceName, lpServiceName);
169 lpService->lpServiceName = lpService->szServiceName;
170 lpService->lpDisplayName = lpService->lpServiceName;
171
172 /* Set the resume count */
173 lpService->dwResumeCount = dwResumeCount++;
174
175 /* Append service entry */
176 InsertTailList(&ServiceListHead,
177 &lpService->ServiceListEntry);
178
179 lpService->Status.dwCurrentState = SERVICE_STOPPED;
180 lpService->Status.dwControlsAccepted = 0;
181 lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
182 lpService->Status.dwServiceSpecificExitCode = 0;
183 lpService->Status.dwCheckPoint = 0;
184 lpService->Status.dwWaitHint = 2000; /* 2 seconds */
185
186 return ERROR_SUCCESS;
187 }
188
189
190 static DWORD
191 CreateServiceListEntry(LPWSTR lpServiceName,
192 HKEY hServiceKey)
193 {
194 PSERVICE lpService = NULL;
195 LPWSTR lpDisplayName = NULL;
196 LPWSTR lpGroup = NULL;
197 DWORD dwSize;
198 DWORD dwError;
199 DWORD dwServiceType;
200 DWORD dwStartType;
201 DWORD dwErrorControl;
202 DWORD dwTagId;
203
204 DPRINT("Service: '%S'\n", lpServiceName);
205 if (*lpServiceName == L'{')
206 return ERROR_SUCCESS;
207
208 dwSize = sizeof(DWORD);
209 dwError = RegQueryValueExW(hServiceKey,
210 L"Type",
211 NULL,
212 NULL,
213 (LPBYTE)&dwServiceType,
214 &dwSize);
215 if (dwError != ERROR_SUCCESS)
216 return ERROR_SUCCESS;
217
218 if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
219 ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
220 (dwServiceType != SERVICE_KERNEL_DRIVER) &&
221 (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
222 return ERROR_SUCCESS;
223
224 DPRINT("Service type: %lx\n", dwServiceType);
225
226 dwSize = sizeof(DWORD);
227 dwError = RegQueryValueExW(hServiceKey,
228 L"Start",
229 NULL,
230 NULL,
231 (LPBYTE)&dwStartType,
232 &dwSize);
233 if (dwError != ERROR_SUCCESS)
234 return ERROR_SUCCESS;
235
236 DPRINT("Start type: %lx\n", dwStartType);
237
238 dwSize = sizeof(DWORD);
239 dwError = RegQueryValueExW(hServiceKey,
240 L"ErrorControl",
241 NULL,
242 NULL,
243 (LPBYTE)&dwErrorControl,
244 &dwSize);
245 if (dwError != ERROR_SUCCESS)
246 return ERROR_SUCCESS;
247
248 DPRINT("Error control: %lx\n", dwErrorControl);
249
250 dwError = RegQueryValueExW(hServiceKey,
251 L"Tag",
252 NULL,
253 NULL,
254 (LPBYTE)&dwTagId,
255 &dwSize);
256 if (dwError != ERROR_SUCCESS)
257 dwTagId = 0;
258
259 DPRINT("Tag: %lx\n", dwTagId);
260
261 dwError = ScmReadString(hServiceKey,
262 L"Group",
263 &lpGroup);
264 if (dwError != ERROR_SUCCESS)
265 lpGroup = NULL;
266
267 DPRINT("Group: %S\n", lpGroup);
268
269 dwError = ScmReadString(hServiceKey,
270 L"DisplayName",
271 &lpDisplayName);
272 if (dwError != ERROR_SUCCESS)
273 lpDisplayName = NULL;
274
275 DPRINT("Display name: %S\n", lpDisplayName);
276
277 dwError = ScmCreateNewServiceRecord(lpServiceName,
278 &lpService);
279 if (dwError != ERROR_SUCCESS)
280 goto done;
281
282 lpService->Status.dwServiceType = dwServiceType;
283 lpService->dwStartType = dwStartType;
284 lpService->dwErrorControl = dwErrorControl;
285 lpService->dwTag = dwTagId;
286
287 if (lpGroup != NULL)
288 {
289 dwError = ScmSetServiceGroup(lpService, lpGroup);
290 if (dwError != ERROR_SUCCESS)
291 goto done;
292 }
293
294 if (lpDisplayName != NULL)
295 {
296 lpService->lpDisplayName = lpDisplayName;
297 lpDisplayName = NULL;
298 }
299
300 DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
301 if (lpService->lpGroup != NULL)
302 {
303 DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
304 }
305 DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
306 lpService->dwStartType,
307 lpService->Status.dwServiceType,
308 lpService->dwTag,
309 lpService->dwErrorControl);
310
311 if (ScmIsDeleteFlagSet(hServiceKey))
312 lpService->bDeleted = TRUE;
313
314 done:;
315 if (lpGroup != NULL)
316 HeapFree(GetProcessHeap(), 0, lpGroup);
317
318 if (lpDisplayName != NULL)
319 HeapFree(GetProcessHeap(), 0, lpDisplayName);
320
321 return dwError;
322 }
323
324
325 DWORD
326 ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
327 {
328 DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
329 WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
330 HKEY hSubKey = 0;
331
332 dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
333 if (!dwRet)
334 {
335 /* Find the maximum subkey length so that we can allocate a buffer */
336 dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
337 &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
338 if (!dwRet)
339 {
340 dwMaxSubkeyLen++;
341 if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
342 /* Name too big: alloc a buffer for it */
343 lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
344
345 if(!lpszName)
346 dwRet = ERROR_NOT_ENOUGH_MEMORY;
347 else
348 {
349 while (dwRet == ERROR_SUCCESS)
350 {
351 dwSize = dwMaxSubkeyLen;
352 dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
353 if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
354 dwRet = ScmDeleteRegKey(hSubKey, lpszName);
355 }
356 if (dwRet == ERROR_NO_MORE_ITEMS)
357 dwRet = ERROR_SUCCESS;
358
359 if (lpszName != szNameBuf)
360 HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
361 }
362 }
363
364 RegCloseKey(hSubKey);
365 if (!dwRet)
366 dwRet = RegDeleteKeyW(hKey, lpszSubKey);
367 }
368 return dwRet;
369 }
370
371
372 VOID
373 ScmDeleteMarkedServices(VOID)
374 {
375 PLIST_ENTRY ServiceEntry;
376 PSERVICE CurrentService;
377 HKEY hServicesKey;
378 DWORD dwError;
379
380 ServiceEntry = ServiceListHead.Flink;
381 while (ServiceEntry != &ServiceListHead)
382 {
383 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
384
385 ServiceEntry = ServiceEntry->Flink;
386
387 if (CurrentService->bDeleted == TRUE)
388 {
389 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
390 L"System\\CurrentControlSet\\Services",
391 0,
392 DELETE,
393 &hServicesKey);
394 if (dwError == ERROR_SUCCESS)
395 {
396 dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
397 RegCloseKey(hServicesKey);
398 if (dwError == ERROR_SUCCESS)
399 {
400 RemoveEntryList(&CurrentService->ServiceListEntry);
401 HeapFree(GetProcessHeap(), 0, CurrentService);
402 }
403 }
404
405 if (dwError != ERROR_SUCCESS)
406 DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
407 }
408 }
409 }
410
411
412 DWORD
413 ScmCreateServiceDatabase(VOID)
414 {
415 WCHAR szSubKey[MAX_PATH];
416 HKEY hServicesKey;
417 HKEY hServiceKey;
418 DWORD dwSubKey;
419 DWORD dwSubKeyLength;
420 FILETIME ftLastChanged;
421 DWORD dwError;
422
423 DPRINT("ScmCreateServiceDatabase() called\n");
424
425 dwError = ScmCreateGroupList();
426 if (dwError != ERROR_SUCCESS)
427 return dwError;
428
429 /* Initialize basic variables */
430 InitializeListHead(&ServiceListHead);
431
432 /* Initialize the database lock */
433 RtlInitializeResource(&DatabaseLock);
434
435 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
436 L"System\\CurrentControlSet\\Services",
437 0,
438 KEY_READ,
439 &hServicesKey);
440 if (dwError != ERROR_SUCCESS)
441 return dwError;
442
443 dwSubKey = 0;
444 for (;;)
445 {
446 dwSubKeyLength = MAX_PATH;
447 dwError = RegEnumKeyExW(hServicesKey,
448 dwSubKey,
449 szSubKey,
450 &dwSubKeyLength,
451 NULL,
452 NULL,
453 NULL,
454 &ftLastChanged);
455 if (dwError == ERROR_SUCCESS &&
456 szSubKey[0] != L'{')
457 {
458 DPRINT("SubKeyName: '%S'\n", szSubKey);
459
460 dwError = RegOpenKeyExW(hServicesKey,
461 szSubKey,
462 0,
463 KEY_READ,
464 &hServiceKey);
465 if (dwError == ERROR_SUCCESS)
466 {
467 dwError = CreateServiceListEntry(szSubKey,
468 hServiceKey);
469
470 RegCloseKey(hServiceKey);
471 }
472 }
473
474 if (dwError != ERROR_SUCCESS)
475 break;
476
477 dwSubKey++;
478 }
479
480 RegCloseKey(hServicesKey);
481
482 /* Delete services that are marked for delete */
483 ScmDeleteMarkedServices();
484
485 DPRINT("ScmCreateServiceDatabase() done\n");
486
487 return ERROR_SUCCESS;
488 }
489
490
491 VOID
492 ScmShutdownServiceDatabase(VOID)
493 {
494 DPRINT("ScmShutdownServiceDatabase() called\n");
495
496 ScmDeleteMarkedServices();
497 RtlDeleteResource(&DatabaseLock);
498
499 DPRINT("ScmShutdownServiceDatabase() done\n");
500 }
501
502
503 static NTSTATUS
504 ScmCheckDriver(PSERVICE Service)
505 {
506 OBJECT_ATTRIBUTES ObjectAttributes;
507 UNICODE_STRING DirName;
508 HANDLE DirHandle;
509 NTSTATUS Status;
510 POBJECT_DIRECTORY_INFORMATION DirInfo;
511 ULONG BufferLength;
512 ULONG DataLength;
513 ULONG Index;
514
515 DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
516
517 if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
518 {
519 RtlInitUnicodeString(&DirName,
520 L"\\Driver");
521 }
522 else
523 {
524 RtlInitUnicodeString(&DirName,
525 L"\\FileSystem");
526 }
527
528 InitializeObjectAttributes(&ObjectAttributes,
529 &DirName,
530 0,
531 NULL,
532 NULL);
533
534 Status = NtOpenDirectoryObject(&DirHandle,
535 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
536 &ObjectAttributes);
537 if (!NT_SUCCESS(Status))
538 {
539 return Status;
540 }
541
542 BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
543 2 * MAX_PATH * sizeof(WCHAR);
544 DirInfo = (OBJECT_DIRECTORY_INFORMATION*) HeapAlloc(GetProcessHeap(),
545 HEAP_ZERO_MEMORY,
546 BufferLength);
547
548 Index = 0;
549 while (TRUE)
550 {
551 Status = NtQueryDirectoryObject(DirHandle,
552 DirInfo,
553 BufferLength,
554 TRUE,
555 FALSE,
556 &Index,
557 &DataLength);
558 if (Status == STATUS_NO_MORE_ENTRIES)
559 {
560 /* FIXME: Add current service to 'failed service' list */
561 DPRINT("Service '%S' failed\n", Service->lpServiceName);
562 break;
563 }
564
565 if (!NT_SUCCESS(Status))
566 break;
567
568 DPRINT("Comparing: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
569
570 if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
571 {
572 DPRINT("Found: '%S' '%wZ'\n",
573 Service->lpServiceName, &DirInfo->Name);
574
575 /* Mark service as 'running' */
576 Service->Status.dwCurrentState = SERVICE_RUNNING;
577
578 /* Mark the service group as 'running' */
579 if (Service->lpGroup != NULL)
580 {
581 Service->lpGroup->ServicesRunning = TRUE;
582 }
583
584 break;
585 }
586 }
587
588 HeapFree(GetProcessHeap(),
589 0,
590 DirInfo);
591 NtClose(DirHandle);
592
593 return STATUS_SUCCESS;
594 }
595
596
597 VOID
598 ScmGetBootAndSystemDriverState(VOID)
599 {
600 PLIST_ENTRY ServiceEntry;
601 PSERVICE CurrentService;
602
603 DPRINT("ScmGetBootAndSystemDriverState() called\n");
604
605 ServiceEntry = ServiceListHead.Flink;
606 while (ServiceEntry != &ServiceListHead)
607 {
608 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
609
610 if (CurrentService->dwStartType == SERVICE_BOOT_START ||
611 CurrentService->dwStartType == SERVICE_SYSTEM_START)
612 {
613 /* Check driver */
614 DPRINT(" Checking service: %S\n", CurrentService->lpServiceName);
615
616 ScmCheckDriver(CurrentService);
617 }
618
619 ServiceEntry = ServiceEntry->Flink;
620 }
621
622 DPRINT("ScmGetBootAndSystemDriverState() done\n");
623 }
624
625
626 DWORD
627 ScmControlService(PSERVICE Service,
628 DWORD dwControl,
629 LPSERVICE_STATUS lpServiceStatus)
630 {
631 PSCM_CONTROL_PACKET ControlPacket;
632 DWORD Count;
633 DWORD TotalLength;
634
635 DPRINT("ScmControlService() called\n");
636
637 TotalLength = wcslen(Service->lpServiceName) + 1;
638
639 ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
640 HEAP_ZERO_MEMORY,
641 sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)));
642 if (ControlPacket == NULL)
643 return ERROR_NOT_ENOUGH_MEMORY;
644
645 ControlPacket->dwControl = dwControl;
646 ControlPacket->hClient = Service->hClient;
647 ControlPacket->dwSize = TotalLength;
648 wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName);
649
650 /* Send the start command */
651 WriteFile(Service->ControlPipeHandle,
652 ControlPacket,
653 sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
654 &Count,
655 NULL);
656
657 /* FIXME: Read the reply */
658
659 /* Release the contol packet */
660 HeapFree(GetProcessHeap(),
661 0,
662 ControlPacket);
663
664 RtlCopyMemory(lpServiceStatus,
665 &Service->Status,
666 sizeof(SERVICE_STATUS));
667
668 DPRINT("ScmControlService) done\n");
669
670 return ERROR_SUCCESS;
671 }
672
673
674 static DWORD
675 ScmSendStartCommand(PSERVICE Service,
676 DWORD argc,
677 LPWSTR *argv)
678 {
679 PSCM_CONTROL_PACKET ControlPacket;
680 DWORD TotalLength;
681 DWORD ArgsLength = 0;
682 DWORD Length;
683 PWSTR Ptr;
684 DWORD Count;
685
686 DPRINT("ScmSendStartCommand() called\n");
687
688 /* Calculate the total length of the start command line */
689 TotalLength = wcslen(Service->lpServiceName) + 1;
690 if (argc > 0)
691 {
692 for (Count = 0; Count < argc; Count++)
693 {
694 DPRINT("Arg: %S\n", argv[Count]);
695 Length = wcslen(argv[Count]) + 1;
696 TotalLength += Length;
697 ArgsLength += Length;
698 }
699 }
700 TotalLength++;
701 DPRINT("ArgsLength: %ld TotalLength: %ld\n", ArgsLength, TotalLength);
702
703 /* Allocate a control packet */
704 ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
705 HEAP_ZERO_MEMORY,
706 sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
707 if (ControlPacket == NULL)
708 return ERROR_NOT_ENOUGH_MEMORY;
709
710 ControlPacket->dwControl = SERVICE_CONTROL_START;
711 ControlPacket->hClient = Service->hClient;
712 ControlPacket->dwSize = TotalLength;
713 Ptr = &ControlPacket->szArguments[0];
714 wcscpy(Ptr, Service->lpServiceName);
715 Ptr += (wcslen(Service->lpServiceName) + 1);
716
717 /* Copy argument list */
718 if (argc > 0)
719 {
720 UNIMPLEMENTED;
721 DPRINT1("Arguments sent to service ignored!\n");
722 #if 0
723 memcpy(Ptr, Arguments, ArgsLength);
724 Ptr += ArgsLength;
725 #endif
726 }
727
728 /* Terminate the argument list */
729 *Ptr = 0;
730
731 /* Send the start command */
732 WriteFile(Service->ControlPipeHandle,
733 ControlPacket,
734 sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
735 &Count,
736 NULL);
737
738 /* FIXME: Read the reply */
739
740 /* Release the contol packet */
741 HeapFree(GetProcessHeap(),
742 0,
743 ControlPacket);
744
745 DPRINT("ScmSendStartCommand() done\n");
746
747 return ERROR_SUCCESS;
748 }
749
750
751 static DWORD
752 ScmStartUserModeService(PSERVICE Service,
753 DWORD argc,
754 LPWSTR *argv)
755 {
756 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
757 PROCESS_INFORMATION ProcessInformation;
758 STARTUPINFOW StartupInfo;
759 UNICODE_STRING ImagePath;
760 ULONG Type;
761 DWORD ServiceCurrent = 0;
762 BOOL Result;
763 NTSTATUS Status;
764 DWORD dwError = ERROR_SUCCESS;
765 WCHAR NtControlPipeName[MAX_PATH + 1];
766 HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
767 DWORD KeyDisposition;
768
769 RtlInitUnicodeString(&ImagePath, NULL);
770
771 /* Get service data */
772 RtlZeroMemory(&QueryTable,
773 sizeof(QueryTable));
774
775 QueryTable[0].Name = L"Type";
776 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
777 QueryTable[0].EntryContext = &Type;
778
779 QueryTable[1].Name = L"ImagePath";
780 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
781 QueryTable[1].EntryContext = &ImagePath;
782
783 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
784 Service->lpServiceName,
785 QueryTable,
786 NULL,
787 NULL);
788 if (!NT_SUCCESS(Status))
789 {
790 DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
791 return RtlNtStatusToDosError(Status);
792 }
793 DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
794 DPRINT("Type: %lx\n", Type);
795
796 /* Get the service number */
797 /* TODO: Create registry entry with correct write access */
798 Status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
799 L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
800 REG_OPTION_VOLATILE,
801 KEY_WRITE | KEY_READ,
802 NULL,
803 &hServiceCurrentKey,
804 &KeyDisposition);
805
806 if (ERROR_SUCCESS != Status)
807 {
808 DPRINT1("RegCreateKeyEx() failed with status %u\n", Status);
809 return Status;
810 }
811
812 if (REG_OPENED_EXISTING_KEY == KeyDisposition)
813 {
814 DWORD KeySize = sizeof(ServiceCurrent);
815 Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize);
816
817 if (ERROR_SUCCESS != Status)
818 {
819 RegCloseKey(hServiceCurrentKey);
820 DPRINT1("RegQueryValueEx() failed with status %u\n", Status);
821 return Status;
822 }
823
824 ServiceCurrent++;
825 }
826
827 Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
828
829 RegCloseKey(hServiceCurrentKey);
830
831 if (ERROR_SUCCESS != Status)
832 {
833 DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status);
834 return Status;
835 }
836
837 /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
838 swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
839 Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
840 PIPE_ACCESS_DUPLEX,
841 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
842 100,
843 8000,
844 4,
845 30000,
846 NULL);
847 DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName);
848 if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
849 {
850 DPRINT1("Failed to create control pipe!\n");
851 return GetLastError();
852 }
853
854 StartupInfo.cb = sizeof(StartupInfo);
855 StartupInfo.lpReserved = NULL;
856 StartupInfo.lpDesktop = NULL;
857 StartupInfo.lpTitle = NULL;
858 StartupInfo.dwFlags = 0;
859 StartupInfo.cbReserved2 = 0;
860 StartupInfo.lpReserved2 = 0;
861
862 Result = CreateProcessW(ImagePath.Buffer,
863 NULL,
864 NULL,
865 NULL,
866 FALSE,
867 DETACHED_PROCESS | CREATE_SUSPENDED,
868 NULL,
869 NULL,
870 &StartupInfo,
871 &ProcessInformation);
872 RtlFreeUnicodeString(&ImagePath);
873
874 if (!Result)
875 {
876 dwError = GetLastError();
877 /* Close control pipe */
878 CloseHandle(Service->ControlPipeHandle);
879 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
880
881 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
882 return dwError;
883 }
884
885 DPRINT("Process Id: %lu Handle %lx\n",
886 ProcessInformation.dwProcessId,
887 ProcessInformation.hProcess);
888 DPRINT("Thread Id: %lu Handle %lx\n",
889 ProcessInformation.dwThreadId,
890 ProcessInformation.hThread);
891
892 /* Get process and thread ids */
893 Service->ProcessId = ProcessInformation.dwProcessId;
894 Service->ThreadId = ProcessInformation.dwThreadId;
895
896 /* Resume Thread */
897 ResumeThread(ProcessInformation.hThread);
898
899 /* Connect control pipe */
900 if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
901 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
902 {
903 DWORD dwRead = 0;
904
905 DPRINT("Control pipe connected!\n");
906
907 /* Read SERVICE_STATUS_HANDLE from pipe */
908 if (!ReadFile(Service->ControlPipeHandle,
909 (LPVOID)&Service->hClient,
910 sizeof(DWORD),
911 &dwRead,
912 NULL))
913 {
914 dwError = GetLastError();
915 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
916 dwError);
917 }
918 else
919 {
920 DPRINT("Received service status %lu\n", Service->hClient);
921
922 /* Send start command */
923 dwError = ScmSendStartCommand(Service, argc, argv);
924 }
925 }
926 else
927 {
928 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
929
930 /* Close control pipe */
931 CloseHandle(Service->ControlPipeHandle);
932 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
933 Service->ProcessId = 0;
934 Service->ThreadId = 0;
935 }
936
937 /* Close process and thread handle */
938 CloseHandle(ProcessInformation.hThread);
939 CloseHandle(ProcessInformation.hProcess);
940
941 return dwError;
942 }
943
944
945 DWORD
946 ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
947 {
948 PSERVICE_GROUP Group = Service->lpGroup;
949 DWORD dwError = ERROR_SUCCESS;
950
951 DPRINT("ScmStartService() called\n");
952
953 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
954 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
955
956 if (Service->Status.dwServiceType & SERVICE_DRIVER)
957 {
958 /* Load driver */
959 dwError = ScmLoadDriver(Service);
960 if (dwError == ERROR_SUCCESS)
961 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
962 }
963 else
964 {
965 /* Start user-mode service */
966 dwError = ScmStartUserModeService(Service, argc, argv);
967 }
968
969 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
970
971 if (dwError == ERROR_SUCCESS)
972 {
973 if (Group != NULL)
974 {
975 Group->ServicesRunning = TRUE;
976 }
977 Service->Status.dwCurrentState = SERVICE_RUNNING;
978 }
979 #if 0
980 else
981 {
982 switch (Service->ErrorControl)
983 {
984 case SERVICE_ERROR_NORMAL:
985 /* FIXME: Log error */
986 break;
987
988 case SERVICE_ERROR_SEVERE:
989 if (IsLastKnownGood == FALSE)
990 {
991 /* FIXME: Boot last known good configuration */
992 }
993 break;
994
995 case SERVICE_ERROR_CRITICAL:
996 if (IsLastKnownGood == FALSE)
997 {
998 /* FIXME: Boot last known good configuration */
999 }
1000 else
1001 {
1002 /* FIXME: BSOD! */
1003 }
1004 break;
1005 }
1006 }
1007 #endif
1008
1009 return dwError;
1010 }
1011
1012
1013 VOID
1014 ScmAutoStartServices(VOID)
1015 {
1016 PLIST_ENTRY GroupEntry;
1017 PLIST_ENTRY ServiceEntry;
1018 PSERVICE_GROUP CurrentGroup;
1019 PSERVICE CurrentService;
1020 ULONG i;
1021
1022 /* Clear 'ServiceVisited' flag */
1023 ServiceEntry = ServiceListHead.Flink;
1024 while (ServiceEntry != &ServiceListHead)
1025 {
1026 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1027 CurrentService->ServiceVisited = FALSE;
1028 ServiceEntry = ServiceEntry->Flink;
1029 }
1030
1031 /* Start all services which are members of an existing group */
1032 GroupEntry = GroupListHead.Flink;
1033 while (GroupEntry != &GroupListHead)
1034 {
1035 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1036
1037 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1038
1039 /* Start all services witch have a valid tag */
1040 for (i = 0; i < CurrentGroup->TagCount; i++)
1041 {
1042 ServiceEntry = ServiceListHead.Flink;
1043 while (ServiceEntry != &ServiceListHead)
1044 {
1045 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1046
1047 if ((CurrentService->lpGroup == CurrentGroup) &&
1048 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1049 (CurrentService->ServiceVisited == FALSE) &&
1050 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1051 {
1052 CurrentService->ServiceVisited = TRUE;
1053 ScmStartService(CurrentService, 0, NULL);
1054 }
1055
1056 ServiceEntry = ServiceEntry->Flink;
1057 }
1058 }
1059
1060 /* Start all services which have an invalid tag or which do not have a tag */
1061 ServiceEntry = ServiceListHead.Flink;
1062 while (ServiceEntry != &ServiceListHead)
1063 {
1064 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1065
1066 if ((CurrentService->lpGroup == CurrentGroup) &&
1067 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1068 (CurrentService->ServiceVisited == FALSE))
1069 {
1070 CurrentService->ServiceVisited = TRUE;
1071 ScmStartService(CurrentService, 0, NULL);
1072 }
1073
1074 ServiceEntry = ServiceEntry->Flink;
1075 }
1076
1077 GroupEntry = GroupEntry->Flink;
1078 }
1079
1080 /* Start all services which are members of any non-existing group */
1081 ServiceEntry = ServiceListHead.Flink;
1082 while (ServiceEntry != &ServiceListHead)
1083 {
1084 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1085
1086 if ((CurrentService->lpGroup != NULL) &&
1087 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1088 (CurrentService->ServiceVisited == FALSE))
1089 {
1090 CurrentService->ServiceVisited = TRUE;
1091 ScmStartService(CurrentService, 0, NULL);
1092 }
1093
1094 ServiceEntry = ServiceEntry->Flink;
1095 }
1096
1097 /* Start all services which are not a member of any group */
1098 ServiceEntry = ServiceListHead.Flink;
1099 while (ServiceEntry != &ServiceListHead)
1100 {
1101 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1102
1103 if ((CurrentService->lpGroup == NULL) &&
1104 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1105 (CurrentService->ServiceVisited == FALSE))
1106 {
1107 CurrentService->ServiceVisited = TRUE;
1108 ScmStartService(CurrentService, 0, NULL);
1109 }
1110
1111 ServiceEntry = ServiceEntry->Flink;
1112 }
1113
1114 /* Clear 'ServiceVisited' flag again */
1115 ServiceEntry = ServiceListHead.Flink;
1116 while (ServiceEntry != &ServiceListHead)
1117 {
1118 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1119 CurrentService->ServiceVisited = FALSE;
1120 ServiceEntry = ServiceEntry->Flink;
1121 }
1122 }
1123
1124
1125 VOID
1126 ScmAutoShutdownServices(VOID)
1127 {
1128 PLIST_ENTRY ServiceEntry;
1129 PSERVICE CurrentService;
1130 SERVICE_STATUS ServiceStatus;
1131
1132 DPRINT("ScmAutoShutdownServices() called\n");
1133
1134 ServiceEntry = ServiceListHead.Flink;
1135 while (ServiceEntry != &ServiceListHead)
1136 {
1137 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1138
1139 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
1140 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
1141 {
1142 /* shutdown service */
1143 ScmControlService(CurrentService, SERVICE_CONTROL_STOP, &ServiceStatus);
1144 }
1145
1146 ServiceEntry = ServiceEntry->Flink;
1147 }
1148
1149 DPRINT("ScmGetBootAndSystemDriverState() done\n");
1150 }
1151
1152 /* EOF */