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