1 /* $Id: database.c,v 1.15 2004/04/12 17:14:54 navaraf Exp $
3 * service control manager
5 * ReactOS Operating System
7 * --------------------------------------------------------------------
9 * This software is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this software; see the file COPYING.LIB. If not, write
21 * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
26 /* INCLUDES *****************************************************************/
28 #define NTOS_MODE_USER
30 #include <rosrtl/string.h>
41 /* GLOBALS *******************************************************************/
43 LIST_ENTRY GroupListHead
;
44 LIST_ENTRY ServiceListHead
;
47 /* FUNCTIONS *****************************************************************/
49 static NTSTATUS STDCALL
50 CreateGroupListRoutine(PWSTR ValueName
,
59 if (ValueType
== REG_SZ
)
61 DPRINT("Data: '%S'\n", (PWCHAR
)ValueData
);
63 Group
= (PSERVICE_GROUP
)HeapAlloc(GetProcessHeap(),
65 sizeof(SERVICE_GROUP
));
68 return(STATUS_INSUFFICIENT_RESOURCES
);
71 if (!RtlCreateUnicodeString(&Group
->GroupName
,
74 return(STATUS_INSUFFICIENT_RESOURCES
);
78 InsertTailList(&GroupListHead
,
79 &Group
->GroupListEntry
);
82 return(STATUS_SUCCESS
);
87 ScmCreateServiceListEntry(PUNICODE_STRING ServiceName
)
89 RTL_QUERY_REGISTRY_TABLE QueryTable
[6];
90 PSERVICE Service
= NULL
;
93 DPRINT("Service: '%wZ'\n", ServiceName
);
95 /* Allocate service entry */
96 Service
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
103 /* Copy service name */
104 Service
->ServiceName
.Length
= ServiceName
->Length
;
105 Service
->ServiceName
.MaximumLength
= ServiceName
->Length
+ sizeof(WCHAR
);
106 Service
->ServiceName
.Buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
107 Service
->ServiceName
.MaximumLength
);
108 if (Service
->ServiceName
.Buffer
== NULL
)
110 HeapFree(GetProcessHeap(), 0, Service
);
113 RtlCopyMemory(Service
->ServiceName
.Buffer
,
115 ServiceName
->Length
);
116 Service
->ServiceName
.Buffer
[ServiceName
->Length
/ sizeof(WCHAR
)] = 0;
118 /* Build registry path */
119 Service
->RegistryPath
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
120 Service
->RegistryPath
.Buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
121 MAX_PATH
* sizeof(WCHAR
));
122 if (Service
->ServiceName
.Buffer
== NULL
)
124 HeapFree(GetProcessHeap(), 0, Service
->ServiceName
.Buffer
);
125 HeapFree(GetProcessHeap(), 0, Service
);
128 wcscpy(Service
->RegistryPath
.Buffer
,
129 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
130 wcscat(Service
->RegistryPath
.Buffer
,
131 Service
->ServiceName
.Buffer
);
132 Service
->RegistryPath
.Length
= wcslen(Service
->RegistryPath
.Buffer
) * sizeof(WCHAR
);
134 /* Get service data */
135 RtlZeroMemory(&QueryTable
,
138 QueryTable
[0].Name
= L
"Start";
139 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
140 QueryTable
[0].EntryContext
= &Service
->Start
;
142 QueryTable
[1].Name
= L
"Type";
143 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
144 QueryTable
[1].EntryContext
= &Service
->Type
;
146 QueryTable
[2].Name
= L
"ErrorControl";
147 QueryTable
[2].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
148 QueryTable
[2].EntryContext
= &Service
->ErrorControl
;
150 QueryTable
[3].Name
= L
"Group";
151 QueryTable
[3].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
152 QueryTable
[3].EntryContext
= &Service
->ServiceGroup
;
154 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
159 if (!NT_SUCCESS(Status
))
161 PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status
);
162 RtlFreeUnicodeString(&Service
->RegistryPath
);
163 RtlFreeUnicodeString(&Service
->ServiceName
);
164 HeapFree(GetProcessHeap(), 0, Service
);
168 DPRINT("ServiceName: '%wZ'\n", &Service
->ServiceName
);
169 DPRINT("RegistryPath: '%wZ'\n", &Service
->RegistryPath
);
170 DPRINT("ServiceGroup: '%wZ'\n", &Service
->ServiceGroup
);
171 DPRINT("Start %lx Type %lx ErrorControl %lx\n",
172 Service
->Start
, Service
->Type
, Service
->ErrorControl
);
174 /* Append service entry */
175 InsertTailList(&ServiceListHead
,
176 &Service
->ServiceListEntry
);
183 ScmCreateServiceDataBase(VOID
)
185 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
186 OBJECT_ATTRIBUTES ObjectAttributes
;
187 UNICODE_STRING ServicesKeyName
;
188 UNICODE_STRING SubKeyName
;
193 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
194 ULONG KeyInfoLength
= 0;
195 ULONG ReturnedLength
;
197 DPRINT("ScmCreateServiceDataBase() called\n");
199 /* Initialize basic variables */
200 InitializeListHead(&GroupListHead
);
201 InitializeListHead(&ServiceListHead
);
203 /* Build group order list */
204 RtlZeroMemory(&QueryTable
,
207 QueryTable
[0].Name
= L
"List";
208 QueryTable
[0].QueryRoutine
= CreateGroupListRoutine
;
210 Status
= RtlQueryRegistryValues(RTL_REGISTRY_CONTROL
,
211 L
"ServiceGroupOrder",
215 if (!NT_SUCCESS(Status
))
218 RtlRosInitUnicodeStringFromLiteral(&ServicesKeyName
,
219 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
221 InitializeObjectAttributes(&ObjectAttributes
,
223 OBJ_CASE_INSENSITIVE
,
227 Status
= RtlpNtOpenKey(&ServicesKey
,
231 if (!NT_SUCCESS(Status
))
234 /* Allocate key info buffer */
235 KeyInfoLength
= sizeof(KEY_BASIC_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
);
236 KeyInfo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KeyInfoLength
);
239 NtClose(ServicesKey
);
240 return(STATUS_INSUFFICIENT_RESOURCES
);
246 Status
= NtEnumerateKey(ServicesKey
,
252 if (NT_SUCCESS(Status
))
254 if (KeyInfo
->NameLength
< MAX_PATH
* sizeof(WCHAR
))
257 SubKeyName
.Length
= KeyInfo
->NameLength
;
258 SubKeyName
.MaximumLength
= KeyInfo
->NameLength
+ sizeof(WCHAR
);
259 SubKeyName
.Buffer
= KeyInfo
->Name
;
260 SubKeyName
.Buffer
[SubKeyName
.Length
/ sizeof(WCHAR
)] = 0;
262 DPRINT("KeyName: '%wZ'\n", &SubKeyName
);
263 if (ScmCreateServiceListEntry(&SubKeyName
) == NULL
)
265 return STATUS_INSUFFICIENT_RESOURCES
;
270 if (!NT_SUCCESS(Status
))
276 HeapFree(GetProcessHeap(), 0, KeyInfo
);
277 NtClose(ServicesKey
);
279 DPRINT("ScmCreateServiceDataBase() done\n");
281 return(STATUS_SUCCESS
);
286 ScmCheckDriver(PSERVICE Service
)
288 OBJECT_ATTRIBUTES ObjectAttributes
;
289 UNICODE_STRING DirName
;
292 PDIRECTORY_BASIC_INFORMATION DirInfo
;
296 PLIST_ENTRY GroupEntry
;
297 PSERVICE_GROUP CurrentGroup
;
299 DPRINT("ScmCheckDriver(%wZ) called\n", &Service
->ServiceName
);
301 if (Service
->Type
== SERVICE_KERNEL_DRIVER
)
303 RtlRosInitUnicodeStringFromLiteral(&DirName
,
308 RtlRosInitUnicodeStringFromLiteral(&DirName
,
312 InitializeObjectAttributes(&ObjectAttributes
,
318 Status
= NtOpenDirectoryObject(&DirHandle
,
319 DIRECTORY_QUERY
| DIRECTORY_TRAVERSE
,
321 if (!NT_SUCCESS(Status
))
326 BufferLength
= sizeof(DIRECTORY_BASIC_INFORMATION
) +
327 2 * MAX_PATH
* sizeof(WCHAR
);
328 DirInfo
= HeapAlloc(GetProcessHeap(),
335 Status
= NtQueryDirectoryObject(DirHandle
,
342 if (Status
== STATUS_NO_MORE_ENTRIES
)
344 /* FIXME: Add current service to 'failed service' list */
345 DPRINT("Service '%wZ' failed\n", &Service
->ServiceName
);
349 if (!NT_SUCCESS(Status
))
352 DPRINT("Comparing: '%wZ' '%wZ'\n", &Service
->ServiceName
, &DirInfo
->ObjectName
);
354 if (RtlEqualUnicodeString(&Service
->ServiceName
, &DirInfo
->ObjectName
, TRUE
))
356 DPRINT("Found: '%wZ' '%wZ'\n", &Service
->ServiceName
, &DirInfo
->ObjectName
);
358 /* Mark service as 'running' */
359 Service
->ServiceRunning
= TRUE
;
361 /* Find the driver's group and mark it as 'running' */
362 if (Service
->ServiceGroup
.Buffer
!= NULL
)
364 GroupEntry
= GroupListHead
.Flink
;
365 while (GroupEntry
!= &GroupListHead
)
367 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
369 DPRINT("Checking group '%wZ'\n", &CurrentGroup
->GroupName
);
370 if (RtlEqualUnicodeString(&Service
->ServiceGroup
, &CurrentGroup
->GroupName
, TRUE
))
372 CurrentGroup
->ServicesRunning
= TRUE
;
375 GroupEntry
= GroupEntry
->Flink
;
382 HeapFree(GetProcessHeap(),
387 return(STATUS_SUCCESS
);
392 ScmGetBootAndSystemDriverState(VOID
)
394 PLIST_ENTRY ServiceEntry
;
395 PSERVICE CurrentService
;
397 DPRINT("ScmGetBootAndSystemDriverState() called\n");
399 ServiceEntry
= ServiceListHead
.Flink
;
400 while (ServiceEntry
!= &ServiceListHead
)
402 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
404 if (CurrentService
->Start
== SERVICE_BOOT_START
||
405 CurrentService
->Start
== SERVICE_SYSTEM_START
)
408 DPRINT(" Checking service: %wZ\n", &CurrentService
->ServiceName
);
410 ScmCheckDriver(CurrentService
);
412 ServiceEntry
= ServiceEntry
->Flink
;
415 DPRINT("ScmGetBootAndSystemDriverState() done\n");
420 ScmStartService(PSERVICE Service
,
421 PSERVICE_GROUP Group
)
423 RTL_QUERY_REGISTRY_TABLE QueryTable
[3];
424 PROCESS_INFORMATION ProcessInformation
;
425 STARTUPINFOW StartupInfo
;
426 UNICODE_STRING ImagePath
;
431 DPRINT("ScmStartService() called\n");
433 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
434 DPRINT("Service->Type: %u\n", Service
->Type
);
436 if (Service
->Type
== SERVICE_KERNEL_DRIVER
||
437 Service
->Type
== SERVICE_FILE_SYSTEM_DRIVER
||
438 Service
->Type
== SERVICE_RECOGNIZER_DRIVER
)
441 DPRINT(" Path: %wZ\n", &Service
->RegistryPath
);
442 Status
= NtLoadDriver(&Service
->RegistryPath
);
446 RtlInitUnicodeString(&ImagePath
, NULL
);
448 /* Get service data */
449 RtlZeroMemory(&QueryTable
,
452 QueryTable
[0].Name
= L
"Type";
453 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
454 QueryTable
[0].EntryContext
= &Type
;
456 QueryTable
[1].Name
= L
"ImagePath";
457 QueryTable
[1].Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
458 QueryTable
[1].EntryContext
= &ImagePath
;
460 Status
= RtlQueryRegistryValues(RTL_REGISTRY_SERVICES
,
461 Service
->ServiceName
.Buffer
,
465 if (NT_SUCCESS(Status
))
467 DPRINT("ImagePath: '%S'\n", ImagePath
.Buffer
);
468 DPRINT("Type: %lx\n", Type
);
470 /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
471 Service
->ControlPipeHandle
= CreateNamedPipeW(L
"\\\\.\\pipe\\net\\NtControlPipe",
473 PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT
,
479 DPRINT("CreateNamedPipeW() done\n");
480 if (Service
->ControlPipeHandle
== INVALID_HANDLE_VALUE
)
482 DPRINT1("Failed to create control pipe!\n");
483 Status
= STATUS_UNSUCCESSFUL
;
487 StartupInfo
.cb
= sizeof(StartupInfo
);
488 StartupInfo
.lpReserved
= NULL
;
489 StartupInfo
.lpDesktop
= NULL
;
490 StartupInfo
.lpTitle
= NULL
;
491 StartupInfo
.dwFlags
= 0;
492 StartupInfo
.cbReserved2
= 0;
493 StartupInfo
.lpReserved2
= 0;
495 Result
= CreateProcessW(ImagePath
.Buffer
,
500 DETACHED_PROCESS
| CREATE_SUSPENDED
,
504 &ProcessInformation
);
505 RtlFreeUnicodeString(&ImagePath
);
509 /* Close control pipe */
510 CloseHandle(Service
->ControlPipeHandle
);
511 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
513 DPRINT("Starting '%S' failed!\n", Service
->ServiceName
.Buffer
);
514 Status
= STATUS_UNSUCCESSFUL
;
518 DPRINT("Process Id: %lu Handle %lx\n",
519 ProcessInformation
.dwProcessId
,
520 ProcessInformation
.hProcess
);
521 DPRINT("Thread Id: %lu Handle %lx\n",
522 ProcessInformation
.dwThreadId
,
523 ProcessInformation
.hThread
);
525 /* Get process and thread ids */
526 Service
->ProcessId
= ProcessInformation
.dwProcessId
;
527 Service
->ThreadId
= ProcessInformation
.dwThreadId
;
530 ResumeThread(ProcessInformation
.hThread
);
532 /* FIXME: connect control pipe */
533 if (ConnectNamedPipe(Service
->ControlPipeHandle
, NULL
))
535 DPRINT("Control pipe connected!\n");
536 Status
= STATUS_SUCCESS
;
540 DPRINT1("Connecting control pipe failed!\n");
542 /* Close control pipe */
543 CloseHandle(Service
->ControlPipeHandle
);
544 Service
->ControlPipeHandle
= INVALID_HANDLE_VALUE
;
545 Service
->ProcessId
= 0;
546 Service
->ThreadId
= 0;
547 Status
= STATUS_UNSUCCESSFUL
;
550 /* Close process and thread handle */
551 CloseHandle(ProcessInformation
.hThread
);
552 CloseHandle(ProcessInformation
.hProcess
);
558 if (NT_SUCCESS(Status
))
562 Group
->ServicesRunning
= TRUE
;
564 Service
->ServiceRunning
= TRUE
;
569 if (CurrentService
->ErrorControl
== 1)
574 else if (CurrentService
->ErrorControl
== 2)
576 if (IsLastKnownGood
== FALSE
)
578 /* Boot last known good configuration */
582 else if (CurrentService
->ErrorControl
== 3)
584 if (IsLastKnownGood
== FALSE
)
586 /* Boot last known good configuration */
598 return Status
; //(STATUS_SUCCESS);
603 ScmAutoStartServices(VOID
)
605 PLIST_ENTRY GroupEntry
;
606 PLIST_ENTRY ServiceEntry
;
607 PSERVICE_GROUP CurrentGroup
;
608 PSERVICE CurrentService
;
610 /* Clear 'ServiceVisited' flag */
611 ServiceEntry
= ServiceListHead
.Flink
;
612 while (ServiceEntry
!= &ServiceListHead
)
614 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
615 CurrentService
->ServiceVisited
= FALSE
;
616 ServiceEntry
= ServiceEntry
->Flink
;
619 /* Start all services which are members of an existing group */
620 GroupEntry
= GroupListHead
.Flink
;
621 while (GroupEntry
!= &GroupListHead
)
623 CurrentGroup
= CONTAINING_RECORD(GroupEntry
, SERVICE_GROUP
, GroupListEntry
);
625 DPRINT("Group '%wZ'\n", &CurrentGroup
->GroupName
);
627 ServiceEntry
= ServiceListHead
.Flink
;
628 while (ServiceEntry
!= &ServiceListHead
)
630 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
632 if ((RtlEqualUnicodeString(&CurrentGroup
->GroupName
, &CurrentService
->ServiceGroup
, TRUE
)) &&
633 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
634 (CurrentService
->ServiceVisited
== FALSE
))
636 CurrentService
->ServiceVisited
= TRUE
;
637 ScmStartService(CurrentService
,
641 ServiceEntry
= ServiceEntry
->Flink
;
644 GroupEntry
= GroupEntry
->Flink
;
647 /* Start all services which are members of any non-existing group */
648 ServiceEntry
= ServiceListHead
.Flink
;
649 while (ServiceEntry
!= &ServiceListHead
)
651 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
653 if ((CurrentGroup
->GroupName
.Length
> 0) &&
654 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
655 (CurrentService
->ServiceVisited
== FALSE
))
657 CurrentService
->ServiceVisited
= TRUE
;
658 ScmStartService(CurrentService
,
662 ServiceEntry
= ServiceEntry
->Flink
;
665 /* Start all services which are not a member of any group */
666 ServiceEntry
= ServiceListHead
.Flink
;
667 while (ServiceEntry
!= &ServiceListHead
)
669 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
671 if ((CurrentGroup
->GroupName
.Length
== 0) &&
672 (CurrentService
->Start
== SERVICE_AUTO_START
) &&
673 (CurrentService
->ServiceVisited
== FALSE
))
675 CurrentService
->ServiceVisited
= TRUE
;
676 ScmStartService(CurrentService
,
680 ServiceEntry
= ServiceEntry
->Flink
;
683 /* Clear 'ServiceVisited' flag again */
684 ServiceEntry
= ServiceListHead
.Flink
;
685 while (ServiceEntry
!= &ServiceListHead
)
687 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
688 CurrentService
->ServiceVisited
= FALSE
;
689 ServiceEntry
= ServiceEntry
->Flink
;
694 * FIXME: Doesn't work!!!
697 ScmFindService(PUNICODE_STRING ServiceName
)
699 PSERVICE CurrentService
;
700 PLIST_ENTRY ServiceEntry
;
702 ServiceEntry
= ServiceListHead
.Flink
;
703 while (ServiceEntry
!= &ServiceListHead
)
705 CurrentService
= CONTAINING_RECORD(ServiceEntry
, SERVICE
, ServiceListEntry
);
706 if (!RtlCompareUnicodeString(ServiceName
, &CurrentService
->ServiceName
, TRUE
))
708 return CurrentService
;
710 ServiceEntry
= ServiceEntry
->Flink
;