services: Support delayed autostart services.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2019-06-20 17:58:45 +02:00 committed by Alexandre Julliard
parent 4c750a35c3
commit d8dec0b3f9
2 changed files with 116 additions and 1 deletions

View File

@ -29,6 +29,7 @@
#include <userenv.h>
#include "wine/debug.h"
#include "wine/heap.h"
#include "svcctl.h"
#include "services.h"
@ -42,6 +43,7 @@ struct scmdatabase *active_database;
DWORD service_pipe_timeout = 10000;
DWORD service_kill_timeout = 60000;
static DWORD default_preshutdown_timeout = 180000;
static DWORD autostart_delay = 120000;
static void *environment = NULL;
static HKEY service_current_key = NULL;
@ -68,6 +70,7 @@ static const WCHAR SZ_TAG[] = {'T','a','g',0};
static const WCHAR SZ_DESCRIPTION[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
static const WCHAR SZ_PRESHUTDOWN[] = {'P','r','e','s','h','u','t','d','o','w','n','T','i','m','e','o','u','t',0};
static const WCHAR SZ_WOW64[] = {'W','O','W','6','4',0};
static const WCHAR SZ_DELAYED_AUTOSTART[] = {'D','e','l','a','y','e','d','A','u','t','o','S','t','a','r','t',0};
static DWORD process_create(const WCHAR *name, struct process_entry **entry)
{
@ -186,6 +189,8 @@ static DWORD load_service_config(HKEY hKey, struct service_entry *entry)
if (load_reg_dword(hKey, SZ_WOW64, &value) == 0 && value == 1)
entry->is_wow64 = TRUE;
if (load_reg_dword(hKey, SZ_DELAYED_AUTOSTART, &value) == 0 && value == 1)
entry->delayed_autostart = TRUE;
WINE_TRACE("Image path = %s\n", wine_dbgstr_w(entry->config.lpBinaryPathName) );
WINE_TRACE("Group = %s\n", wine_dbgstr_w(entry->config.lpLoadOrderGroup) );
@ -329,11 +334,101 @@ static int __cdecl compare_tags(const void *a, const void *b)
return service_a->config.dwTagId - service_b->config.dwTagId;
}
static PTP_CLEANUP_GROUP delayed_autostart_cleanup;
struct delayed_autostart_params
{
unsigned int count;
struct service_entry **services;
};
static void CALLBACK delayed_autostart_cancel_callback(void *object, void *userdata)
{
struct delayed_autostart_params *params = object;
while(params->count--)
release_service(params->services[params->count]);
heap_free(params->services);
heap_free(params);
}
static void CALLBACK delayed_autostart_callback(TP_CALLBACK_INSTANCE *instance, void *context,
TP_WAIT *wait, TP_WAIT_RESULT result)
{
struct delayed_autostart_params *params = context;
struct service_entry *service;
unsigned int i;
DWORD err;
if (result == WAIT_TIMEOUT)
{
scmdatabase_lock_startup(active_database, INFINITE);
for (i = 0; i < params->count; i++)
{
service = params->services[i];
if (service->status.dwCurrentState == SERVICE_STOPPED)
{
TRACE("Starting deleyed auto-start service %s\n", debugstr_w(service->name));
err = service_start(service, 0, NULL);
if (err != ERROR_SUCCESS)
FIXME("Delayed auto-start service %s failed to start: %d\n",
wine_dbgstr_w(service->name), err);
}
release_service(service);
}
scmdatabase_unlock_startup(active_database);
}
heap_free(params->services);
heap_free(params);
CloseThreadpoolWait(wait);
}
static BOOL schedule_delayed_autostart(struct service_entry **services, unsigned int count)
{
struct delayed_autostart_params *params;
TP_CALLBACK_ENVIRON environment;
LARGE_INTEGER timestamp;
TP_WAIT *wait;
FILETIME ft;
if (!(delayed_autostart_cleanup = CreateThreadpoolCleanupGroup()))
{
ERR("CreateThreadpoolCleanupGroup failed with error %u\n", GetLastError());
return FALSE;
}
if (!(params = heap_alloc(sizeof(*params)))) return FALSE;
params->count = count;
params->services = services;
memset(&environment, 0, sizeof(environment));
environment.Version = 1;
environment.CleanupGroup = delayed_autostart_cleanup;
environment.CleanupGroupCancelCallback = delayed_autostart_cancel_callback;
timestamp.QuadPart = (ULONGLONG)autostart_delay * -10000;
ft.dwLowDateTime = timestamp.u.LowPart;
ft.dwHighDateTime = timestamp.u.HighPart;
if (!(wait = CreateThreadpoolWait(delayed_autostart_callback, params, &environment)))
{
ERR("CreateThreadpoolWait failed: %u\n", GetLastError());
heap_free(params);
return FALSE;
}
SetThreadpoolWait(wait, params, &ft);
return TRUE;
}
static void scmdatabase_autostart_services(struct scmdatabase *db)
{
struct service_entry **services_list;
unsigned int i = 0;
unsigned int size = 32;
unsigned int delayed_cnt = 0;
struct service_entry *service;
services_list = HeapAlloc(GetProcessHeap(), 0, size * sizeof(services_list[0]));
@ -370,6 +465,12 @@ static void scmdatabase_autostart_services(struct scmdatabase *db)
{
DWORD err;
service = services_list[i];
if (service->delayed_autostart)
{
TRACE("delayed starting %s\n", wine_dbgstr_w(service->name));
services_list[delayed_cnt++] = service;
continue;
}
err = service_start(service, 0, NULL);
if (err != ERROR_SUCCESS)
WINE_FIXME("Auto-start service %s failed to start: %d\n",
@ -378,7 +479,9 @@ static void scmdatabase_autostart_services(struct scmdatabase *db)
}
scmdatabase_unlock_startup(db);
HeapFree(GetProcessHeap(), 0, services_list);
if (!delayed_cnt || !schedule_delayed_autostart(services_list, delayed_cnt))
heap_free(services_list);
}
static void scmdatabase_wait_terminate(struct scmdatabase *db)
@ -1115,6 +1218,8 @@ static void load_registry_parameters(void)
{'S','e','r','v','i','c','e','s','P','i','p','e','T','i','m','e','o','u','t',0};
static const WCHAR killtimeoutW[] =
{'W','a','i','t','T','o','K','i','l','l','S','e','r','v','i','c','e','T','i','m','e','o','u','t',0};
static const WCHAR autostartdelayW[] =
{'A','u','t','o','S','t','a','r','t','D','e','l','a','y',0};
HKEY key;
WCHAR buffer[64];
DWORD type, count, val;
@ -1131,6 +1236,10 @@ static void load_registry_parameters(void)
type == REG_SZ && (val = wcstol( buffer, NULL, 10 )))
service_kill_timeout = val;
count = sizeof(val);
if (!RegQueryValueExW( key, autostartdelayW, NULL, &type, (BYTE *)&val, &count ) && type == REG_DWORD)
autostart_delay = val;
RegCloseKey( key );
}
@ -1164,6 +1273,11 @@ int main(int argc, char *argv[])
SetEvent(started_event);
WaitForSingleObject(exit_event, INFINITE);
scmdatabase_wait_terminate(active_database);
if (delayed_autostart_cleanup)
{
CloseThreadpoolCleanupGroupMembers(delayed_autostart_cleanup, TRUE, NULL);
CloseThreadpoolCleanupGroup(delayed_autostart_cleanup);
}
RPC_Stop();
}
scmdatabase_destroy(active_database);

View File

@ -63,6 +63,7 @@ struct service_entry
BOOL force_shutdown;
BOOL marked_for_delete;
BOOL is_wow64;
BOOL delayed_autostart;
struct list handles;
};