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