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