schedsvc: Add support for running tasks at specified time.
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
a3566fd6e1
commit
70cedb3e5e
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#include "windef.h"
|
||||
#include "atsvc.h"
|
||||
#include "mstask.h"
|
||||
|
@ -90,6 +91,235 @@ static CRITICAL_SECTION_DEBUG cs_debug =
|
|||
};
|
||||
static CRITICAL_SECTION at_job_list_section = { &cs_debug, -1, 0, 0, 0, 0 };
|
||||
|
||||
static void filetime_add_ms(FILETIME *ft, LONGLONG ms)
|
||||
{
|
||||
union u_ftll
|
||||
{
|
||||
FILETIME ft;
|
||||
LONGLONG ll;
|
||||
} *ftll = (union u_ftll *)ft;
|
||||
|
||||
ftll->ll += ms * (ULONGLONG)10000;
|
||||
}
|
||||
|
||||
static void filetime_add_minutes(FILETIME *ft, LONG minutes)
|
||||
{
|
||||
filetime_add_ms(ft, (LONGLONG)minutes * 60 * 1000);
|
||||
}
|
||||
|
||||
static void filetime_add_hours(FILETIME *ft, LONG hours)
|
||||
{
|
||||
filetime_add_minutes(ft, (LONGLONG)hours * 60);
|
||||
}
|
||||
|
||||
static void filetime_add_days(FILETIME *ft, LONG days)
|
||||
{
|
||||
filetime_add_hours(ft, (LONGLONG)days * 24);
|
||||
}
|
||||
|
||||
static void filetime_add_weeks(FILETIME *ft, ULONG weeks)
|
||||
{
|
||||
filetime_add_days(ft, (LONGLONG)weeks * 7);
|
||||
}
|
||||
|
||||
static void get_begin_time(const TASK_TRIGGER *trigger, FILETIME *ft)
|
||||
{
|
||||
SYSTEMTIME st;
|
||||
|
||||
st.wYear = trigger->wBeginYear;
|
||||
st.wMonth = trigger->wBeginMonth;
|
||||
st.wDay = trigger->wBeginDay;
|
||||
st.wDayOfWeek = 0;
|
||||
st.wHour = 0;
|
||||
st.wMinute = 0;
|
||||
st.wSecond = 0;
|
||||
st.wMilliseconds = 0;
|
||||
SystemTimeToFileTime(&st, ft);
|
||||
}
|
||||
|
||||
static void get_end_time(const TASK_TRIGGER *trigger, FILETIME *ft)
|
||||
{
|
||||
SYSTEMTIME st;
|
||||
|
||||
if (!(trigger->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE))
|
||||
{
|
||||
ft->dwHighDateTime = ~0u;
|
||||
ft->dwLowDateTime = ~0u;
|
||||
return;
|
||||
}
|
||||
|
||||
st.wYear = trigger->wEndYear;
|
||||
st.wMonth = trigger->wEndMonth;
|
||||
st.wDay = trigger->wEndDay;
|
||||
st.wDayOfWeek = 0;
|
||||
st.wHour = 0;
|
||||
st.wMinute = 0;
|
||||
st.wSecond = 0;
|
||||
st.wMilliseconds = 0;
|
||||
SystemTimeToFileTime(&st, ft);
|
||||
}
|
||||
|
||||
static BOOL trigger_get_next_runtime(const TASK_TRIGGER *trigger, const FILETIME *current_ft, FILETIME *rt)
|
||||
{
|
||||
SYSTEMTIME st, current_st;
|
||||
FILETIME begin_ft, end_ft, trigger_ft;
|
||||
|
||||
if (trigger->rgFlags & TASK_TRIGGER_FLAG_DISABLED)
|
||||
return FALSE;
|
||||
|
||||
FileTimeToSystemTime(current_ft, ¤t_st);
|
||||
|
||||
get_begin_time(trigger, &begin_ft);
|
||||
get_end_time(trigger, &end_ft);
|
||||
|
||||
switch (trigger->TriggerType)
|
||||
{
|
||||
case TASK_EVENT_TRIGGER_ON_IDLE:
|
||||
case TASK_EVENT_TRIGGER_AT_SYSTEMSTART:
|
||||
case TASK_EVENT_TRIGGER_AT_LOGON:
|
||||
return FALSE;
|
||||
|
||||
case TASK_TIME_TRIGGER_ONCE:
|
||||
st = current_st;
|
||||
st.wHour = trigger->wStartHour;
|
||||
st.wMinute = trigger->wStartMinute;
|
||||
st.wSecond = 0;
|
||||
st.wMilliseconds = 0;
|
||||
SystemTimeToFileTime(&st, &trigger_ft);
|
||||
if (CompareFileTime(&begin_ft, &trigger_ft) <= 0 && CompareFileTime(&trigger_ft, &end_ft) < 0)
|
||||
{
|
||||
*rt = trigger_ft;
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_TIME_TRIGGER_DAILY:
|
||||
st = current_st;
|
||||
st.wHour = trigger->wStartHour;
|
||||
st.wMinute = trigger->wStartMinute;
|
||||
st.wSecond = 0;
|
||||
st.wMilliseconds = 0;
|
||||
SystemTimeToFileTime(&st, &trigger_ft);
|
||||
while (CompareFileTime(&trigger_ft, &end_ft) < 0)
|
||||
{
|
||||
if (CompareFileTime(&trigger_ft, &begin_ft) >= 0)
|
||||
{
|
||||
*rt = trigger_ft;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
filetime_add_days(&trigger_ft, trigger->Type.Daily.DaysInterval);
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_TIME_TRIGGER_WEEKLY:
|
||||
if (!trigger->Type.Weekly.rgfDaysOfTheWeek)
|
||||
break; /* avoid infinite loop */
|
||||
|
||||
st = current_st;
|
||||
st.wHour = trigger->wStartHour;
|
||||
st.wMinute = trigger->wStartMinute;
|
||||
st.wSecond = 0;
|
||||
st.wMilliseconds = 0;
|
||||
SystemTimeToFileTime(&st, &trigger_ft);
|
||||
while (CompareFileTime(&trigger_ft, &end_ft) < 0)
|
||||
{
|
||||
FileTimeToSystemTime(&trigger_ft, &st);
|
||||
|
||||
if (CompareFileTime(&trigger_ft, &begin_ft) >= 0)
|
||||
{
|
||||
if (trigger->Type.Weekly.rgfDaysOfTheWeek & (1 << st.wDayOfWeek))
|
||||
{
|
||||
*rt = trigger_ft;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (st.wDayOfWeek == 0 && trigger->Type.Weekly.WeeksInterval > 1) /* Sunday, goto next week */
|
||||
filetime_add_weeks(&trigger_ft, trigger->Type.Weekly.WeeksInterval - 1);
|
||||
else /* check next weekday */
|
||||
filetime_add_days(&trigger_ft, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
FIXME("trigger type %u is not handled\n", trigger->TriggerType);
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL job_get_next_runtime(struct job_t *job, FILETIME *current_ft, FILETIME *next_rt)
|
||||
{
|
||||
FILETIME trigger_rt;
|
||||
BOOL have_next_rt = FALSE;
|
||||
USHORT i;
|
||||
|
||||
for (i = 0; i < job->trigger_count; i++)
|
||||
{
|
||||
if (trigger_get_next_runtime(&job->trigger[i], current_ft, &trigger_rt))
|
||||
{
|
||||
if (!have_next_rt || CompareFileTime(&trigger_rt, next_rt) < 0)
|
||||
{
|
||||
*next_rt = trigger_rt;
|
||||
have_next_rt = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return have_next_rt;
|
||||
}
|
||||
|
||||
/* Returns next runtime in UTC */
|
||||
BOOL get_next_runtime(LARGE_INTEGER *rt)
|
||||
{
|
||||
FILETIME current_ft, job_rt, next_job_rt;
|
||||
BOOL have_next_rt = FALSE;
|
||||
struct job_t *job;
|
||||
|
||||
GetSystemTimeAsFileTime(¤t_ft);
|
||||
FileTimeToLocalFileTime(¤t_ft, ¤t_ft);
|
||||
|
||||
EnterCriticalSection(&at_job_list_section);
|
||||
|
||||
LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
|
||||
{
|
||||
if (job_get_next_runtime(job, ¤t_ft, &job_rt))
|
||||
{
|
||||
if (!have_next_rt || CompareFileTime(&job_rt, &next_job_rt) < 0)
|
||||
{
|
||||
next_job_rt = job_rt;
|
||||
have_next_rt = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&at_job_list_section);
|
||||
|
||||
if (have_next_rt)
|
||||
{
|
||||
LocalFileTimeToFileTime(&next_job_rt, &next_job_rt);
|
||||
rt->u.LowPart = next_job_rt.dwLowDateTime;
|
||||
rt->u.HighPart = next_job_rt.dwHighDateTime;
|
||||
}
|
||||
|
||||
return have_next_rt;
|
||||
}
|
||||
|
||||
static BOOL job_runs_at(struct job_t *job, FILETIME *begin_ft, FILETIME *end_ft)
|
||||
{
|
||||
FILETIME job_ft;
|
||||
|
||||
if (job_get_next_runtime(job, begin_ft, &job_ft))
|
||||
{
|
||||
if (CompareFileTime(&job_ft, end_ft) < 0)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static DWORD load_unicode_strings(const char *data, DWORD limit, struct job_t *job)
|
||||
{
|
||||
DWORD i, data_size = 0;
|
||||
|
@ -729,6 +959,39 @@ void check_task_state(void)
|
|||
LeaveCriticalSection(&at_job_list_section);
|
||||
}
|
||||
|
||||
static void run_job(struct job_t *job)
|
||||
{
|
||||
job->data.flags |= 0x04000000;
|
||||
update_job_status(job);
|
||||
}
|
||||
|
||||
void check_task_time(void)
|
||||
{
|
||||
FILETIME current_ft, begin_ft, end_ft;
|
||||
struct job_t *job;
|
||||
|
||||
GetSystemTimeAsFileTime(¤t_ft);
|
||||
FileTimeToLocalFileTime(¤t_ft, ¤t_ft);
|
||||
|
||||
/* Give -1/+1 minute margin */
|
||||
begin_ft = current_ft;
|
||||
filetime_add_minutes(&begin_ft, -1);
|
||||
end_ft = current_ft;
|
||||
filetime_add_minutes(&end_ft, 1);
|
||||
|
||||
EnterCriticalSection(&at_job_list_section);
|
||||
|
||||
LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
|
||||
{
|
||||
if (job_runs_at(job, &begin_ft, &end_ft))
|
||||
{
|
||||
run_job(job);
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&at_job_list_section);
|
||||
}
|
||||
|
||||
void remove_job(const WCHAR *name)
|
||||
{
|
||||
struct job_t *job;
|
||||
|
|
|
@ -28,6 +28,8 @@ 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;
|
||||
BOOL get_next_runtime(LARGE_INTEGER *rt) DECLSPEC_HIDDEN;
|
||||
void check_task_time(void) DECLSPEC_HIDDEN;
|
||||
|
||||
static inline WCHAR *heap_strdupW(const WCHAR *src)
|
||||
{
|
||||
|
|
|
@ -46,12 +46,20 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
|
|||
{
|
||||
static const WCHAR tasksW[] = { '\\','T','a','s','k','s','\\',0 };
|
||||
WCHAR path[MAX_PATH];
|
||||
HANDLE htasks, hport;
|
||||
HANDLE htasks, hport, htimer;
|
||||
JOBOBJECT_ASSOCIATE_COMPLETION_PORT info;
|
||||
OVERLAPPED ov;
|
||||
LARGE_INTEGER period;
|
||||
|
||||
TRACE("Starting...\n");
|
||||
|
||||
htimer = CreateWaitableTimerW(NULL, FALSE, NULL);
|
||||
if (htimer == NULL)
|
||||
{
|
||||
ERR("CreateWaitableTimer failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GetWindowsDirectoryW(path, MAX_PATH);
|
||||
lstrcatW(path, tasksW);
|
||||
|
||||
|
@ -98,7 +106,7 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
|
|||
FILE_NOTIFY_INFORMATION data;
|
||||
WCHAR name_buffer[MAX_PATH];
|
||||
} info;
|
||||
HANDLE events[3];
|
||||
HANDLE events[4];
|
||||
DWORD ret;
|
||||
|
||||
/* the buffer must be DWORD aligned */
|
||||
|
@ -115,12 +123,22 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
|
|||
FIXME("got multiple entries\n");
|
||||
|
||||
events[0] = done_event;
|
||||
events[1] = ov.hEvent;
|
||||
events[1] = htimer;
|
||||
events[2] = hport;
|
||||
events[3] = ov.hEvent;
|
||||
|
||||
ret = WaitForMultipleObjects(3, events, FALSE, INFINITE);
|
||||
ret = WaitForMultipleObjects(4, events, FALSE, INFINITE);
|
||||
/* Done event */
|
||||
if (ret == WAIT_OBJECT_0) break;
|
||||
|
||||
/* Next runtime timer */
|
||||
if (ret == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
check_task_time();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Job queue */
|
||||
if (ret == WAIT_OBJECT_0 + 2)
|
||||
{
|
||||
DWORD msg;
|
||||
|
@ -139,6 +157,7 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Directory change notification */
|
||||
info.data.FileName[info.data.FileNameLength/sizeof(WCHAR)] = 0;
|
||||
|
||||
switch (info.data.Action)
|
||||
|
@ -176,8 +195,16 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
|
|||
}
|
||||
|
||||
check_task_state();
|
||||
|
||||
if (get_next_runtime(&period))
|
||||
{
|
||||
if (!SetWaitableTimer(htimer, &period, 0, NULL, NULL, FALSE))
|
||||
ERR("SetWaitableTimer failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
CancelWaitableTimer(htimer);
|
||||
CloseHandle(htimer);
|
||||
CloseHandle(ov.hEvent);
|
||||
CloseHandle(hport);
|
||||
CloseHandle(hjob_queue);
|
||||
|
|
Loading…
Reference in New Issue