f33d437955b1f75f4b3613e7afd1b46b10e3d48d
[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 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 return ERROR_NOT_ENOUGH_MEMORY;
704
705 ControlPacket->dwControl = dwControl;
706 ControlPacket->dwSize = TotalLength;
707 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
708 wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName);
709
710 /* Send the control packet */
711 WriteFile(Service->ControlPipeHandle,
712 ControlPacket,
713 sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
714 &dwWriteCount,
715 NULL);
716
717 /* Read the reply */
718 ReadFile(Service->ControlPipeHandle,
719 &ReplyPacket,
720 sizeof(SCM_REPLY_PACKET),
721 &dwReadCount,
722 NULL);
723
724 /* Release the contol packet */
725 HeapFree(GetProcessHeap(),
726 0,
727 ControlPacket);
728
729 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
730 {
731 dwError = ReplyPacket.dwError;
732 }
733
734 LeaveCriticalSection(&ControlServiceCriticalSection);
735
736 DPRINT("ScmControlService() done\n");
737
738 return dwError;
739 }
740
741
742 static DWORD
743 ScmSendStartCommand(PSERVICE Service,
744 DWORD argc,
745 LPWSTR *argv)
746 {
747 PSCM_CONTROL_PACKET ControlPacket;
748 SCM_REPLY_PACKET ReplyPacket;
749 DWORD TotalLength;
750 DWORD ArgsLength = 0;
751 DWORD Length;
752 PWSTR Ptr;
753 DWORD dwWriteCount = 0;
754 DWORD dwReadCount = 0;
755 DWORD dwError = ERROR_SUCCESS;
756 DWORD i;
757
758 DPRINT("ScmSendStartCommand() called\n");
759
760 /* Calculate the total length of the start command line */
761 TotalLength = wcslen(Service->lpServiceName) + 1;
762 if (argc > 0)
763 {
764 for (i = 0; i < argc; i++)
765 {
766 DPRINT("Arg: %S\n", argv[i]);
767 Length = wcslen(argv[i]) + 1;
768 TotalLength += Length;
769 ArgsLength += Length;
770 }
771 }
772 TotalLength++;
773 DPRINT("ArgsLength: %ld TotalLength: %ld\n", ArgsLength, TotalLength);
774
775 /* Allocate a control packet */
776 ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
777 HEAP_ZERO_MEMORY,
778 sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
779 if (ControlPacket == NULL)
780 return ERROR_NOT_ENOUGH_MEMORY;
781
782 ControlPacket->dwControl = SERVICE_CONTROL_START;
783 ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
784 ControlPacket->dwSize = TotalLength;
785 Ptr = &ControlPacket->szArguments[0];
786 wcscpy(Ptr, Service->lpServiceName);
787 Ptr += (wcslen(Service->lpServiceName) + 1);
788
789 /* Copy argument list */
790 if (argc > 0)
791 {
792 UNIMPLEMENTED;
793 DPRINT1("Arguments sent to service ignored!\n");
794 #if 0
795 memcpy(Ptr, Arguments, ArgsLength);
796 Ptr += ArgsLength;
797 #endif
798 }
799
800 /* Terminate the argument list */
801 *Ptr = 0;
802
803 /* Send the start command */
804 WriteFile(Service->ControlPipeHandle,
805 ControlPacket,
806 sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
807 &dwWriteCount,
808 NULL);
809
810 /* Read the reply */
811 ReadFile(Service->ControlPipeHandle,
812 &ReplyPacket,
813 sizeof(SCM_REPLY_PACKET),
814 &dwReadCount,
815 NULL);
816
817 /* Release the contol packet */
818 HeapFree(GetProcessHeap(),
819 0,
820 ControlPacket);
821
822 if (dwReadCount == sizeof(SCM_REPLY_PACKET))
823 {
824 dwError = ReplyPacket.dwError;
825 }
826
827 DPRINT("ScmSendStartCommand() done\n");
828
829 return dwError;
830 }
831
832
833 static DWORD
834 ScmStartUserModeService(PSERVICE Service,
835 DWORD argc,
836 LPWSTR *argv)
837 {
838 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
839 PROCESS_INFORMATION ProcessInformation;
840 STARTUPINFOW StartupInfo;
841 UNICODE_STRING ImagePath;
842 ULONG Type;
843 DWORD ServiceCurrent = 0;
844 BOOL Result;
845 NTSTATUS Status;
846 DWORD dwError = ERROR_SUCCESS;
847 WCHAR NtControlPipeName[MAX_PATH + 1];
848 HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
849 DWORD KeyDisposition;
850 DWORD dwProcessId;
851
852 RtlInitUnicodeString(&ImagePath, NULL);
853
854 /* Get service data */
855 RtlZeroMemory(&QueryTable,
856 sizeof(QueryTable));
857
858 QueryTable[0].Name = L"Type";
859 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
860 QueryTable[0].EntryContext = &Type;
861
862 QueryTable[1].Name = L"ImagePath";
863 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
864 QueryTable[1].EntryContext = &ImagePath;
865
866 Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
867 Service->lpServiceName,
868 QueryTable,
869 NULL,
870 NULL);
871 if (!NT_SUCCESS(Status))
872 {
873 DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
874 return RtlNtStatusToDosError(Status);
875 }
876 DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
877 DPRINT("Type: %lx\n", Type);
878
879 /* Get the service number */
880 /* TODO: Create registry entry with correct write access */
881 Status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
882 L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
883 REG_OPTION_VOLATILE,
884 KEY_WRITE | KEY_READ,
885 NULL,
886 &hServiceCurrentKey,
887 &KeyDisposition);
888
889 if (ERROR_SUCCESS != Status)
890 {
891 DPRINT1("RegCreateKeyEx() failed with status %u\n", Status);
892 return Status;
893 }
894
895 if (REG_OPENED_EXISTING_KEY == KeyDisposition)
896 {
897 DWORD KeySize = sizeof(ServiceCurrent);
898 Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize);
899
900 if (ERROR_SUCCESS != Status)
901 {
902 RegCloseKey(hServiceCurrentKey);
903 DPRINT1("RegQueryValueEx() failed with status %u\n", Status);
904 return Status;
905 }
906
907 ServiceCurrent++;
908 }
909
910 Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
911
912 RegCloseKey(hServiceCurrentKey);
913
914 if (ERROR_SUCCESS != Status)
915 {
916 DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status);
917 return Status;
918 }
919
920 /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
921 swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
922
923 DPRINT1("Service: %p ImagePath: %wZ PipeName: %S\n", Service, &ImagePath, NtControlPipeName);
924
925 Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
926 PIPE_ACCESS_DUPLEX,
927 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
928 100,
929 8000,
930 4,
931 30000,
932 NULL);
933 DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName);
934 if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
935 {
936 DPRINT1("Failed to create control pipe!\n");
937 return GetLastError();
938 }
939
940 StartupInfo.cb = sizeof(StartupInfo);
941 StartupInfo.lpReserved = NULL;
942 StartupInfo.lpDesktop = NULL;
943 StartupInfo.lpTitle = NULL;
944 StartupInfo.dwFlags = 0;
945 StartupInfo.cbReserved2 = 0;
946 StartupInfo.lpReserved2 = 0;
947
948 Result = CreateProcessW(NULL,
949 ImagePath.Buffer,
950 NULL,
951 NULL,
952 FALSE,
953 DETACHED_PROCESS | CREATE_SUSPENDED,
954 NULL,
955 NULL,
956 &StartupInfo,
957 &ProcessInformation);
958 RtlFreeUnicodeString(&ImagePath);
959
960 if (!Result)
961 {
962 dwError = GetLastError();
963 /* Close control pipe */
964 CloseHandle(Service->ControlPipeHandle);
965 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
966
967 DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
968 return dwError;
969 }
970
971 DPRINT("Process Id: %lu Handle %lx\n",
972 ProcessInformation.dwProcessId,
973 ProcessInformation.hProcess);
974 DPRINT("Thread Id: %lu Handle %lx\n",
975 ProcessInformation.dwThreadId,
976 ProcessInformation.hThread);
977
978 /* Get process and thread ids */
979 Service->ProcessId = ProcessInformation.dwProcessId;
980 Service->ThreadId = ProcessInformation.dwThreadId;
981
982 /* Resume Thread */
983 ResumeThread(ProcessInformation.hThread);
984
985 /* Connect control pipe */
986 if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
987 TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
988 {
989 DWORD dwRead = 0;
990
991 DPRINT("Control pipe connected!\n");
992
993 /* Read SERVICE_STATUS_HANDLE from pipe */
994 if (!ReadFile(Service->ControlPipeHandle,
995 (LPVOID)&dwProcessId,
996 sizeof(DWORD),
997 &dwRead,
998 NULL))
999 {
1000 dwError = GetLastError();
1001 DPRINT1("Reading the service control pipe failed (Error %lu)\n",
1002 dwError);
1003 }
1004 else
1005 {
1006 DPRINT("Received service process ID %lu\n", dwProcessId);
1007
1008 /* Send start command */
1009 dwError = ScmSendStartCommand(Service, argc, argv);
1010 }
1011 }
1012 else
1013 {
1014 DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1015
1016 /* Close control pipe */
1017 CloseHandle(Service->ControlPipeHandle);
1018 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
1019 Service->ProcessId = 0;
1020 Service->ThreadId = 0;
1021 }
1022
1023 /* Close process and thread handle */
1024 CloseHandle(ProcessInformation.hThread);
1025 CloseHandle(ProcessInformation.hProcess);
1026
1027 return dwError;
1028 }
1029
1030
1031 DWORD
1032 ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
1033 {
1034 PSERVICE_GROUP Group = Service->lpGroup;
1035 DWORD dwError = ERROR_SUCCESS;
1036
1037 DPRINT("ScmStartService() called\n");
1038
1039 DPRINT1("Start Service %p (%S)\n", Service, Service->lpServiceName);
1040
1041 EnterCriticalSection(&ControlServiceCriticalSection);
1042
1043 if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1044 {
1045 DPRINT1("Service %S is already running!\n", Service->lpServiceName);
1046 LeaveCriticalSection(&ControlServiceCriticalSection);
1047 return ERROR_SERVICE_ALREADY_RUNNING;
1048 }
1049
1050 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
1051 DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1052
1053 if (Service->Status.dwServiceType & SERVICE_DRIVER)
1054 {
1055 /* Load driver */
1056 dwError = ScmLoadDriver(Service);
1057 if (dwError == ERROR_SUCCESS)
1058 {
1059 Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1060 Service->Status.dwCurrentState = SERVICE_RUNNING;
1061 }
1062 }
1063 else
1064 {
1065 /* Start user-mode service */
1066 dwError = ScmStartUserModeService(Service, argc, argv);
1067 if (dwError == ERROR_SUCCESS)
1068 {
1069 #ifdef USE_SERVICE_START_PENDING
1070 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1071 #else
1072 Service->Status.dwCurrentState = SERVICE_RUNNING;
1073 #endif
1074 }
1075 }
1076
1077 LeaveCriticalSection(&ControlServiceCriticalSection);
1078
1079 DPRINT("ScmStartService() done (Error %lu)\n", dwError);
1080
1081 if (dwError == ERROR_SUCCESS)
1082 {
1083 if (Group != NULL)
1084 {
1085 Group->ServicesRunning = TRUE;
1086 }
1087 }
1088 #if 0
1089 else
1090 {
1091 switch (Service->ErrorControl)
1092 {
1093 case SERVICE_ERROR_NORMAL:
1094 /* FIXME: Log error */
1095 break;
1096
1097 case SERVICE_ERROR_SEVERE:
1098 if (IsLastKnownGood == FALSE)
1099 {
1100 /* FIXME: Boot last known good configuration */
1101 }
1102 break;
1103
1104 case SERVICE_ERROR_CRITICAL:
1105 if (IsLastKnownGood == FALSE)
1106 {
1107 /* FIXME: Boot last known good configuration */
1108 }
1109 else
1110 {
1111 /* FIXME: BSOD! */
1112 }
1113 break;
1114 }
1115 }
1116 #endif
1117
1118 return dwError;
1119 }
1120
1121
1122 VOID
1123 ScmAutoStartServices(VOID)
1124 {
1125 PLIST_ENTRY GroupEntry;
1126 PLIST_ENTRY ServiceEntry;
1127 PSERVICE_GROUP CurrentGroup;
1128 PSERVICE CurrentService;
1129 ULONG i;
1130
1131 /* Clear 'ServiceVisited' flag */
1132 ServiceEntry = ServiceListHead.Flink;
1133 while (ServiceEntry != &ServiceListHead)
1134 {
1135 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1136 CurrentService->ServiceVisited = FALSE;
1137 ServiceEntry = ServiceEntry->Flink;
1138 }
1139
1140 /* Start all services which are members of an existing group */
1141 GroupEntry = GroupListHead.Flink;
1142 while (GroupEntry != &GroupListHead)
1143 {
1144 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
1145
1146 DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
1147
1148 /* Start all services witch have a valid tag */
1149 for (i = 0; i < CurrentGroup->TagCount; i++)
1150 {
1151 ServiceEntry = ServiceListHead.Flink;
1152 while (ServiceEntry != &ServiceListHead)
1153 {
1154 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1155
1156 if ((CurrentService->lpGroup == CurrentGroup) &&
1157 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1158 (CurrentService->ServiceVisited == FALSE) &&
1159 (CurrentService->dwTag == CurrentGroup->TagArray[i]))
1160 {
1161 CurrentService->ServiceVisited = TRUE;
1162 ScmStartService(CurrentService, 0, NULL);
1163 }
1164
1165 ServiceEntry = ServiceEntry->Flink;
1166 }
1167 }
1168
1169 /* Start all services which have an invalid tag or which do not have a tag */
1170 ServiceEntry = ServiceListHead.Flink;
1171 while (ServiceEntry != &ServiceListHead)
1172 {
1173 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1174
1175 if ((CurrentService->lpGroup == CurrentGroup) &&
1176 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1177 (CurrentService->ServiceVisited == FALSE))
1178 {
1179 CurrentService->ServiceVisited = TRUE;
1180 ScmStartService(CurrentService, 0, NULL);
1181 }
1182
1183 ServiceEntry = ServiceEntry->Flink;
1184 }
1185
1186 GroupEntry = GroupEntry->Flink;
1187 }
1188
1189 /* Start all services which are members of any non-existing group */
1190 ServiceEntry = ServiceListHead.Flink;
1191 while (ServiceEntry != &ServiceListHead)
1192 {
1193 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1194
1195 if ((CurrentService->lpGroup != NULL) &&
1196 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1197 (CurrentService->ServiceVisited == FALSE))
1198 {
1199 CurrentService->ServiceVisited = TRUE;
1200 ScmStartService(CurrentService, 0, NULL);
1201 }
1202
1203 ServiceEntry = ServiceEntry->Flink;
1204 }
1205
1206 /* Start all services which are not a member of any group */
1207 ServiceEntry = ServiceListHead.Flink;
1208 while (ServiceEntry != &ServiceListHead)
1209 {
1210 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1211
1212 if ((CurrentService->lpGroup == NULL) &&
1213 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
1214 (CurrentService->ServiceVisited == FALSE))
1215 {
1216 CurrentService->ServiceVisited = TRUE;
1217 ScmStartService(CurrentService, 0, NULL);
1218 }
1219
1220 ServiceEntry = ServiceEntry->Flink;
1221 }
1222
1223 /* Clear 'ServiceVisited' flag again */
1224 ServiceEntry = ServiceListHead.Flink;
1225 while (ServiceEntry != &ServiceListHead)
1226 {
1227 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1228 CurrentService->ServiceVisited = FALSE;
1229 ServiceEntry = ServiceEntry->Flink;
1230 }
1231 }
1232
1233
1234 VOID
1235 ScmAutoShutdownServices(VOID)
1236 {
1237 PLIST_ENTRY ServiceEntry;
1238 PSERVICE CurrentService;
1239
1240 DPRINT("ScmAutoShutdownServices() called\n");
1241
1242 ServiceEntry = ServiceListHead.Flink;
1243 while (ServiceEntry != &ServiceListHead)
1244 {
1245 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1246
1247 if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
1248 CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
1249 {
1250 /* shutdown service */
1251 ScmControlService(CurrentService, SERVICE_CONTROL_STOP);
1252 }
1253
1254 ServiceEntry = ServiceEntry->Flink;
1255 }
1256
1257 DPRINT("ScmGetBootAndSystemDriverState() done\n");
1258 }
1259
1260
1261 BOOL
1262 ScmLockDatabaseExclusive(VOID)
1263 {
1264 return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
1265 }
1266
1267
1268 BOOL
1269 ScmLockDatabaseShared(VOID)
1270 {
1271 return RtlAcquireResourceShared(&DatabaseLock, TRUE);
1272 }
1273
1274
1275 VOID
1276 ScmUnlockDatabase(VOID)
1277 {
1278 RtlReleaseResource(&DatabaseLock);
1279 }
1280
1281
1282 VOID
1283 ScmInitNamedPipeCriticalSection(VOID)
1284 {
1285 InitializeCriticalSection(&ControlServiceCriticalSection);
1286 }
1287
1288
1289 VOID
1290 ScmDeleteNamedPipeCriticalSection(VOID)
1291 {
1292 DeleteCriticalSection(&ControlServiceCriticalSection);
1293 }
1294
1295 /* EOF */