From 2b8d47694d07c6d82fe27589cb40c4d9a6084e85 Mon Sep 17 00:00:00 2001 From: Dmitry Timoshkov Date: Tue, 17 Apr 2018 15:12:06 +0800 Subject: [PATCH] schedsvc: Add support for reading .job files. Signed-off-by: Dmitry Timoshkov Signed-off-by: Alexandre Julliard --- dlls/schedsvc/atsvc.c | 301 +++++++++++++++++++++++++++++++ dlls/schedsvc/schedsvc_private.h | 1 + dlls/schedsvc/svc_main.c | 9 + include/mstask.idl | 14 ++ 4 files changed, 325 insertions(+) diff --git a/dlls/schedsvc/atsvc.c b/dlls/schedsvc/atsvc.c index 6bb8d4c707d..baa808a97b6 100644 --- a/dlls/schedsvc/atsvc.c +++ b/dlls/schedsvc/atsvc.c @@ -22,10 +22,311 @@ #include "windef.h" #include "atsvc.h" +#include "mstask.h" +#include "wine/list.h" #include "wine/debug.h" +#include "schedsvc_private.h" + WINE_DEFAULT_DEBUG_CHANNEL(schedsvc); +/* lmat.h defines those, but other types in that file conflict + * with generated atsvc.h typedefs. + */ +#define JOB_ADD_CURRENT_DATE 0x08 +#define JOB_NONINTERACTIVE 0x10 + +typedef struct +{ + USHORT product_version; + USHORT file_version; + UUID uuid; + USHORT name_size_offset; + USHORT trigger_offset; + USHORT error_retry_count; + USHORT error_retry_interval; + USHORT idle_deadline; + USHORT idle_wait; + UINT priority; + UINT maximum_runtime; + UINT exit_code; + UINT status; + UINT flags; + SYSTEMTIME last_runtime; +} FIXDLEN_DATA; + +struct job_t +{ + struct list entry; + WCHAR *name; + AT_ENUM info; +}; + +static LONG current_jobid = 1; + +static struct list at_job_list = LIST_INIT(at_job_list); +static CRITICAL_SECTION at_job_list_section; +static CRITICAL_SECTION_DEBUG cs_debug = +{ + 0, 0, &at_job_list_section, + { &cs_debug.ProcessLocksList, &cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": at_job_list_section") } +}; +static CRITICAL_SECTION at_job_list_section = { &cs_debug, -1, 0, 0, 0, 0 }; + +static DWORD load_unicode_strings(const char *data, DWORD limit, AT_ENUM *info) +{ + DWORD i, data_size = 0; + USHORT len; + + for (i = 0; i < 5; i++) + { + if (limit < sizeof(USHORT)) + { + TRACE("invalid string %u offset\n", i); + break; + } + + len = *(USHORT *)data; + data += sizeof(USHORT); + data_size += sizeof(USHORT); + limit -= sizeof(USHORT); + if (limit < len * sizeof(WCHAR)) + { + TRACE("invalid string %u size\n", i); + break; + } + + TRACE("string %u: %s\n", i, wine_dbgstr_wn((const WCHAR *)data, len)); + + if (i == 0) + info->Command = heap_strdupW((const WCHAR *)data); + + data += len * sizeof(WCHAR); + data_size += len * sizeof(WCHAR); + } + + return data_size; +} + +/* FIXME: read more data, currently only Command is handled */ +static BOOL load_job_data(const char *data, DWORD size, AT_ENUM *info) +{ + const FIXDLEN_DATA *fixed; + const SYSTEMTIME *st; + DWORD unicode_strings_size, data_size, triggers_size; + USHORT instance_count, triggers_count, i; + const USHORT *signature; + const TASK_TRIGGER *trigger; + + memset(info, 0, sizeof(*info)); + + if (size < sizeof(*fixed)) + { + TRACE("no space for FIXDLEN_DATA\n"); + return FALSE; + } + + fixed = (const FIXDLEN_DATA *)data; + + TRACE("product_version %04x\n", fixed->product_version); + TRACE("file_version %04x\n", fixed->file_version); + TRACE("uuid %s\n", wine_dbgstr_guid(&fixed->uuid)); + + TRACE("name_size_offset %04x\n", fixed->name_size_offset); + TRACE("trigger_offset %04x\n", fixed->trigger_offset); + TRACE("error_retry_count %u\n", fixed->error_retry_count); + TRACE("error_retry_interval %u\n", fixed->error_retry_interval); + TRACE("idle_deadline %u\n", fixed->idle_deadline); + TRACE("idle_wait %u\n", fixed->idle_wait); + TRACE("priority %08x\n", fixed->priority); + TRACE("maximum_runtime %u\n", fixed->maximum_runtime); + TRACE("exit_code %#x\n", fixed->exit_code); + TRACE("status %08x\n", fixed->status); + TRACE("flags %08x\n", fixed->flags); + st = &fixed->last_runtime; + TRACE("last_runtime %d/%d/%d wday %d %d:%d:%d.%03d\n", + st->wDay, st->wMonth, st->wYear, st->wDayOfWeek, + st->wHour, st->wMinute, st->wSecond, st->wMilliseconds); + + /* Instance Count */ + if (size < sizeof(*fixed) + sizeof(USHORT)) + { + TRACE("no space for instance count\n"); + return FALSE; + } + + instance_count = *(const USHORT *)(data + sizeof(*fixed)); + TRACE("instance count %u\n", instance_count); + + if (fixed->name_size_offset + sizeof(USHORT) < size) + unicode_strings_size = load_unicode_strings(data + fixed->name_size_offset, size - fixed->name_size_offset, info); + else + { + TRACE("invalid name_size_offset\n"); + return FALSE; + } + TRACE("unicode strings end at %#x\n", fixed->name_size_offset + unicode_strings_size); + + if (size < fixed->trigger_offset + sizeof(USHORT)) + { + TRACE("no space for triggers count\n"); + return FALSE; + } + triggers_count = *(const USHORT *)(data + fixed->trigger_offset); + TRACE("triggers_count %u\n", triggers_count); + triggers_size = size - fixed->trigger_offset - sizeof(USHORT); + TRACE("triggers_size %u\n", triggers_size); + trigger = (const TASK_TRIGGER *)(data + fixed->trigger_offset + sizeof(USHORT)); + + data += fixed->name_size_offset + unicode_strings_size; + size -= fixed->name_size_offset + unicode_strings_size; + + /* User Data */ + if (size < sizeof(USHORT)) + { + TRACE("no space for user data size\n"); + return FALSE; + } + + data_size = *(const USHORT *)data; + if (size < sizeof(USHORT) + data_size) + { + TRACE("no space for user data\n"); + return FALSE; + } + TRACE("User Data size %#x\n", data_size); + + size -= sizeof(USHORT) + data_size; + data += sizeof(USHORT) + data_size; + + /* Reserved Data */ + if (size < sizeof(USHORT)) + { + TRACE("no space for reserved data size\n"); + return FALSE; + } + + data_size = *(const USHORT *)data; + if (size < sizeof(USHORT) + data_size) + { + TRACE("no space for reserved data\n"); + return FALSE; + } + TRACE("Reserved Data size %#x\n", data_size); + + size -= sizeof(USHORT) + data_size; + data += sizeof(USHORT) + data_size; + + /* Trigger Data */ + TRACE("trigger_offset %04x, triggers end at %04x\n", fixed->trigger_offset, + (DWORD)(fixed->trigger_offset + sizeof(USHORT) + triggers_count * sizeof(TASK_TRIGGER))); + + triggers_count = *(const USHORT *)data; + TRACE("triggers_count %u\n", triggers_count); + trigger = (const TASK_TRIGGER *)(data + sizeof(USHORT)); + + if (triggers_count * sizeof(TASK_TRIGGER) > triggers_size) + { + TRACE("no space for triggers data\n"); + return FALSE; + } + + for (i = 0; i < triggers_count; i++) + { + TRACE("%u: cbTriggerSize = %#x\n", i, trigger[i].cbTriggerSize); + if (trigger[i].cbTriggerSize != sizeof(TASK_TRIGGER)) + TRACE("invalid cbTriggerSize\n"); + TRACE("Reserved1 = %#x\n", trigger[i].Reserved1); + TRACE("wBeginYear = %u\n", trigger->wBeginYear); + TRACE("wBeginMonth = %u\n", trigger->wBeginMonth); + TRACE("wBeginDay = %u\n", trigger->wBeginDay); + TRACE("wEndYear = %u\n", trigger->wEndYear); + TRACE("wEndMonth = %u\n", trigger->wEndMonth); + TRACE("wEndDay = %u\n", trigger->wEndDay); + TRACE("wStartHour = %u\n", trigger->wStartHour); + TRACE("wStartMinute = %u\n", trigger->wStartMinute); + TRACE("MinutesDuration = %u\n", trigger->MinutesDuration); + TRACE("MinutesInterval = %u\n", trigger->MinutesInterval); + TRACE("rgFlags = %u\n", trigger->rgFlags); + TRACE("TriggerType = %u\n", trigger->TriggerType); + TRACE("Reserved2 = %u\n", trigger->Reserved2); + TRACE("wRandomMinutesInterval = %u\n", trigger->wRandomMinutesInterval); + } + + size -= sizeof(USHORT) + triggers_count * sizeof(TASK_TRIGGER); + data += sizeof(USHORT) + triggers_count * sizeof(TASK_TRIGGER); + + if (size < 2 * sizeof(USHORT) + 64) + { + TRACE("no space for signature\n"); + return TRUE; /* signature is optional */ + } + + signature = (const USHORT *)data; + TRACE("signature version %04x, client version %04x\n", signature[0], signature[1]); + + return TRUE; +} + +void add_job(const WCHAR *name) +{ + HANDLE file, mapping; + DWORD size, try; + void *data; + struct job_t *job; + + job = heap_alloc_zero(sizeof(*job)); + if (!job) return; + + try = 1; + for (;;) + { + file = CreateFileW(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); + if (file == INVALID_HANDLE_VALUE) + { + TRACE("Failed to open %s, error %u\n", debugstr_w(name), GetLastError()); + if (try++ >= 3) break; + Sleep(100); + continue; + } + + size = GetFileSize(file, NULL); + + mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, 0); + if (!mapping) + { + TRACE("Failed to create file mapping %s, error %u\n", debugstr_w(name), GetLastError()); + CloseHandle(file); + break; + } + + data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + if (data) + { + if (load_job_data(data, size, &job->info)) + { + EnterCriticalSection(&at_job_list_section); + job->name = heap_strdupW(name); + job->info.JobId = current_jobid++; + list_add_tail(&at_job_list, &job->entry); + LeaveCriticalSection(&at_job_list_section); + } + UnmapViewOfFile(data); + } + + CloseHandle(mapping); + CloseHandle(file); + break; + } + + if (!job->info.JobId) + { + heap_free(job->info.Command); + heap_free(job); + } +} + DWORD __cdecl NetrJobAdd(ATSVC_HANDLE server_name, AT_INFO *info, DWORD *jobid) { FIXME("%s,%p,%p: stub\n", debugstr_w(server_name), info, jobid); diff --git a/dlls/schedsvc/schedsvc_private.h b/dlls/schedsvc/schedsvc_private.h index 4404486998a..1e5d575d70a 100644 --- a/dlls/schedsvc/schedsvc_private.h +++ b/dlls/schedsvc/schedsvc_private.h @@ -23,6 +23,7 @@ #include "wine/unicode.h" void schedsvc_auto_start(void) DECLSPEC_HIDDEN; +void add_job(const WCHAR *name) DECLSPEC_HIDDEN; static inline WCHAR *heap_strdupW(const WCHAR *src) { diff --git a/dlls/schedsvc/svc_main.c b/dlls/schedsvc/svc_main.c index 3de025335c8..0835d71b427 100644 --- a/dlls/schedsvc/svc_main.c +++ b/dlls/schedsvc/svc_main.c @@ -82,6 +82,15 @@ static DWORD WINAPI tasks_monitor_thread(void *arg) switch (info.data.Action) { + case FILE_ACTION_ADDED: + TRACE("FILE_ACTION_ADDED %s\n", debugstr_w(info.data.FileName)); + + GetWindowsDirectoryW(path, MAX_PATH); + lstrcatW(path, tasksW); + lstrcatW(path, info.data.FileName); + add_job(path); + break; + default: FIXME("%s: action %#x not handled\n", debugstr_w(info.data.FileName), info.data.Action); break; diff --git a/include/mstask.idl b/include/mstask.idl index 0a79b79118d..703ae01c274 100644 --- a/include/mstask.idl +++ b/include/mstask.idl @@ -55,6 +55,20 @@ cpp_quote("#define TASK_OCTOBER 0x200") cpp_quote("#define TASK_NOVEMBER 0x400") cpp_quote("#define TASK_DECEMBER 0x800") +cpp_quote("#define TASK_FLAG_INTERACTIVE 0x0001") +cpp_quote("#define TASK_FLAG_DELETE_WHEN_DONE 0x0002") +cpp_quote("#define TASK_FLAG_DISABLED 0x0004") +cpp_quote("#define TASK_FLAG_START_ONLY_IF_IDLE 0x0010") +cpp_quote("#define TASK_FLAG_KILL_ON_IDLE_END 0x0020") +cpp_quote("#define TASK_FLAG_DONT_START_IF_ON_BATTERIES 0x0040") +cpp_quote("#define TASK_FLAG_KILL_IF_GOING_ON_BATTERIES 0x0080") +cpp_quote("#define TASK_FLAG_RUN_ONLY_IF_DOCKED 0x0100") +cpp_quote("#define TASK_FLAG_HIDDEN 0x0200") +cpp_quote("#define TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET 0x0400") +cpp_quote("#define TASK_FLAG_RESTART_ON_IDLE_RESUME 0x0800") +cpp_quote("#define TASK_FLAG_SYSTEM_REQUIRED 0x1000") +cpp_quote("#define TASK_FLAG_RUN_ONLY_IF_LOGGED_ON 0x2000") + cpp_quote("#define TASK_TRIGGER_FLAG_HAS_END_DATE 0x1") cpp_quote("#define TASK_TRIGGER_FLAG_KILL_AT_DURATION_END 0x2") cpp_quote("#define TASK_TRIGGER_FLAG_DISABLED 0x4")