Commit f628cbba authored by Sebastian Lackner's avatar Sebastian Lackner Committed by Alexandre Julliard

services: Add a separate winedevice service for each kernel driver.

parent b783f207
...@@ -36,6 +36,7 @@ cpp_quote("#define SVCCTL_STARTED_EVENT {'_','_','w','i','n','e','_','S','v','c' ...@@ -36,6 +36,7 @@ cpp_quote("#define SVCCTL_STARTED_EVENT {'_','_','w','i','n','e','_','S','v','c'
/* Service startup protocol over control pipe - not compatible with Windows */ /* Service startup protocol over control pipe - not compatible with Windows */
cpp_quote("#define SERVICE_PROTOCOL_MAGIC 0x57494e45") cpp_quote("#define SERVICE_PROTOCOL_MAGIC 0x57494e45")
cpp_quote("#define SERVICE_CONTROL_START 0") cpp_quote("#define SERVICE_CONTROL_START 0")
cpp_quote("#define SERVICE_CONTROL_FORWARD_FLAG 0x80000000")
typedef struct service_start_info_t typedef struct service_start_info_t
{ {
......
...@@ -85,6 +85,7 @@ struct sc_lock ...@@ -85,6 +85,7 @@ struct sc_lock
struct scmdatabase *db; struct scmdatabase *db;
}; };
static const WCHAR emptyW[] = {0};
static PTP_CLEANUP_GROUP cleanup_group; static PTP_CLEANUP_GROUP cleanup_group;
HANDLE exit_event; HANDLE exit_event;
...@@ -125,6 +126,44 @@ static void terminate_after_timeout(struct process_entry *process, DWORD timeout ...@@ -125,6 +126,44 @@ static void terminate_after_timeout(struct process_entry *process, DWORD timeout
release_process(process); release_process(process);
} }
static void CALLBACK shutdown_callback(TP_CALLBACK_INSTANCE *instance, void *context)
{
struct process_entry *process = context;
DWORD result;
result = WaitForSingleObject(process->control_mutex, 30000);
if (result == WAIT_OBJECT_0)
{
process_send_control(process, FALSE, emptyW, SERVICE_CONTROL_STOP, NULL, 0, &result);
ReleaseMutex(process->control_mutex);
}
release_process(process);
}
static void shutdown_shared_process(struct process_entry *process)
{
TP_CALLBACK_ENVIRON environment;
struct service_entry *service;
struct scmdatabase *db = process->db;
scmdatabase_lock(db);
LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
{
if (service->process != process) continue;
service->status.dwCurrentState = SERVICE_STOP_PENDING;
}
scmdatabase_unlock(db);
memset(&environment, 0, sizeof(environment));
environment.Version = 1;
environment.CleanupGroup = cleanup_group;
environment.CleanupGroupCancelCallback = group_cancel_callback;
if (!TrySubmitThreadpoolCallback(shutdown_callback, grab_process(process), &environment))
release_process(process);
}
static void free_service_strings(struct service_entry *old, struct service_entry *new) static void free_service_strings(struct service_entry *old, struct service_entry *new)
{ {
QUERY_SERVICE_CONFIGW *old_cfg = &old->config; QUERY_SERVICE_CONFIGW *old_cfg = &old->config;
...@@ -763,6 +802,8 @@ DWORD __cdecl svcctl_SetServiceStatus( ...@@ -763,6 +802,8 @@ DWORD __cdecl svcctl_SetServiceStatus(
service->service_entry->process = NULL; service->service_entry->process = NULL;
if (!--process->use_count) if (!--process->use_count)
terminate_after_timeout(process, service_kill_timeout); terminate_after_timeout(process, service_kill_timeout);
if (service->service_entry->shared_process && process->use_count <= 1)
shutdown_shared_process(process);
release_process(process); release_process(process);
} }
...@@ -1030,13 +1071,21 @@ static BOOL process_send_command(struct process_entry *process, const void *data ...@@ -1030,13 +1071,21 @@ static BOOL process_send_command(struct process_entry *process, const void *data
/****************************************************************************** /******************************************************************************
* process_send_control * process_send_control
*/ */
BOOL process_send_control(struct process_entry *process, const WCHAR *name, DWORD control, BOOL process_send_control(struct process_entry *process, BOOL shared_process, const WCHAR *name,
const BYTE *data, DWORD data_size, DWORD *result) DWORD control, const BYTE *data, DWORD data_size, DWORD *result)
{ {
service_start_info *ssi; service_start_info *ssi;
DWORD len; DWORD len;
BOOL r; BOOL r;
if (shared_process)
{
control |= SERVICE_CONTROL_FORWARD_FLAG;
data = (BYTE *)name;
data_size = (strlenW(name) + 1) * sizeof(WCHAR);
name = emptyW;
}
/* calculate how much space we need to send the startup info */ /* calculate how much space we need to send the startup info */
len = (strlenW(name) + 1) * sizeof(WCHAR) + data_size; len = (strlenW(name) + 1) * sizeof(WCHAR) + data_size;
...@@ -1086,6 +1135,7 @@ DWORD __cdecl svcctl_ControlService( ...@@ -1086,6 +1135,7 @@ DWORD __cdecl svcctl_ControlService(
DWORD access_required; DWORD access_required;
struct sc_service_handle *service; struct sc_service_handle *service;
struct process_entry *process; struct process_entry *process;
BOOL shared_process;
DWORD result; DWORD result;
WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus); WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
...@@ -1165,6 +1215,7 @@ DWORD __cdecl svcctl_ControlService( ...@@ -1165,6 +1215,7 @@ DWORD __cdecl svcctl_ControlService(
/* Hold a reference to the process while sending the command. */ /* Hold a reference to the process while sending the command. */
process = grab_process(service->service_entry->process); process = grab_process(service->service_entry->process);
shared_process = service->service_entry->shared_process;
service_unlock(service->service_entry); service_unlock(service->service_entry);
if (!process) if (!process)
...@@ -1177,7 +1228,8 @@ DWORD __cdecl svcctl_ControlService( ...@@ -1177,7 +1228,8 @@ DWORD __cdecl svcctl_ControlService(
return ERROR_SERVICE_REQUEST_TIMEOUT; return ERROR_SERVICE_REQUEST_TIMEOUT;
} }
if (process_send_control(process, service->service_entry->name, dwControl, NULL, 0, &result)) if (process_send_control(process, shared_process, service->service_entry->name,
dwControl, NULL, 0, &result))
result = ERROR_SUCCESS; result = ERROR_SUCCESS;
if (lpServiceStatus) if (lpServiceStatus)
......
...@@ -59,6 +59,7 @@ struct service_entry ...@@ -59,6 +59,7 @@ struct service_entry
LPWSTR dependOnServices; LPWSTR dependOnServices;
LPWSTR dependOnGroups; LPWSTR dependOnGroups;
struct process_entry *process; struct process_entry *process;
BOOL shared_process;
BOOL force_shutdown; BOOL force_shutdown;
BOOL marked_for_delete; BOOL marked_for_delete;
BOOL is_wow64; BOOL is_wow64;
...@@ -95,8 +96,8 @@ DWORD service_start(struct service_entry *service, DWORD service_argc, LPCWSTR * ...@@ -95,8 +96,8 @@ DWORD service_start(struct service_entry *service, DWORD service_argc, LPCWSTR *
struct process_entry *grab_process(struct process_entry *process); struct process_entry *grab_process(struct process_entry *process);
void release_process(struct process_entry *process); void release_process(struct process_entry *process);
BOOL process_send_control(struct process_entry *process, const WCHAR *name, DWORD control, BOOL process_send_control(struct process_entry *process, BOOL winedevice, const WCHAR *name,
const BYTE *data, DWORD data_size, DWORD *result); DWORD control, const BYTE *data, DWORD data_size, DWORD *result);
void process_terminate(struct process_entry *process); void process_terminate(struct process_entry *process);
extern DWORD service_pipe_timeout; extern DWORD service_pipe_timeout;
......
...@@ -43,6 +43,7 @@ WINE_DECLARE_DEBUG_CHANNEL(relay); ...@@ -43,6 +43,7 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ); extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
static const WCHAR winedeviceW[] = {'w','i','n','e','d','e','v','i','c','e',0};
static SERVICE_STATUS_HANDLE service_handle; static SERVICE_STATUS_HANDLE service_handle;
static PTP_CLEANUP_GROUP cleanup_group; static PTP_CLEANUP_GROUP cleanup_group;
static SC_HANDLE manager_handle; static SC_HANDLE manager_handle;
...@@ -486,20 +487,57 @@ static void shutdown_drivers( void ) ...@@ -486,20 +487,57 @@ static void shutdown_drivers( void )
shutdown_in_progress = TRUE; shutdown_in_progress = TRUE;
} }
static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name )
{
struct wine_rb_entry *entry;
DWORD result = NO_ERROR;
if (shutdown_in_progress)
return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
EnterCriticalSection( &drivers_cs );
entry = wine_rb_get( &wine_drivers, driver_name );
switch (ctrl)
{
case SERVICE_CONTROL_START:
if (entry) break;
result = RtlNtStatusToDosError(create_driver( driver_name ));
break;
case SERVICE_CONTROL_STOP:
if (!entry) break;
result = RtlNtStatusToDosError(unload_driver( entry, FALSE ));
break;
default:
FIXME( "got driver ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
break;
}
LeaveCriticalSection( &drivers_cs );
return result;
}
static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
{ {
const WCHAR *driver_name = context; const WCHAR *service_group = context;
if (ctrl & SERVICE_CONTROL_FORWARD_FLAG)
{
if (!event_data) return ERROR_INVALID_PARAMETER;
return device_handler( ctrl & ~SERVICE_CONTROL_FORWARD_FLAG, (const WCHAR *)event_data );
}
switch (ctrl) switch (ctrl)
{ {
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_SHUTDOWN:
WINE_TRACE( "shutting down %s\n", wine_dbgstr_w(driver_name) ); TRACE( "shutting down %s\n", wine_dbgstr_w(service_group) );
set_service_status( service_handle, SERVICE_STOP_PENDING, 0 ); set_service_status( service_handle, SERVICE_STOP_PENDING, 0 );
shutdown_drivers(); shutdown_drivers();
return NO_ERROR; return NO_ERROR;
default: default:
WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) ); FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(service_group) );
set_service_status( service_handle, SERVICE_RUNNING, set_service_status( service_handle, SERVICE_RUNNING,
SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ); SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
return NO_ERROR; return NO_ERROR;
...@@ -508,8 +546,7 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_ ...@@ -508,8 +546,7 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_
static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
{ {
const WCHAR *driver_name = argv[0]; const WCHAR *service_group = (argc >= 2) ? argv[1] : argv[0];
NTSTATUS status;
if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL )))
return; return;
...@@ -517,16 +554,16 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) ...@@ -517,16 +554,16 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
return; return;
if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ))) if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
return; return;
if (!(service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, (void *)driver_name ))) if (!(service_handle = RegisterServiceCtrlHandlerExW( winedeviceW, service_handler, (void *)service_group )))
return; return;
EnterCriticalSection( &drivers_cs ); TRACE( "starting service group %s\n", wine_dbgstr_w(service_group) );
status = create_driver( driver_name ); set_service_status( service_handle, SERVICE_RUNNING,
LeaveCriticalSection( &drivers_cs ); SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
if (status == STATUS_SUCCESS) wine_ntoskrnl_main_loop( stop_event );
wine_ntoskrnl_main_loop( stop_event );
TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group) );
set_service_status( service_handle, SERVICE_STOPPED, 0 ); set_service_status( service_handle, SERVICE_STOPPED, 0 );
CloseServiceHandle( manager_handle ); CloseServiceHandle( manager_handle );
CloseThreadpoolCleanupGroup( cleanup_group ); CloseThreadpoolCleanupGroup( cleanup_group );
...@@ -537,13 +574,7 @@ int wmain( int argc, WCHAR *argv[] ) ...@@ -537,13 +574,7 @@ int wmain( int argc, WCHAR *argv[] )
{ {
SERVICE_TABLE_ENTRYW service_table[2]; SERVICE_TABLE_ENTRYW service_table[2];
if (!argv[1]) service_table[0].lpServiceName = (void *)winedeviceW;
{
WINE_ERR( "missing device name, winedevice isn't supposed to be run manually\n" );
return 1;
}
service_table[0].lpServiceName = argv[1];
service_table[0].lpServiceProc = ServiceMain; service_table[0].lpServiceProc = ServiceMain;
service_table[1].lpServiceName = NULL; service_table[1].lpServiceName = NULL;
service_table[1].lpServiceProc = NULL; service_table[1].lpServiceProc = NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment