Signed-off-by: Dmitry Timoshkov dmitry@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");