[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 EnterCriticalSection(&StartServiceCriticalSection);
926
927 /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
928 swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
929 Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
930 PIPE_ACCESS_DUPLEX,
931 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
932 100,
933 8000,
934 4,
935 30000,
936 NULL);
937 DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName);
938 if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
939 {
940 DPRINT1("Failed to create control pipe!\n");
941 LeaveCriticalSection(&StartServiceCriticalSection);
942 return GetLastError();
943 }
944
945 StartupInfo.cb = sizeof(StartupInfo);
946 StartupInfo.lpReserved = NULL;
947 StartupInfo.lpDesktop = NULL;
948 StartupInfo.lpTitle = NULL;
949 StartupInfo.dwFlags = 0;
950 StartupInfo.cbReserved2 = 0;
951 StartupInfo.lpReserved2 = 0;
952
953 Result = CreateProcessW(NULL,
954 ImagePath.Buffer,
955 NULL,
956 NULL,
957 FALSE,
958 DETACHED_PROCESS | CREATE_SUSPENDED,
959 NULL,
960 NULL,
961 &StartupInfo,
962 &ProcessInformation);
963 RtlFreeUnicodeString(&ImagePath);
964
965 if (!Result)
966 {
967 dwError = GetLastError();
968 /* Close control pipe */
969 CloseHandle(Service->ControlPipeHandle);
970 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
971
972 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
973 LeaveCriticalSection(&StartServiceCriticalSection);
974 return dwError;
975 }
976
977 DPRINT("Process Id: %lu Handle %lx\n",
978 ProcessInformation.dwProcessId,
979 ProcessInformation.hProcess);
980 DPRINT("Thread Id: %lu Handle %lx\n",
981 ProcessInformation.dwThreadId,
982 ProcessInformation.hThread);
983
984 /* Get process and thread ids */
985 Service->ProcessId = ProcessInformation.dwProcessId;
986 Service->ThreadId = ProcessInformation.dwThreadId;
987
988 /* Resume Thread */
989 ResumeThread(ProcessInformation.hThread);
990
991 /* Connect control pipe */
992 if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
993 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
994 {
995 DWORD dwRead = 0;
996
997 DPRINT("Control pipe connected!\n");
998
999 /* Read SERVICE_STATUS_HANDLE from pipe */
1000 if (!ReadFile(Service->ControlPipeHandle,
1001 (LPVOID)&dwProcessId,
1002 sizeof(DWORD),
1003 &dwRead,
1004 NULL))
1005 {
1006 dwError = GetLastError();
1007 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
1008 dwError);
1009 }
1010 else
1011 {
1012 DPRINT("Received service process ID %lu\n", dwProcessId);
1013
1014 /* Send start command */
1015 dwError = ScmSendStartCommand(Service, argc, argv);
1016 }
1017 }
1018 else
1019 {
1020 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1021
1022 /* Close control pipe */
1023 CloseHandle(Service->ControlPipeHandle);
1024 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
1025 Service->ProcessId = 0;
1026 Service->ThreadId = 0;
1027 }
1028
1029 /* Close process and thread handle */
1030 CloseHandle(ProcessInformation.hThread);
1031 CloseHandle(ProcessInformation.hProcess);
1032
1033 LeaveCriticalSection(&StartServiceCriticalSection);
1034
1035 return dwError;
1036 }
1037
1038
1039 DWORD
1040 ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
1041 {
1042 PSERVICE_GROUP Group = Service->lpGroup;
1043 DWORD dwError = ERROR_SUCCESS;
1044
1045 DPRINT("ScmStartService() called\n");
1046
1047 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
1048 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1049
1050 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1051 {
1052 /* Load driver */
1053 dwError = ScmLoadDriver(Service);
1054 if (dwError == ERROR_SUCCESS)
1055 {
1056 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1057 Service->Status.dwCurrentState = SERVICE_RUNNING;
1058 }
1059 }
1060 else
1061 {
1062 /* Start user-mode service */
1063 dwError = ScmStartUserModeService(Service, argc, argv);
1064 if (dwError == ERROR_SUCCESS)
1065 {
1066 #ifdef USE_SERVICE_START_PENDING
1067 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1068 #else
1069 Service->Status.dwCurrentState = SERVICE_RUNNING;
1070 #endif
1071 }
1072 }
1073
1074 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1075
1076 if (dwError == ERROR_SUCCESS)
1077 {
1078 if (Group != NULL)
1079 {
1080 Group->ServicesRunning = TRUE;
1081 }
1082 }
1083 #if 0
1084 else
1085 {
1086 switch (Service->ErrorControl)
1087 {
1088 case SERVICE_ERROR_NORMAL:
1089 /* FIXME: Log error */
1090 break;
1091
1092 case SERVICE_ERROR_SEVERE:
1093 if (IsLastKnownGood == FALSE)
1094 {
1095 /* FIXME: Boot last known good configuration */
1096 }
1097 break;
1098
1099 case SERVICE_ERROR_CRITICAL:
1100 if (IsLastKnownGood == FALSE)
1101 {
1102 /* FIXME: Boot last known good configuration */
1103 }
1104 else
1105 {
1106 /* FIXME: BSOD! */
1107 }
1108 break;
1109 }
1110 }
1111 #endif
1112
1113 return dwError;
1114 }
1115
1116
1117 VOID
1118 ScmAutoStartServices(VOID)
1119 {
1120 PLIST_ENTRY GroupEntry;
1121 PLIST_ENTRY ServiceEntry;
1122 PSERVICE_GROUP CurrentGroup;
1123 PSERVICE CurrentService;
1124 ULONG i;
1125
1126 /* Clear 'ServiceVisited' flag */
1127 ServiceEntry = ServiceListHead.Flink;
1128 while (ServiceEntry != &ServiceListHead)
1129 {
1130 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1131 CurrentService->ServiceVisited = FALSE;
1132 ServiceEntry = ServiceEntry->Flink;
1133 }
1134
1135 /* Start all services which are members of an existing group */
1136 GroupEntry = GroupListHead.Flink;
1137 while (GroupEntry != &GroupListHead)
1138 {
1139 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1140
1141 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1142
1143 /* Start all services witch have a valid tag */
1144 for (i = 0; i < CurrentGroup->TagCount; i++)
1145 {
1146 ServiceEntry = ServiceListHead.Flink;
1147 while (ServiceEntry != &ServiceListHead)
1148 {
1149 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1150
1151 if ((CurrentService->lpGroup == CurrentGroup) &&
1152 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1153 (CurrentService->ServiceVisited == FALSE) &&
1154 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1155 {
1156 CurrentService->ServiceVisited = TRUE;
1157 ScmStartService(CurrentService, 0, NULL);
1158 }
1159
1160 ServiceEntry = ServiceEntry->Flink;
1161 }
1162 }
1163
1164 /* Start all services which have an invalid tag or which do not have a tag */
1165 ServiceEntry = ServiceListHead.Flink;
1166 while (ServiceEntry != &ServiceListHead)
1167 {
1168 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1169
1170 if ((CurrentService->lpGroup == CurrentGroup) &&
1171 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1172 (CurrentService->ServiceVisited == FALSE))
1173 {
1174 CurrentService->ServiceVisited = TRUE;
1175 ScmStartService(CurrentService, 0, NULL);
1176 }
1177
1178 ServiceEntry = ServiceEntry->Flink;
1179 }
1180
1181 GroupEntry = GroupEntry->Flink;
1182 }
1183
1184 /* Start all services which are members of any non-existing group */
1185 ServiceEntry = ServiceListHead.Flink;
1186 while (ServiceEntry != &ServiceListHead)
1187 {
1188 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1189
1190 if ((CurrentService->lpGroup != NULL) &&
1191 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1192 (CurrentService->ServiceVisited == FALSE))
1193 {
1194 CurrentService->ServiceVisited = TRUE;
1195 ScmStartService(CurrentService, 0, NULL);
1196 }
1197
1198 ServiceEntry = ServiceEntry->Flink;
1199 }
1200
1201 /* Start all services which are not a member of any group */
1202 ServiceEntry = ServiceListHead.Flink;
1203 while (ServiceEntry != &ServiceListHead)
1204 {
1205 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1206
1207 if ((CurrentService->lpGroup == NULL) &&
1208 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1209 (CurrentService->ServiceVisited == FALSE))
1210 {
1211 CurrentService->ServiceVisited = TRUE;
1212 ScmStartService(CurrentService, 0, NULL);
1213 }
1214
1215 ServiceEntry = ServiceEntry->Flink;
1216 }
1217
1218 /* Clear 'ServiceVisited' flag again */
1219 ServiceEntry = ServiceListHead.Flink;
1220 while (ServiceEntry != &ServiceListHead)
1221 {
1222 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1223 CurrentService->ServiceVisited = FALSE;
1224 ServiceEntry = ServiceEntry->Flink;
1225 }
1226 }
1227
1228
1229 VOID
1230 ScmAutoShutdownServices(VOID)
1231 {
1232 PLIST_ENTRY ServiceEntry;
1233 PSERVICE CurrentService;
1234
1235 DPRINT("ScmAutoShutdownServices() called\n");
1236
1237 ServiceEntry = ServiceListHead.Flink;
1238 while (ServiceEntry != &ServiceListHead)
1239 {
1240 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1241
1242 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
1243 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
1244 {
1245 /* shutdown service */
1246 ScmControlService(CurrentService, SERVICE_CONTROL_STOP);
1247 }
1248
1249 ServiceEntry = ServiceEntry->Flink;
1250 }
1251
1252 DPRINT("ScmGetBootAndSystemDriverState() done\n");
1253 }
1254
1255
1256 BOOL
1257 ScmLockDatabaseExclusive(VOID)
1258 {
1259 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
1260 }
1261
1262
1263 BOOL
1264 ScmLockDatabaseShared(VOID)
1265 {
1266 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
1267 }
1268
1269
1270 VOID
1271 ScmUnlockDatabase(VOID)
1272 {
1273 RtlReleaseResource(&DatabaseLock);
1274 }
1275
1276
1277 VOID
1278 ScmInitNamedPipeCriticalSection(VOID)
1279 {
1280 InitializeCriticalSection(&NamedPipeCriticalSection);
1281 InitializeCriticalSection(&StartServiceCriticalSection);
1282 }
1283
1284
1285 VOID
1286 ScmDeleteNamedPipeCriticalSection(VOID)
1287 {
1288 DeleteCriticalSection(&StartServiceCriticalSection);
1289 DeleteCriticalSection(&NamedPipeCriticalSection);
1290 }
1291
1292 /* EOF */