services: Add a separate winedevice service for each kernel driver.
Signed-off-by: Sebastian Lackner <sebastian@fds-team.de> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
b783f207fe
commit
f628cbbaa1
|
@ -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
|
||||||
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
|
||||||
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(
|
||||||
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
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* 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(
|
||||||
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(
|
||||||
|
|
||||||
/* 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(
|
||||||
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)
|
||||||
|
|
|
@ -683,35 +683,6 @@ static DWORD get_service_binary_path(const struct service_entry *service_entry,
|
||||||
|
|
||||||
ExpandEnvironmentStringsW(service_entry->config.lpBinaryPathName, *path, size);
|
ExpandEnvironmentStringsW(service_entry->config.lpBinaryPathName, *path, size);
|
||||||
|
|
||||||
if (service_entry->config.dwServiceType == SERVICE_KERNEL_DRIVER ||
|
|
||||||
service_entry->config.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
|
|
||||||
{
|
|
||||||
static const WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',' ',0};
|
|
||||||
WCHAR system_dir[MAX_PATH];
|
|
||||||
DWORD type, len;
|
|
||||||
|
|
||||||
GetSystemDirectoryW( system_dir, MAX_PATH );
|
|
||||||
if (is_win64)
|
|
||||||
{
|
|
||||||
if (!GetBinaryTypeW( *path, &type ))
|
|
||||||
{
|
|
||||||
HeapFree( GetProcessHeap(), 0, *path );
|
|
||||||
return GetLastError();
|
|
||||||
}
|
|
||||||
if (type == SCS_32BIT_BINARY) GetSystemWow64DirectoryW( system_dir, MAX_PATH );
|
|
||||||
}
|
|
||||||
|
|
||||||
len = strlenW( system_dir ) + sizeof(winedeviceW)/sizeof(WCHAR) + strlenW(service_entry->name);
|
|
||||||
HeapFree( GetProcessHeap(), 0, *path );
|
|
||||||
if (!(*path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
|
|
||||||
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
||||||
|
|
||||||
lstrcpyW( *path, system_dir );
|
|
||||||
lstrcatW( *path, winedeviceW );
|
|
||||||
lstrcatW( *path, service_entry->name );
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if service image is configured to systemdir, redirect it to wow64 systemdir */
|
/* if service image is configured to systemdir, redirect it to wow64 systemdir */
|
||||||
if (service_entry->is_wow64)
|
if (service_entry->is_wow64)
|
||||||
{
|
{
|
||||||
|
@ -743,13 +714,83 @@ static DWORD get_service_binary_path(const struct service_entry *service_entry,
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD service_start_process(struct service_entry *service_entry, struct process_entry **new_process)
|
static DWORD get_winedevice_binary_path(WCHAR **path, BOOL *is_wow64)
|
||||||
|
{
|
||||||
|
static const WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',0};
|
||||||
|
WCHAR system_dir[MAX_PATH];
|
||||||
|
DWORD type;
|
||||||
|
|
||||||
|
if (!is_win64)
|
||||||
|
*is_wow64 = FALSE;
|
||||||
|
else if (GetBinaryTypeW(*path, &type))
|
||||||
|
*is_wow64 = (type == SCS_32BIT_BINARY);
|
||||||
|
else
|
||||||
|
return GetLastError();
|
||||||
|
|
||||||
|
GetSystemDirectoryW(system_dir, MAX_PATH);
|
||||||
|
HeapFree(GetProcessHeap(), 0, *path);
|
||||||
|
if (!(*path = HeapAlloc(GetProcessHeap(), 0, strlenW(system_dir) * sizeof(WCHAR) + sizeof(winedeviceW))))
|
||||||
|
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
||||||
|
|
||||||
|
strcpyW(*path, system_dir);
|
||||||
|
strcatW(*path, winedeviceW);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD add_winedevice_service(const struct service_entry *service, WCHAR *path, BOOL is_wow64,
|
||||||
|
struct service_entry **entry)
|
||||||
|
{
|
||||||
|
static const WCHAR format[] = {'W','i','n','e','d','e','v','i','c','e','%','u',0};
|
||||||
|
static WCHAR name[sizeof(format)/sizeof(WCHAR) + 10]; /* strlenW("4294967295") */
|
||||||
|
static DWORD current = 0;
|
||||||
|
struct scmdatabase *db = service->db;
|
||||||
|
DWORD err;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
sprintfW(name, format, ++current);
|
||||||
|
if (!scmdatabase_find_service(db, name)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = service_create(name, entry);
|
||||||
|
if (err != ERROR_SUCCESS)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
(*entry)->is_wow64 = is_wow64;
|
||||||
|
(*entry)->config.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||||
|
(*entry)->config.dwStartType = SERVICE_DEMAND_START;
|
||||||
|
(*entry)->status.dwServiceType = (*entry)->config.dwServiceType;
|
||||||
|
|
||||||
|
if (!((*entry)->config.lpBinaryPathName = strdupW(path)))
|
||||||
|
goto error;
|
||||||
|
if (!((*entry)->config.lpServiceStartName = strdupW(SZ_LOCAL_SYSTEM)))
|
||||||
|
goto error;
|
||||||
|
if (!((*entry)->config.lpDisplayName = strdupW(name)))
|
||||||
|
goto error;
|
||||||
|
if (service->config.lpLoadOrderGroup &&
|
||||||
|
!((*entry)->config.lpLoadOrderGroup = strdupW(service->config.lpLoadOrderGroup)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
(*entry)->db = db;
|
||||||
|
|
||||||
|
list_add_tail(&db->services, &(*entry)->entry);
|
||||||
|
mark_for_delete(*entry);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free_service_entry(*entry);
|
||||||
|
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD service_start_process(struct service_entry *service_entry, struct process_entry **new_process,
|
||||||
|
BOOL *shared_process)
|
||||||
{
|
{
|
||||||
struct process_entry *process;
|
struct process_entry *process;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
STARTUPINFOW si;
|
STARTUPINFOW si;
|
||||||
|
BOOL is_wow64 = FALSE;
|
||||||
HANDLE token;
|
HANDLE token;
|
||||||
LPWSTR path = NULL;
|
WCHAR *path;
|
||||||
DWORD err;
|
DWORD err;
|
||||||
BOOL r;
|
BOOL r;
|
||||||
|
|
||||||
|
@ -769,18 +810,80 @@ static DWORD service_start_process(struct service_entry *service_entry, struct p
|
||||||
|
|
||||||
service_entry->force_shutdown = FALSE;
|
service_entry->force_shutdown = FALSE;
|
||||||
|
|
||||||
|
if ((err = get_service_binary_path(service_entry, &path)))
|
||||||
|
{
|
||||||
|
service_unlock(service_entry);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service_entry->config.dwServiceType == SERVICE_KERNEL_DRIVER ||
|
||||||
|
service_entry->config.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
|
||||||
|
{
|
||||||
|
struct service_entry *winedevice_entry;
|
||||||
|
WCHAR *group;
|
||||||
|
|
||||||
|
if ((err = get_winedevice_binary_path(&path, &is_wow64)))
|
||||||
|
{
|
||||||
|
service_unlock(service_entry);
|
||||||
|
HeapFree(GetProcessHeap(), 0, path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = add_winedevice_service(service_entry, path, is_wow64, &winedevice_entry);
|
||||||
|
HeapFree(GetProcessHeap(), 0, path);
|
||||||
|
if (err != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
service_unlock(service_entry);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
group = strdupW(winedevice_entry->config.lpLoadOrderGroup);
|
||||||
|
service_unlock(service_entry);
|
||||||
|
|
||||||
|
err = service_start(winedevice_entry, group != NULL, (const WCHAR **)&group);
|
||||||
|
HeapFree(GetProcessHeap(), 0, group);
|
||||||
|
if (err != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
release_service(winedevice_entry);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_lock(service_entry);
|
||||||
|
process = grab_process(winedevice_entry->process);
|
||||||
|
release_service(winedevice_entry);
|
||||||
|
|
||||||
|
if (!process)
|
||||||
|
{
|
||||||
|
service_unlock(service_entry);
|
||||||
|
return ERROR_SERVICE_REQUEST_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_entry->status.dwCurrentState = SERVICE_START_PENDING;
|
||||||
|
service_entry->status.dwControlsAccepted = 0;
|
||||||
|
ResetEvent(service_entry->status_changed_event);
|
||||||
|
|
||||||
|
service_entry->process = grab_process(process);
|
||||||
|
service_entry->shared_process = *shared_process = TRUE;
|
||||||
|
process->use_count++;
|
||||||
|
service_unlock(service_entry);
|
||||||
|
|
||||||
|
err = WaitForSingleObject(process->control_mutex, 30000);
|
||||||
|
if (err != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
release_process(process);
|
||||||
|
return ERROR_SERVICE_REQUEST_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*new_process = process;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
if ((err = process_create(service_get_pipe_name(), &process)))
|
if ((err = process_create(service_get_pipe_name(), &process)))
|
||||||
{
|
{
|
||||||
WINE_ERR("failed to create process object for %s, error = %u\n",
|
WINE_ERR("failed to create process object for %s, error = %u\n",
|
||||||
wine_dbgstr_w(service_entry->name), err);
|
wine_dbgstr_w(service_entry->name), err);
|
||||||
service_unlock(service_entry);
|
service_unlock(service_entry);
|
||||||
return err;
|
HeapFree(GetProcessHeap(), 0, path);
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = get_service_binary_path(service_entry, &path)))
|
|
||||||
{
|
|
||||||
service_unlock(service_entry);
|
|
||||||
free_process_entry(process);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,8 +907,8 @@ static DWORD service_start_process(struct service_entry *service_entry, struct p
|
||||||
|
|
||||||
scmdatabase_add_process(service_entry->db, process);
|
scmdatabase_add_process(service_entry->db, process);
|
||||||
service_entry->process = grab_process(process);
|
service_entry->process = grab_process(process);
|
||||||
|
service_entry->shared_process = *shared_process = FALSE;
|
||||||
process->use_count++;
|
process->use_count++;
|
||||||
|
|
||||||
service_unlock(service_entry);
|
service_unlock(service_entry);
|
||||||
|
|
||||||
r = CreateProcessW(NULL, path, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, environment, NULL, &si, &pi);
|
r = CreateProcessW(NULL, path, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, environment, NULL, &si, &pi);
|
||||||
|
@ -846,8 +949,8 @@ static DWORD service_wait_for_startup(struct service_entry *service, struct proc
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* process_send_start_message
|
* process_send_start_message
|
||||||
*/
|
*/
|
||||||
static DWORD process_send_start_message(struct process_entry *process, const WCHAR *name,
|
static DWORD process_send_start_message(struct process_entry *process, BOOL shared_process,
|
||||||
const WCHAR **argv, DWORD argc)
|
const WCHAR *name, const WCHAR **argv, DWORD argc)
|
||||||
{
|
{
|
||||||
OVERLAPPED overlapped;
|
OVERLAPPED overlapped;
|
||||||
DWORD i, len, result;
|
DWORD i, len, result;
|
||||||
|
@ -896,7 +999,8 @@ static DWORD process_send_start_message(struct process_entry *process, const WCH
|
||||||
}
|
}
|
||||||
*p = 0;
|
*p = 0;
|
||||||
|
|
||||||
if (!process_send_control(process, name, SERVICE_CONTROL_START, (const BYTE *)str, len, &result))
|
if (!process_send_control(process, shared_process, name,
|
||||||
|
SERVICE_CONTROL_START, (const BYTE *)str, len, &result))
|
||||||
result = ERROR_SERVICE_REQUEST_TIMEOUT;
|
result = ERROR_SERVICE_REQUEST_TIMEOUT;
|
||||||
|
|
||||||
HeapFree(GetProcessHeap(), 0, str);
|
HeapFree(GetProcessHeap(), 0, str);
|
||||||
|
@ -906,21 +1010,28 @@ static DWORD process_send_start_message(struct process_entry *process, const WCH
|
||||||
DWORD service_start(struct service_entry *service, DWORD service_argc, LPCWSTR *service_argv)
|
DWORD service_start(struct service_entry *service, DWORD service_argc, LPCWSTR *service_argv)
|
||||||
{
|
{
|
||||||
struct process_entry *process = NULL;
|
struct process_entry *process = NULL;
|
||||||
|
BOOL shared_process;
|
||||||
DWORD err;
|
DWORD err;
|
||||||
|
|
||||||
err = service_start_process(service, &process);
|
err = service_start_process(service, &process, &shared_process);
|
||||||
if (err == ERROR_SUCCESS)
|
if (err == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
err = process_send_start_message(process, service->name, service_argv, service_argc);
|
err = process_send_start_message(process, shared_process, service->name, service_argv, service_argc);
|
||||||
|
|
||||||
if (err == ERROR_SUCCESS)
|
if (err == ERROR_SUCCESS)
|
||||||
err = service_wait_for_startup(service, process);
|
err = service_wait_for_startup(service, process);
|
||||||
|
|
||||||
if (err == ERROR_SUCCESS)
|
if (err != ERROR_SUCCESS)
|
||||||
ReleaseMutex(process->control_mutex);
|
{
|
||||||
else
|
service_lock(service);
|
||||||
process_terminate(process);
|
service->status.dwCurrentState = SERVICE_STOPPED;
|
||||||
|
service->process = NULL;
|
||||||
|
if (!--process->use_count) process_terminate(process);
|
||||||
|
release_process(process);
|
||||||
|
service_unlock(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseMutex(process->control_mutex);
|
||||||
release_process(process);
|
release_process(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
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 )
|
||||||
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_
|
||||||
|
|
||||||
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 )
|
||||||
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[] )
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue