services: Move ControlService and StartServiceW from advapi32.dll to services.exe.

This also changes the architecture such that services.exe is the server 
end of the control pipe and the service is the client end.
oldstable
Rob Shearman 2008-03-28 17:07:39 +00:00 committed by Alexandre Julliard
parent 7afd9a977a
commit 9a6fc01d84
5 changed files with 477 additions and 339 deletions

View File

@ -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; i<argc; i++)
len += strlenW(argv[i])+1;
ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, str[len]));
ssi->cmd = WINESERV_STARTINFO;
ssi->size = len;
/* copy service args into a single buffer*/
p = &ssi->str[0];
for (i=0; i<argc; i++)
{
strcpyW(p, argv[i]);
p += strlenW(p) + 1;
}
*p=0;
r = WriteFile(pipe, ssi, FIELD_OFFSET(service_start_info, str[len]), &count, NULL);
if (r)
{
r = ReadFile(pipe, &result, sizeof result, &count, NULL);
if (r && result)
{
SetLastError(result);
r = FALSE;
}
}
HeapFree(GetProcessHeap(),0,ssi);
return r;
}
/******************************************************************************
* service_handle_get_status
*/
static BOOL service_handle_get_status(HANDLE pipe, const service_data *service)
{
DWORD count = 0;
TRACE("\n");
return WriteFile(pipe, &service->status,
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;
}
/******************************************************************************

View File

@ -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,

View File

@ -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; i<argc; i++)
len += strlenW(argv[i])+1;
ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, str[len]));
ssi->cmd = WINESERV_STARTINFO;
ssi->size = len;
/* copy service args into a single buffer*/
p = &ssi->str[0];
for (i=0; i<argc; i++)
{
strcpyW(p, argv[i]);
p += strlenW(p) + 1;
}
*p=0;
r = WriteFile(pipe, ssi, FIELD_OFFSET(service_start_info, str[len]), &count, NULL);
if (r)
{
r = ReadFile(pipe, &result, sizeof result, &count, NULL);
if (r && result)
{
SetLastError(result);
r = FALSE;
}
}
HeapFree(GetProcessHeap(),0,ssi);
return r;
}
/******************************************************************************
* service_accepts_control
*/
static BOOL service_accepts_control(const struct service_entry *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_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)
{

View File

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

View File

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