[PATCH 4/4] schedsvc: Add support for executing tasks.
Dmitry Timoshkov
dmitry at baikal.ru
Wed May 30 23:21:34 CDT 2018
Signed-off-by: Dmitry Timoshkov <dmitry at baikal.ru>
---
dlls/schedsvc/atsvc.c | 182 ++++++++++++++++++++++++++++++++++++---
dlls/schedsvc/schedsvc_private.h | 3 +
dlls/schedsvc/svc_main.c | 60 ++++++++++++-
3 files changed, 230 insertions(+), 15 deletions(-)
diff --git a/dlls/schedsvc/atsvc.c b/dlls/schedsvc/atsvc.c
index 05335de3f3..9497c900a7 100644
--- a/dlls/schedsvc/atsvc.c
+++ b/dlls/schedsvc/atsvc.c
@@ -66,9 +66,19 @@ struct job_t
USHORT instance_count;
};
+struct running_job_t
+{
+ struct list entry;
+ UUID uuid;
+ HANDLE process;
+ DWORD pid;
+};
+
static LONG current_jobid = 1;
static struct list at_job_list = LIST_INIT(at_job_list);
+static struct list running_job_list = LIST_INIT(running_job_list);
+
static CRITICAL_SECTION at_job_list_section;
static CRITICAL_SECTION_DEBUG cs_debug =
{
@@ -359,11 +369,6 @@ void add_job(const WCHAR *name)
return;
}
- if (job->data.flags & 0x08000000)
- FIXME("Terminate(%s): not implemented\n", debugstr_w(job->info.Command));
- else if (job->data.flags & 0x04000000)
- FIXME("Run(%s): not implemented\n", debugstr_w(job->info.Command));
-
EnterCriticalSection(&at_job_list_section);
job->name = heap_strdupW(name);
job->info.JobId = current_jobid++;
@@ -545,25 +550,180 @@ failed:
return ret;
}
-static struct job_t *find_job(DWORD jobid, const WCHAR *name)
+static struct job_t *find_job(DWORD jobid, const WCHAR *name, const UUID *id)
{
struct job_t *job;
LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
{
- if ((name && !lstrcmpiW(job->name, name)) || job->info.JobId == jobid)
+ if (job->info.JobId == jobid || (name && !lstrcmpiW(job->name, name)) || (id && IsEqualGUID(&job->data.uuid, id)))
return job;
}
return NULL;
}
+static void update_job_status(struct job_t *job)
+{
+ HANDLE hfile;
+ DWORD try, size;
+#include "pshpack2.h"
+ struct
+ {
+ UINT exit_code;
+ UINT status;
+ UINT flags;
+ SYSTEMTIME last_runtime;
+ WORD instance_count;
+ } state;
+#include "poppack.h"
+
+ try = 1;
+ for (;;)
+ {
+ hfile = CreateFileW(job->name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ if (hfile != INVALID_HANDLE_VALUE) break;
+
+ if (try++ >= 3)
+ {
+ ERR("Failed to update %s, error %u\n", debugstr_w(job->name), GetLastError());
+ return;
+ }
+ Sleep(100);
+ }
+
+ if (SetFilePointer(hfile, FIELD_OFFSET(FIXDLEN_DATA, exit_code), NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER)
+ {
+ state.exit_code = job->data.exit_code;
+ state.status = job->data.status;
+ state.flags = job->data.flags;
+ state.last_runtime = job->data.last_runtime;
+ state.instance_count = job->instance_count;
+ WriteFile(hfile, &state, sizeof(state), &size, NULL);
+ }
+
+ CloseHandle(hfile);
+}
+
+void update_process_status(DWORD pid)
+{
+ struct running_job_t *runjob;
+
+ EnterCriticalSection(&at_job_list_section);
+
+ LIST_FOR_EACH_ENTRY(runjob, &running_job_list, struct running_job_t, entry)
+ {
+ if (runjob->pid == pid)
+ {
+ struct job_t *job = find_job(0, NULL, &runjob->uuid);
+ if (job)
+ {
+ DWORD exit_code = STILL_ACTIVE;
+
+ GetExitCodeProcess(runjob->process, &exit_code);
+
+ if (exit_code != STILL_ACTIVE)
+ {
+ CloseHandle(runjob->process);
+ list_remove(&runjob->entry);
+ heap_free(runjob);
+
+ job->data.exit_code = exit_code;
+ job->data.status = SCHED_S_TASK_TERMINATED;
+ job->data.flags &= ~0x0c000000;
+ job->instance_count = 0;
+ update_job_status(job);
+ }
+ }
+ break;
+ }
+ }
+
+ LeaveCriticalSection(&at_job_list_section);
+}
+
+void check_task_state(void)
+{
+ struct job_t *job;
+ struct running_job_t *runjob;
+
+ EnterCriticalSection(&at_job_list_section);
+
+ LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
+ {
+ if (job->data.flags & 0x08000000)
+ {
+ TRACE("terminating process %s\n", debugstr_w(job->info.Command));
+
+ LIST_FOR_EACH_ENTRY(runjob, &running_job_list, struct running_job_t, entry)
+ {
+ if (IsEqualGUID(&job->data.uuid, &runjob->uuid))
+ {
+ TerminateProcess(runjob->process, 0);
+ update_process_status(runjob->pid);
+ break;
+ }
+ }
+ }
+ else if (job->data.flags & 0x04000000)
+ {
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+
+ TRACE("running process %s\n", debugstr_w(job->info.Command));
+
+ if (job->instance_count)
+ FIXME("process %s is already running\n", debugstr_w(job->info.Command));
+
+ runjob = heap_alloc(sizeof(*runjob));
+ if (runjob)
+ {
+ static WCHAR winsta0[] = { 'W','i','n','S','t','a','0',0 };
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ /* FIXME: if (job->data.flags & TASK_FLAG_INTERACTIVE) */
+ si.lpDesktop = winsta0;
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOWNORMAL;
+ TRACE("executing %s %s at %s\n", debugstr_w(job->info.Command), debugstr_w(job->params), debugstr_w(job->curdir));
+ if (CreateProcessW(job->info.Command, job->params, NULL, NULL, FALSE, 0, NULL, job->curdir, &si, &pi))
+ {
+ CloseHandle(pi.hThread);
+
+ GetLocalTime(&job->data.last_runtime);
+ job->data.exit_code = 0;
+ job->data.status = SCHED_S_TASK_RUNNING;
+ job->instance_count = 1;
+
+ runjob->uuid = job->data.uuid;
+ runjob->process = pi.hProcess;
+ runjob->pid = pi.dwProcessId;
+ list_add_tail(&running_job_list, &runjob->entry);
+ add_process_to_queue(pi.hProcess);
+ }
+ else
+ {
+ WARN("failed to execute %s\n", debugstr_w(job->info.Command));
+ job->data.status = SCHED_S_TASK_HAS_NOT_RUN;
+ job->instance_count = 0;
+ }
+ }
+
+ job->data.flags &= ~0x0c000000;
+ update_job_status(job);
+ }
+ }
+
+ LeaveCriticalSection(&at_job_list_section);
+}
+
void remove_job(const WCHAR *name)
{
struct job_t *job;
EnterCriticalSection(&at_job_list_section);
- job = find_job(0, name);
+ job = find_job(0, name, NULL);
if (job)
{
list_remove(&job->entry);
@@ -596,7 +756,7 @@ DWORD __cdecl NetrJobAdd(ATSVC_HANDLE server_name, AT_INFO *info, DWORD *jobid)
for (i = 0; i < 5; i++)
{
EnterCriticalSection(&at_job_list_section);
- job = find_job(0, task_name);
+ job = find_job(0, task_name, NULL);
LeaveCriticalSection(&at_job_list_section);
if (job)
@@ -640,7 +800,7 @@ DWORD __cdecl NetrJobDel(ATSVC_HANDLE server_name, DWORD min_jobid, DWORD max_jo
for (jobid = min_jobid; jobid <= max_jobid; jobid++)
{
- struct job_t *job = find_job(jobid, NULL);
+ struct job_t *job = find_job(jobid, NULL, NULL);
if (!job)
{
@@ -735,7 +895,7 @@ DWORD __cdecl NetrJobGetInfo(ATSVC_HANDLE server_name, DWORD jobid, AT_INFO **in
EnterCriticalSection(&at_job_list_section);
- job = find_job(jobid, NULL);
+ job = find_job(jobid, NULL, NULL);
if (job)
{
AT_INFO *info_ret = heap_alloc(sizeof(*info_ret));
diff --git a/dlls/schedsvc/schedsvc_private.h b/dlls/schedsvc/schedsvc_private.h
index 215bba003e..f5cec2a7cb 100644
--- a/dlls/schedsvc/schedsvc_private.h
+++ b/dlls/schedsvc/schedsvc_private.h
@@ -25,6 +25,9 @@
void schedsvc_auto_start(void) DECLSPEC_HIDDEN;
void add_job(const WCHAR *name) DECLSPEC_HIDDEN;
void remove_job(const WCHAR *name) DECLSPEC_HIDDEN;
+void check_task_state(void) DECLSPEC_HIDDEN;
+void add_process_to_queue(HANDLE hproc) DECLSPEC_HIDDEN;
+void update_process_status(DWORD pid) 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 f021d63b98..9342b50e4b 100644
--- a/dlls/schedsvc/svc_main.c
+++ b/dlls/schedsvc/svc_main.c
@@ -34,13 +34,20 @@ WINE_DEFAULT_DEBUG_CHANNEL(schedsvc);
static const WCHAR scheduleW[] = {'S','c','h','e','d','u','l','e',0};
static SERVICE_STATUS_HANDLE schedsvc_handle;
-static HANDLE done_event;
+static HANDLE done_event, hjob_queue;
+
+void add_process_to_queue(HANDLE process)
+{
+ if (!AssignProcessToJobObject(hjob_queue, process))
+ ERR("AssignProcessToJobObject failed");
+}
static DWORD WINAPI tasks_monitor_thread(void *arg)
{
static const WCHAR tasksW[] = { '\\','T','a','s','k','s','\\',0 };
WCHAR path[MAX_PATH];
- HANDLE htasks;
+ HANDLE htasks, hport;
+ JOBOBJECT_ASSOCIATE_COMPLETION_PORT info;
OVERLAPPED ov;
TRACE("Starting...\n");
@@ -59,6 +66,28 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
return -1;
}
+ hjob_queue = CreateJobObjectW(NULL, NULL);
+ if (!hjob_queue)
+ {
+ ERR("CreateJobObject failed");
+ return -1;
+ }
+
+ hport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
+ if (!hport)
+ {
+ ERR("CreateIoCompletionPort failed");
+ return -1;
+ }
+
+ info.CompletionKey = hjob_queue;
+ info.CompletionPort = hport;
+ if (!SetInformationJobObject(hjob_queue, JobObjectAssociateCompletionPortInformation, &info, sizeof(info)))
+ {
+ ERR("SetInformationJobObject failed");
+ return -1;
+ }
+
memset(&ov, 0, sizeof(ov));
ov.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
@@ -69,7 +98,7 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
FILE_NOTIFY_INFORMATION data;
WCHAR name_buffer[MAX_PATH];
} info;
- HANDLE events[2];
+ HANDLE events[3];
DWORD ret;
/* the buffer must be DWORD aligned */
@@ -87,10 +116,29 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
events[0] = done_event;
events[1] = ov.hEvent;
+ events[2] = hport;
- ret = WaitForMultipleObjects(2, events, FALSE, INFINITE);
+ ret = WaitForMultipleObjects(3, events, FALSE, INFINITE);
if (ret == WAIT_OBJECT_0) break;
+ if (ret == WAIT_OBJECT_0 + 2)
+ {
+ DWORD msg;
+ ULONG_PTR dummy, pid;
+
+ if (GetQueuedCompletionStatus(hport, &msg, &dummy, (OVERLAPPED **)&pid, 0))
+ {
+ if (msg == JOB_OBJECT_MSG_EXIT_PROCESS)
+ {
+ TRACE("got message: process %#lx has terminated\n", pid);
+ update_process_status(pid);
+ }
+ else
+ FIXME("got message %#x from the job\n", msg);
+ }
+ continue;
+ }
+
info.data.FileName[info.data.FileNameLength/sizeof(WCHAR)] = 0;
switch (info.data.Action)
@@ -126,9 +174,13 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
FIXME("%s: action %#x not handled\n", debugstr_w(info.data.FileName), info.data.Action);
break;
}
+
+ check_task_state();
}
CloseHandle(ov.hEvent);
+ CloseHandle(hport);
+ CloseHandle(hjob_queue);
CloseHandle(htasks);
TRACE("Finished.\n");
--
2.16.3
More information about the wine-devel
mailing list