|
|
//+---------------------------------------------------------------------------
//
// Scheduling Agent Service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: netsch.cxx
//
// Contents: Server-side Net Scheduler RPC implementation.
//
// Classes: None.
//
// RPC: NetrJobAdd
// NetrJobDel
// NetrJobEnum
// NetrJobGetInfo
//
// Functions: CreateAtJobPath
// GetAtJobIdFromFileName
// InitializeNetScheduleApi
// UninitializeNetScheduleApi
//
// History: 11-Nov-95 MarkBl Created.
// 02-Feb-01 JBenton Fixed BUG 303146 - 64bit pointer alignment problem
//
//----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "debug.hxx"
#include <align.h>
#include <apperr.h>
#include <lmerr.h>
#include <netevent.h>
extern "C" { #include <netlib.h>
} #include "atsvc.h"
#include "..\inc\resource.h"
#include "globals.hxx"
#include "svc_core.hxx"
#include "atsec.hxx"
#include "proto.hxx"
//
// Manifests below taken from the existing At service. Values must *not*
// change to maintain compatibility.
//
#define MAXIMUM_COMMAND_LENGTH (MAX_PATH - 1)
#define MAXIMUM_JOB_TIME (24 * 60 * 60 * 1000 - 1)
#define DAYS_OF_WEEK 0x7F // 7 bits for 7 days.
#define DAYS_OF_MONTH 0x7FFFFFFF // 31 bits for 31 days.
// This is not localized - it is a registry key (indirectly) from the At service
#define SCHEDULE_EVENTLOG_NAME TEXT("Schedule")
//
// Converts an HRESULT to a WIN32 status code. Masks off everything but
// the error code.
//
// BUGBUG : Review.
//
#define WIN32_FROM_HRESULT(x) (HRESULT_CODE(x))
//
// Minimum and maximum buffer size returned in an enumeration.
//
// 02/05/01-jbenton : this macro is used to a unicode buffer so must be even
// to avoid alignment problems (bug 303146).
#define BUFFER_LENGTH_MINIMUM (sizeof(AT_ENUM) + (MAXIMUM_COMMAND_LENGTH+1)*sizeof(WCHAR))
#define BUFFER_LENGTH_MAXIMUM 65536
//
// Ballpark maximum command string length.
//
// BUGBUG : Review this value.
//
#define COMMAND_STRING_LENGTH_APPROX (((MAX_PATH / 4) + 1) * sizeof(WCHAR))
#define ASTERISK_STR L"*"
#define BACKSLASH_STR L"\\"
void CreateAtJobPath(DWORD, WCHAR *, size_t); DWORD GetAtJobIdFromFileName(WCHAR *); void GetNextAtID(LPDWORD);
WCHAR * gpwszAtJobPathTemplate = NULL;
//+---------------------------------------------------------------------------
//
// RPC: NetrJobAdd
//
// Synopsis: Add a single At job.
//
// Arguments: [ServerName] -- Unused.
// [pAtInfo] -- New job information.
// [pJobId] -- Returned job id.
//
// Returns: BUGBUG : Problem mapping a HRESULT to WIN32. Masking off the
// facility & error bits is insufficient.
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS NetrJobAdd(ATSVC_HANDLE ServerName, LPAT_INFO pAtInfo, LPDWORD pJobId) { schDebugOut((DEB_ITRACE, "NetrJobAdd ServerName(%ws), pAtInfo(0x%x)\n", (ServerName != NULL) ? ServerName : L"(local)", pAtInfo));
UNREFERENCED_PARAMETER(ServerName);
NET_API_STATUS Status = NERR_Success;
Status = AtCheckSecurity(AT_JOB_ADD); if (Status != NERR_Success) { return ERROR_ACCESS_DENIED; }
//
// Validate arguments.
//
if ( (pAtInfo->Command == NULL) || (wcslen(pAtInfo->Command) > MAXIMUM_COMMAND_LENGTH) || (pAtInfo->JobTime > MAXIMUM_JOB_TIME) || (pAtInfo->DaysOfWeek & ~DAYS_OF_WEEK) || (pAtInfo->DaysOfMonth & ~DAYS_OF_MONTH) || (pAtInfo->Flags & ~JOB_INPUT_FLAGS)) { return(ERROR_INVALID_PARAMETER); }
//
// TBD : Logic to punt the submission if the service is paused.
//
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Have the global schedule instance add the At job.
//
HRESULT hr = g_pSched->m_pSch->AddAtJobWithHash(*pAtInfo, pJobId);
if (FAILED(hr)) { //
// Convert the HRESULT to a WIN32 status code.
//
Status = WIN32_FROM_HRESULT(hr); }
LeaveCriticalSection(&gcsNetScheduleCritSection);
return(Status); }
//+---------------------------------------------------------------------------
//
// RPC: NetrJobDel
//
// Synopsis: Delete the At jobs in the range specified.
//
// Arguments: [ServerName] -- Unused.
// [MinJobId] -- Range lower bound, inclusive.
// [MaxJobId] -- Range upper bound, inclusive.
//
// Returns: NERR_Sucess
// ERROR_INVALID_PARAMETER
// APE_AT_ID_NOT_FOUND
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS NetrJobDel(ATSVC_HANDLE ServerName, DWORD MinJobId, DWORD MaxJobId) { schDebugOut((DEB_ITRACE, "NetrJobDel ServerName(%ws), MinJobId(%d), MaxJobId(%d)\n", (ServerName != NULL) ? ServerName : L"(local)", MinJobId, MaxJobId));
UNREFERENCED_PARAMETER(ServerName);
NET_API_STATUS Status;
Status = AtCheckSecurity(AT_JOB_DEL); if (Status != NERR_Success) { return ERROR_ACCESS_DENIED; }
//
// Validate range.
//
if (MinJobId > MaxJobId) { return(ERROR_INVALID_PARAMETER); }
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Delete the indicated At job objects from storage.
//
// NB : To maintain compatibility with the existing AT service, if at
// least one job is deleted successfully, return success; otherwise,
// return APE_ID_NOT_FOUND.
//
WCHAR wszPath[MAX_PATH + 1]; BOOL fJobDeleted = FALSE; HRESULT hr; BOOL fDeleteAll = FALSE;
//
// Test for delete-all; signaled by passing a MaxJobId of 0xffffffff.
//
if (MaxJobId == 0xffffffff) { //
// Get the actual maximum ID value (this fixes bug 55839).
//
GetNextAtID(&MaxJobId); fDeleteAll = TRUE; }
CJob * pJob = CJob::Create();
if (pJob) { for (DWORD i = MinJobId; i <= MaxJobId; i++) { CreateAtJobPath(i, wszPath, MAX_PATH + 1);
//
// Make sure this is really an AT job, and not one that's just
// named like one. Just load the fixed-length data and check for
// the at flag.
//
hr = pJob->LoadP(wszPath, 0, FALSE, FALSE);
if (SUCCEEDED(hr)) { DWORD rgFlags;
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE) { if (DeleteFile(wszPath)) { fJobDeleted = TRUE; } } } else { schDebugOut((DEB_IWARN, "LoadP(%S) hr=0x%x\n", wszPath, hr)); } } pJob->Release(); }
//
// If the user asked to delete all at jobs, reset the next id to 1
//
if (fDeleteAll) { (void) g_pSched->m_pSch->ResetAtID(); }
LeaveCriticalSection(&gcsNetScheduleCritSection);
Status = fJobDeleted ? NERR_Success : APE_AT_ID_NOT_FOUND;
return(Status); }
//+---------------------------------------------------------------------------
//
// RPC: NetrJobEnum
//
// Synopsis: Enumerate At jobs.
//
// Arguments: [ServerName] -- Unused.
// [pEnumContainer] -- Returned enumeration (AT_JOB_INFO
// array and size).
// [PreferredMaximumLength] -- Preferred buffer size maximum. If
// -1, allocate as needed.
// [pTotalEntries] -- Returns the total number of
// entries available.
// [pResumeHandle] -- Enumeration context. Indexes the
// the At jobs directory.
//
// Returns: BUGBUG : Problem here too with HRESULTs mapped to WIN32 status
// codes.
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS NetrJobEnum( ATSVC_HANDLE ServerName, LPAT_ENUM_CONTAINER pEnumContainer, DWORD PreferredMaximumLength, LPDWORD pTotalEntries, LPDWORD pResumeHandle) { schDebugOut((DEB_ITRACE, "NetrJobEnum ServerName(%ws), pEnumContainer(0x%x), " \ "PreferredMaximumLength(%d)\n", (ServerName != NULL) ? ServerName : L"(local)", pEnumContainer, PreferredMaximumLength));
UNREFERENCED_PARAMETER(ServerName);
WCHAR wszCommand[MAX_PATH + 1]; WIN32_FIND_DATA fd; NET_API_STATUS Status; HANDLE hFileFindContext; LPBYTE pbBuffer; LPBYTE pbStringsOffset; PAT_ENUM pAtEnum; DWORD cbBufferSize; DWORD cbCommandSize; DWORD cJobsEnumerated; DWORD iEnumContext; DWORD i; DWORD rgFlags; HRESULT hr;
Status = NERR_Success; pbBuffer = NULL; cJobsEnumerated = 0; i = 0;
//
// pEnumContainer is defined in the IDL file as [in,out] though it
// should only be [out]. This can't be changed in the IDL file for
// backwards compatibility, so check it here. Without this check,
// we'll leak memory if the user gives a non-NULL buffer.
//
if (pEnumContainer->Buffer != NULL) { return ERROR_INVALID_PARAMETER; }
Status = AtCheckSecurity(AT_JOB_ENUM); if (Status != NERR_Success) { return ERROR_ACCESS_DENIED; }
if (pResumeHandle != NULL) { iEnumContext = *pResumeHandle; } else { iEnumContext = 0; } //
// Allocate one job object that will be reused.
//
CJob * pJob = CJob::Create(); if (pJob == NULL) { return ERROR_NOT_ENOUGH_MEMORY; }
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Compute the total number of At jobs (i.e., the number of At jobs in
// the At subdirectory). This number is used to update the pTotalEntries
// argument, and may be used for enumeration buffer size computation.
//
DWORD cAtJobTotal = 0;
hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
if (hFileFindContext == INVALID_HANDLE_VALUE) { //
// Nothing to enumerate.
//
*pTotalEntries = 0; goto EnumExit; }
do { //
// If somebody renamed an At job, don't enumerate it. This is to
// prevent us from returning duplicate IDs as a result of finding jobs
// like At1.job and At01.job.
//
if (!IsValidAtFilename(fd.cFileName)) { continue; }
hr = LoadAtJob(pJob, fd.cFileName); if (FAILED(hr)) { // we don't want to return the job,
// but failing altogether is a little drastic
// ERR_OUT("NetrJobEnum: pJob->Load", hr);
// Status = WIN32_FROM_HRESULT(hr);
// FindClose(hFileFindContext);
// goto EnumExit;
continue; // skip it and go on
}
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE) { cAtJobTotal++; } } while (FindNextFile(hFileFindContext, &fd));
FindClose(hFileFindContext);
if (!cAtJobTotal) { //
// Nothing to enumerate.
//
*pTotalEntries = 0; goto EnumExit; }
//
// Get buffer size.
//
if (PreferredMaximumLength != -1) { //
// Caller has specified a preferred buffer size.
//
// 02/05/01-jbenton : buffer size must be even to avoid
// alignment errors. (bug 303146).
cbBufferSize = ROUND_DOWN_COUNT(PreferredMaximumLength, ALIGN_WCHAR); } else { //
// Compute a "best-guess" buffer size to return all of the data.
// If we underestimate the buffer size, we'll return as much data
// as the buffer allows, plus a return code of ERROR_MORE_DATA.
//
cbBufferSize = (sizeof(AT_ENUM) + COMMAND_STRING_LENGTH_APPROX) * cAtJobTotal; }
//
// Restrict buffer size.
//
cbBufferSize = (DWORD)max(cbBufferSize, BUFFER_LENGTH_MINIMUM); cbBufferSize = min(cbBufferSize, BUFFER_LENGTH_MAXIMUM);
//
// The enumeration context is utilized as an index in the find first/next
// file result. If non-zero, enumerate the directory until the number
// of AT jobs enumerated equals the caller's enumeration context.
//
// BUGBUG : This is quite a departure from the existing At service, but
// I'm confident it should not present a problem. Note for
// review.
//
// Seek to the enumeration context index.
//
hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
if (hFileFindContext == INVALID_HANDLE_VALUE) { //
// Nothing to enumerate.
//
*pTotalEntries = 0; goto EnumExit; }
i = 0; do { if (!IsValidAtFilename(fd.cFileName)) { continue; }
hr = LoadAtJob(pJob, fd.cFileName); if (FAILED(hr)) { // we don't want to return the job,
// but failing altogether is a little drastic
// ERR_OUT("NetrJobEnum: pJob->Load", hr);
// Status = WIN32_FROM_HRESULT(hr);
// FindClose(hFileFindContext);
// goto EnumExit;
continue; // skip it and go on
}
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE) { i++;
if (i > iEnumContext) { break; } }
} while (FindNextFile(hFileFindContext, &fd));
if (i <= iEnumContext) { //
// The above enumeration seek failed to find any more AT jobs
// beyond the Resume handle count. Thus, the enumeration is
// complete. Nothing else to enumerate.
//
FindClose(hFileFindContext); *pTotalEntries = 0; goto EnumExit; }
//
// Update pTotalEntries argument. It is the difference between the total
// number of jobs and the number of jobs previously enumerated.
//
*pTotalEntries = cAtJobTotal - i + 1;
pbBuffer = (LPBYTE)MIDL_user_allocate(cbBufferSize);
if (pbBuffer == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; CHECK_HRESULT(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)); goto EnumExit; }
//
// Begin the enumeration.
//
pbStringsOffset = pbBuffer + cbBufferSize; pAtEnum = (PAT_ENUM)pbBuffer;
//
// To have arrived here, the resume handle seek above will have left us
// a valid AT job object in pJob and the corresponding rgFlags.
//
do { if (rgFlags & JOB_I_FLAG_NET_SCHEDULE) { if (pbStringsOffset <= (LPBYTE)pAtEnum + sizeof(AT_ENUM)) { //
// Buffer full.
//
Status = ERROR_MORE_DATA; break; }
//
// Get At job information.
//
DWORD CommandSize = MAX_PATH + 1; AT_INFO AtInfo;
hr = pJob->GetAtInfo(&AtInfo, wszCommand, &CommandSize);
if (SUCCEEDED(hr)) { //
// Copy fixed portion.
//
pAtEnum->JobId = GetAtJobIdFromFileName(fd.cFileName); pAtEnum->JobTime = AtInfo.JobTime; pAtEnum->DaysOfMonth = AtInfo.DaysOfMonth; pAtEnum->DaysOfWeek = AtInfo.DaysOfWeek; pAtEnum->Flags = AtInfo.Flags;
//
// Copy variable data.
//
// whack one off of CommandSize
// because NetpCopyStringToBuffer doesn't want the NULL counted
BOOL fRet = NetpCopyStringToBuffer( wszCommand, CommandSize -1, (LPBYTE)(pAtEnum + 1), (LPWSTR *)&pbStringsOffset, &pAtEnum->Command);
if (!fRet) { Status = ERROR_MORE_DATA; break; }
pAtEnum++; cJobsEnumerated++; iEnumContext++; } }
//
// Get the next filename, skipping any that have been renamed
//
BOOL fFoundAnotherAtJob = FALSE;
do {
while (fFoundAnotherAtJob = FindNextFile(hFileFindContext, &fd)) { if (IsValidAtFilename(fd.cFileName)) { break; } }
if (!fFoundAnotherAtJob) { //
// No more files.
//
break; }
hr = LoadAtJob(pJob, fd.cFileName); } // we wish to continue doing this until we find a good one
while (FAILED(hr)); // but if we broke out without finding a good job above, we're done
if (!fFoundAnotherAtJob) break; pJob->GetAllFlags(&rgFlags);
} while (TRUE);
FindClose(hFileFindContext);
//
// Reset enumeration context if everything has been read.
//
if (Status == NERR_Success) { iEnumContext = 0; }
EnumExit:
LeaveCriticalSection(&gcsNetScheduleCritSection);
if (pJob) { pJob->Release(); }
pEnumContainer->EntriesRead = cJobsEnumerated;
if (cJobsEnumerated == 0 && pbBuffer != NULL) { MIDL_user_free(pbBuffer); pbBuffer = NULL; }
pEnumContainer->Buffer = (LPAT_ENUM)pbBuffer;
if (pResumeHandle != NULL) { *pResumeHandle = iEnumContext; }
return(Status); }
//+---------------------------------------------------------------------------
//
// RPC: NetrJobGetInfo
//
// Synopsis: Get information on an At job.
//
// Arguments: [ServerName] -- Unused.
// [JobId] -- Target At job.
// [ppAtInfo] -- Returned information.
//
// Returns: BUGBUG : Problem here too with HRESULTs mapped to WIN32 status
// codes.
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS NetrJobGetInfo(ATSVC_HANDLE ServerName, DWORD JobId, LPAT_INFO * ppAtInfo) { schDebugOut((DEB_ITRACE, "NetrJobGetInfo ServerName(%ws), JobId(%d)\n", (ServerName != NULL) ? ServerName : L"(local)", JobId));
UNREFERENCED_PARAMETER(ServerName);
AT_INFO AtInfo; PAT_INFO pAtInfo; NET_API_STATUS Status; WCHAR wszPath[MAX_PATH + 1]; WCHAR wszCommand[MAX_PATH + 1]; WCHAR wszJobId[10 + 1]; DWORD CommandSize; HRESULT hr;
Status = NERR_Success; pAtInfo = NULL;
Status = AtCheckSecurity(AT_JOB_GET_INFO); if (Status != NERR_Success) { return ERROR_ACCESS_DENIED; }
//
// Create the file name from the ID.
//
CreateAtJobPath(JobId, wszPath, MAX_PATH + 1); schDebugOut((DEB_ITRACE, "At job name: %S\n", wszPath));
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Ensure the job object exists in storage.
//
if (GetFileAttributes(wszPath) == -1 && GetLastError() == ERROR_FILE_NOT_FOUND) { //
// Job object does not exist.
//
Status = APE_AT_ID_NOT_FOUND; CHECK_HRESULT(HRESULT_FROM_WIN32(Status)); goto GetInfoExit; }
//
// Command size. A character count throughout the call to GetAtJob;
// a byte count thereafter.
//
CommandSize = MAX_PATH + 1;
//
// Get At job information.
//
hr = g_pSched->m_pSch->GetAtJob(wszPath, &AtInfo, wszCommand, &CommandSize);
if (FAILED(hr)) { //
// Convert the HRESULT to a WIN32 status code.
//
Status = WIN32_FROM_HRESULT(hr);
if (Status == ERROR_FILE_NOT_FOUND) { Status = APE_AT_ID_NOT_FOUND; } goto GetInfoExit; }
CommandSize *= sizeof(WCHAR); // Character count -> Byte count
pAtInfo = (PAT_INFO)MIDL_user_allocate(sizeof(AT_INFO) + CommandSize);
if (pAtInfo == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; CHECK_HRESULT(HRESULT_FROM_WIN32(Status)); goto GetInfoExit; }
*pAtInfo = AtInfo;
pAtInfo->Command = (LPWSTR)(pAtInfo + 1);
CopyMemory(pAtInfo->Command, wszCommand, CommandSize);
GetInfoExit:
LeaveCriticalSection(&gcsNetScheduleCritSection);
*ppAtInfo = pAtInfo;
return(Status); }
//+---------------------------------------------------------------------------
//
// Function: GetAtJobIdFromFileName
//
// Synopsis: Return the DWORD At job id from an At filename. At filenames
// are named according the following convention: "At<nnnn>.Job".
// The "<nnnn>" portion is the At job id in string form.
// eg: "At132.Job"
//
// Arguments: [pwszAtFileName] -- At path/filename.
//
// Returns: Non-zero At job id.
// Zero if the filename is not recognized as an At filename.
//
// Notes: None.
//
//----------------------------------------------------------------------------
DWORD GetAtJobIdFromFileName(WCHAR * pwszAtFileName) { static ULONG ccAtJobFilenamePrefix = 0;
schAssert(pwszAtFileName != NULL);
if (ccAtJobFilenamePrefix == 0) { ccAtJobFilenamePrefix = ARRAY_LEN(TSZ_AT_JOB_PREFIX) - 1; }
//
// Refer to the last (right-most) path element.
//
WCHAR * pwsz = wcsrchr(pwszAtFileName, L'\\');
if (pwsz == NULL) { pwsz = pwszAtFileName; }
//
// Skip past the "At" filename portion.
//
if (_wcsnicmp(pwsz, TSZ_AT_JOB_PREFIX, ccAtJobFilenamePrefix) == 0) { pwsz += ccAtJobFilenamePrefix; } else { //
// Unknown filename. At least, it's known if this is an At job.
// Proceed no further.
//
return(0); }
//
// Isolate the At job Id portion of the path. Do so by temporarilly
// replacing the extension period character with a null character.
//
WCHAR * pwszExt = wcsrchr(pwsz, L'.');
if (pwszExt != NULL) { *pwszExt = L'\0'; }
//
// Convert the Id to integer from string form.
//
DWORD AtJobId = _wtol(pwsz);
//
// Restore period character.
//
if (pwszExt != NULL) { *pwszExt = L'.'; }
return(AtJobId); }
//+---------------------------------------------------------------------------
//
// Function: CreateAtJobPath
//
// Synopsis: Constructs a path in the form:
// "...\Jobs\At_Jobs\At<nnnn>.job"
// where <nnnn> is the At job id.
//
// Arguments: [JobId] -- At job Id.
// [pwszPath] -- Returned path.
// [cchBuff] -- size of path buffer
//
// Returns: None.
//
// Notes: None.
//
//----------------------------------------------------------------------------
void CreateAtJobPath(DWORD JobId, WCHAR* pwszPath, size_t cchBuff) { WCHAR wszJobId[10 + 1];
StringCchPrintf(wszJobId, 10 + 1, L"%d", JobId);
StringCchCopy(pwszPath, cchBuff, gpwszAtJobPathTemplate); StringCchCat(pwszPath, cchBuff, wszJobId); StringCchCat(pwszPath, cchBuff, TSZ_DOTJOB); }
//+---------------------------------------------------------------------------
//
// Function: InitializeNetScheduleApi
//
// Synopsis: Initializes globals used by the server-side NetScheduleXXX.
// Associated globals:
//
// gpwszAtJobPathTemplate -- Used to construct full paths to
// At jobs in the At jobs directory.
// (eg: "...\At_Jobs\At")
// gcsNetScheduleCritSection -- Used to serialize thread access
// to server-side NetScheduleXXX
// RPC.
//
// Arguments: None.
//
// Returns: S_OK
// E_OUTOFMEMORY
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT InitializeNetScheduleApi(void) { WCHAR wszBuffer[MAX_PATH + 1]; ULONG ccAtJobPathTemplate; HRESULT hr;
NET_API_STATUS Status;
Status = AtCreateSecurityObject();
if (Status != NERR_Success) { hr = Status; CHECK_HRESULT(hr); goto InitializeError; }
schAssert(g_TasksFolderInfo.ptszPath);
ULONG ccFolderPath; ccFolderPath = wcslen(g_TasksFolderInfo.ptszPath);
//
// Create the At job path template. For use in NetScheduleJobAdd/Del.
// Example: "<Job Folder Path>\At". To which the Job Id (string form) +
// the ".job" extension is appended.
//
ccAtJobPathTemplate = wcslen(g_TasksFolderInfo.ptszPath) + ARRAY_LEN(TSZ_AT_JOB_PREFIX) + 1; // '\' + null terminator
gpwszAtJobPathTemplate = new WCHAR[ccAtJobPathTemplate];
if (gpwszAtJobPathTemplate == NULL) { hr = E_OUTOFMEMORY; CHECK_HRESULT(hr); goto InitializeError; }
StringCchCopy(gpwszAtJobPathTemplate, ccAtJobPathTemplate, g_TasksFolderInfo.ptszPath); StringCchCat(gpwszAtJobPathTemplate, ccAtJobPathTemplate, BACKSLASH_STR TSZ_AT_JOB_PREFIX);
//
// Register the Event Source, which is used to report NetSchedule
// errors in the event log - for NT4 ATSVC compatibility
//
g_hAtEventSource = RegisterEventSource(NULL, SCHEDULE_EVENTLOG_NAME); if (g_hAtEventSource == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); CHECK_HRESULT(hr); goto InitializeError; }
return(S_OK);
InitializeError:
if (gpwszAtJobPathTemplate != NULL) { delete gpwszAtJobPathTemplate; gpwszAtJobPathTemplate = NULL; }
return(hr); }
//+---------------------------------------------------------------------------
//
// Function: UninitializeNetScheduleApi
//
// Synopsis: Un-does work done in InitializeNetScheduleApi.
//
// Arguments: None.
//
// Returns: None.
//
// Notes: None.
//
//----------------------------------------------------------------------------
void UninitializeNetScheduleApi(void) { //
// Clean up the event logging for downlevel jobs
//
if (g_hAtEventSource != NULL) { DeregisterEventSource(g_hAtEventSource); g_hAtEventSource = NULL; }
if (gpwszAtJobPathTemplate != NULL) { delete gpwszAtJobPathTemplate; gpwszAtJobPathTemplate = NULL; }
AtDeleteSecurityObject(); }
//+---------------------------------------------------------------------------
//
// Function: IsAdminFileOwner
//
// Synopsis: Ensure the file owner is an adminstrator. Currently used to
// determine if AT jobs are owned by administrators. Local system
// ownership is allowed as well.
//
// Arguments: [pwszFile] -- Checked file.
//
// Returns: TRUE -- The owner is an admin or local system.
// FALSE -- The owner isn't an admin or local system, or the
// attempt to confirm ownership identity failed.
//
// Notes: None.
//
//----------------------------------------------------------------------------
BOOL IsAdminFileOwner(LPCWSTR pwszFile) { #define SECDESCR_STACK_BUFFER_SIZE 512
BYTE rgbBuffer[SECDESCR_STACK_BUFFER_SIZE]; PSECURITY_DESCRIPTOR pOwnerSecDescr = rgbBuffer; DWORD cbSize = SECDESCR_STACK_BUFFER_SIZE; DWORD cbSizeNeeded = 0; BOOL fAllocatedBuffer;
if (GetFileSecurity(pwszFile, OWNER_SECURITY_INFORMATION, pOwnerSecDescr, cbSize, &cbSizeNeeded)) { //
// The information fit within the stack-allocated buffer.
// This should cover 90% of the cases.
//
} else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbSizeNeeded) { //
// Too much data. We'll need to allocate memory on the heap.
//
fAllocatedBuffer = TRUE; pOwnerSecDescr = (SECURITY_DESCRIPTOR *)new BYTE[cbSizeNeeded];
if (pOwnerSecDescr == NULL) { return FALSE; }
if (!GetFileSecurity(pwszFile, OWNER_SECURITY_INFORMATION, pOwnerSecDescr, cbSizeNeeded, &cbSizeNeeded)) { delete pOwnerSecDescr; return FALSE; } } else { //
// An unexpected error occurred. Disallow access.
//
return FALSE; }
//
// Get the owner sid.
//
PSID pOwnerSid; BOOL fOwnerDefaulted; BOOL fRet = FALSE;
if (GetSecurityDescriptorOwner(pOwnerSecDescr, &pOwnerSid, &fOwnerDefaulted)) { if (IsValidSid(pOwnerSid)) { //
// Enumerate the subauthorities to check for the admin RID.
//
for (DWORD i = *GetSidSubAuthorityCount(pOwnerSid); i; i--) { DWORD SubAuthority = *GetSidSubAuthority(pOwnerSid, i);
if (SubAuthority == DOMAIN_ALIAS_RID_ADMINS || SubAuthority == SECURITY_LOCAL_SYSTEM_RID) { //
// Done. Owner is an admin or local system.
//
fRet = TRUE; break; } } } }
if (pOwnerSecDescr != rgbBuffer) { delete pOwnerSecDescr; }
return fRet; }
|