diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index 0b29493282a..30e7622526c 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -71,22 +71,10 @@ static const GENERIC_MAPPING svc_generic = { SERVICE_ALL_ACCESS }; -typedef struct service_start_info_t -{ - DWORD cmd; - DWORD size; - WCHAR str[1]; -} service_start_info; - -#define WINESERV_STARTINFO 1 -#define WINESERV_GETSTATUS 2 -#define WINESERV_SENDCONTROL 3 - typedef struct service_data_t { LPHANDLER_FUNCTION_EX handler; LPVOID context; - SERVICE_STATUS_PROCESS status; HANDLE thread; BOOL unicode : 1; union { @@ -576,7 +564,6 @@ static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count HeapFree(GetProcessHeap(), 0, service->args); service->args = args; args = NULL; - service->status.dwCurrentState = SERVICE_START_PENDING; service->thread = CreateThread( NULL, 0, service_thread, service, 0, NULL ); SetEvent( service_event ); /* notify the main loop */ @@ -588,136 +575,6 @@ end: return TRUE; } -/****************************************************************************** - * service_send_start_message - */ -static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc) -{ - DWORD i, len, count, result; - service_start_info *ssi; - LPWSTR p; - BOOL r; - - TRACE("%p %p %d\n", pipe, argv, argc); - - /* calculate how much space do we need to send the startup info */ - len = 1; - for (i=0; icmd = WINESERV_STARTINFO; - ssi->size = len; - - /* copy service args into a single buffer*/ - p = &ssi->str[0]; - for (i=0; istatus, - sizeof service->status, &count, NULL); -} - -/****************************************************************************** - * service_send_control - */ -static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result) -{ - DWORD cmd[2], count = 0; - BOOL r; - - cmd[0] = WINESERV_SENDCONTROL; - cmd[1] = dwControl; - r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL); - if (!r || count != sizeof cmd) - { - ERR("service protocol error - failed to write pipe!\n"); - return r; - } - r = ReadFile(pipe, result, sizeof *result, &count, NULL); - if (!r || count != sizeof *result) - ERR("service protocol error - failed to read pipe " - "r = %d count = %d!\n", r, count); - return r; -} - -/****************************************************************************** - * service_accepts_control - */ -static BOOL service_accepts_control(const service_data *service, DWORD dwControl) -{ - DWORD a = service->status.dwControlsAccepted; - - switch (dwControl) - { - case SERVICE_CONTROL_INTERROGATE: - return TRUE; - case SERVICE_CONTROL_STOP: - if (a&SERVICE_ACCEPT_STOP) - return TRUE; - break; - case SERVICE_CONTROL_SHUTDOWN: - if (a&SERVICE_ACCEPT_SHUTDOWN) - return TRUE; - break; - case SERVICE_CONTROL_PAUSE: - case SERVICE_CONTROL_CONTINUE: - if (a&SERVICE_ACCEPT_PAUSE_CONTINUE) - return TRUE; - break; - case SERVICE_CONTROL_PARAMCHANGE: - if (a&SERVICE_ACCEPT_PARAMCHANGE) - return TRUE; - break; - case SERVICE_CONTROL_NETBINDADD: - case SERVICE_CONTROL_NETBINDREMOVE: - case SERVICE_CONTROL_NETBINDENABLE: - case SERVICE_CONTROL_NETBINDDISABLE: - if (a&SERVICE_ACCEPT_NETBINDCHANGE) - return TRUE; - case SERVICE_CONTROL_HARDWAREPROFILECHANGE: - if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE) - return TRUE; - break; - case SERVICE_CONTROL_POWEREVENT: - if (a&SERVICE_ACCEPT_POWEREVENT) - return TRUE; - break; - case SERVICE_CONTROL_SESSIONCHANGE: - if (a&SERVICE_ACCEPT_SESSIONCHANGE) - return TRUE; - break; - } - return FALSE; -} - /****************************************************************************** * service_handle_control */ @@ -728,11 +585,8 @@ static BOOL service_handle_control(HANDLE pipe, service_data *service, TRACE("received control %d\n", dwControl); - if (service_accepts_control(service, dwControl)) - { - if (service->handler) - ret = service->handler(dwControl, 0, NULL, service->context); - } + if (service->handler) + ret = service->handler(dwControl, 0, NULL, service->context); return WriteFile(pipe, &ret, sizeof ret, &count, NULL); } @@ -742,22 +596,16 @@ static BOOL service_handle_control(HANDLE pipe, service_data *service, static DWORD WINAPI service_control_dispatcher(LPVOID arg) { service_data *service = arg; - LPWSTR name; HANDLE pipe, event; TRACE("%p %s\n", service, debugstr_w(service->name)); - /* create a pipe to talk to the rest of the world with */ - name = service_get_pipe_name(service->name); - pipe = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE|PIPE_WAIT, 1, 256, 256, 10000, NULL ); + pipe = service_open_pipe(service->name); if (pipe==INVALID_HANDLE_VALUE) ERR("failed to create pipe for %s, error = %d\n", debugstr_w(service->name), GetLastError()); - HeapFree(GetProcessHeap(), 0, name); - /* let the process who started us know we've tried to create a pipe */ event = service_get_event_handle(service->name); SetEvent(event); @@ -771,13 +619,6 @@ static DWORD WINAPI service_control_dispatcher(LPVOID arg) BOOL r; DWORD count, req[2] = {0,0}; - r = ConnectNamedPipe(pipe, NULL); - if (!r && GetLastError() != ERROR_PIPE_CONNECTED) - { - ERR("pipe connect failed\n"); - break; - } - r = ReadFile( pipe, &req, sizeof req, &count, NULL ); if (!r || count!=sizeof req) { @@ -791,18 +632,12 @@ static DWORD WINAPI service_control_dispatcher(LPVOID arg) case WINESERV_STARTINFO: service_handle_start(pipe, service, req[1]); break; - case WINESERV_GETSTATUS: - service_handle_get_status(pipe, service); - break; case WINESERV_SENDCONTROL: service_handle_control(pipe, service, req[1]); break; default: ERR("received invalid command %d length %d\n", req[0], req[1]); } - - FlushFileBuffers(pipe); - DisconnectNamedPipe(pipe); } CloseHandle(pipe); @@ -828,10 +663,7 @@ static BOOL service_run_threads(void) EnterCriticalSection( &service_cs ); for (i = 0; i < nb_services; i++) - { - services[i]->status.dwProcessId = GetCurrentProcessId(); CloseHandle( CreateThread( NULL, 0, service_control_dispatcher, services[i], 0, NULL )); - } LeaveCriticalSection( &service_cs ); /* wait for all the threads to pack up and exit */ @@ -1131,8 +963,7 @@ BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl, LPSERVICE_STATUS lpServiceStatus ) { struct sc_service *hsvc; - BOOL ret = FALSE; - HANDLE handle; + DWORD err; TRACE("%p %d %p\n", hService, dwControl, lpServiceStatus); @@ -1143,45 +974,14 @@ BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl, return FALSE; } - if (lpServiceStatus) + err = svcctl_ControlService(hsvc->hdr.server_handle, dwControl, lpServiceStatus); + if (err != ERROR_SUCCESS) { - ret = QueryServiceStatus(hService, lpServiceStatus); - if (!ret) - { - ERR("failed to query service status\n"); - SetLastError(ERROR_SERVICE_NOT_ACTIVE); - return FALSE; - } - - switch (lpServiceStatus->dwCurrentState) - { - case SERVICE_STOPPED: - SetLastError(ERROR_SERVICE_NOT_ACTIVE); - return FALSE; - case SERVICE_START_PENDING: - if (dwControl==SERVICE_CONTROL_STOP) - break; - /* fall thru */ - case SERVICE_STOP_PENDING: - SetLastError(ERROR_SERVICE_CANNOT_ACCEPT_CTRL); - return FALSE; - } + SetLastError(err); + return FALSE; } - handle = service_open_pipe(hsvc->name); - if (handle!=INVALID_HANDLE_VALUE) - { - DWORD result = ERROR_SUCCESS; - ret = service_send_control(handle, dwControl, &result); - CloseHandle(handle); - if (result!=ERROR_SUCCESS) - { - SetLastError(result); - ret = FALSE; - } - } - - return ret; + return TRUE; } /****************************************************************************** @@ -1527,109 +1327,6 @@ BOOL WINAPI StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs, return r; } -/****************************************************************************** - * service_start_process [INTERNAL] - */ -static DWORD service_start_process(struct sc_service *hsvc, LPDWORD ppid) -{ - static const WCHAR _ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0}; - PROCESS_INFORMATION pi; - STARTUPINFOW si; - LPWSTR path = NULL, str; - DWORD type, size, ret, svc_type; - HANDLE handles[2]; - BOOL r; - - size = sizeof(svc_type); - if (RegQueryValueExW(hsvc->hkey, szType, NULL, &type, (LPBYTE)&svc_type, &size) || type != REG_DWORD) - svc_type = 0; - - if (svc_type == SERVICE_KERNEL_DRIVER) - { - static const WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',' ',0}; - DWORD len = GetSystemDirectoryW( NULL, 0 ) + sizeof(winedeviceW)/sizeof(WCHAR) + strlenW(hsvc->name); - - if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; - GetSystemDirectoryW( path, len ); - lstrcatW( path, winedeviceW ); - lstrcatW( path, hsvc->name ); - } - else - { - /* read the executable path from the registry */ - size = 0; - ret = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, NULL, &size); - if (ret!=ERROR_SUCCESS) - return FALSE; - str = HeapAlloc(GetProcessHeap(),0,size); - ret = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, (LPBYTE)str, &size); - if (ret==ERROR_SUCCESS) - { - size = ExpandEnvironmentStringsW(str,NULL,0); - path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR)); - ExpandEnvironmentStringsW(str,path,size); - } - HeapFree(GetProcessHeap(),0,str); - if (!path) - return FALSE; - } - - /* wait for the process to start and set an event or terminate */ - handles[0] = service_get_event_handle( hsvc->name ); - ZeroMemory(&si, sizeof(STARTUPINFOW)); - si.cb = sizeof(STARTUPINFOW); - if (!(svc_type & SERVICE_INTERACTIVE_PROCESS)) - { - static WCHAR desktopW[] = {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n','\\','D','e','f','a','u','l','t',0}; - si.lpDesktop = desktopW; - } - - r = CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); - if (r) - { - if (ppid) *ppid = pi.dwProcessId; - - handles[1] = pi.hProcess; - ret = WaitForMultipleObjectsEx(2, handles, FALSE, 30000, FALSE); - if(ret != WAIT_OBJECT_0) - { - SetLastError(ERROR_IO_PENDING); - r = FALSE; - } - - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); - } - CloseHandle( handles[0] ); - HeapFree(GetProcessHeap(),0,path); - return r; -} - -static BOOL service_wait_for_startup(SC_HANDLE hService) -{ - DWORD i; - SERVICE_STATUS status; - BOOL r = FALSE; - - TRACE("%p\n", hService); - - for (i=0; i<20; i++) - { - status.dwCurrentState = 0; - r = QueryServiceStatus(hService, &status); - if (!r) - break; - if (status.dwCurrentState == SERVICE_RUNNING) - { - TRACE("Service started successfully\n"); - break; - } - r = FALSE; - if (status.dwCurrentState != SERVICE_START_PENDING) break; - Sleep(100 * i); - } - return r; -} /****************************************************************************** * StartServiceW [ADVAPI32.@] @@ -1640,9 +1337,7 @@ BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs, LPCWSTR *lpServiceArgVectors) { struct sc_service *hsvc; - BOOL r = FALSE; - SC_LOCK hLock; - HANDLE handle = INVALID_HANDLE_VALUE; + DWORD err; TRACE("%p %d %p\n", hService, dwNumServiceArgs, lpServiceArgVectors); @@ -1650,35 +1345,17 @@ BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs, if (!hsvc) { SetLastError(ERROR_INVALID_HANDLE); - return r; + return FALSE; } - hLock = LockServiceDatabase((SC_HANDLE)hsvc->scm); - if (!hLock) - return r; - - handle = service_open_pipe(hsvc->name); - if (handle==INVALID_HANDLE_VALUE) + err = svcctl_StartServiceW(hsvc->hdr.server_handle, dwNumServiceArgs, lpServiceArgVectors); + if (err != ERROR_SUCCESS) { - /* start the service process */ - if (service_start_process(hsvc, NULL)) - handle = service_open_pipe(hsvc->name); + SetLastError(err); + return FALSE; } - if (handle != INVALID_HANDLE_VALUE) - { - r = service_send_start_message(handle, lpServiceArgVectors, dwNumServiceArgs); - CloseHandle(handle); - } - - UnlockServiceDatabase( hLock ); - - TRACE("returning %d\n", r); - - if (r) - service_wait_for_startup(hService); - - return r; + return TRUE; } /****************************************************************************** diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index a629f6a188d..4053524c7cf 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -31,6 +31,16 @@ cpp_quote("#define SVCCTL_ENDPOINT {'\\\\','p','i','p','e','\\\\','s','v','c','c /* 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}") +/* 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;") + +cpp_quote("#define WINESERV_STARTINFO 1") +cpp_quote("#define WINESERV_SENDCONTROL 2") [ uuid(367abb81-9844-35f1-ad32-98f038001003), @@ -48,6 +58,9 @@ interface svcctl /* undocumented access rights */ cpp_quote("#define SERVICE_SET_STATUS 0x8000") + /* undocumented access rights */ + cpp_quote("#define SERVICE_SET_STATUS 0x8000") + cpp_quote("#if 0 /* already defined in winsvc.h */") typedef struct _QUERY_SERVICE_CONFIGW { DWORD dwServiceType; @@ -82,6 +95,13 @@ cpp_quote("#endif") [in,out] SC_RPC_HANDLE *handle ); + /* Compatible with Windows function 0x01 */ + DWORD svcctl_ControlService( + [in] SC_RPC_HANDLE hService, + [in] DWORD dwControl, + [out] SERVICE_STATUS *lpServiceStatus + ); + /* Compatible with Windows function 0x02 */ DWORD svcctl_DeleteService( [in] SC_RPC_HANDLE hService @@ -162,6 +182,13 @@ cpp_quote("#endif") [in] SC_RPC_HANDLE hService, [out] QUERY_SERVICE_CONFIGW *config); + /* Untested with Windows function 0x13 */ + DWORD svcctl_StartServiceW( + [in] SC_RPC_HANDLE hService, + [in] DWORD dwNumServiceArgs, + [in,unique,size_is(dwNumServiceArgs)] LPCWSTR *lpServiceArgVectors + ); + /* Compatible with Windows function 0x14 */ DWORD svcctl_GetServiceDisplayNameW( [in] SC_RPC_HANDLE hSCManager, diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 0a59ed40696..b92580e4e8d 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -371,6 +371,7 @@ DWORD svcctl_CreateServiceW( entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup); entry->config.lpServiceStartName = strdupW(lpServiceStartName); entry->config.lpDisplayName = strdupW(lpDisplayName); + entry->control_pipe = INVALID_HANDLE_VALUE; if (lpdwTagId) /* TODO: in most situations a non-NULL tagid will generate a ERROR_INVALID_PARAMETER */ entry->config.dwTagId = *lpdwTagId; @@ -599,6 +600,9 @@ DWORD svcctl_SetServiceStatus( service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint; unlock_services(); + if (service->service_entry->status_changed_event) + SetEvent(service->service_entry->status_changed_event); + return ERROR_SUCCESS; } @@ -648,6 +652,429 @@ DWORD svcctl_QueryServiceStatusEx( return ERROR_SUCCESS; } +static HANDLE service_get_event_handle(LPCWSTR service) +{ + static const WCHAR prefix[] = { + '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0}; + LPWSTR name; + DWORD len; + HANDLE handle; + + len = sizeof prefix + strlenW(service)*sizeof(WCHAR); + name = HeapAlloc(GetProcessHeap(), 0, len); + strcpyW(name, prefix); + strcatW(name, service); + handle = CreateEventW(NULL, TRUE, FALSE, name); + HeapFree(GetProcessHeap(), 0, name); + return handle; +} + +static LPWSTR service_get_pipe_name(LPCWSTR service) +{ + static const WCHAR prefix[] = { '\\','\\','.','\\','p','i','p','e','\\', + '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0}; + LPWSTR name; + DWORD len; + + len = sizeof prefix + strlenW(service)*sizeof(WCHAR); + name = HeapAlloc(GetProcessHeap(), 0, len); + strcpyW(name, prefix); + strcatW(name, service); + return name; +} + +static DWORD service_start_process(struct service_entry *service_entry) +{ + PROCESS_INFORMATION pi; + STARTUPINFOW si; + LPWSTR path = NULL; + DWORD size, ret; + HANDLE handles[2]; + BOOL r; + + lock_services(); + + if (service_entry->config.dwServiceType == SERVICE_KERNEL_DRIVER) + { + static const WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',' ',0}; + DWORD len = GetSystemDirectoryW( NULL, 0 ) + sizeof(winedeviceW)/sizeof(WCHAR) + strlenW(service_entry->name); + + if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + GetSystemDirectoryW( path, len ); + lstrcatW( path, winedeviceW ); + lstrcatW( path, service_entry->name ); + } + else + { + size = ExpandEnvironmentStringsW(service_entry->config.lpBinaryPathName,NULL,0); + path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR)); + if (!path) + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + ExpandEnvironmentStringsW(service_entry->config.lpBinaryPathName,path,size); + } + + /* wait for the process to start and set an event or terminate */ + handles[0] = service_get_event_handle( service_entry->name ); + ZeroMemory(&si, sizeof(STARTUPINFOW)); + si.cb = sizeof(STARTUPINFOW); + if (!(service_entry->config.dwServiceType & SERVICE_INTERACTIVE_PROCESS)) + { + static WCHAR desktopW[] = {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n','\\','D','e','f','a','u','l','t',0}; + si.lpDesktop = desktopW; + } + + unlock_services(); + + r = CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + HeapFree(GetProcessHeap(),0,path); + if (!r) + { + CloseHandle( handles[0] ); + return GetLastError(); + } + + handles[1] = pi.hProcess; + ret = WaitForMultipleObjectsEx(2, handles, FALSE, 30000, FALSE); + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + CloseHandle( handles[0] ); + + if(ret != WAIT_OBJECT_0) + return ERROR_SERVICE_REQUEST_TIMEOUT; + + /* FIXME */ + service_entry->status.dwCurrentState = SERVICE_START_PENDING; + service_entry->status.dwProcessId = pi.dwProcessId; + + return ERROR_SUCCESS; +} + +static DWORD service_wait_for_startup(struct service_entry *service_entry) +{ + WINE_TRACE("%p\n", service_entry); + + for (;;) + { + DWORD dwCurrentStatus; + DWORD ret = WaitForSingleObject(service_entry->status_changed_event, 20000); + if (ret == WAIT_TIMEOUT) + return ERROR_SERVICE_REQUEST_TIMEOUT; + lock_services(); + dwCurrentStatus = service_entry->status.dwCurrentState; + unlock_services(); + if (dwCurrentStatus == SERVICE_RUNNING) + { + WINE_TRACE("Service started successfully\n"); + return ERROR_SUCCESS; + } + if (dwCurrentStatus != SERVICE_START_PENDING) + return ERROR_SERVICE_REQUEST_TIMEOUT; + } +} + +/****************************************************************************** + * service_send_start_message + */ +static BOOL service_send_start_message(HANDLE pipe, 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); + + r = ConnectNamedPipe(pipe, NULL); + if (!r && GetLastError() != ERROR_PIPE_CONNECTED) + { + WINE_ERR("pipe connect failed\n"); + return FALSE; + } + + /* calculate how much space do we need to send the startup info */ + len = 1; + for (i=0; icmd = WINESERV_STARTINFO; + ssi->size = len; + + /* copy service args into a single buffer*/ + p = &ssi->str[0]; + for (i=0; istatus.dwControlsAccepted; + + switch (dwControl) + { + case SERVICE_CONTROL_INTERROGATE: + return TRUE; + case SERVICE_CONTROL_STOP: + if (a&SERVICE_ACCEPT_STOP) + return TRUE; + break; + case SERVICE_CONTROL_SHUTDOWN: + if (a&SERVICE_ACCEPT_SHUTDOWN) + return TRUE; + break; + case SERVICE_CONTROL_PAUSE: + case SERVICE_CONTROL_CONTINUE: + if (a&SERVICE_ACCEPT_PAUSE_CONTINUE) + return TRUE; + break; + case SERVICE_CONTROL_PARAMCHANGE: + if (a&SERVICE_ACCEPT_PARAMCHANGE) + return TRUE; + break; + case SERVICE_CONTROL_NETBINDADD: + case SERVICE_CONTROL_NETBINDREMOVE: + case SERVICE_CONTROL_NETBINDENABLE: + case SERVICE_CONTROL_NETBINDDISABLE: + if (a&SERVICE_ACCEPT_NETBINDCHANGE) + return TRUE; + case SERVICE_CONTROL_HARDWAREPROFILECHANGE: + if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE) + return TRUE; + break; + case SERVICE_CONTROL_POWEREVENT: + if (a&SERVICE_ACCEPT_POWEREVENT) + return TRUE; + break; + case SERVICE_CONTROL_SESSIONCHANGE: + if (a&SERVICE_ACCEPT_SESSIONCHANGE) + return TRUE; + break; + } + return FALSE; +} + +/****************************************************************************** + * service_send_control + */ +static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result) +{ + DWORD cmd[2], count = 0; + BOOL r; + + cmd[0] = WINESERV_SENDCONTROL; + cmd[1] = dwControl; + r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL); + if (!r || count != sizeof cmd) + { + WINE_ERR("service protocol error - failed to write pipe!\n"); + return r; + } + r = ReadFile(pipe, result, sizeof *result, &count, NULL); + if (!r || count != sizeof *result) + WINE_ERR("service protocol error - failed to read pipe " + "r = %d count = %d!\n", r, count); + return r; +} + +DWORD svcctl_StartServiceW( + SC_RPC_HANDLE hService, + DWORD dwNumServiceArgs, + LPCWSTR *lpServiceArgVectors) +{ + struct sc_service *service; + DWORD err; + LPWSTR name; + + WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors); + + if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0) + return err; + + err = lock_service_database(); + if (err != ERROR_SUCCESS) + return err; + + if (service->service_entry->control_pipe != INVALID_HANDLE_VALUE) + { + unlock_service_database(); + return ERROR_SERVICE_ALREADY_RUNNING; + } + + service->service_entry->control_mutex = CreateMutexW(NULL, TRUE, NULL); + + if (!service->service_entry->status_changed_event) + service->service_entry->status_changed_event = CreateEventW(NULL, TRUE, FALSE, NULL); + + name = service_get_pipe_name(service->service_entry->name); + service->service_entry->control_pipe = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE|PIPE_WAIT, 1, 256, 256, 10000, NULL ); + HeapFree(GetProcessHeap(), 0, name); + if (service->service_entry->control_pipe==INVALID_HANDLE_VALUE) + { + WINE_ERR("failed to create pipe for %s, error = %d\n", + wine_dbgstr_w(service->service_entry->name), GetLastError()); + unlock_service_database(); + return GetLastError(); + } + + err = service_start_process(service->service_entry); + + unlock_service_database(); + + if (err == ERROR_SUCCESS) + { + if (!service_send_start_message(service->service_entry->control_pipe, + lpServiceArgVectors, dwNumServiceArgs)) + err = ERROR_SERVICE_REQUEST_TIMEOUT; + } + + WINE_TRACE("returning %d\n", err); + + if (err == ERROR_SUCCESS) + err = service_wait_for_startup(service->service_entry); + + ReleaseMutex(service->service_entry->control_mutex); + + return err; +} + +DWORD svcctl_ControlService( + SC_RPC_HANDLE hService, + DWORD dwControl, + SERVICE_STATUS *lpServiceStatus) +{ + DWORD access_required; + struct sc_service *service; + DWORD err; + BOOL ret; + HANDLE control_mutex; + HANDLE control_pipe; + + WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus); + + switch (dwControl) + { + case SERVICE_CONTROL_CONTINUE: + case SERVICE_CONTROL_NETBINDADD: + case SERVICE_CONTROL_NETBINDDISABLE: + case SERVICE_CONTROL_NETBINDENABLE: + case SERVICE_CONTROL_NETBINDREMOVE: + case SERVICE_CONTROL_PARAMCHANGE: + case SERVICE_CONTROL_PAUSE: + access_required = SERVICE_PAUSE_CONTINUE; + break; + case SERVICE_CONTROL_INTERROGATE: + access_required = SERVICE_INTERROGATE; + break; + case SERVICE_CONTROL_STOP: + access_required = SERVICE_STOP; + break; + default: + if (dwControl >= 128 && dwControl <= 255) + access_required = SERVICE_USER_DEFINED_CONTROL; + else + return ERROR_INVALID_PARAMETER; + } + + if ((err = validate_service_handle(hService, access_required, &service)) != 0) + return err; + + lock_services(); + + if (lpServiceStatus) + { + lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType; + lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState; + lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted; + lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode; + lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode; + lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint; + lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint; + } + + if (!service_accepts_control(service->service_entry, dwControl)) + { + unlock_services(); + return ERROR_INVALID_SERVICE_CONTROL; + } + + switch (service->service_entry->status.dwCurrentState) + { + case SERVICE_STOPPED: + unlock_services(); + return ERROR_SERVICE_NOT_ACTIVE; + case SERVICE_START_PENDING: + if (dwControl==SERVICE_CONTROL_STOP) + break; + /* fall thru */ + case SERVICE_STOP_PENDING: + unlock_services(); + return ERROR_SERVICE_CANNOT_ACCEPT_CTRL; + } + + /* prevent races by caching these variables and clearing them on + * stop here instead of outside the services lock */ + control_mutex = service->service_entry->control_mutex; + control_pipe = service->service_entry->control_pipe; + if (dwControl == SERVICE_CONTROL_STOP) + { + service->service_entry->control_mutex = NULL; + service->service_entry->control_pipe = NULL; + } + + unlock_services(); + + ret = WaitForSingleObject(control_mutex, 30000); + if (ret) + { + DWORD result = ERROR_SUCCESS; + + ret = service_send_control(control_pipe, dwControl, &result); + + if (dwControl == SERVICE_CONTROL_STOP) + { + CloseHandle(control_mutex); + CloseHandle(control_pipe); + } + else + ReleaseMutex(control_mutex); + + return result; + } + else + { + if (dwControl == SERVICE_CONTROL_STOP) + { + CloseHandle(control_mutex); + CloseHandle(control_pipe); + } + return ERROR_SERVICE_REQUEST_TIMEOUT; + } +} + DWORD svcctl_CloseServiceHandle( SC_RPC_HANDLE *handle) { diff --git a/programs/services/services.c b/programs/services/services.c index 1962dc9ea46..ae189dc0e94 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -79,6 +79,9 @@ void free_service_entry(struct service_entry *entry) HeapFree(GetProcessHeap(), 0, entry->config.lpServiceStartName); HeapFree(GetProcessHeap(), 0, entry->config.lpDisplayName); HeapFree(GetProcessHeap(), 0, entry->description); + CloseHandle(entry->control_mutex); + CloseHandle(entry->control_pipe); + CloseHandle(entry->status_changed_event); HeapFree(GetProcessHeap(), 0, entry); } @@ -362,6 +365,7 @@ static DWORD load_services(void) entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry)); entry->name = strdupW(szName); + entry->control_pipe = INVALID_HANDLE_VALUE; WINE_TRACE("Loading service %s\n", wine_dbgstr_w(szName)); err = RegOpenKeyExW(hServicesRootKey, szName, 0, KEY_READ | KEY_WRITE, &hServiceKey); diff --git a/programs/services/services.h b/programs/services/services.h index 4c7c656ddfe..b02616636ad 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -33,6 +33,9 @@ struct service_entry LPWSTR description; LPWSTR dependOnServices; LPWSTR dependOnGroups; + HANDLE control_mutex; + HANDLE control_pipe; + HANDLE status_changed_event; }; BOOL validate_service_name(LPCWSTR name);