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