diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index 20f7af59237..0b29493282a 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -954,21 +954,26 @@ BOOL WINAPI StartServiceCtrlDispatcherW( const SERVICE_TABLE_ENTRYW *servent ) */ SC_LOCK WINAPI LockServiceDatabase (SC_HANDLE hSCManager) { - HANDLE ret; + struct sc_manager *hscm; + SC_RPC_LOCK hLock; + DWORD err; TRACE("%p\n",hSCManager); - ret = CreateSemaphoreW( NULL, 1, 1, szSCMLock ); - if( ret && GetLastError() == ERROR_ALREADY_EXISTS ) + hscm = sc_handle_get_handle_data( hSCManager, SC_HTYPE_MANAGER ); + if (!hscm) { - CloseHandle( ret ); - ret = NULL; - SetLastError( ERROR_SERVICE_DATABASE_LOCKED ); + SetLastError( ERROR_INVALID_HANDLE ); + return NULL; } - TRACE("returning %p\n", ret); - - return ret; + err = svcctl_LockServiceDatabase(hscm->hdr.server_handle, &hLock); + if (err != ERROR_SUCCESS) + { + SetLastError(err); + return NULL; + } + return hLock; } /****************************************************************************** @@ -976,9 +981,18 @@ SC_LOCK WINAPI LockServiceDatabase (SC_HANDLE hSCManager) */ BOOL WINAPI UnlockServiceDatabase (SC_LOCK ScLock) { + DWORD err; + SC_RPC_LOCK hRpcLock = ScLock; + TRACE("%p\n",ScLock); - return CloseHandle( ScLock ); + err = svcctl_UnlockServiceDatabase(&hRpcLock); + if (err != ERROR_SUCCESS) + { + SetLastError(err); + return FALSE; + } + return TRUE; } /****************************************************************************** diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index c0190e7a68a..a629f6a188d 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -43,6 +43,7 @@ interface svcctl /* handle types */ typedef [handle] LPCWSTR MACHINE_HANDLEW; typedef [context_handle] void *SC_RPC_HANDLE; + typedef [context_handle] void *SC_RPC_LOCK; /* undocumented access rights */ cpp_quote("#define SERVICE_SET_STATUS 0x8000") @@ -86,12 +87,23 @@ cpp_quote("#endif") [in] SC_RPC_HANDLE hService ); + /* Compatible with Windows function 0x03 */ + DWORD svcctl_LockServiceDatabase( + [in] SC_RPC_HANDLE hSCManager, + [out] SC_RPC_LOCK *phLock + ); + /* Compatible with Windows function 0x07 */ DWORD svcctl_SetServiceStatus( [in] SC_RPC_HANDLE hServiceStatus, [in] LPSERVICE_STATUS lpServiceStatus ); + /* Compatible with Windows function 0x08 */ + DWORD svcctl_UnlockServiceDatabase( + [in,out] SC_RPC_LOCK *phLock + ); + /* Compatible with Windows function 0x0b */ DWORD svcctl_ChangeServiceConfigW( [in] SC_RPC_HANDLE hService, diff --git a/programs/services/rpc.c b/programs/services/rpc.c index f780f7ef5b6..0a59ed40696 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -88,6 +88,11 @@ struct sc_service /* service handle */ struct service_entry *service_entry; }; +struct sc_lock +{ + char dummy; /* no state currently used */ +}; + /* Check if the given handle is of the required type and allows the requested access. */ static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr) { @@ -654,6 +659,51 @@ DWORD svcctl_CloseServiceHandle( return ERROR_SUCCESS; } +static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock) +{ + unlock_service_database(); + HeapFree(GetProcessHeap(), 0, hLock); +} + +void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock) +{ + SC_RPC_LOCK_destroy(hLock); +} + +DWORD svcctl_LockServiceDatabase( + SC_RPC_HANDLE hSCManager, + SC_RPC_LOCK *phLock) +{ + struct sc_manager *manager; + DWORD err; + + WINE_TRACE("(%p, %p)\n", hSCManager, phLock); + + if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS) + return err; + + err = lock_service_database(); + if (err != ERROR_SUCCESS) + return err; + + *phLock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock)); + if (!*phLock) + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + + return ERROR_SUCCESS; +} + +DWORD svcctl_UnlockServiceDatabase( + SC_RPC_LOCK *phLock) +{ + WINE_TRACE("(&%p)\n", *phLock); + + SC_RPC_LOCK_destroy(*phLock); + *phLock = NULL; + + return ERROR_SUCCESS; +} + DWORD RPC_MainLoop(void) { WCHAR transport[] = SVCCTL_TRANSPORT; diff --git a/programs/services/services.c b/programs/services/services.c index 2e2246962b4..1962dc9ea46 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -274,6 +274,20 @@ BOOL validate_service_config(struct service_entry *entry) return TRUE; } +static LONG service_lock = FALSE; + +DWORD lock_service_database(void) +{ + if (InterlockedCompareExchange(&service_lock, TRUE, FALSE)) + return ERROR_SERVICE_DATABASE_LOCKED; + return ERROR_SUCCESS; +} + +void unlock_service_database(void) +{ + InterlockedCompareExchange(&service_lock, FALSE, TRUE); +} + void lock_services(void) { EnterCriticalSection(&services_list_cs); diff --git a/programs/services/services.h b/programs/services/services.h index 30b120e5ba9..4c7c656ddfe 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -45,6 +45,9 @@ DWORD save_service_config(struct service_entry *entry); void free_service_entry(struct service_entry *entry); void release_service(struct service_entry *service); +DWORD lock_service_database(void); +void unlock_service_database(void); + void lock_services(void); void unlock_services(void);