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