From 05ed889bb267d858236a9023478f2d479d8a0ff3 Mon Sep 17 00:00:00 2001 From: Eric Kohl Date: Sat, 7 Jul 2012 15:35:40 +0000 Subject: [PATCH] =?UTF-8?q?[SERVICES]=20Implement=20service=20database=20l?= =?UTF-8?q?ocking=20via=20RLockServiceDatabase=20/=20RUnlockServiceDatabas?= =?UTF-8?q?e=20etc...=20and=20use=20this=20functionality.=20Patch=20by=20H?= =?UTF-8?q?erm=C3=A8s=20B=C3=A9lusca.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I did not rename the database lock functions because the functions names RLockServiceDatabase and RUnlockServiceDatabase are misleading. They do not lock any database but lock the service start function. The existing functions ScmLockDatabaseExclusive, ScmLockDatabaseShared and ScmUnlockDatabase do protect (lock) the service database (service, group and image lists) form concurrent access. That is also the reason for renaming the SC_LOCK_STRUCT to START_LOCK. See issue #7131 for more details. svn path=/trunk/; revision=56844 --- reactos/base/system/services/CMakeLists.txt | 1 + reactos/base/system/services/lock.c | 187 ++++++++++++++++++++ reactos/base/system/services/rpcserver.c | 104 +++++++++-- reactos/base/system/services/services.c | 45 +++-- reactos/base/system/services/services.h | 19 ++ reactos/dll/win32/advapi32/service/scm.c | 4 +- reactos/include/reactos/idl/svcctl.idl | 6 +- 7 files changed, 337 insertions(+), 29 deletions(-) create mode 100644 reactos/base/system/services/lock.c diff --git a/reactos/base/system/services/CMakeLists.txt b/reactos/base/system/services/CMakeLists.txt index 7853a478f1b..b25d03f01ed 100644 --- a/reactos/base/system/services/CMakeLists.txt +++ b/reactos/base/system/services/CMakeLists.txt @@ -12,6 +12,7 @@ list(APPEND SOURCE database.c driver.c groupdb.c + lock.c rpcserver.c services.c services.rc diff --git a/reactos/base/system/services/lock.c b/reactos/base/system/services/lock.c new file mode 100644 index 00000000000..5480d84c607 --- /dev/null +++ b/reactos/base/system/services/lock.c @@ -0,0 +1,187 @@ +/* + * PROJECT: ReactOS Service Control Manager + * LICENSE: GPL - See COPYING in the top level directory + * FILE: base/system/services/lock.c + * PURPOSE: Service lock functions + * COPYRIGHT: Copyright 2002-2006 Eric Kohl + */ + +/* INCLUDES *****************************************************************/ + +#include "services.h" + +#define NDEBUG +#include + + +/* GLOBALS *******************************************************************/ + +static PSTART_LOCK pServiceStartLock = NULL; + + +/* FUNCTIONS *****************************************************************/ + +/* + * TRUE if locked by the Service Control Manager, FALSE otherwise + */ + +DWORD +ScmAcquireServiceStartLock(IN BOOL IsServiceController, + OUT LPSC_RPC_LOCK lpLock) +{ + DWORD dwRequiredSize; + DWORD dwError = ERROR_SUCCESS; + + *lpLock = NULL; + + /* Lock the service database exclusively */ + ScmLockDatabaseExclusive(); + + if (pServiceStartLock != NULL) + { + dwError = ERROR_SERVICE_DATABASE_LOCKED; + goto done; + } + + /* Allocate a new lock for the database */ + dwRequiredSize = sizeof(START_LOCK); + + if (!IsServiceController) + { + /* FIXME: dwRequiredSize += RtlLengthSid(UserSid <-- to be retrieved); */ + } + + pServiceStartLock = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwRequiredSize); + if (pServiceStartLock == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + pServiceStartLock->Tag = LOCK_TAG; + pServiceStartLock->TimeWhenLocked = (DWORD)time(NULL); + + /* FIXME: Retrieve the owner SID. Use IsServiceController. */ + pServiceStartLock->LockOwnerSid = (PSID)NULL; + + *lpLock = (LPSC_RPC_LOCK)pServiceStartLock; + +done: + /* Unlock the service database */ + ScmUnlockDatabase(); + + return dwError; +} + + +DWORD +ScmReleaseServiceStartLock(IN OUT LPSC_RPC_LOCK lpLock) +{ + PSTART_LOCK pStartLock; + DWORD dwError = ERROR_SUCCESS; + + if (lpLock == NULL) + return ERROR_INVALID_SERVICE_LOCK; + + pStartLock = (PSTART_LOCK)*lpLock; + + if (pStartLock->Tag != LOCK_TAG) + return ERROR_INVALID_SERVICE_LOCK; + + /* Lock the service database exclusively */ + ScmLockDatabaseExclusive(); + + /* Release the lock handle */ + if ((pStartLock == pServiceStartLock) && + (pServiceStartLock != NULL)) + { + HeapFree(GetProcessHeap(), 0, pServiceStartLock); + pServiceStartLock = NULL; + *lpLock = NULL; + + dwError = ERROR_SUCCESS; + } + else + { + dwError = ERROR_INVALID_SERVICE_LOCK; + } + + /* Unlock the service database */ + ScmUnlockDatabase(); + + return dwError; +} + + +/* + * Helper functions for RQueryServiceLockStatusW() and + * RQueryServiceLockStatusA(). + * We suppose that lpLockStatus points to a valid + * well-sized buffer. + */ +VOID +ScmQueryServiceLockStatusW(OUT LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus) +{ + /* Lock the service database shared */ + ScmLockDatabaseShared(); + + if (pServiceStartLock != NULL) + { + lpLockStatus->fIsLocked = TRUE; + + /* FIXME: Retrieve the owner name. */ + lpLockStatus->lpLockOwner = NULL; + + lpLockStatus->dwLockDuration = (DWORD)time(NULL) - pServiceStartLock->TimeWhenLocked; + } + else + { + lpLockStatus->fIsLocked = FALSE; + + wcscpy((LPWSTR)(lpLockStatus + 1), L""); + lpLockStatus->lpLockOwner = (LPWSTR)(ULONG_PTR)sizeof(QUERY_SERVICE_LOCK_STATUSW); + + lpLockStatus->dwLockDuration = 0; + } + + /* Unlock the whole SC manager */ + ScmUnlockDatabase(); + + return; +} + + +VOID +ScmQueryServiceLockStatusA(OUT LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus) +{ + /* Lock the service database shared */ + ScmLockDatabaseShared(); + + if (pServiceStartLock != NULL) + { + lpLockStatus->fIsLocked = TRUE; + + /* FIXME: Retrieve the owner name. */ + lpLockStatus->lpLockOwner = NULL; + + lpLockStatus->dwLockDuration = (DWORD)time(NULL) - pServiceStartLock->TimeWhenLocked; + } + else + { + lpLockStatus->fIsLocked = FALSE; + + strcpy((LPSTR)(lpLockStatus + 1), ""); + lpLockStatus->lpLockOwner = (LPSTR)(ULONG_PTR)sizeof(QUERY_SERVICE_LOCK_STATUSA); + + lpLockStatus->dwLockDuration = 0; + } + + /* Unlock the whole SC manager */ + ScmUnlockDatabase(); + + return; +} + +/* EOF */ diff --git a/reactos/base/system/services/rpcserver.c b/reactos/base/system/services/rpcserver.c index e83a776f97c..a4a91db1ed3 100644 --- a/reactos/base/system/services/rpcserver.c +++ b/reactos/base/system/services/rpcserver.c @@ -1307,7 +1307,7 @@ DWORD RLockServiceDatabase( DPRINT("RLockServiceDatabase() called\n"); - *lpLock = 0; + *lpLock = NULL; hMgr = ScmGetServiceManagerFromHandle(hSCManager); if (hMgr == NULL) @@ -1320,12 +1320,7 @@ DWORD RLockServiceDatabase( SC_MANAGER_LOCK)) return ERROR_ACCESS_DENIED; -// return ScmLockDatabase(0, hMgr->0xC, hLock); - - /* FIXME: Lock the database */ - *lpLock = (SC_RPC_LOCK)0x12345678; /* Dummy! */ - - return ERROR_SUCCESS; + return ScmAcquireServiceStartLock(FALSE, lpLock); } @@ -1706,8 +1701,8 @@ DWORD RSetServiceStatus( DWORD RUnlockServiceDatabase( LPSC_RPC_LOCK Lock) { - UNIMPLEMENTED; - return ERROR_SUCCESS; + DPRINT("RUnlockServiceDatabase(%p)\n", Lock); + return ScmReleaseServiceStartLock(Lock); } @@ -2857,12 +2852,41 @@ Done: /* Function 18 */ DWORD RQueryServiceLockStatusW( SC_RPC_HANDLE hSCManager, - LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus, + LPBYTE lpBuf, // LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus, DWORD cbBufSize, LPBOUNDED_DWORD_4K pcbBytesNeeded) { - UNIMPLEMENTED; - return ERROR_CALL_NOT_IMPLEMENTED; + LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus = (LPQUERY_SERVICE_LOCK_STATUSW)lpBuf; + PMANAGER_HANDLE hMgr; + DWORD dwRequiredSize; + + if (!lpLockStatus || !pcbBytesNeeded) + return ERROR_INVALID_PARAMETER; + + hMgr = ScmGetServiceManagerFromHandle(hSCManager); + if (hMgr == NULL) + { + DPRINT1("Invalid service manager handle!\n"); + return ERROR_INVALID_HANDLE; + } + + if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess, + SC_MANAGER_QUERY_LOCK_STATUS)) + { + DPRINT("Insufficient access rights! 0x%lx\n", hMgr->Handle.DesiredAccess); + return ERROR_ACCESS_DENIED; + } + + /* HACK: we need to compute instead the real length of the owner name */ + dwRequiredSize = sizeof(QUERY_SERVICE_LOCK_STATUSW) + sizeof(WCHAR); + *pcbBytesNeeded = dwRequiredSize; + + if (cbBufSize < dwRequiredSize) + return ERROR_INSUFFICIENT_BUFFER; + + ScmQueryServiceLockStatusW(lpLockStatus); + + return ERROR_SUCCESS; } @@ -2875,6 +2899,7 @@ DWORD RStartServiceW( DWORD dwError = ERROR_SUCCESS; PSERVICE_HANDLE hSvc; PSERVICE lpService = NULL; + SC_RPC_LOCK Lock = NULL; DWORD i; DPRINT("RStartServiceW(%p %lu %p) called\n", hService, argc, argv); @@ -2917,9 +2942,17 @@ DWORD RStartServiceW( if (lpService->bDeleted) return ERROR_SERVICE_MARKED_FOR_DELETE; + /* Acquire the service start lock until the service has been started */ + dwError = ScmAcquireServiceStartLock(TRUE, &Lock); + if (dwError != ERROR_SUCCESS) + return dwError; + /* Start the service */ dwError = ScmStartService(lpService, argc, (LPWSTR*)argv); + /* Release the service start lock */ + ScmReleaseServiceStartLock(&Lock); + return dwError; } @@ -4090,12 +4123,41 @@ Done: /* Function 30 */ DWORD RQueryServiceLockStatusA( SC_RPC_HANDLE hSCManager, - LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, + LPBYTE lpBuf, // LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, DWORD cbBufSize, LPBOUNDED_DWORD_4K pcbBytesNeeded) { - UNIMPLEMENTED; - return ERROR_CALL_NOT_IMPLEMENTED; + LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus = (LPQUERY_SERVICE_LOCK_STATUSA)lpBuf; + PMANAGER_HANDLE hMgr; + DWORD dwRequiredSize; + + if (!lpLockStatus || !pcbBytesNeeded) + return ERROR_INVALID_PARAMETER; + + hMgr = ScmGetServiceManagerFromHandle(hSCManager); + if (hMgr == NULL) + { + DPRINT1("Invalid service manager handle!\n"); + return ERROR_INVALID_HANDLE; + } + + if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess, + SC_MANAGER_QUERY_LOCK_STATUS)) + { + DPRINT("Insufficient access rights! 0x%lx\n", hMgr->Handle.DesiredAccess); + return ERROR_ACCESS_DENIED; + } + + /* FIXME: we need to compute instead the real length of the owner name */ + dwRequiredSize = sizeof(QUERY_SERVICE_LOCK_STATUSA) + sizeof(CHAR); + *pcbBytesNeeded = dwRequiredSize; + + if (cbBufSize < dwRequiredSize) + return ERROR_INSUFFICIENT_BUFFER; + + ScmQueryServiceLockStatusA(lpLockStatus); + + return ERROR_SUCCESS; } @@ -4108,6 +4170,7 @@ DWORD RStartServiceA( DWORD dwError = ERROR_SUCCESS; PSERVICE_HANDLE hSvc; PSERVICE lpService = NULL; + SC_RPC_LOCK Lock = NULL; LPWSTR *lpVector = NULL; DWORD i; DWORD dwLength; @@ -4183,6 +4246,17 @@ DWORD RStartServiceA( /* Start the service */ dwError = ScmStartService(lpService, argc, lpVector); + /* Acquire the service start lock until the service has been started */ + dwError = ScmAcquireServiceStartLock(TRUE, &Lock); + if (dwError != ERROR_SUCCESS) + goto done; + + /* Start the service */ + dwError = ScmStartService(lpService, argc, lpVector); + + /* Release the service start lock */ + ScmReleaseServiceStartLock(&Lock); + done: /* Free the Unicode argument vector */ if (lpVector != NULL) diff --git a/reactos/base/system/services/services.c b/reactos/base/system/services/services.c index e69190b8ea8..d15bfe26c6f 100644 --- a/reactos/base/system/services/services.c +++ b/reactos/base/system/services/services.c @@ -376,7 +376,8 @@ wWinMain(HINSTANCE hInstance, LPWSTR lpCmdLine, int nShowCmd) { - HANDLE hScmStartEvent; + HANDLE hScmStartEvent = NULL; + SC_RPC_LOCK Lock = NULL; DWORD dwError; DPRINT("SERVICES: Service Control Manager\n"); @@ -385,23 +386,36 @@ wWinMain(HINSTANCE hInstance, if (!ScmCreateStartEvent(&hScmStartEvent)) { DPRINT1("SERVICES: Failed to create start event\n"); - ExitThread(0); + goto done; } DPRINT("SERVICES: created start event with handle %p.\n", hScmStartEvent); + /* Create the shutdown event */ + hScmShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hScmShutdownEvent == NULL) + { + DPRINT1("SERVICES: Failed to create shutdown event\n"); + goto done; + } + // ScmInitThreadManager(); /* FIXME: more initialization */ + /* Read the control set values */ + if (!ScmGetControlSetValues()) + { + DPRINT1("SERVICES: failed to read the control set values\n"); + goto done; + } /* Create the service database */ dwError = ScmCreateServiceDatabase(); if (dwError != ERROR_SUCCESS) { DPRINT1("SERVICES: failed to create SCM database (Error %lu)\n", dwError); - CloseHandle(hScmStartEvent); - ExitThread(0); + goto done; } /* Update service database */ @@ -429,26 +443,37 @@ wWinMain(HINSTANCE hInstance, ScmInitNamedPipeCriticalSection(); + /* Acquire the service start lock until autostart services have been started */ + dwError = ScmAcquireServiceStartLock(TRUE, &Lock); + if (dwError != ERROR_SUCCESS) + { + DPRINT1("SERVICES: failed to acquire the service start lock (Error %lu)\n", dwError); + goto done; + } + /* Start auto-start services */ ScmAutoStartServices(); /* FIXME: more to do ? */ + /* Release the service start lock */ + ScmReleaseServiceStartLock(&Lock); DPRINT("SERVICES: Running.\n"); - /* Create the shutdown event and wait until it gets set */ - hScmShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if (hScmShutdownEvent) - WaitForSingleObject(hScmShutdownEvent, INFINITE); + /* Wait until the shutdown event gets signaled */ + WaitForSingleObject(hScmShutdownEvent, INFINITE); +done: ScmDeleteNamedPipeCriticalSection(); /* Close the shutdown event */ - CloseHandle(hScmShutdownEvent); + if (hScmShutdownEvent != NULL) + CloseHandle(hScmShutdownEvent); /* Close the start event */ - CloseHandle(hScmStartEvent); + if (hScmStartEvent != NULL) + CloseHandle(hScmStartEvent); DPRINT("SERVICES: Finished.\n"); diff --git a/reactos/base/system/services/services.h b/reactos/base/system/services/services.h index 6346afc8609..fe6d67d6269 100644 --- a/reactos/base/system/services/services.h +++ b/reactos/base/system/services/services.h @@ -3,6 +3,7 @@ */ #include +#include #define WIN32_NO_STATUS #include #include @@ -67,6 +68,16 @@ typedef struct _SERVICE } SERVICE, *PSERVICE; +#define LOCK_TAG 0x4C697041 /* 'ApiL' */ + +typedef struct _START_LOCK +{ + DWORD Tag; /* Must be LOCK_TAG */ + DWORD TimeWhenLocked; /* Number of seconds since 1970 */ + PSID LockOwnerSid; /* It is NULL if the SCM aquired the lock */ +} START_LOCK, *PSTART_LOCK; + + /* VARIABLES ***************************************************************/ extern LIST_ENTRY ServiceListHead; @@ -150,6 +161,14 @@ DWORD ScmSetServiceGroup(PSERVICE lpService, LPCWSTR lpGroupName); +/* lock.c */ +DWORD ScmAcquireServiceStartLock(IN BOOL IsServiceController, + OUT LPSC_RPC_LOCK lpLock); +DWORD ScmReleaseServiceStartLock(IN OUT LPSC_RPC_LOCK lpLock); +VOID ScmQueryServiceLockStatusW(OUT LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus); +VOID ScmQueryServiceLockStatusA(OUT LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus); + + /* rpcserver.c */ VOID ScmStartRpcServer(VOID); diff --git a/reactos/dll/win32/advapi32/service/scm.c b/reactos/dll/win32/advapi32/service/scm.c index 78ba9797143..81bffc73096 100644 --- a/reactos/dll/win32/advapi32/service/scm.c +++ b/reactos/dll/win32/advapi32/service/scm.c @@ -2286,7 +2286,7 @@ QueryServiceLockStatusA(SC_HANDLE hSCManager, { /* Call to services.exe using RPC */ dwError = RQueryServiceLockStatusA((SC_RPC_HANDLE)hSCManager, - lpStatusPtr, + (LPBYTE)lpStatusPtr, dwBufferSize, pcbBytesNeeded); } @@ -2348,7 +2348,7 @@ QueryServiceLockStatusW(SC_HANDLE hSCManager, { /* Call to services.exe using RPC */ dwError = RQueryServiceLockStatusW((SC_RPC_HANDLE)hSCManager, - lpStatusPtr, + (LPBYTE)lpStatusPtr, dwBufferSize, pcbBytesNeeded); } diff --git a/reactos/include/reactos/idl/svcctl.idl b/reactos/include/reactos/idl/svcctl.idl index 2febd675415..bc071f00767 100644 --- a/reactos/include/reactos/idl/svcctl.idl +++ b/reactos/include/reactos/idl/svcctl.idl @@ -443,7 +443,8 @@ interface svcctl /* Function 18 */ DWORD RQueryServiceLockStatusW( [in] SC_RPC_HANDLE hSCManager, - [out] LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus, + [out, size_is(cbBufSize)] LPBYTE lpLockStatus, + /* FIXME: should be [out] LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus, */ [in, range(0, 1024*4)] DWORD cbBufSize, [out] LPBOUNDED_DWORD_4K pcbBytesNeeded); @@ -555,7 +556,8 @@ interface svcctl /* Function 30 */ DWORD RQueryServiceLockStatusA( [in] SC_RPC_HANDLE hSCManager, - [out] LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, + [out, size_is(cbBufSize)] LPBYTE lpLockStatus, + /* FIXME: should be [out] LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, */ [in, range(0, 1024*4)] DWORD cbBufSize, [out] LPBOUNDED_DWORD_4K pcbBytesNeeded);