8e7a90c880556edd43f6670943561e75f45a89ff
[reactos.git] / reactos / base / services / schedsvc / job.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Services
4 * FILE: base/services/schedsvc/job.c
5 * PURPOSE: Scheduling service
6 * PROGRAMMER: Eric Kohl <eric.kohl@reactos.org>
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "precomp.h"
12
13 WINE_DEFAULT_DEBUG_CHANNEL(schedsvc);
14
15
16 /* GLOBALS ******************************************************************/
17
18 typedef struct _SCHEDULE
19 {
20 DWORD JobTime;
21 DWORD DaysOfMonth;
22 UCHAR DaysOfWeek;
23 UCHAR Flags;
24 WORD Reserved;
25 } SCHEDULE, PSCHEDULE;
26
27 DWORD dwNextJobId = 0;
28 DWORD dwJobCount = 0;
29 LIST_ENTRY JobListHead;
30 RTL_RESOURCE JobListLock;
31
32 LIST_ENTRY StartListHead;
33 RTL_RESOURCE StartListLock;
34
35
36 /* FUNCTIONS *****************************************************************/
37
38 static
39 VOID
40 GetJobName(
41 HKEY hJobsKey,
42 PWSTR pszJobName)
43 {
44 WCHAR szNameBuffer[9];
45 FILETIME SystemTime;
46 ULONG ulSeed, ulValue;
47 HKEY hKey;
48 LONG lError;
49
50 GetSystemTimeAsFileTime(&SystemTime);
51 ulSeed = SystemTime.dwLowDateTime;
52 for (;;)
53 {
54 ulValue = RtlRandomEx(&ulSeed);
55 swprintf(szNameBuffer, L"%08lx", ulValue);
56
57 hKey = NULL;
58 lError = RegOpenKeyEx(hJobsKey,
59 szNameBuffer,
60 0,
61 KEY_READ,
62 &hKey);
63 if (lError != ERROR_SUCCESS)
64 {
65 wcscpy(pszJobName, szNameBuffer);
66 return;
67 }
68
69 RegCloseKey(hKey);
70 }
71 }
72
73
74 LONG
75 SaveJob(
76 _In_ PJOB pJob)
77 {
78 SCHEDULE Schedule;
79 HKEY hJobsKey = NULL, hJobKey = NULL;
80 LONG lError;
81
82 TRACE("SaveJob()\n");
83
84 lError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
85 L"System\\CurrentControlSet\\Services\\Schedule\\Jobs",
86 0,
87 NULL,
88 REG_OPTION_NON_VOLATILE,
89 KEY_WRITE,
90 NULL,
91 &hJobsKey,
92 NULL);
93 if (lError != ERROR_SUCCESS)
94 goto done;
95
96 GetJobName(hJobsKey, pJob->Name);
97
98 lError = RegCreateKeyExW(hJobsKey,
99 pJob->Name,
100 0,
101 NULL,
102 REG_OPTION_NON_VOLATILE,
103 KEY_WRITE,
104 NULL,
105 &hJobKey,
106 NULL);
107 if (lError != ERROR_SUCCESS)
108 goto done;
109
110 Schedule.JobTime = pJob->JobTime;
111 Schedule.DaysOfMonth = pJob->DaysOfMonth;
112 Schedule.DaysOfWeek = pJob->DaysOfWeek;
113 Schedule.Flags = pJob->Flags;
114
115 lError = RegSetValueEx(hJobKey,
116 L"Schedule",
117 0,
118 REG_BINARY,
119 (PBYTE)&Schedule,
120 sizeof(Schedule));
121 if (lError != ERROR_SUCCESS)
122 goto done;
123
124 lError = RegSetValueEx(hJobKey,
125 L"Command",
126 0,
127 REG_SZ,
128 (PBYTE)pJob->Command,
129 (wcslen(pJob->Command) + 1) * sizeof(WCHAR));
130 if (lError != ERROR_SUCCESS)
131 goto done;
132
133 done:
134 if (hJobKey != NULL)
135 RegCloseKey(hJobKey);
136
137 if (hJobsKey != NULL)
138 RegCloseKey(hJobsKey);
139
140 return lError;
141 }
142
143
144 LONG
145 DeleteJob(
146 _In_ PJOB pJob)
147 {
148 HKEY hJobsKey = NULL;
149 LONG lError;
150
151 TRACE("DeleteJob()\n");
152
153 lError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
154 L"System\\CurrentControlSet\\Services\\Schedule\\Jobs",
155 0,
156 NULL,
157 REG_OPTION_NON_VOLATILE,
158 KEY_WRITE,
159 NULL,
160 &hJobsKey,
161 NULL);
162 if (lError != ERROR_SUCCESS)
163 goto done;
164
165 lError = RegDeleteKey(hJobsKey,
166 pJob->Name);
167 if (lError != ERROR_SUCCESS)
168 goto done;
169
170 done:
171 if (hJobsKey != NULL)
172 RegCloseKey(hJobsKey);
173
174 return lError;
175 }
176
177
178 LONG
179 LoadJobs(VOID)
180 {
181 SCHEDULE Schedule;
182 WCHAR szNameBuffer[32];
183 DWORD dwNameLength, dwIndex, dwSize;
184 HKEY hJobsKey = NULL, hJobKey = NULL;
185 PJOB pJob = NULL;
186 LONG lError;
187
188 TRACE("LoadJobs()\n");
189
190 lError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
191 L"System\\CurrentControlSet\\Services\\Schedule\\Jobs",
192 0,
193 NULL,
194 REG_OPTION_NON_VOLATILE,
195 KEY_READ,
196 NULL,
197 &hJobsKey,
198 NULL);
199 if (lError != ERROR_SUCCESS)
200 goto done;
201
202 for (dwIndex = 0; dwIndex < 1000; dwIndex++)
203 {
204 dwNameLength = 32;
205 lError = RegEnumKeyEx(hJobsKey,
206 dwIndex,
207 szNameBuffer,
208 &dwNameLength,
209 NULL,
210 NULL,
211 NULL,
212 NULL);
213 if (lError != ERROR_SUCCESS)
214 {
215 lError = ERROR_SUCCESS;
216 break;
217 }
218
219 TRACE("KeyName: %S\n", szNameBuffer);
220
221 lError = RegOpenKeyEx(hJobsKey,
222 szNameBuffer,
223 0,
224 KEY_READ,
225 &hJobKey);
226 if (lError != ERROR_SUCCESS)
227 break;
228
229 dwSize = sizeof(SCHEDULE);
230 lError = RegQueryValueEx(hJobKey,
231 L"Schedule",
232 NULL,
233 NULL,
234 (PBYTE)&Schedule,
235 &dwSize);
236 if (lError == ERROR_SUCCESS)
237 {
238 dwSize = 0;
239 RegQueryValueEx(hJobKey,
240 L"Command",
241 NULL,
242 NULL,
243 NULL,
244 &dwSize);
245 if (dwSize != 0)
246 {
247 /* Allocate a new job object */
248 pJob = HeapAlloc(GetProcessHeap(),
249 HEAP_ZERO_MEMORY,
250 sizeof(JOB) + dwSize - sizeof(WCHAR));
251 if (pJob == NULL)
252 {
253 lError = ERROR_OUTOFMEMORY;
254 break;
255 }
256
257 lError = RegQueryValueEx(hJobKey,
258 L"Command",
259 NULL,
260 NULL,
261 (PBYTE)pJob->Command,
262 &dwSize);
263 if (lError != ERROR_SUCCESS)
264 break;
265
266 wcscpy(pJob->Name, szNameBuffer);
267 pJob->JobTime = Schedule.JobTime;
268 pJob->DaysOfMonth = Schedule.DaysOfMonth;
269 pJob->DaysOfWeek = Schedule.DaysOfWeek;
270 pJob->Flags = Schedule.Flags;
271
272 /* Acquire the job list lock exclusively */
273 RtlAcquireResourceExclusive(&JobListLock, TRUE);
274
275 /* Assign a new job ID */
276 pJob->JobId = dwNextJobId++;
277 dwJobCount++;
278
279 /* Append the new job to the job list */
280 InsertTailList(&JobListHead, &pJob->JobEntry);
281
282 /* Release the job list lock */
283 RtlReleaseResource(&JobListLock);
284
285 /* Calculate the next start time */
286 CalculateNextStartTime(pJob);
287
288 // Insert job into the start list
289
290 // Update the start timer
291
292 pJob = NULL;
293 }
294 }
295
296 RegCloseKey(hJobKey);
297 hJobKey = NULL;
298 }
299
300 done:
301 if (pJob != NULL)
302 HeapFree(GetProcessHeap(), 0, pJob);
303
304 if (hJobKey != NULL)
305 RegCloseKey(hJobKey);
306
307 if (hJobsKey != NULL)
308 RegCloseKey(hJobsKey);
309
310 return lError;
311 }
312
313
314 static
315 WORD
316 DaysOfMonth(
317 WORD wMonth,
318 WORD wYear)
319 {
320 WORD wDaysArray[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
321
322 if (wMonth == 2 && wYear % 4 == 0 && wYear % 400 != 0)
323 return 29;
324
325 return wDaysArray[wMonth];
326 }
327
328
329 VOID
330 CalculateNextStartTime(PJOB pJob)
331 {
332 SYSTEMTIME StartTime;
333 DWORD_PTR Now;
334
335 GetLocalTime(&StartTime);
336
337 Now = (DWORD_PTR)StartTime.wHour * 3600000 +
338 (DWORD_PTR)StartTime.wMinute * 60000;
339
340 StartTime.wMilliseconds = 0;
341 StartTime.wSecond = 0;
342 StartTime.wHour = (WORD)(pJob->JobTime / 3600000);
343 StartTime.wMinute = (WORD)((pJob->JobTime % 3600000) / 60000);
344
345 /* Start the job tomorrow */
346 if (Now > pJob->JobTime)
347 {
348 if (StartTime.wDay + 1 > DaysOfMonth(StartTime.wMonth, StartTime.wYear))
349 {
350 if (StartTime.wMonth == 12)
351 {
352 StartTime.wDay = 1;
353 StartTime.wMonth = 1;
354 StartTime.wYear++;
355 }
356 else
357 {
358 StartTime.wDay = 1;
359 StartTime.wMonth++;
360 }
361 }
362 else
363 {
364 StartTime.wDay++;
365 }
366 }
367
368 ERR("Next start: %02hu:%02hu %02hu.%02hu.%hu\n", StartTime.wHour,
369 StartTime.wMinute, StartTime.wDay, StartTime.wMonth, StartTime.wYear);
370
371 SystemTimeToFileTime(&StartTime, &pJob->StartTime);
372 }