From 33914a1bf426f0d0d0670263b5b9f6fb24a87251 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 8 May 2008 18:08:41 +0200 Subject: [PATCH] services: Send the service name in the control requests. Only start a single dispatcher thread for all services. --- dlls/advapi32/service.c | 185 ++++++++++++++++++++--------------- include/wine/svcctl.idl | 21 ++-- programs/services/rpc.c | 23 +++-- programs/services/services.c | 25 ++--- 4 files changed, 148 insertions(+), 106 deletions(-) diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index a1407dd521c..29c1a091674 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -81,6 +81,7 @@ typedef struct service_data_t LPHANDLER_FUNCTION_EX handler; LPVOID context; HANDLE thread; + SC_HANDLE handle; BOOL unicode : 1; union { LPSERVICE_MAIN_FUNCTIONA a; @@ -356,6 +357,17 @@ static HANDLE service_open_pipe(void) return handle; } +static service_data *find_service_by_name( const WCHAR *name ) +{ + unsigned int i; + + if (nb_services == 1) /* only one service (FIXME: should depend on OWN_PROCESS etc.) */ + return services[0]; + for (i = 0; i < nb_services; i++) + if (!strcmpiW( name, services[i]->name )) return services[i]; + return NULL; +} + /****************************************************************************** * service_thread * @@ -420,57 +432,37 @@ static DWORD WINAPI service_thread(LPVOID arg) /****************************************************************************** * service_handle_start */ -static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count) +static DWORD service_handle_start(service_data *service, const WCHAR *data, DWORD count) { - DWORD read = 0, result = 0; - LPWSTR args; - BOOL r; - - TRACE("%p %p %d\n", pipe, service, count); - - args = HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR)); - r = ReadFile(pipe, args, count*sizeof(WCHAR), &read, NULL); - if (!r || count!=read/sizeof(WCHAR) || args[count-1]) - { - ERR("pipe read failed r = %d count = %d read = %d args[n-1]=%s\n", - r, count, read, debugstr_wn(args, count)); - goto end; - } + TRACE("%s argsize %u\n", debugstr_w(service->name), count); if (service->thread) { WARN("service is not stopped\n"); - result = ERROR_SERVICE_ALREADY_RUNNING; - goto end; + return ERROR_SERVICE_ALREADY_RUNNING; } HeapFree(GetProcessHeap(), 0, service->args); - service->args = args; - args = NULL; + service->args = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); + memcpy( service->args, data, count * sizeof(WCHAR) ); service->thread = CreateThread( NULL, 0, service_thread, service, 0, NULL ); SetEvent( service_event ); /* notify the main loop */ - -end: - HeapFree(GetProcessHeap(), 0, args); - WriteFile( pipe, &result, sizeof result, &read, NULL ); - - return TRUE; + return 0; } /****************************************************************************** * service_handle_control */ -static BOOL service_handle_control(HANDLE pipe, service_data *service, - DWORD dwControl) +static DWORD service_handle_control(service_data *service, DWORD dwControl) { - DWORD count, ret = ERROR_INVALID_SERVICE_CONTROL; + DWORD ret = ERROR_INVALID_SERVICE_CONTROL; + + TRACE("%s control %u\n", debugstr_w(service->name), dwControl); - TRACE("received control %d\n", dwControl); - if (service->handler) ret = service->handler(dwControl, 0, NULL, service->context); - return WriteFile(pipe, &ret, sizeof ret, &count, NULL); + return ret; } /****************************************************************************** @@ -478,61 +470,108 @@ static BOOL service_handle_control(HANDLE pipe, service_data *service, */ static DWORD WINAPI service_control_dispatcher(LPVOID arg) { - service_data *service = arg; + SC_HANDLE manager; HANDLE pipe; - TRACE("%p %s\n", service, debugstr_w(service->name)); + if (!(manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ))) + { + ERR("failed to open service manager error %u\n", GetLastError()); + return 0; + } pipe = service_open_pipe(); if (pipe==INVALID_HANDLE_VALUE) { - ERR("failed to create pipe for %s, error = %d\n", - debugstr_w(service->name), GetLastError()); + ERR("failed to create control pipe error = %d\n", GetLastError()); return 0; } /* dispatcher loop */ while (1) { + service_data *service; + service_start_info info; + WCHAR *data = NULL; BOOL r; - DWORD count, req[2] = {0,0}; + DWORD data_size = 0, count, result; - r = ReadFile( pipe, &req, sizeof req, &count, NULL ); + r = ReadFile( pipe, &info, FIELD_OFFSET(service_start_info,data), &count, NULL ); if (!r) { if (GetLastError() != ERROR_BROKEN_PIPE) ERR( "pipe read failed error %u\n", GetLastError() ); break; } - if (count != sizeof(req)) + if (count != FIELD_OFFSET(service_start_info,data)) { ERR( "partial pipe read %u\n", count ); break; } + if (count < info.total_size) + { + data_size = info.total_size - FIELD_OFFSET(service_start_info,data); + data = HeapAlloc( GetProcessHeap(), 0, data_size ); + r = ReadFile( pipe, data, data_size, &count, NULL ); + if (!r) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + ERR( "pipe read failed error %u\n", GetLastError() ); + break; + } + if (count != data_size) + { + ERR( "partial pipe read %u/%u\n", count, data_size ); + break; + } + } + + /* find the service */ + + if (!(service = find_service_by_name( data ))) + { + FIXME( "got request %u for unknown service %s\n", info.cmd, debugstr_w(data)); + result = ERROR_INVALID_PARAMETER; + goto done; + } + + TRACE( "got request %u for service %s\n", info.cmd, debugstr_w(data) ); /* handle the request */ - switch (req[0]) + switch (info.cmd) { case WINESERV_STARTINFO: - service_handle_start(pipe, service, req[1]); + if (!service->handle) + { + if (!(service->handle = OpenServiceW( manager, data, SERVICE_SET_STATUS ))) + FIXME( "failed to open service %s\n", debugstr_w(data) ); + } + result = service_handle_start(service, data + info.name_size, + data_size / sizeof(WCHAR) - info.name_size ); break; case WINESERV_SENDCONTROL: - service_handle_control(pipe, service, req[1]); + result = service_handle_control(service, info.control); break; default: - ERR("received invalid command %d length %d\n", req[0], req[1]); + ERR("received invalid command %u\n", info.cmd); + result = ERROR_INVALID_PARAMETER; + break; } + + done: + WriteFile(pipe, &result, sizeof(result), &count, NULL); + HeapFree( GetProcessHeap(), 0, data ); } CloseHandle(pipe); + CloseServiceHandle( manager ); return 1; } /****************************************************************************** - * service_run_threads + * service_run_main_thread */ -static BOOL service_run_threads(void) +static BOOL service_run_main_thread(void) { DWORD i, n, ret; HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS]; @@ -540,22 +579,19 @@ static BOOL service_run_threads(void) service_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + /* FIXME: service_control_dispatcher should be merged into the main thread */ wait_handles[0] = __wine_make_process_system(); - wait_handles[1] = service_event; + wait_handles[1] = CreateThread( NULL, 0, service_control_dispatcher, NULL, 0, NULL ); + wait_handles[2] = service_event; - TRACE("Starting %d pipe listener threads. Services running as process %d\n", + TRACE("Starting %d services running as process %d\n", nb_services, GetCurrentProcessId()); - EnterCriticalSection( &service_cs ); - for (i = 0; i < nb_services; i++) - CloseHandle( CreateThread( NULL, 0, service_control_dispatcher, services[i], 0, NULL )); - LeaveCriticalSection( &service_cs ); - /* wait for all the threads to pack up and exit */ for (;;) { EnterCriticalSection( &service_cs ); - for (i = 0, n = 2; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++) + for (i = 0, n = 3; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++) { if (!services[i]->thread) continue; wait_services[n] = i; @@ -571,6 +607,12 @@ static BOOL service_run_threads(void) ExitProcess(0); } else if (ret == 1) + { + TRACE( "control dispatcher exited, shutting down\n" ); + /* FIXME: we should maybe send a shutdown control to running services */ + ExitProcess(0); + } + else if (ret == 2) { continue; /* rebuild the list */ } @@ -578,7 +620,7 @@ static BOOL service_run_threads(void) { services[wait_services[ret]]->thread = 0; CloseHandle( wait_handles[ret] ); - if (n == 3) return TRUE; /* it was the last running thread */ + if (n == 4) return TRUE; /* it was the last running thread */ } else return FALSE; } @@ -616,7 +658,7 @@ BOOL WINAPI StartServiceCtrlDispatcherA( const SERVICE_TABLE_ENTRYA *servent ) services[i] = info; } - service_run_threads(); + service_run_main_thread(); return ret; } @@ -661,7 +703,7 @@ BOOL WINAPI StartServiceCtrlDispatcherW( const SERVICE_TABLE_ENTRYW *servent ) services[i] = info; } - service_run_threads(); + service_run_main_thread(); return ret; } @@ -2214,40 +2256,23 @@ SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExA( LPCSTR name, LPHANDL SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExW( LPCWSTR lpServiceName, LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext ) { - SC_HANDLE hService; - SC_HANDLE hSCM; - unsigned int i; + service_data *service; + SC_HANDLE hService = 0; BOOL found = FALSE; TRACE("%s %p %p\n", debugstr_w(lpServiceName), lpHandlerProc, lpContext); - hSCM = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ); - if (!hSCM) - return NULL; - hService = OpenServiceW( hSCM, lpServiceName, SERVICE_SET_STATUS ); - CloseServiceHandle(hSCM); - if (!hService) - return NULL; - EnterCriticalSection( &service_cs ); - for (i = 0; i < nb_services; i++) + if ((service = find_service_by_name( lpServiceName ))) { - if(!strcmpW(lpServiceName, services[i]->name)) - { - services[i]->handler = lpHandlerProc; - services[i]->context = lpContext; - found = TRUE; - break; - } + service->handler = lpHandlerProc; + service->context = lpContext; + hService = service->handle; + found = TRUE; } LeaveCriticalSection( &service_cs ); - if (!found) - { - CloseServiceHandle(hService); - SetLastError(ERROR_SERVICE_DOES_NOT_EXIST); - return NULL; - } + if (!found) SetLastError(ERROR_SERVICE_DOES_NOT_EXIST); return (SERVICE_STATUS_HANDLE)hService; } diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index 443f6c9fcc9..65dfb6279ea 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -32,15 +32,20 @@ cpp_quote("#define SVCCTL_ENDPOINT {'\\\\','p','i','p','e','\\\\','s','v','c','c cpp_quote("#define SVCCTL_STARTED_EVENT {'_','_','w','i','n','e','_','S','v','c','c','t','l','S','t','a','r','t','e','d',0}") /* Service startup protocol over control pipe - not compatible with Windows */ -cpp_quote("typedef struct service_start_info_t") -cpp_quote("{") -cpp_quote(" DWORD cmd;") -cpp_quote(" DWORD size;") -cpp_quote(" WCHAR str[1];") -cpp_quote("} service_start_info;") +enum service_pipe_command +{ + WINESERV_STARTINFO = 1, + WINESERV_SENDCONTROL = 2 +}; -cpp_quote("#define WINESERV_STARTINFO 1") -cpp_quote("#define WINESERV_SENDCONTROL 2") +typedef struct service_start_info_t +{ + enum service_pipe_command cmd; /* request code */ + DWORD total_size; /* total request size */ + DWORD name_size; /* size of name in data buffer */ + DWORD control; /* control code */ + WCHAR data[1]; +} service_start_info; [ uuid(367abb81-9844-35f1-ad32-98f038001003), diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 616daa9052e..3176a1e3952 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -708,15 +708,24 @@ static BOOL service_accepts_control(const struct service_entry *service, DWORD d /****************************************************************************** * service_send_control */ -static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result) +static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result) { - DWORD cmd[2], count = 0; + service_start_info *ssi; + DWORD len, count = 0; BOOL r; - cmd[0] = WINESERV_SENDCONTROL; - cmd[1] = dwControl; - r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL); - if (!r || count != sizeof cmd) + /* calculate how much space we need to send the startup info */ + len = strlenW(service->name) + 1; + + ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len])); + ssi->cmd = WINESERV_SENDCONTROL; + ssi->control = dwControl; + ssi->total_size = FIELD_OFFSET(service_start_info, data[len]); + ssi->name_size = strlenW(service->name) + 1; + strcpyW( ssi->data, service->name ); + + r = WriteFile(pipe, ssi, ssi->total_size, &count, NULL); + if (!r || count != ssi->total_size) { WINE_ERR("service protocol error - failed to write pipe!\n"); return r; @@ -837,7 +846,7 @@ DWORD svcctl_ControlService( { DWORD result = ERROR_SUCCESS; - ret = service_send_control(control_pipe, dwControl, &result); + ret = service_send_control(service->service_entry, control_pipe, dwControl, &result); if (dwControl == SERVICE_CONTROL_STOP) { diff --git a/programs/services/services.c b/programs/services/services.c index 5b5fbc04085..806fbb6c563 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -621,17 +621,17 @@ static DWORD service_wait_for_startup(struct service_entry *service_entry, HANDL /****************************************************************************** * service_send_start_message */ -static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc) +static BOOL service_send_start_message(struct service_entry *service, LPCWSTR *argv, DWORD argc) { DWORD i, len, count, result; service_start_info *ssi; LPWSTR p; BOOL r; - WINE_TRACE("%p %p %d\n", pipe, argv, argc); + WINE_TRACE("%s %p %d\n", wine_dbgstr_w(service->name), argv, argc); /* FIXME: this can block so should be done in another thread */ - r = ConnectNamedPipe(pipe, NULL); + r = ConnectNamedPipe(service->control_pipe, NULL); if (!r && GetLastError() != ERROR_PIPE_CONNECTED) { WINE_ERR("pipe connect failed\n"); @@ -639,16 +639,20 @@ static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc) } /* calculate how much space do we need to send the startup info */ - len = 1; + len = strlenW(service->name) + 1; for (i=0; icmd = WINESERV_STARTINFO; - ssi->size = len; + ssi->control = 0; + ssi->total_size = FIELD_OFFSET(service_start_info, data[len]); + ssi->name_size = strlenW(service->name) + 1; + strcpyW( ssi->data, service->name ); /* copy service args into a single buffer*/ - p = &ssi->str[0]; + p = &ssi->data[ssi->name_size]; for (i=0; icontrol_pipe, ssi, ssi->total_size, &count, NULL); if (r) { - r = ReadFile(pipe, &result, sizeof result, &count, NULL); + r = ReadFile(service->control_pipe, &result, sizeof result, &count, NULL); if (r && result) { SetLastError(result); @@ -709,8 +713,7 @@ DWORD service_start(struct service_entry *service, DWORD service_argc, LPCWSTR * if (err == ERROR_SUCCESS) { - if (!service_send_start_message(service->control_pipe, - service_argv, service_argc)) + if (!service_send_start_message(service, service_argv, service_argc)) err = ERROR_SERVICE_REQUEST_TIMEOUT; }