From 4275fbf603d69e70ea0133e4b9abea44086812d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Zalewski?= Date: Fri, 28 Mar 2008 16:56:00 +0000 Subject: [PATCH] services: Start a local RPC server. --- .gitignore | 5 + dlls/advapi32/Makefile.in | 3 + dlls/advapi32/service.c | 151 +++++++++++++++++++++++---- dlls/advapi32/svcctl.idl | 1 + dlls/advapi32/tests/service.c | 3 - include/Makefile.in | 1 + include/wine/svcctl.idl | 59 +++++++++++ programs/services/Makefile.in | 5 +- programs/services/rpc.c | 188 ++++++++++++++++++++++++++++++++++ programs/services/services.c | 8 +- programs/services/svcctl.idl | 1 + 11 files changed, 401 insertions(+), 24 deletions(-) create mode 100644 dlls/advapi32/svcctl.idl create mode 100644 include/wine/svcctl.idl create mode 100644 programs/services/rpc.c create mode 100644 programs/services/svcctl.idl diff --git a/.gitignore b/.gitignore index 4003d886342..826912d135e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ dlls/actxprxy/actxprxy_servprov.h dlls/actxprxy/actxprxy_servprov_i.c dlls/actxprxy/actxprxy_servprov_p.c dlls/advapi32/libadvapi32.def +dlls/advapi32/svcctl.h +dlls/advapi32/svcctl_c.c dlls/advapi32/tests/*.ok dlls/advapi32/tests/advapi32_crosstest.exe dlls/advapi32/tests/testlist.c @@ -538,6 +540,7 @@ include/unknwn.h include/urlhist.h include/urlmon.h include/wine/itss.h +include/wine/svcctl.h include/wtypes.h include/xmldom.h include/xmldso.h @@ -583,6 +586,8 @@ programs/rpcss/rpcss programs/rundll32/rundll32 programs/secedit/secedit programs/services/services +programs/services/svcctl.h +programs/services/svcctl_s.c programs/spoolsv/spoolsv programs/start/start programs/svchost/svchost diff --git a/dlls/advapi32/Makefile.in b/dlls/advapi32/Makefile.in index 072bda102a9..d9236dc4831 100644 --- a/dlls/advapi32/Makefile.in +++ b/dlls/advapi32/Makefile.in @@ -7,6 +7,7 @@ MODULE = advapi32.dll IMPORTLIB = advapi32 IMPORTS = kernel32 ntdll EXTRALIBS = @SECURITYLIB@ +DELAYIMPORTS = rpcrt4 C_SRCS = \ advapi.c \ @@ -24,6 +25,8 @@ C_SRCS = \ security.c \ service.c +IDL_C_SRCS = svcctl.idl + RC_SRCS = version.rc @MAKE_DLL_RULES@ diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index 82b5d1210ea..49063776809 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -36,6 +36,8 @@ #include "lmcons.h" #include "lmserver.h" +#include "svcctl.h" + WINE_DEFAULT_DEBUG_CHANNEL(service); static const WCHAR szLocalSystem[] = {'L','o','c','a','l','S','y','s','t','e','m',0}; @@ -45,6 +47,16 @@ static const WCHAR szServiceManagerKey[] = { 'S','y','s','t','e','m','\\', static const WCHAR szSCMLock[] = {'A','D','V','A','P','I','_','S','C','M', 'L','O','C','K',0}; +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len) +{ + return HeapAlloc(GetProcessHeap(), 0, len); +} + +void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr) +{ + HeapFree(GetProcessHeap(), 0, ptr); +} + static const GENERIC_MAPPING scm_generic = { (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS), (STANDARD_RIGHTS_WRITE | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_MODIFY_BOOT_CONFIG), @@ -117,6 +129,7 @@ struct sc_handle SC_HANDLE_TYPE htype; DWORD ref_count; sc_handle_destructor destroy; + SC_RPC_HANDLE server_handle; /* server-side handle */ }; struct sc_manager /* service control manager handle */ @@ -230,6 +243,102 @@ static inline LPWSTR SERV_dupmulti(LPCSTR str) return wstr; } +/****************************************************************************** + * RPC connection with servies.exe + */ + +static BOOL check_services_exe(void) +{ + static const WCHAR svcctl_started_event[] = SVCCTL_STARTED_EVENT; + HANDLE hEvent = OpenEventW(SYNCHRONIZE, FALSE, svcctl_started_event); + if (hEvent == NULL) /* need to start services.exe */ + { + static const WCHAR services[] = {'\\','s','e','r','v','i','c','e','s','.','e','x','e',0}; + PROCESS_INFORMATION out; + STARTUPINFOW si; + HANDLE wait_handles[2]; + WCHAR path[MAX_PATH]; + + if (!GetSystemDirectoryW(path, MAX_PATH - strlenW(services))) + return FALSE; + strcatW(path, services); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + if (!CreateProcessW(path, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &out)) + { + ERR("Couldn't start services.exe: error %u\n", GetLastError()); + return FALSE; + } + CloseHandle(out.hThread); + + hEvent = CreateEventW(NULL, TRUE, FALSE, svcctl_started_event); + wait_handles[0] = hEvent; + wait_handles[1] = out.hProcess; + + /* wait for the event to become available or the process to exit */ + if ((WaitForMultipleObjects(2, wait_handles, FALSE, INFINITE)) == WAIT_OBJECT_0 + 1) + { + DWORD exit_code; + GetExitCodeProcess(out.hProcess, &exit_code); + ERR("Unexpected termination of services.exe - exit code %d\n", exit_code); + CloseHandle(out.hProcess); + CloseHandle(hEvent); + return FALSE; + } + + TRACE("services.exe started successfully\n"); + CloseHandle(out.hProcess); + CloseHandle(hEvent); + return TRUE; + } + + TRACE("Waiting for services.exe to be available\n"); + WaitForSingleObject(hEvent, INFINITE); + TRACE("Services.exe are available\n"); + CloseHandle(hEvent); + + return TRUE; +} + +handle_t __RPC_USER MACHINE_HANDLEW_bind(MACHINE_HANDLEW MachineName) +{ + WCHAR transport[] = SVCCTL_TRANSPORT; + WCHAR endpoint[] = SVCCTL_ENDPOINT; + LPWSTR server_copy = NULL; + RPC_WSTR binding_str; + RPC_STATUS status; + handle_t rpc_handle; + + /* unlike Windows we start services.exe on demand. We start it always as + * checking if this is our address can be tricky */ + if (!check_services_exe()) + return NULL; + + status = RpcStringBindingComposeW(NULL, transport, (RPC_WSTR)MachineName, endpoint, NULL, &binding_str); + HeapFree(GetProcessHeap(), 0, server_copy); + if (status != RPC_S_OK) + { + ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status); + return NULL; + } + + status = RpcBindingFromStringBindingW(binding_str, &rpc_handle); + RpcStringFreeW(&binding_str); + + if (status != RPC_S_OK) + { + ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status); + return NULL; + } + + return rpc_handle; +} + +void __RPC_USER MACHINE_HANDLEW_unbind(MACHINE_HANDLEW MachineName, handle_t h) +{ + RpcBindingFree(&h); +} + /****************************************************************************** * registry access functions and data */ @@ -959,29 +1068,15 @@ SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName, TRACE("(%s,%s,0x%08x)\n", debugstr_w(lpMachineName), debugstr_w(lpDatabaseName), dwDesiredAccess); - if( lpDatabaseName && lpDatabaseName[0] ) - { - if( strcmpiW( lpDatabaseName, SERVICES_ACTIVE_DATABASEW ) == 0 ) - { - /* noop, all right */ - } - else if( strcmpiW( lpDatabaseName, SERVICES_FAILED_DATABASEW ) == 0 ) - { - SetLastError( ERROR_DATABASE_DOES_NOT_EXIST ); - return NULL; - } - else - { - SetLastError( ERROR_INVALID_NAME ); - return NULL; - } - } - manager = sc_handle_alloc( SC_HTYPE_MANAGER, sizeof (struct sc_manager), sc_handle_destroy_manager ); if (!manager) return NULL; + r = svcctl_OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess, &manager->hdr.server_handle); + if (r!=ERROR_SUCCESS) + goto error; + r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg); if (r!=ERROR_SUCCESS) goto error; @@ -1093,10 +1188,28 @@ BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl, BOOL WINAPI CloseServiceHandle( SC_HANDLE hSCObject ) { + struct sc_handle *obj; + DWORD err; + TRACE("%p\n", hSCObject); + if (hSCObject == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } - sc_handle_free( (struct sc_handle*) hSCObject ); + obj = (struct sc_handle *)hSCObject; + if (obj->server_handle) /* service handles currently don't have RPC connections */ + err = svcctl_CloseServiceHandle(&obj->server_handle); + else + err = ERROR_SUCCESS; + sc_handle_free( obj ); + if (err != ERROR_SUCCESS) + { + SetLastError(err); + return FALSE; + } return TRUE; } diff --git a/dlls/advapi32/svcctl.idl b/dlls/advapi32/svcctl.idl new file mode 100644 index 00000000000..101e0f21f65 --- /dev/null +++ b/dlls/advapi32/svcctl.idl @@ -0,0 +1 @@ +#include "wine/svcctl.idl" diff --git a/dlls/advapi32/tests/service.c b/dlls/advapi32/tests/service.c index 9b4eb728fb8..5b841346c5b 100644 --- a/dlls/advapi32/tests/service.c +++ b/dlls/advapi32/tests/service.c @@ -706,11 +706,8 @@ static void test_close(void) /* NULL handle */ SetLastError(0xdeadbeef); ret = CloseServiceHandle(NULL); - todo_wine - { ok(!ret, "Expected failure\n"); ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); - } /* TODO: Add some tests with invalid handles. These produce errors on Windows but crash on Wine */ diff --git a/include/Makefile.in b/include/Makefile.in index 18c0c02b0dc..ad85850aae6 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -67,6 +67,7 @@ IDL_H_SRCS = \ urlhist.idl \ urlmon.idl \ wine/itss.idl \ + wine/svcctl.idl \ wtypes.idl \ xmldom.idl \ xmldso.idl diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl new file mode 100644 index 00000000000..492aca9d147 --- /dev/null +++ b/include/wine/svcctl.idl @@ -0,0 +1,59 @@ +/* + * svcctl interface definitions - exported by services.exe to access the + * services database + * + * Copyright 2007 Google (Mikolaj Zalewski) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import "wtypes.idl"; + +/* + * some defined for the C code + */ +cpp_quote("#define SVCCTL_TRANSPORT {'n','c','a','c','n','_','n','p',0}") +cpp_quote("#define SVCCTL_ENDPOINT {'\\\\','p','i','p','e','\\\\','s','v','c','c','t','l',0}") + +/* Not the Windows event name - if needed the true one can be found in Inside Windows */ +cpp_quote("#define SVCCTL_STARTED_EVENT {'_','_','w','i','n','e','_','S','v','c','c','t','l','S','t','a','r','t','e','d',0}") + + +[ + uuid(367abb81-9844-35f1-ad32-98f038001003), + version(2.0), + pointer_default(unique), + endpoint("ncacn_np:[\\pipe\\svcctl]") +] +interface svcctl +{ + /* handle types */ + typedef [handle] LPCWSTR MACHINE_HANDLEW; + typedef [context_handle] void *SC_RPC_HANDLE; + + /* Compatible with Windows function 0x00 */ + DWORD svcctl_CloseServiceHandle( + [in,out] SC_RPC_HANDLE *handle + ); + + /* Compatible with Windows function 0x0f */ + DWORD svcctl_OpenSCManagerW( + [in,unique] MACHINE_HANDLEW MachineName, + [in,unique] LPCWSTR DatabaseName, + [in] DWORD dwAccessMask, + [out] SC_RPC_HANDLE *handle + ); + +} diff --git a/programs/services/Makefile.in b/programs/services/Makefile.in index 67c3db5acb0..8bbd0ad438c 100644 --- a/programs/services/Makefile.in +++ b/programs/services/Makefile.in @@ -5,12 +5,15 @@ VPATH = @srcdir@ MODULE = services.exe APPMODE = -mconsole IMPORTS = kernel32 -IMPORTS = advapi32 kernel32 +IMPORTS = rpcrt4 advapi32 kernel32 ntdll C_SRCS = \ + rpc.c \ services.c \ utils.c +IDL_S_SRCS = svcctl.idl + @MAKE_PROG_RULES@ @DEPENDENCIES@ # everything below this line is overwritten by make depend diff --git a/programs/services/rpc.c b/programs/services/rpc.c new file mode 100644 index 00000000000..a641119f55e --- /dev/null +++ b/programs/services/rpc.c @@ -0,0 +1,188 @@ +/* + * Services.exe - RPC functions + * + * Copyright 2007 Google (Mikolaj Zalewski) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include + +#include "wine/list.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +#include "services.h" +#include "svcctl.h" + +extern HANDLE __wine_make_process_system(void); + +WINE_DEFAULT_DEBUG_CHANNEL(service); + +static CRITICAL_SECTION g_handle_table_cs; +static CRITICAL_SECTION_DEBUG g_handle_table_cs_debug = +{ + 0, 0, &g_handle_table_cs, + { &g_handle_table_cs_debug.ProcessLocksList, + &g_handle_table_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": g_handle_table_cs") } +}; +static CRITICAL_SECTION g_handle_table_cs = { &g_handle_table_cs_debug, -1, 0, 0, 0, 0 }; + +static const GENERIC_MAPPING g_scm_generic = +{ + (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS), + (STANDARD_RIGHTS_WRITE | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_MODIFY_BOOT_CONFIG), + (STANDARD_RIGHTS_EXECUTE | SC_MANAGER_CONNECT | SC_MANAGER_LOCK), + SC_MANAGER_ALL_ACCESS +}; + +typedef enum +{ + SC_HTYPE_DONT_CARE = 0, + SC_HTYPE_MANAGER, + SC_HTYPE_SERVICE +} SC_HANDLE_TYPE; + +struct sc_handle +{ + SC_HANDLE_TYPE type; + DWORD access; +}; + +struct sc_manager /* service control manager handle */ +{ + struct sc_handle hdr; +}; + +DWORD svcctl_OpenSCManagerW( + MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */ + LPCWSTR DatabaseName, + DWORD dwAccessMask, + SC_RPC_HANDLE *handle) +{ + struct sc_manager *manager; + + WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask); + + if (DatabaseName != NULL && DatabaseName[0]) + { + if (strcmpW(DatabaseName, SERVICES_FAILED_DATABASEW) == 0) + return ERROR_DATABASE_DOES_NOT_EXIST; + if (strcmpW(DatabaseName, SERVICES_ACTIVE_DATABASEW) != 0) + return ERROR_INVALID_NAME; + } + + if (!(manager = HeapAlloc(GetProcessHeap(), 0, sizeof(*manager)))) + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + + manager->hdr.type = SC_HTYPE_MANAGER; + + if (dwAccessMask & MAXIMUM_ALLOWED) + dwAccessMask |= SC_MANAGER_ALL_ACCESS; + manager->hdr.access = dwAccessMask; + RtlMapGenericMask(&manager->hdr.access, &g_scm_generic); + *handle = &manager->hdr; + + return ERROR_SUCCESS; +} + +static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle) +{ + struct sc_handle *hdr = (struct sc_handle *)handle; + switch (hdr->type) + { + case SC_HTYPE_MANAGER: + { + struct sc_manager *manager = (struct sc_manager *)hdr; + HeapFree(GetProcessHeap(), 0, manager); + break; + } + default: + WINE_ERR("invalid handle type %d\n", hdr->type); + RpcRaiseException(ERROR_INVALID_HANDLE); + } +} + +DWORD svcctl_CloseServiceHandle( + SC_RPC_HANDLE *handle) +{ + WINE_TRACE("(&%p)\n", *handle); + + SC_RPC_HANDLE_destroy(*handle); + *handle = NULL; + + return ERROR_SUCCESS; +} + +DWORD RPC_MainLoop(void) +{ + WCHAR transport[] = SVCCTL_TRANSPORT; + WCHAR endpoint[] = SVCCTL_ENDPOINT; + HANDLE hSleepHandle; + DWORD err; + + if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS) + { + WINE_ERR("RpcServerUseProtseq failed with error %u\n", err); + return err; + } + + if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS) + { + WINE_ERR("RpcServerRegisterIf failed with error %u", err); + return err; + } + + if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS) + { + WINE_ERR("RpcServerListen failed with error %u\n", err); + return err; + } + + WINE_TRACE("Entered main loop\n"); + hSleepHandle = __wine_make_process_system(); + SetEvent(g_hStartedEvent); + do + { + err = WaitForSingleObjectEx(hSleepHandle, INFINITE, TRUE); + WINE_TRACE("Wait returned %d\n", err); + } while (err != WAIT_OBJECT_0); + + WINE_TRACE("Object signaled - wine shutdown\n"); + return ERROR_SUCCESS; +} + +void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle) +{ + SC_RPC_HANDLE_destroy(handle); +} + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len) +{ + return HeapAlloc(GetProcessHeap(), 0, len); +} + +void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr) +{ + HeapFree(GetProcessHeap(), 0, ptr); +} diff --git a/programs/services/services.c b/programs/services/services.c index c5be9e6fd9a..8f4e1b720c6 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -23,10 +23,12 @@ #include #include #include +#include #include "wine/list.h" #include "wine/unicode.h" #include "wine/debug.h" +#include "svcctl.h" #include "services.h" @@ -34,6 +36,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(service); +HANDLE g_hStartedEvent; + static struct list g_services; /* Registry constants */ @@ -196,9 +200,11 @@ static DWORD load_services(void) int main(int argc, char *argv[]) { + static const WCHAR svcctl_started_event[] = SVCCTL_STARTED_EVENT; DWORD err; + g_hStartedEvent = CreateEventW(NULL, TRUE, FALSE, svcctl_started_event); list_init(&g_services); if ((err = load_services()) != ERROR_SUCCESS) return err; - return 0; + return RPC_MainLoop(); } diff --git a/programs/services/svcctl.idl b/programs/services/svcctl.idl new file mode 100644 index 00000000000..101e0f21f65 --- /dev/null +++ b/programs/services/svcctl.idl @@ -0,0 +1 @@ +#include "wine/svcctl.idl"