mirror of
https://github.com/reactos/reactos.git
synced 2025-08-11 13:15:39 +00:00

Insert a job into the start list when it is loaded or added. Remove it from the start list when it gets deleted. The start list is sorted by start time. svn path=/trunk/; revision=74317
455 lines
12 KiB
C
455 lines
12 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Services
|
|
* FILE: base/services/schedsvc/job.c
|
|
* PURPOSE: Scheduling service
|
|
* PROGRAMMER: Eric Kohl <eric.kohl@reactos.org>
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(schedsvc);
|
|
|
|
|
|
/* GLOBALS ******************************************************************/
|
|
|
|
typedef struct _SCHEDULE
|
|
{
|
|
DWORD JobTime;
|
|
DWORD DaysOfMonth;
|
|
UCHAR DaysOfWeek;
|
|
UCHAR Flags;
|
|
WORD Reserved;
|
|
} SCHEDULE, PSCHEDULE;
|
|
|
|
DWORD dwNextJobId = 0;
|
|
DWORD dwJobCount = 0;
|
|
LIST_ENTRY JobListHead;
|
|
RTL_RESOURCE JobListLock;
|
|
|
|
LIST_ENTRY StartListHead;
|
|
RTL_RESOURCE StartListLock;
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static
|
|
VOID
|
|
GetJobName(
|
|
HKEY hJobsKey,
|
|
PWSTR pszJobName)
|
|
{
|
|
WCHAR szNameBuffer[9];
|
|
FILETIME SystemTime;
|
|
ULONG ulSeed, ulValue;
|
|
HKEY hKey;
|
|
LONG lError;
|
|
|
|
GetSystemTimeAsFileTime(&SystemTime);
|
|
ulSeed = SystemTime.dwLowDateTime;
|
|
for (;;)
|
|
{
|
|
ulValue = RtlRandomEx(&ulSeed);
|
|
swprintf(szNameBuffer, L"%08lx", ulValue);
|
|
|
|
hKey = NULL;
|
|
lError = RegOpenKeyEx(hJobsKey,
|
|
szNameBuffer,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (lError != ERROR_SUCCESS)
|
|
{
|
|
wcscpy(pszJobName, szNameBuffer);
|
|
return;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
|
|
LONG
|
|
SaveJob(
|
|
_In_ PJOB pJob)
|
|
{
|
|
SCHEDULE Schedule;
|
|
HKEY hJobsKey = NULL, hJobKey = NULL;
|
|
LONG lError;
|
|
|
|
TRACE("SaveJob()\n");
|
|
|
|
lError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\Schedule\\Jobs",
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hJobsKey,
|
|
NULL);
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
GetJobName(hJobsKey, pJob->Name);
|
|
|
|
lError = RegCreateKeyExW(hJobsKey,
|
|
pJob->Name,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hJobKey,
|
|
NULL);
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
Schedule.JobTime = pJob->JobTime;
|
|
Schedule.DaysOfMonth = pJob->DaysOfMonth;
|
|
Schedule.DaysOfWeek = pJob->DaysOfWeek;
|
|
Schedule.Flags = pJob->Flags;
|
|
|
|
lError = RegSetValueEx(hJobKey,
|
|
L"Schedule",
|
|
0,
|
|
REG_BINARY,
|
|
(PBYTE)&Schedule,
|
|
sizeof(Schedule));
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
lError = RegSetValueEx(hJobKey,
|
|
L"Command",
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE)pJob->Command,
|
|
(wcslen(pJob->Command) + 1) * sizeof(WCHAR));
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
done:
|
|
if (hJobKey != NULL)
|
|
RegCloseKey(hJobKey);
|
|
|
|
if (hJobsKey != NULL)
|
|
RegCloseKey(hJobsKey);
|
|
|
|
return lError;
|
|
}
|
|
|
|
|
|
LONG
|
|
DeleteJob(
|
|
_In_ PJOB pJob)
|
|
{
|
|
HKEY hJobsKey = NULL;
|
|
LONG lError;
|
|
|
|
TRACE("DeleteJob()\n");
|
|
|
|
lError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\Schedule\\Jobs",
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hJobsKey,
|
|
NULL);
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
lError = RegDeleteKey(hJobsKey,
|
|
pJob->Name);
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
done:
|
|
if (hJobsKey != NULL)
|
|
RegCloseKey(hJobsKey);
|
|
|
|
return lError;
|
|
}
|
|
|
|
|
|
LONG
|
|
LoadJobs(VOID)
|
|
{
|
|
SCHEDULE Schedule;
|
|
WCHAR szNameBuffer[32];
|
|
DWORD dwNameLength, dwIndex, dwSize;
|
|
HKEY hJobsKey = NULL, hJobKey = NULL;
|
|
PJOB pJob = NULL;
|
|
LONG lError;
|
|
|
|
TRACE("LoadJobs()\n");
|
|
|
|
lError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\Schedule\\Jobs",
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hJobsKey,
|
|
NULL);
|
|
if (lError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
for (dwIndex = 0; dwIndex < 1000; dwIndex++)
|
|
{
|
|
dwNameLength = 32;
|
|
lError = RegEnumKeyEx(hJobsKey,
|
|
dwIndex,
|
|
szNameBuffer,
|
|
&dwNameLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (lError != ERROR_SUCCESS)
|
|
{
|
|
lError = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("KeyName: %S\n", szNameBuffer);
|
|
|
|
lError = RegOpenKeyEx(hJobsKey,
|
|
szNameBuffer,
|
|
0,
|
|
KEY_READ,
|
|
&hJobKey);
|
|
if (lError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
dwSize = sizeof(SCHEDULE);
|
|
lError = RegQueryValueEx(hJobKey,
|
|
L"Schedule",
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)&Schedule,
|
|
&dwSize);
|
|
if (lError == ERROR_SUCCESS)
|
|
{
|
|
dwSize = 0;
|
|
RegQueryValueEx(hJobKey,
|
|
L"Command",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwSize);
|
|
if (dwSize != 0)
|
|
{
|
|
/* Allocate a new job object */
|
|
pJob = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(JOB) + dwSize - sizeof(WCHAR));
|
|
if (pJob == NULL)
|
|
{
|
|
lError = ERROR_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
lError = RegQueryValueEx(hJobKey,
|
|
L"Command",
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)pJob->Command,
|
|
&dwSize);
|
|
if (lError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
wcscpy(pJob->Name, szNameBuffer);
|
|
pJob->JobTime = Schedule.JobTime;
|
|
pJob->DaysOfMonth = Schedule.DaysOfMonth;
|
|
pJob->DaysOfWeek = Schedule.DaysOfWeek;
|
|
pJob->Flags = Schedule.Flags;
|
|
|
|
/* Acquire the job list lock exclusively */
|
|
RtlAcquireResourceExclusive(&JobListLock, TRUE);
|
|
|
|
/* Assign a new job ID */
|
|
pJob->JobId = dwNextJobId++;
|
|
dwJobCount++;
|
|
|
|
// Cancel the start timer
|
|
|
|
/* Append the new job to the job list */
|
|
InsertTailList(&JobListHead, &pJob->JobEntry);
|
|
|
|
/* Calculate the next start time */
|
|
CalculateNextStartTime(pJob);
|
|
|
|
/* Insert the job into the start list */
|
|
InsertJobIntoStartList(&StartListHead, pJob);
|
|
#if 0
|
|
DumpStartList(&StartListHead);
|
|
#endif
|
|
|
|
// Update the start timer
|
|
|
|
/* Release the job list lock */
|
|
RtlReleaseResource(&JobListLock);
|
|
|
|
pJob = NULL;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hJobKey);
|
|
hJobKey = NULL;
|
|
}
|
|
|
|
done:
|
|
if (pJob != NULL)
|
|
HeapFree(GetProcessHeap(), 0, pJob);
|
|
|
|
if (hJobKey != NULL)
|
|
RegCloseKey(hJobKey);
|
|
|
|
if (hJobsKey != NULL)
|
|
RegCloseKey(hJobsKey);
|
|
|
|
return lError;
|
|
}
|
|
|
|
|
|
static
|
|
WORD
|
|
DaysOfMonth(
|
|
WORD wMonth,
|
|
WORD wYear)
|
|
{
|
|
WORD wDaysArray[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
if (wMonth == 2 && wYear % 4 == 0 && wYear % 400 != 0)
|
|
return 29;
|
|
|
|
return wDaysArray[wMonth];
|
|
}
|
|
|
|
|
|
VOID
|
|
CalculateNextStartTime(PJOB pJob)
|
|
{
|
|
SYSTEMTIME StartTime;
|
|
FILETIME FileTime;
|
|
DWORD_PTR Now;
|
|
|
|
TRACE("CalculateNextStartTime(%p)\n", pJob);
|
|
|
|
GetLocalTime(&StartTime);
|
|
|
|
Now = (DWORD_PTR)StartTime.wHour * 3600000 +
|
|
(DWORD_PTR)StartTime.wMinute * 60000;
|
|
|
|
StartTime.wMilliseconds = 0;
|
|
StartTime.wSecond = 0;
|
|
StartTime.wHour = (WORD)(pJob->JobTime / 3600000);
|
|
StartTime.wMinute = (WORD)((pJob->JobTime % 3600000) / 60000);
|
|
|
|
/* Start the job tomorrow */
|
|
if (Now > pJob->JobTime)
|
|
{
|
|
if (StartTime.wDay + 1 > DaysOfMonth(StartTime.wMonth, StartTime.wYear))
|
|
{
|
|
if (StartTime.wMonth == 12)
|
|
{
|
|
StartTime.wDay = 1;
|
|
StartTime.wMonth = 1;
|
|
StartTime.wYear++;
|
|
}
|
|
else
|
|
{
|
|
StartTime.wDay = 1;
|
|
StartTime.wMonth++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StartTime.wDay++;
|
|
}
|
|
}
|
|
|
|
TRACE("Next start: %02hu:%02hu %02hu.%02hu.%hu\n", StartTime.wHour,
|
|
StartTime.wMinute, StartTime.wDay, StartTime.wMonth, StartTime.wYear);
|
|
|
|
SystemTimeToFileTime(&StartTime, &FileTime);
|
|
pJob->StartTime.u.LowPart = FileTime.dwLowDateTime;
|
|
pJob->StartTime.u.HighPart = FileTime.dwHighDateTime;
|
|
}
|
|
|
|
|
|
VOID
|
|
InsertJobIntoStartList(
|
|
_In_ PLIST_ENTRY StartListHead,
|
|
_In_ PJOB pJob)
|
|
{
|
|
PLIST_ENTRY CurrentEntry, PreviousEntry;
|
|
PJOB CurrentJob;
|
|
|
|
if (IsListEmpty(StartListHead))
|
|
{
|
|
InsertHeadList(StartListHead, &pJob->StartEntry);
|
|
return;
|
|
}
|
|
|
|
CurrentEntry = StartListHead->Flink;
|
|
while (CurrentEntry != StartListHead)
|
|
{
|
|
CurrentJob = CONTAINING_RECORD(CurrentEntry, JOB, StartEntry);
|
|
|
|
if ((CurrentEntry == StartListHead->Flink) &&
|
|
(pJob->StartTime.QuadPart < CurrentJob->StartTime.QuadPart))
|
|
{
|
|
/* Insert before the first entry */
|
|
InsertHeadList(StartListHead, &pJob->StartEntry);
|
|
return;
|
|
}
|
|
|
|
if (pJob->StartTime.QuadPart < CurrentJob->StartTime.QuadPart)
|
|
{
|
|
/* Insert between the previous and the current entry */
|
|
PreviousEntry = CurrentEntry->Blink;
|
|
pJob->StartEntry.Blink = PreviousEntry;
|
|
pJob->StartEntry.Flink = CurrentEntry;
|
|
PreviousEntry->Flink = &pJob->StartEntry;
|
|
CurrentEntry->Blink = &pJob->StartEntry;
|
|
return;
|
|
}
|
|
|
|
if ((CurrentEntry->Flink == StartListHead) &&
|
|
(pJob->StartTime.QuadPart >= CurrentJob->StartTime.QuadPart))
|
|
{
|
|
/* Insert after the last entry */
|
|
InsertTailList(StartListHead, &pJob->StartEntry);
|
|
return;
|
|
}
|
|
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpStartList(
|
|
_In_ PLIST_ENTRY StartListHead)
|
|
{
|
|
PLIST_ENTRY CurrentEntry;
|
|
PJOB CurrentJob;
|
|
|
|
CurrentEntry = StartListHead->Flink;
|
|
while (CurrentEntry != StartListHead)
|
|
{
|
|
CurrentJob = CONTAINING_RECORD(CurrentEntry, JOB, StartEntry);
|
|
|
|
TRACE("%3lu: %016I64x\n", CurrentJob->JobId, CurrentJob->StartTime.QuadPart);
|
|
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
}
|
|
}
|
|
|
|
/* EOF */
|