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