/*
- *
- * service control manager
- *
- * ReactOS Operating System
- *
- * --------------------------------------------------------------------
- *
- * This software is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; see the file COPYING.LIB. If not, write
- * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
- * MA 02139, USA.
+ * PROJECT: ReactOS Service Control Manager
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: base/system/services/database.c
+ * PURPOSE: Database control interface
+ * COPYRIGHT: Copyright 2002-2006 Eric Kohl
+ * Copyright 2006 Hervé Poussineau <hpoussin@reactos.org>
+ * Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
+ * Gregor Brunmar <gregor.brunmar@home.se>
*
*/
/* FUNCTIONS *****************************************************************/
+
PSERVICE
-ScmGetServiceEntryByName(LPWSTR lpServiceName)
+ScmGetServiceEntryByName(LPCWSTR lpServiceName)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
PSERVICE
-ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName)
+ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
}
+PSERVICE
+ScmGetServiceEntryByClientHandle(HANDLE Handle)
+{
+ PLIST_ENTRY ServiceEntry;
+ PSERVICE CurrentService;
+
+ DPRINT("ScmGetServiceEntryByClientHandle() called\n");
+ DPRINT("looking for %p\n", Handle);
+
+ ServiceEntry = ServiceListHead.Flink;
+ while (ServiceEntry != &ServiceListHead)
+ {
+ CurrentService = CONTAINING_RECORD(ServiceEntry,
+ SERVICE,
+ ServiceListEntry);
+
+ if (CurrentService->hClient == Handle)
+ {
+ DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
+ return CurrentService;
+ }
+
+ ServiceEntry = ServiceEntry->Flink;
+ }
+
+ DPRINT("Couldn't find a matching service\n");
+
+ return NULL;
+}
+
+
DWORD
-ScmCreateNewServiceRecord(LPWSTR lpServiceName,
+ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
PSERVICE *lpServiceRecord)
{
PSERVICE lpService = NULL;
/* Set the resume count */
lpService->dwResumeCount = dwResumeCount++;
- /* Append service entry */
+ /* Append service record */
InsertTailList(&ServiceListHead,
&lpService->ServiceListEntry);
+ /* Initialize the service status */
lpService->Status.dwCurrentState = SERVICE_STOPPED;
lpService->Status.dwControlsAccepted = 0;
lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
}
+VOID
+ScmDeleteServiceRecord(PSERVICE lpService)
+{
+ DPRINT("Deleting Service %S\n", lpService->lpServiceName);
+
+ /* Delete the display name */
+ if (lpService->lpDisplayName != NULL &&
+ lpService->lpDisplayName != lpService->lpServiceName)
+ HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
+
+ /* Decrement the image reference counter */
+ if (lpService->lpImage)
+ lpService->lpImage->dwServiceRefCount--;
+
+ /* Decrement the group reference counter */
+ if (lpService->lpGroup)
+ lpService->lpGroup->dwRefCount--;
+
+ /* FIXME: SecurityDescriptor */
+
+ /* Close the control pipe */
+ if (lpService->ControlPipeHandle != INVALID_HANDLE_VALUE)
+ CloseHandle(lpService->ControlPipeHandle);
+
+ /* Remove the Service from the List */
+ RemoveEntryList(&lpService->ServiceListEntry);
+
+ DPRINT("Deleted Service %S\n", lpService->lpServiceName);
+
+ /* Delete the service record */
+ HeapFree(GetProcessHeap(), 0, lpService);
+
+ DPRINT("Done\n");
+}
+
+
static DWORD
-CreateServiceListEntry(LPWSTR lpServiceName,
+CreateServiceListEntry(LPCWSTR lpServiceName,
HKEY hServiceKey)
{
PSERVICE lpService = NULL;
}
+DWORD
+ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
+{
+ DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
+ WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
+ HKEY hSubKey = 0;
+
+ dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
+ if (!dwRet)
+ {
+ /* Find the maximum subkey length so that we can allocate a buffer */
+ dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
+ &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (!dwRet)
+ {
+ dwMaxSubkeyLen++;
+ if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
+ /* Name too big: alloc a buffer for it */
+ lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
+
+ if(!lpszName)
+ dwRet = ERROR_NOT_ENOUGH_MEMORY;
+ else
+ {
+ while (dwRet == ERROR_SUCCESS)
+ {
+ dwSize = dwMaxSubkeyLen;
+ dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
+ if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
+ dwRet = ScmDeleteRegKey(hSubKey, lpszName);
+ }
+ if (dwRet == ERROR_NO_MORE_ITEMS)
+ dwRet = ERROR_SUCCESS;
+
+ if (lpszName != szNameBuf)
+ HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
+ }
+ }
+
+ RegCloseKey(hSubKey);
+ if (!dwRet)
+ dwRet = RegDeleteKeyW(hKey, lpszSubKey);
+ }
+ return dwRet;
+}
+
+
VOID
ScmDeleteMarkedServices(VOID)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
+ HKEY hServicesKey;
+ DWORD dwError;
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
if (CurrentService->bDeleted == TRUE)
{
- DPRINT1("Delete service: %S\n", CurrentService->lpServiceName);
+ dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"System\\CurrentControlSet\\Services",
+ 0,
+ DELETE,
+ &hServicesKey);
+ if (dwError == ERROR_SUCCESS)
+ {
+ dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
+ RegCloseKey(hServicesKey);
+ if (dwError == ERROR_SUCCESS)
+ {
+ RemoveEntryList(&CurrentService->ServiceListEntry);
+ HeapFree(GetProcessHeap(), 0, CurrentService);
+ }
+ }
+
+ if (dwError != ERROR_SUCCESS)
+ DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
+ }
+ }
+}
+
+
+VOID
+WaitForLSA(VOID)
+{
+ HANDLE hEvent;
+ DWORD dwError;
- /* FIXME: Delete the registry keys */
+ DPRINT("WaitForLSA() called\n");
- /* FIXME: Delete the service record from the list */
+ hEvent = CreateEventW(NULL,
+ TRUE,
+ FALSE,
+ L"LSA_RPC_SERVER_ACTIVE");
+ if (hEvent == NULL)
+ {
+ dwError = GetLastError();
+ DPRINT1("Failed to create the notication event (Error %lu)\n", dwError);
+ if (dwError == ERROR_ALREADY_EXISTS)
+ {
+ hEvent = OpenEventW(SYNCHRONIZE,
+ FALSE,
+ L"LSA_RPC_SERVER_ACTIVE");
+ if (hEvent != NULL)
+ {
+ DPRINT1("Could not open the notification event!\n");
+ return;
+ }
}
}
+
+ DPRINT("Wait for LSA!\n");
+ WaitForSingleObject(hEvent, INFINITE);
+ DPRINT("LSA is available!\n");
+
+ CloseHandle(hEvent);
+
+ DPRINT("WaitForLSA() done\n");
}
RegCloseKey(hServicesKey);
+ /* Wait for LSA */
+ WaitForLSA();
+
/* Delete services that are marked for delete */
ScmDeleteMarkedServices();
}
+VOID
+ScmShutdownServiceDatabase(VOID)
+{
+ DPRINT("ScmShutdownServiceDatabase() called\n");
+
+ ScmDeleteMarkedServices();
+ RtlDeleteResource(&DatabaseLock);
+
+ DPRINT("ScmShutdownServiceDatabase() done\n");
+}
+
+
static NTSTATUS
ScmCheckDriver(PSERVICE Service)
{
{
PSCM_CONTROL_PACKET ControlPacket;
DWORD Count;
+ DWORD TotalLength;
DPRINT("ScmControlService() called\n");
- ControlPacket = (SCM_CONTROL_PACKET*) HeapAlloc(GetProcessHeap(),
- HEAP_ZERO_MEMORY,
- sizeof(SCM_CONTROL_PACKET));
+ TotalLength = wcslen(Service->lpServiceName) + 1;
+
+ ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)));
if (ControlPacket == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
ControlPacket->dwControl = dwControl;
+ ControlPacket->hClient = Service->hClient;
+ ControlPacket->dwSize = TotalLength;
+ wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName);
- /* Send the start command */
+ /* Send the control packet */
WriteFile(Service->ControlPipeHandle,
ControlPacket,
- sizeof(SCM_CONTROL_PACKET),
+ sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
&Count,
NULL);
0,
ControlPacket);
+ RtlCopyMemory(lpServiceStatus,
+ &Service->Status,
+ sizeof(SERVICE_STATUS));
+
DPRINT("ScmControlService) done\n");
return ERROR_SUCCESS;
static DWORD
ScmSendStartCommand(PSERVICE Service,
- LPWSTR Arguments)
+ DWORD argc,
+ LPWSTR *argv)
{
PSCM_CONTROL_PACKET ControlPacket;
DWORD TotalLength;
/* Calculate the total length of the start command line */
TotalLength = wcslen(Service->lpServiceName) + 1;
- if (Arguments != NULL)
+ if (argc > 0)
{
- Ptr = Arguments;
- while (*Ptr)
+ for (Count = 0; Count < argc; Count++)
{
- Length = wcslen(Ptr) + 1;
+ DPRINT("Arg: %S\n", argv[Count]);
+ Length = wcslen(argv[Count]) + 1;
TotalLength += Length;
ArgsLength += Length;
- Ptr += Length;
- DPRINT("Arg: %S\n", Ptr);
}
}
TotalLength++;
- DPRINT("ArgsLength: %ld\nTotalLength: %ld\n\n", ArgsLength, TotalLength);
+ DPRINT("ArgsLength: %ld TotalLength: %ld\n", ArgsLength, TotalLength);
/* Allocate a control packet */
- ControlPacket = (SCM_CONTROL_PACKET*) HeapAlloc(GetProcessHeap(),
- HEAP_ZERO_MEMORY,
- sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
+ ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
if (ControlPacket == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
ControlPacket->dwControl = SERVICE_CONTROL_START;
+ ControlPacket->hClient = Service->hClient;
ControlPacket->dwSize = TotalLength;
Ptr = &ControlPacket->szArguments[0];
wcscpy(Ptr, Service->lpServiceName);
Ptr += (wcslen(Service->lpServiceName) + 1);
/* Copy argument list */
- if (Arguments != NULL)
+ if (argc > 0)
{
+ UNIMPLEMENTED;
+ DPRINT1("Arguments sent to service ignored!\n");
+#if 0
memcpy(Ptr, Arguments, ArgsLength);
Ptr += ArgsLength;
+#endif
}
/* Terminate the argument list */
static DWORD
ScmStartUserModeService(PSERVICE Service,
- LPWSTR lpArgs)
+ DWORD argc,
+ LPWSTR *argv)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[3];
PROCESS_INFORMATION ProcessInformation;
STARTUPINFOW StartupInfo;
UNICODE_STRING ImagePath;
ULONG Type;
+ DWORD ServiceCurrent = 0;
BOOL Result;
NTSTATUS Status;
DWORD dwError = ERROR_SUCCESS;
+ WCHAR NtControlPipeName[MAX_PATH + 1];
+ HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
+ DWORD KeyDisposition;
RtlInitUnicodeString(&ImagePath, NULL);
DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
DPRINT("Type: %lx\n", Type);
- /* Create '\\.\pipe\net\NtControlPipe' instance */
- Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
+ /* Get the service number */
+ /* TODO: Create registry entry with correct write access */
+ Status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
+ REG_OPTION_VOLATILE,
+ KEY_WRITE | KEY_READ,
+ NULL,
+ &hServiceCurrentKey,
+ &KeyDisposition);
+
+ if (ERROR_SUCCESS != Status)
+ {
+ DPRINT1("RegCreateKeyEx() failed with status %u\n", Status);
+ return Status;
+ }
+
+ if (REG_OPENED_EXISTING_KEY == KeyDisposition)
+ {
+ DWORD KeySize = sizeof(ServiceCurrent);
+ Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize);
+
+ if (ERROR_SUCCESS != Status)
+ {
+ RegCloseKey(hServiceCurrentKey);
+ DPRINT1("RegQueryValueEx() failed with status %u\n", Status);
+ return Status;
+ }
+
+ ServiceCurrent++;
+ }
+
+ Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
+
+ RegCloseKey(hServiceCurrentKey);
+
+ if (ERROR_SUCCESS != Status)
+ {
+ DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status);
+ return Status;
+ }
+
+ /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
+ swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
+ Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
100,
4,
30000,
NULL);
- DPRINT("CreateNamedPipeW() done\n");
+ DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName);
if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
{
DPRINT1("Failed to create control pipe!\n");
StartupInfo.cbReserved2 = 0;
StartupInfo.lpReserved2 = 0;
- Result = CreateProcessW(ImagePath.Buffer,
- NULL,
+ Result = CreateProcessW(NULL,
+ ImagePath.Buffer,
NULL,
NULL,
FALSE,
ResumeThread(ProcessInformation.hThread);
/* Connect control pipe */
- if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
+ if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
+ TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
{
- DWORD dwProcessId = 0;
DWORD dwRead = 0;
DPRINT("Control pipe connected!\n");
- /* Read thread id from pipe */
+ /* Read SERVICE_STATUS_HANDLE from pipe */
if (!ReadFile(Service->ControlPipeHandle,
- (LPVOID)&dwProcessId,
+ (LPVOID)&Service->hClient,
sizeof(DWORD),
&dwRead,
NULL))
}
else
{
- DPRINT("Received process id %lu\n", dwProcessId);
+ DPRINT("Received service status %lu\n", Service->hClient);
/* Send start command */
- dwError = ScmSendStartCommand(Service, lpArgs);
+ dwError = ScmSendStartCommand(Service, argc, argv);
}
}
else
{
- dwError = GetLastError();
- DPRINT("Connecting control pipe failed!\n");
+ DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
/* Close control pipe */
CloseHandle(Service->ControlPipeHandle);
DWORD
-ScmStartService(PSERVICE Service, LPWSTR lpArgs)
+ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
{
PSERVICE_GROUP Group = Service->lpGroup;
DWORD dwError = ERROR_SUCCESS;
else
{
/* Start user-mode service */
- dwError = ScmStartUserModeService(Service, lpArgs);
+ dwError = ScmStartUserModeService(Service, argc, argv);
}
DPRINT("ScmStartService() done (Error %lu)\n", dwError);
{
Group->ServicesRunning = TRUE;
}
- Service->Status.dwCurrentState = SERVICE_RUNNING;
+ Service->Status.dwCurrentState = SERVICE_START_PENDING;
}
#if 0
else
(CurrentService->dwTag == CurrentGroup->TagArray[i]))
{
CurrentService->ServiceVisited = TRUE;
- ScmStartService(CurrentService, NULL);
+ ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
- ScmStartService(CurrentService, NULL);
+ ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
- ScmStartService(CurrentService, NULL);
+ ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
- ScmStartService(CurrentService, NULL);
+ ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
}
+
+VOID
+ScmAutoShutdownServices(VOID)
+{
+ PLIST_ENTRY ServiceEntry;
+ PSERVICE CurrentService;
+ SERVICE_STATUS ServiceStatus;
+
+ DPRINT("ScmAutoShutdownServices() called\n");
+
+ ServiceEntry = ServiceListHead.Flink;
+ while (ServiceEntry != &ServiceListHead)
+ {
+ CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+ if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
+ CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
+ {
+ /* shutdown service */
+ ScmControlService(CurrentService, SERVICE_CONTROL_STOP, &ServiceStatus);
+ }
+
+ ServiceEntry = ServiceEntry->Flink;
+ }
+
+ DPRINT("ScmGetBootAndSystemDriverState() done\n");
+}
+
/* EOF */