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);