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