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