4 * Copyright 2018 Dmitry Timoshkov
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define NONAMELESSUNION
27 #include "wine/list.h"
28 #include "wine/debug.h"
30 #include "schedsvc_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(schedsvc
);
34 /* lmat.h defines those, but other types in that file conflict
35 * with generated atsvc.h typedefs.
37 #define JOB_ADD_CURRENT_DATE 0x08
38 #define JOB_NONINTERACTIVE 0x10
42 USHORT product_version
;
45 USHORT name_size_offset
;
46 USHORT trigger_offset
;
47 USHORT error_retry_count
;
48 USHORT error_retry_interval
;
56 SYSTEMTIME last_runtime
;
67 USHORT instance_count
;
69 TASK_TRIGGER
*trigger
;
80 static LONG current_jobid
= 1;
82 static struct list at_job_list
= LIST_INIT(at_job_list
);
83 static struct list running_job_list
= LIST_INIT(running_job_list
);
85 static CRITICAL_SECTION at_job_list_section
;
86 static CRITICAL_SECTION_DEBUG cs_debug
=
88 0, 0, &at_job_list_section
,
89 { &cs_debug
.ProcessLocksList
, &cs_debug
.ProcessLocksList
},
90 0, 0, { (DWORD_PTR
)(__FILE__
": at_job_list_section") }
92 static CRITICAL_SECTION at_job_list_section
= { &cs_debug
, -1, 0, 0, 0, 0 };
94 static void filetime_add_ms(FILETIME
*ft
, LONGLONG ms
)
100 } *ftll
= (union u_ftll
*)ft
;
102 ftll
->ll
+= ms
* (ULONGLONG
)10000;
105 static void filetime_add_minutes(FILETIME
*ft
, LONG minutes
)
107 filetime_add_ms(ft
, (LONGLONG
)minutes
* 60 * 1000);
110 static void filetime_add_hours(FILETIME
*ft
, LONG hours
)
112 filetime_add_minutes(ft
, (LONGLONG
)hours
* 60);
115 static void filetime_add_days(FILETIME
*ft
, LONG days
)
117 filetime_add_hours(ft
, (LONGLONG
)days
* 24);
120 static void filetime_add_weeks(FILETIME
*ft
, ULONG weeks
)
122 filetime_add_days(ft
, (LONGLONG
)weeks
* 7);
125 static void get_begin_time(const TASK_TRIGGER
*trigger
, FILETIME
*ft
)
129 st
.wYear
= trigger
->wBeginYear
;
130 st
.wMonth
= trigger
->wBeginMonth
;
131 st
.wDay
= trigger
->wBeginDay
;
136 st
.wMilliseconds
= 0;
137 SystemTimeToFileTime(&st
, ft
);
140 static void get_end_time(const TASK_TRIGGER
*trigger
, FILETIME
*ft
)
144 if (!(trigger
->rgFlags
& TASK_TRIGGER_FLAG_HAS_END_DATE
))
146 ft
->dwHighDateTime
= ~0u;
147 ft
->dwLowDateTime
= ~0u;
151 st
.wYear
= trigger
->wEndYear
;
152 st
.wMonth
= trigger
->wEndMonth
;
153 st
.wDay
= trigger
->wEndDay
;
158 st
.wMilliseconds
= 0;
159 SystemTimeToFileTime(&st
, ft
);
162 static BOOL
trigger_get_next_runtime(const TASK_TRIGGER
*trigger
, const FILETIME
*current_ft
, FILETIME
*rt
)
164 SYSTEMTIME st
, current_st
;
165 FILETIME begin_ft
, end_ft
, trigger_ft
;
167 if (trigger
->rgFlags
& TASK_TRIGGER_FLAG_DISABLED
)
170 FileTimeToSystemTime(current_ft
, ¤t_st
);
172 get_begin_time(trigger
, &begin_ft
);
173 get_end_time(trigger
, &end_ft
);
175 switch (trigger
->TriggerType
)
177 case TASK_EVENT_TRIGGER_ON_IDLE
:
178 case TASK_EVENT_TRIGGER_AT_SYSTEMSTART
:
179 case TASK_EVENT_TRIGGER_AT_LOGON
:
182 case TASK_TIME_TRIGGER_ONCE
:
184 st
.wHour
= trigger
->wStartHour
;
185 st
.wMinute
= trigger
->wStartMinute
;
187 st
.wMilliseconds
= 0;
188 SystemTimeToFileTime(&st
, &trigger_ft
);
189 if (CompareFileTime(&begin_ft
, &trigger_ft
) <= 0 && CompareFileTime(&trigger_ft
, &end_ft
) < 0)
196 case TASK_TIME_TRIGGER_DAILY
:
198 st
.wHour
= trigger
->wStartHour
;
199 st
.wMinute
= trigger
->wStartMinute
;
201 st
.wMilliseconds
= 0;
202 SystemTimeToFileTime(&st
, &trigger_ft
);
203 while (CompareFileTime(&trigger_ft
, &end_ft
) < 0)
205 if (CompareFileTime(&trigger_ft
, &begin_ft
) >= 0)
211 filetime_add_days(&trigger_ft
, trigger
->Type
.Daily
.DaysInterval
);
215 case TASK_TIME_TRIGGER_WEEKLY
:
216 if (!trigger
->Type
.Weekly
.rgfDaysOfTheWeek
)
217 break; /* avoid infinite loop */
220 st
.wHour
= trigger
->wStartHour
;
221 st
.wMinute
= trigger
->wStartMinute
;
223 st
.wMilliseconds
= 0;
224 SystemTimeToFileTime(&st
, &trigger_ft
);
225 while (CompareFileTime(&trigger_ft
, &end_ft
) < 0)
227 FileTimeToSystemTime(&trigger_ft
, &st
);
229 if (CompareFileTime(&trigger_ft
, &begin_ft
) >= 0)
231 if (trigger
->Type
.Weekly
.rgfDaysOfTheWeek
& (1 << st
.wDayOfWeek
))
238 if (st
.wDayOfWeek
== 0 && trigger
->Type
.Weekly
.WeeksInterval
> 1) /* Sunday, goto next week */
239 filetime_add_weeks(&trigger_ft
, trigger
->Type
.Weekly
.WeeksInterval
- 1);
240 else /* check next weekday */
241 filetime_add_days(&trigger_ft
, 1);
246 FIXME("trigger type %u is not handled\n", trigger
->TriggerType
);
253 static BOOL
job_get_next_runtime(struct job_t
*job
, FILETIME
*current_ft
, FILETIME
*next_rt
)
256 BOOL have_next_rt
= FALSE
;
259 for (i
= 0; i
< job
->trigger_count
; i
++)
261 if (trigger_get_next_runtime(&job
->trigger
[i
], current_ft
, &trigger_rt
))
263 if (!have_next_rt
|| CompareFileTime(&trigger_rt
, next_rt
) < 0)
265 *next_rt
= trigger_rt
;
274 /* Returns next runtime in UTC */
275 BOOL
get_next_runtime(LARGE_INTEGER
*rt
)
277 FILETIME current_ft
, job_rt
, next_job_rt
;
278 BOOL have_next_rt
= FALSE
;
281 GetSystemTimeAsFileTime(¤t_ft
);
282 FileTimeToLocalFileTime(¤t_ft
, ¤t_ft
);
284 EnterCriticalSection(&at_job_list_section
);
286 LIST_FOR_EACH_ENTRY(job
, &at_job_list
, struct job_t
, entry
)
288 if (job_get_next_runtime(job
, ¤t_ft
, &job_rt
))
290 if (!have_next_rt
|| CompareFileTime(&job_rt
, &next_job_rt
) < 0)
292 next_job_rt
= job_rt
;
298 LeaveCriticalSection(&at_job_list_section
);
302 LocalFileTimeToFileTime(&next_job_rt
, &next_job_rt
);
303 rt
->u
.LowPart
= next_job_rt
.dwLowDateTime
;
304 rt
->u
.HighPart
= next_job_rt
.dwHighDateTime
;
310 static BOOL
job_runs_at(struct job_t
*job
, FILETIME
*begin_ft
, FILETIME
*end_ft
)
314 if (job_get_next_runtime(job
, begin_ft
, &job_ft
))
316 if (CompareFileTime(&job_ft
, end_ft
) < 0)
323 static DWORD
load_unicode_strings(const char *data
, DWORD limit
, struct job_t
*job
)
325 DWORD i
, data_size
= 0;
328 for (i
= 0; i
< 5; i
++)
330 if (limit
< sizeof(USHORT
))
332 TRACE("invalid string %u offset\n", i
);
336 len
= *(USHORT
*)data
;
337 data
+= sizeof(USHORT
);
338 data_size
+= sizeof(USHORT
);
339 limit
-= sizeof(USHORT
);
340 if (limit
< len
* sizeof(WCHAR
))
342 TRACE("invalid string %u size\n", i
);
346 TRACE("string %u: %s\n", i
, wine_dbgstr_wn((const WCHAR
*)data
, len
));
351 job
->info
.Command
= heap_strdupW((const WCHAR
*)data
);
355 job
->params
= heap_strdupW((const WCHAR
*)data
);
359 job
->curdir
= heap_strdupW((const WCHAR
*)data
);
366 data
+= len
* sizeof(WCHAR
);
367 data_size
+= len
* sizeof(WCHAR
);
373 static BOOL
load_job_data(const char *data
, DWORD size
, struct job_t
*info
)
375 const FIXDLEN_DATA
*fixed
;
376 const SYSTEMTIME
*st
;
377 DWORD unicode_strings_size
, data_size
, triggers_size
;
379 const USHORT
*signature
;
380 const TASK_TRIGGER
*trigger
;
382 memset(info
, 0, sizeof(*info
));
384 if (size
< sizeof(*fixed
))
386 TRACE("no space for FIXDLEN_DATA\n");
390 fixed
= (const FIXDLEN_DATA
*)data
;
393 TRACE("product_version %04x\n", fixed
->product_version
);
394 TRACE("file_version %04x\n", fixed
->file_version
);
395 TRACE("uuid %s\n", wine_dbgstr_guid(&fixed
->uuid
));
397 if (fixed
->file_version
!= 0x0001)
399 TRACE("invalid file version\n");
403 TRACE("name_size_offset %04x\n", fixed
->name_size_offset
);
404 TRACE("trigger_offset %04x\n", fixed
->trigger_offset
);
405 TRACE("error_retry_count %u\n", fixed
->error_retry_count
);
406 TRACE("error_retry_interval %u\n", fixed
->error_retry_interval
);
407 TRACE("idle_deadline %u\n", fixed
->idle_deadline
);
408 TRACE("idle_wait %u\n", fixed
->idle_wait
);
409 TRACE("priority %08x\n", fixed
->priority
);
410 TRACE("maximum_runtime %u\n", fixed
->maximum_runtime
);
411 TRACE("exit_code %#x\n", fixed
->exit_code
);
412 TRACE("status %08x\n", fixed
->status
);
413 TRACE("flags %08x\n", fixed
->flags
);
414 st
= &fixed
->last_runtime
;
415 TRACE("last_runtime %d/%d/%d wday %d %d:%d:%d.%03d\n",
416 st
->wDay
, st
->wMonth
, st
->wYear
, st
->wDayOfWeek
,
417 st
->wHour
, st
->wMinute
, st
->wSecond
, st
->wMilliseconds
);
420 if (size
< sizeof(*fixed
) + sizeof(USHORT
))
422 TRACE("no space for instance count\n");
426 info
->instance_count
= *(const USHORT
*)(data
+ sizeof(*fixed
));
427 TRACE("instance count %u\n", info
->instance_count
);
429 if (fixed
->name_size_offset
+ sizeof(USHORT
) < size
)
430 unicode_strings_size
= load_unicode_strings(data
+ fixed
->name_size_offset
, size
- fixed
->name_size_offset
, info
);
433 TRACE("invalid name_size_offset\n");
436 TRACE("unicode strings end at %#x\n", fixed
->name_size_offset
+ unicode_strings_size
);
438 if (size
< fixed
->trigger_offset
+ sizeof(USHORT
))
440 TRACE("no space for triggers count\n");
443 info
->trigger_count
= *(const USHORT
*)(data
+ fixed
->trigger_offset
);
444 TRACE("trigger_count %u\n", info
->trigger_count
);
445 triggers_size
= size
- fixed
->trigger_offset
- sizeof(USHORT
);
446 TRACE("triggers_size %u\n", triggers_size
);
448 data
+= fixed
->name_size_offset
+ unicode_strings_size
;
449 size
-= fixed
->name_size_offset
+ unicode_strings_size
;
452 if (size
< sizeof(USHORT
))
454 TRACE("no space for user data size\n");
458 data_size
= *(const USHORT
*)data
;
459 if (size
< sizeof(USHORT
) + data_size
)
461 TRACE("no space for user data\n");
464 TRACE("User Data size %#x\n", data_size
);
466 size
-= sizeof(USHORT
) + data_size
;
467 data
+= sizeof(USHORT
) + data_size
;
470 if (size
< sizeof(USHORT
))
472 TRACE("no space for reserved data size\n");
476 data_size
= *(const USHORT
*)data
;
477 if (size
< sizeof(USHORT
) + data_size
)
479 TRACE("no space for reserved data\n");
482 TRACE("Reserved Data size %#x\n", data_size
);
484 size
-= sizeof(USHORT
) + data_size
;
485 data
+= sizeof(USHORT
) + data_size
;
488 TRACE("trigger_offset %04x, triggers end at %04x\n", fixed
->trigger_offset
,
489 (DWORD
)(fixed
->trigger_offset
+ sizeof(USHORT
) + info
->trigger_count
* sizeof(TASK_TRIGGER
)));
491 info
->trigger_count
= *(const USHORT
*)data
;
492 TRACE("trigger_count %u\n", info
->trigger_count
);
493 trigger
= (const TASK_TRIGGER
*)(data
+ sizeof(USHORT
));
495 if (info
->trigger_count
* sizeof(TASK_TRIGGER
) > triggers_size
)
497 TRACE("no space for triggers data\n");
501 info
->trigger
= heap_alloc(info
->trigger_count
* sizeof(info
->trigger
[0]));
504 TRACE("not enough memory for trigger data\n");
508 for (i
= 0; i
< info
->trigger_count
; i
++)
510 TRACE("%u: cbTriggerSize = %#x\n", i
, trigger
[i
].cbTriggerSize
);
511 if (trigger
[i
].cbTriggerSize
!= sizeof(TASK_TRIGGER
))
512 TRACE("invalid cbTriggerSize\n");
513 TRACE("Reserved1 = %#x\n", trigger
[i
].Reserved1
);
514 TRACE("wBeginYear = %u\n", trigger
[i
].wBeginYear
);
515 TRACE("wBeginMonth = %u\n", trigger
[i
].wBeginMonth
);
516 TRACE("wBeginDay = %u\n", trigger
[i
].wBeginDay
);
517 TRACE("wEndYear = %u\n", trigger
[i
].wEndYear
);
518 TRACE("wEndMonth = %u\n", trigger
[i
].wEndMonth
);
519 TRACE("wEndDay = %u\n", trigger
[i
].wEndDay
);
520 TRACE("wStartHour = %u\n", trigger
[i
].wStartHour
);
521 TRACE("wStartMinute = %u\n", trigger
[i
].wStartMinute
);
522 TRACE("MinutesDuration = %u\n", trigger
[i
].MinutesDuration
);
523 TRACE("MinutesInterval = %u\n", trigger
[i
].MinutesInterval
);
524 TRACE("rgFlags = %u\n", trigger
[i
].rgFlags
);
525 TRACE("TriggerType = %u\n", trigger
[i
].TriggerType
);
526 TRACE("Reserved2 = %u\n", trigger
[i
].Reserved2
);
527 TRACE("wRandomMinutesInterval = %u\n", trigger
[i
].wRandomMinutesInterval
);
529 info
->trigger
[i
] = trigger
[i
];
532 size
-= sizeof(USHORT
) + info
->trigger_count
* sizeof(TASK_TRIGGER
);
533 data
+= sizeof(USHORT
) + info
->trigger_count
* sizeof(TASK_TRIGGER
);
535 if (size
< 2 * sizeof(USHORT
) + 64)
537 TRACE("no space for signature\n");
538 return TRUE
; /* signature is optional */
541 signature
= (const USHORT
*)data
;
542 TRACE("signature version %04x, client version %04x\n", signature
[0], signature
[1]);
547 static BOOL
load_job(const WCHAR
*name
, struct job_t
*info
)
549 HANDLE file
, mapping
;
557 file
= CreateFileW(name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
558 if (file
== INVALID_HANDLE_VALUE
)
560 TRACE("Failed to open %s, error %u\n", debugstr_w(name
), GetLastError());
561 if (try++ >= 3) break;
566 size
= GetFileSize(file
, NULL
);
568 mapping
= CreateFileMappingW(file
, NULL
, PAGE_READONLY
, 0, 0, 0);
571 TRACE("Failed to create file mapping %s, error %u\n", debugstr_w(name
), GetLastError());
576 data
= MapViewOfFile(mapping
, FILE_MAP_READ
, 0, 0, 0);
579 ret
= load_job_data(data
, size
, info
);
580 UnmapViewOfFile(data
);
583 CloseHandle(mapping
);
591 static void free_job_info(AT_ENUM
*info
)
593 heap_free(info
->Command
);
596 static void free_job(struct job_t
*job
)
598 free_job_info(&job
->info
);
599 heap_free(job
->name
);
600 heap_free(job
->params
);
601 heap_free(job
->curdir
);
602 heap_free(job
->trigger
);
606 void add_job(const WCHAR
*name
)
610 job
= heap_alloc_zero(sizeof(*job
));
613 if (!load_job(name
, job
))
619 EnterCriticalSection(&at_job_list_section
);
620 job
->name
= heap_strdupW(name
);
621 job
->info
.JobId
= current_jobid
++;
622 list_add_tail(&at_job_list
, &job
->entry
);
623 LeaveCriticalSection(&at_job_list_section
);
626 static inline BOOL
is_file(const WIN32_FIND_DATAW
*data
)
628 return !(data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
631 void load_at_tasks(void)
633 static const WCHAR tasksW
[] = { '\\','T','a','s','k','s','\\',0 };
634 static const WCHAR allW
[] = { '*',0 };
635 WCHAR windir
[MAX_PATH
], path
[MAX_PATH
];
636 WIN32_FIND_DATAW data
;
639 GetWindowsDirectoryW(windir
, MAX_PATH
);
640 lstrcpyW(path
, windir
);
641 lstrcatW(path
, tasksW
);
642 lstrcatW(path
, allW
);
644 handle
= FindFirstFileW(path
, &data
);
645 if (handle
== INVALID_HANDLE_VALUE
) return;
651 lstrcpyW(path
, windir
);
652 lstrcatW(path
, tasksW
);
654 if (lstrlenW(path
) + lstrlenW(data
.cFileName
) < MAX_PATH
)
656 lstrcatW(path
, data
.cFileName
);
660 FIXME("too long file name %s\n", debugstr_w(data
.cFileName
));
662 } while (FindNextFileW(handle
, &data
));
667 static BOOL
write_signature(HANDLE hfile
)
671 USHORT SignatureVersion
;
672 USHORT ClientVersion
;
677 signature
.SignatureVersion
= 0x0001;
678 signature
.ClientVersion
= 0x0001;
679 memset(&signature
.md5
, 0, sizeof(signature
.md5
));
681 return WriteFile(hfile
, &signature
, sizeof(signature
), &size
, NULL
);
684 static BOOL
write_reserved_data(HANDLE hfile
)
690 } user
= { 8, { 0xff,0x0f,0x1d,0,0,0,0,0 } };
693 return WriteFile(hfile
, &user
, sizeof(user
), &size
, NULL
);
696 static BOOL
write_trigger(HANDLE hfile
, const AT_INFO
*info
)
701 TASK_TRIGGER trigger
;
704 if (!WriteFile(hfile
, &count
, sizeof(count
), &size
, NULL
))
708 if (!(info
->Flags
& JOB_ADD_CURRENT_DATE
))
710 /* FIXME: parse AT_INFO */
713 trigger
.cbTriggerSize
= sizeof(trigger
);
714 trigger
.Reserved1
= 0;
715 trigger
.wBeginYear
= st
.wYear
;
716 trigger
.wBeginMonth
= st
.wMonth
;
717 trigger
.wBeginDay
= st
.wDay
;
718 trigger
.wEndYear
= st
.wYear
;
719 trigger
.wEndMonth
= st
.wMonth
;
720 trigger
.wEndDay
= st
.wDay
;
721 trigger
.wStartHour
= st
.wHour
;
722 trigger
.wStartMinute
= st
.wMinute
;
723 trigger
.MinutesDuration
= 0;
724 trigger
.MinutesInterval
= 0;
726 trigger
.rgFlags
= TASK_TRIGGER_FLAG_HAS_END_DATE
;
727 trigger
.TriggerType
= TASK_TIME_TRIGGER_MONTHLYDATE
;
728 trigger
.Type
.MonthlyDate
.rgfDays
= 0;
729 trigger
.Type
.MonthlyDate
.rgfMonths
= 0xffff;
730 trigger
.Reserved2
= 0;
731 trigger
.wRandomMinutesInterval
= 0;
733 return WriteFile(hfile
, &trigger
, sizeof(trigger
), &size
, NULL
);
736 static BOOL
write_unicode_string(HANDLE hfile
, const WCHAR
*str
)
741 count
= str
? (lstrlenW(str
) + 1) : 0;
742 if (!WriteFile(hfile
, &count
, sizeof(count
), &size
, NULL
))
745 if (!str
) return TRUE
;
747 count
*= sizeof(WCHAR
);
748 return WriteFile(hfile
, str
, count
, &size
, NULL
);
751 static BOOL
create_job(const WCHAR
*job_name
, const AT_INFO
*info
)
753 static WCHAR authorW
[] = { 'W','i','n','e',0 };
754 static WCHAR commentW
[] = { 'C','r','e','a','t','e','d',' ','b','y',' ','W','i','n','e',0 };
761 TRACE("trying to create job %s\n", debugstr_w(job_name
));
762 hfile
= CreateFileW(job_name
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, 0);
763 if (hfile
== INVALID_HANDLE_VALUE
)
767 fixed
.product_version
= MAKEWORD(ver
>> 8, ver
);
768 fixed
.file_version
= 0x0001;
769 UuidCreate(&fixed
.uuid
);
770 fixed
.name_size_offset
= sizeof(fixed
) + sizeof(USHORT
); /* FIXDLEN_DATA + Instance Count */
771 fixed
.trigger_offset
= sizeof(fixed
) + sizeof(USHORT
); /* FIXDLEN_DATA + Instance Count */
772 fixed
.trigger_offset
+= sizeof(USHORT
) + (lstrlenW(info
->Command
) + 1) * sizeof(WCHAR
); /* Application Name */
773 fixed
.trigger_offset
+= sizeof(USHORT
); /* Parameters */
774 fixed
.trigger_offset
+= sizeof(USHORT
); /* Working Directory */
775 fixed
.trigger_offset
+= sizeof(USHORT
) + (lstrlenW(authorW
) + 1) * sizeof(WCHAR
); /* Author */
776 fixed
.trigger_offset
+= sizeof(USHORT
) + (lstrlenW(commentW
) + 1) * sizeof(WCHAR
); /* Comment */
777 fixed
.trigger_offset
+= sizeof(USHORT
); /* User Data */
778 fixed
.trigger_offset
+= 10; /* Reserved Data */
779 fixed
.error_retry_count
= 0;
780 fixed
.error_retry_interval
= 0;
781 fixed
.idle_deadline
= 60;
782 fixed
.idle_wait
= 10;
783 fixed
.priority
= NORMAL_PRIORITY_CLASS
;
784 fixed
.maximum_runtime
= 259200000;
786 fixed
.status
= SCHED_S_TASK_HAS_NOT_RUN
;
787 fixed
.flags
= TASK_FLAG_DELETE_WHEN_DONE
;
788 if (!(info
->Flags
& JOB_NONINTERACTIVE
))
789 fixed
.flags
|= TASK_FLAG_INTERACTIVE
;
790 /* FIXME: add other flags */
791 memset(&fixed
.last_runtime
, 0, sizeof(fixed
.last_runtime
));
793 if (!WriteFile(hfile
, &fixed
, sizeof(fixed
), &size
, NULL
))
798 if (!WriteFile(hfile
, &word
, sizeof(word
), &size
, NULL
))
800 /* Application Name */
801 if (!write_unicode_string(hfile
, info
->Command
))
804 if (!write_unicode_string(hfile
, NULL
))
806 /* Working Directory */
807 if (!write_unicode_string(hfile
, NULL
))
810 if (!write_unicode_string(hfile
, authorW
))
813 if (!write_unicode_string(hfile
, commentW
))
818 if (!WriteFile(hfile
, &word
, sizeof(word
), &size
, NULL
))
822 if (!write_reserved_data(hfile
))
826 if (!write_trigger(hfile
, info
))
830 if (!write_signature(hfile
))
837 if (!ret
) DeleteFileW(job_name
);
841 static struct job_t
*find_job(DWORD jobid
, const WCHAR
*name
, const UUID
*id
)
845 LIST_FOR_EACH_ENTRY(job
, &at_job_list
, struct job_t
, entry
)
847 if (job
->info
.JobId
== jobid
|| (name
&& !lstrcmpiW(job
->name
, name
)) || (id
&& IsEqualGUID(&job
->data
.uuid
, id
)))
854 static void update_job_status(struct job_t
*job
)
858 #include "pshpack2.h"
864 SYSTEMTIME last_runtime
;
872 hfile
= CreateFileW(job
->name
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
873 if (hfile
!= INVALID_HANDLE_VALUE
) break;
877 ERR("Failed to update %s, error %u\n", debugstr_w(job
->name
), GetLastError());
883 if (SetFilePointer(hfile
, FIELD_OFFSET(FIXDLEN_DATA
, exit_code
), NULL
, FILE_BEGIN
) != INVALID_SET_FILE_POINTER
)
885 state
.exit_code
= job
->data
.exit_code
;
886 state
.status
= job
->data
.status
;
887 state
.flags
= job
->data
.flags
;
888 state
.last_runtime
= job
->data
.last_runtime
;
889 state
.instance_count
= job
->instance_count
;
890 WriteFile(hfile
, &state
, sizeof(state
), &size
, NULL
);
896 void update_process_status(DWORD pid
)
898 struct running_job_t
*runjob
;
900 EnterCriticalSection(&at_job_list_section
);
902 LIST_FOR_EACH_ENTRY(runjob
, &running_job_list
, struct running_job_t
, entry
)
904 if (runjob
->pid
== pid
)
906 struct job_t
*job
= find_job(0, NULL
, &runjob
->uuid
);
909 DWORD exit_code
= STILL_ACTIVE
;
911 GetExitCodeProcess(runjob
->process
, &exit_code
);
913 if (exit_code
!= STILL_ACTIVE
)
915 CloseHandle(runjob
->process
);
916 list_remove(&runjob
->entry
);
919 job
->data
.exit_code
= exit_code
;
920 job
->data
.status
= SCHED_S_TASK_TERMINATED
;
921 job
->data
.flags
&= ~0x0c000000;
922 job
->instance_count
= 0;
923 update_job_status(job
);
930 LeaveCriticalSection(&at_job_list_section
);
933 void check_task_state(void)
936 struct running_job_t
*runjob
;
938 EnterCriticalSection(&at_job_list_section
);
940 LIST_FOR_EACH_ENTRY(job
, &at_job_list
, struct job_t
, entry
)
942 if (job
->data
.flags
& 0x08000000)
944 TRACE("terminating process %s\n", debugstr_w(job
->info
.Command
));
946 LIST_FOR_EACH_ENTRY(runjob
, &running_job_list
, struct running_job_t
, entry
)
948 if (IsEqualGUID(&job
->data
.uuid
, &runjob
->uuid
))
950 TerminateProcess(runjob
->process
, 0);
951 update_process_status(runjob
->pid
);
956 else if (job
->data
.flags
& 0x04000000)
959 PROCESS_INFORMATION pi
;
961 TRACE("running process %s\n", debugstr_w(job
->info
.Command
));
963 if (job
->instance_count
)
964 FIXME("process %s is already running\n", debugstr_w(job
->info
.Command
));
966 runjob
= heap_alloc(sizeof(*runjob
));
969 static WCHAR winsta0
[] = { 'W','i','n','S','t','a','0',0 };
971 memset(&si
, 0, sizeof(si
));
973 /* FIXME: if (job->data.flags & TASK_FLAG_INTERACTIVE) */
974 si
.lpDesktop
= winsta0
;
975 si
.dwFlags
= STARTF_USESHOWWINDOW
;
976 si
.wShowWindow
= SW_SHOWNORMAL
;
977 TRACE("executing %s %s at %s\n", debugstr_w(job
->info
.Command
), debugstr_w(job
->params
), debugstr_w(job
->curdir
));
978 if (CreateProcessW(job
->info
.Command
, job
->params
, NULL
, NULL
, FALSE
, 0, NULL
, job
->curdir
, &si
, &pi
))
980 CloseHandle(pi
.hThread
);
982 GetLocalTime(&job
->data
.last_runtime
);
983 job
->data
.exit_code
= 0;
984 job
->data
.status
= SCHED_S_TASK_RUNNING
;
985 job
->instance_count
= 1;
987 runjob
->uuid
= job
->data
.uuid
;
988 runjob
->process
= pi
.hProcess
;
989 runjob
->pid
= pi
.dwProcessId
;
990 list_add_tail(&running_job_list
, &runjob
->entry
);
991 add_process_to_queue(pi
.hProcess
);
995 WARN("failed to execute %s\n", debugstr_w(job
->info
.Command
));
996 job
->data
.status
= SCHED_S_TASK_HAS_NOT_RUN
;
997 job
->instance_count
= 0;
1001 job
->data
.flags
&= ~0x0c000000;
1002 update_job_status(job
);
1006 LeaveCriticalSection(&at_job_list_section
);
1009 static void run_job(struct job_t
*job
)
1011 job
->data
.flags
|= 0x04000000;
1012 update_job_status(job
);
1015 void check_task_time(void)
1017 FILETIME current_ft
, begin_ft
, end_ft
;
1020 GetSystemTimeAsFileTime(¤t_ft
);
1021 FileTimeToLocalFileTime(¤t_ft
, ¤t_ft
);
1023 /* Give -1/+1 minute margin */
1024 begin_ft
= current_ft
;
1025 filetime_add_minutes(&begin_ft
, -1);
1026 end_ft
= current_ft
;
1027 filetime_add_minutes(&end_ft
, 1);
1029 EnterCriticalSection(&at_job_list_section
);
1031 LIST_FOR_EACH_ENTRY(job
, &at_job_list
, struct job_t
, entry
)
1033 if (job_runs_at(job
, &begin_ft
, &end_ft
))
1039 LeaveCriticalSection(&at_job_list_section
);
1042 void remove_job(const WCHAR
*name
)
1046 EnterCriticalSection(&at_job_list_section
);
1047 job
= find_job(0, name
, NULL
);
1050 list_remove(&job
->entry
);
1053 LeaveCriticalSection(&at_job_list_section
);
1056 DWORD __cdecl
NetrJobAdd(ATSVC_HANDLE server_name
, AT_INFO
*info
, DWORD
*jobid
)
1058 WCHAR windir
[MAX_PATH
];
1060 TRACE("%s,%p,%p\n", debugstr_w(server_name
), info
, jobid
);
1062 GetWindowsDirectoryW(windir
, MAX_PATH
);
1066 static const WCHAR fmtW
[] = { '\\','T','a','s','k','s','\\','A','t','%','u','.','j','o','b',0 };
1067 WCHAR task_name
[MAX_PATH
], name
[32];
1069 strcpyW(task_name
, windir
);
1070 sprintfW(name
, fmtW
, current_jobid
);
1071 strcatW(task_name
, name
);
1072 if (create_job(task_name
, info
))
1077 for (i
= 0; i
< 5; i
++)
1079 EnterCriticalSection(&at_job_list_section
);
1080 job
= find_job(0, task_name
, NULL
);
1081 LeaveCriticalSection(&at_job_list_section
);
1085 *jobid
= job
->info
.JobId
;
1094 ERR("couldn't find just created job %s\n", debugstr_w(task_name
));
1095 return ERROR_FILE_NOT_FOUND
;
1101 if (GetLastError() != ERROR_FILE_EXISTS
)
1104 TRACE("create_job error %u\n", GetLastError());
1105 return GetLastError();
1108 InterlockedIncrement(¤t_jobid
);
1111 return ERROR_SUCCESS
;
1114 DWORD __cdecl
NetrJobDel(ATSVC_HANDLE server_name
, DWORD min_jobid
, DWORD max_jobid
)
1116 DWORD jobid
, ret
= APE_AT_ID_NOT_FOUND
;
1118 TRACE("%s,%u,%u\n", debugstr_w(server_name
), min_jobid
, max_jobid
);
1120 EnterCriticalSection(&at_job_list_section
);
1122 for (jobid
= min_jobid
; jobid
<= max_jobid
; jobid
++)
1124 struct job_t
*job
= find_job(jobid
, NULL
, NULL
);
1128 TRACE("job %u not found\n", jobid
);
1129 ret
= APE_AT_ID_NOT_FOUND
;
1133 TRACE("deleting job %s\n", debugstr_w(job
->name
));
1134 if (!DeleteFileW(job
->name
))
1136 ret
= GetLastError();
1140 ret
= ERROR_SUCCESS
;
1143 LeaveCriticalSection(&at_job_list_section
);
1147 static void free_container(AT_ENUM_CONTAINER
*container
)
1151 for (i
= 0; i
< container
->EntriesRead
; i
++)
1152 heap_free(container
->Buffer
[i
].Command
);
1154 heap_free(container
->Buffer
);
1157 DWORD __cdecl
NetrJobEnum(ATSVC_HANDLE server_name
, AT_ENUM_CONTAINER
*container
,
1158 DWORD max_length
, DWORD
*total
, DWORD
*resume
)
1163 TRACE("%s,%p,%u,%p,%p\n", debugstr_w(server_name
), container
, max_length
, total
, resume
);
1167 container
->EntriesRead
= 0;
1170 container
->Buffer
= heap_alloc(allocated
* sizeof(AT_ENUM
));
1171 if (!container
->Buffer
) return ERROR_NOT_ENOUGH_MEMORY
;
1173 EnterCriticalSection(&at_job_list_section
);
1175 LIST_FOR_EACH_ENTRY(job
, &at_job_list
, struct job_t
, entry
)
1177 if (container
->EntriesRead
>= max_length
)
1179 *resume
= container
->EntriesRead
;
1183 if (allocated
<= container
->EntriesRead
)
1185 AT_ENUM
*new_buffer
;
1188 new_buffer
= heap_realloc(container
->Buffer
, allocated
* sizeof(AT_ENUM
));
1191 free_container(container
);
1192 LeaveCriticalSection(&at_job_list_section
);
1193 return ERROR_NOT_ENOUGH_MEMORY
;
1195 container
->Buffer
= new_buffer
;
1198 container
->Buffer
[container
->EntriesRead
] = job
->info
;
1199 container
->Buffer
[container
->EntriesRead
].Command
= heap_strdupW(job
->info
.Command
);
1200 container
->EntriesRead
++;
1203 LeaveCriticalSection(&at_job_list_section
);
1205 *total
= container
->EntriesRead
;
1207 return ERROR_SUCCESS
;
1210 DWORD __cdecl
NetrJobGetInfo(ATSVC_HANDLE server_name
, DWORD jobid
, AT_INFO
**info
)
1213 DWORD ret
= APE_AT_ID_NOT_FOUND
;
1215 TRACE("%s,%u,%p\n", debugstr_w(server_name
), jobid
, info
);
1217 EnterCriticalSection(&at_job_list_section
);
1219 job
= find_job(jobid
, NULL
, NULL
);
1222 AT_INFO
*info_ret
= heap_alloc(sizeof(*info_ret
));
1224 ret
= ERROR_NOT_ENOUGH_MEMORY
;
1227 info_ret
->JobTime
= job
->info
.JobTime
;
1228 info_ret
->DaysOfMonth
= job
->info
.DaysOfMonth
;
1229 info_ret
->DaysOfWeek
= job
->info
.DaysOfWeek
;
1230 info_ret
->Flags
= job
->info
.Flags
;
1231 info_ret
->Command
= heap_strdupW(job
->info
.Command
);
1233 ret
= ERROR_SUCCESS
;
1237 LeaveCriticalSection(&at_job_list_section
);