diff options
Diffstat (limited to 'private/net/svcdlls/at/server')
-rw-r--r-- | private/net/svcdlls/at/server/at.h | 384 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/atapi.c | 636 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/atdebug.c | 295 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/atenv.c | 176 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/atmain.c | 1060 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/atreg.c | 603 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/atsec.c | 246 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/attime.c | 355 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/makefile | 6 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/run.c | 242 | ||||
-rw-r--r-- | private/net/svcdlls/at/server/sources | 38 |
11 files changed, 4041 insertions, 0 deletions
diff --git a/private/net/svcdlls/at/server/at.h b/private/net/svcdlls/at/server/at.h new file mode 100644 index 000000000..fb49cf7a2 --- /dev/null +++ b/private/net/svcdlls/at/server/at.h @@ -0,0 +1,384 @@ +/*++ + +Copyright (c) 1987-92 Microsoft Corporation + +Module Name: + + at.h + +Abstract: + + Local include file for the SCHEDULE service. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + +#include <nt.h> // NT definitions +#include <ntrtl.h> // NT runtime library definitions +#include <nturtl.h> + +#include <netevent.h> +#include <windef.h> // Win32 type definitions +#include <winbase.h> // Win32 base API prototypes +#include <winsvc.h> // Win32 service control APIs +#include <winreg.h> // HKEY + +#include <lmcons.h> // LAN Manager common definitions +#include <lmerr.h> // LAN Manager network error definitions +#include <lmsname.h> // LAN Manager service names +#include <lmapibuf.h> // NetApiBufferFree + +#include <netlib.h> // LAN Man utility routines +#include <netlibnt.h> // NetpNtStatusToApiStatus +#include <netdebug.h> // NetpDbgPrint +#include <tstring.h> // Transitional string functions +#include <icanon.h> // I_Net canonicalize functions +#include <align.h> // ROUND_UP_COUNT macro + +#include <services.h> // LMSVCS_GLOBAL_DATA +#include <apperr.h> // APE_AT_ID_NOT_FOUND + +#include <rpc.h> // DataTypes and runtime APIs +#include <rpcutil.h> // Prototypes for MIDL user functions +#include <atsvc.h> // Generated by the MIDL complier + + +#if DBG +#define AT_DEBUG +#endif // DBG + + +// +// atmain.c will #include this file with ATDATA_ALLOCATE defined. +// That will cause each of these variables to be allocated. +// +#ifdef ATDATA_ALLOCATE +#define EXTERN +#define INIT( _x) = _x +#else +#define EXTERN extern +#define INIT( _x) +#endif + +// +// Server side data structures. +// + +typedef struct _AT_RECORD { + LIST_ENTRY RuntimeList; // queue this on a doubly linked list + LIST_ENTRY JobIdList; // queue this on a doubly linked list + LARGE_INTEGER Runtime; // next time to run (secs from 01.Jan.1970) + DWORD JobId; // unique job id + PWCHAR Name; // name of this task in registry + WORD CommandSize; // in bytes, including NULL terminator + WORD NameSize; // not really needed, but fill in to DWORD + + DWORD JobTime; // time of day to run, in miliseconds from midnight + DWORD DaysOfMonth; // bitmask for days of month to run + UCHAR DaysOfWeek; // bitmask for days of week to run + UCHAR Flags; // see lmat.h + UCHAR JobDay; // index of WeekDay or MonthDay when to run +#ifdef AT_DEBUG + UCHAR Debug; // for debugging purposes +#endif // AT_DEBUG + PWCHAR Command; // command & data to execute +} AT_RECORD, *PAT_RECORD, *LPAT_RECORD; + +typedef struct _AT_SCHEDULE { + DWORD JobTime; // time of day to run, in seconds from midnight + DWORD DaysOfMonth; // bitmask for days of month to run + UCHAR DaysOfWeek; // bitmask for days of week to run + UCHAR Flags; // see lmat.h + WORD Reserved; // padding, since registry pads them as well +} AT_SCHEDULE, *PAT_SCHEDULE, *LPAT_SCHEDULE; + + +typedef struct _AT_TIME { + LARGE_INTEGER LargeInteger; // time since Jan.01,1970 (in NT_TICK-s) + DWORD TickCount; // time since most recent boot (in WINDOWS_TICK-s) + DWORD CurrentTime; // time in 24h day, (in WINDOWS_TICK-s) + WORD CurrentYear; + WORD CurrentMonth; // January=1, February=2, ... + WORD CurrentDayOfWeek; // Monday=0, Tuesday=1, ... + WORD CurrentDay; // first=1, second=2, ... +} AT_TIME, *PAT_TIME, *LPAT_TIME; + + + +// +// Functions exported by atmain.c +// +VOID AtReportEvent( + IN WORD EventType, + IN DWORD MessageId, + IN WORD StringsCount, + IN LPWSTR * StringArray, + IN DWORD RawDataBufferLength OPTIONAL, + IN LPVOID RawDataBuffer + ); + + +// +// Functions exported by atenv.c +// +VOID AtSetEnvironment( LPSTARTUPINFO pStartupInfo); + +// +// Functions exported by attime.c +// +VOID AtCalculateRuntime( + IN OUT PAT_RECORD pRecord, + IN PAT_TIME pTime + ); +VOID AtTimeGetCurrents( IN OUT PAT_TIME pTime); +VOID AtTimeGet( OUT PAT_TIME pTime); + + +// +// Functions exported by atreg.c +// +DWORD AtCreateKey( PAT_RECORD pRecord); +BOOL AtDeleteKey( PAT_RECORD pRecord); +VOID AtInsertRecord( + PAT_RECORD pNewRecord, + DWORD QueueMask + ); +NET_API_STATUS AtMakeDataFromRegistry( IN PAT_TIME pTime); +BOOL AtPermitServerOperators( VOID); +VOID AtRemoveRecord( + PAT_RECORD pNewRecord, + DWORD QueueMask + ); +BOOL AtSystemInteractive( VOID); + + +// +// Functions exported by atsec.c +// +NET_API_STATUS +AtCheckSecurity( + ACCESS_MASK DesiredAccess + ); +NET_API_STATUS +AtCreateSecurityObject( + VOID + ); +NET_API_STATUS +AtDeleteSecurityObject( + VOID + ); + +#ifdef NOT_YET +// +// Is job enabled or disabled. +// +// This flag is not supported yet - i.e. code to disable (and then re-enable) +// jobs is missing. +// +#define JOB_IS_ENABLED 0x40 // set if enabled +#endif // NOT_YET + + + +// +// Bitmask used with AtGlobalTasks. They describe outstanding tasks to be +// done by the main schedule service thread once it wakes up. +// + +#define AT_SERVICE_SHUTDOWN 0x0004 + + +// +// Bitmask describing on what global queues we should operate on. +// + +#define RUNTIME_QUEUE 0x0001 +#define JOBID_QUEUE 0x0002 +#define BOTH_QUEUES (RUNTIME_QUEUE | JOBID_QUEUE) + + +// +// For a /NEXT type of program, JOB_CLEAR_WEEKDAY flag determines whether +// to clear WEEKLY or MONTHLY schedule. +// +#define JOB_CLEAR_WEEKDAY 0x80 // set if WEEKLY schedule + + +#define JOB_INVALID_DAY 0xFF + + + +#define MAXIMUM_COMMAND_LENGTH (MAX_PATH - 1) // == 259, cmd.exe uses this + +#define AT_REGISTRY_PATH L"System\\CurrentControlSet\\Services\\Schedule" +#define WINDOWS_REGISTRY_PATH L"System\\CurrentControlSet\\Control\\Windows" +#define WINDOWS_VALUE_NAME L"NoInteractiveServices" +#define INTERACTIVE_DESKTOP L"WinSta0\\Default" +#define LSA_REGISTRY_PATH L"System\\CurrentControlSet\\Control\\Lsa" +#define LSA_SUBMIT_CONTROL L"SubmitControl" +#define LSA_SERVER_OPERATORS 0x00000001 // can they submit jobs, etc. + +#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 + +#define AT_SCHEDULE_NAME L"Schedule" +#define AT_COMMAND_NAME L"Command" + +// +// WINDOWS_TICK is one milisecond, i.e. 1e-3 seconds +// NT_TICK is hundred nanoseconds, i.e. 1e-7 seconds +// +#define NT_TICKS_IN_WINDOWS_TICK 10000L +#define ONE_MINUTE_IN_NT_TICKS (60000L * NT_TICKS_IN_WINDOWS_TICK) +#define MAXIMUM_FINITE_SLEEP_TIME ((DWORD)-2) // in windows ticks +#define MAX_BUSY_TIMEOUT (300*1000L) // 5 MIN in WINDOWS_TICK-s +#define MAX_LAZY_TIMEOUT (3600*1000L); // 60 MIN in WINDOWS_TICK-s + +#define AT_WAIT_HINT_TIME 5000L + + +// +// The following are used only when we create new keys via NetrJobAdd. +// A user can edit registry directly and define key names larger than this. +// +#define AT_KEY_NAME_MAX_LEN 8 +#define AT_KEY_NAME_SIZE ((AT_KEY_NAME_MAX_LEN + 1) * sizeof( WCHAR)) + + + +// +// BUGBUG Need to define ERROR_SERVICE_PAUSED in one of common include +// BUGBUG files. This error means that request is denied because service +// BUGBUG is paused. For now, set it to 65535. +// +#define ERROR_SERVICE_PAUSED 0xFFFF + + + +// +// Object specific access masks +// + +#define AT_JOB_ADD 0x0001 +#define AT_JOB_DEL 0x0002 +#define AT_JOB_ENUM 0x0004 +#define AT_JOB_GET_INFO 0x0008 + + +// +// Defines used to indicate how far we managed to initialize the Schedule +// service before an error is encountered and the extent of clean up needed +// + +#define AT_EVENT_CREATED 0x00000001 +#define AT_QUEUES_CREATED 0x00000002 +#define AT_RPC_SERVER_STARTED 0x00000004 +#define AT_SECURITY_OBJECT_CREATED 0x00000008 + + +//-------------------------------------------------------------------// +// // +// Global variables // +// // +//-------------------------------------------------------------------// + + +EXTERN CRITICAL_SECTION AtGlobalCriticalSection; +EXTERN CRITICAL_SECTION AtGlobalProtectLogFile; +EXTERN DWORD AtGlobalTasks; +EXTERN SERVICE_STATUS AtGlobalServiceStatus; +EXTERN SERVICE_STATUS_HANDLE AtGlobalServiceStatusHandle; +EXTERN DWORD AtGlobalJobId; +EXTERN LIST_ENTRY AtGlobalRuntimeListHead; +EXTERN LIST_ENTRY AtGlobalJobIdListHead; +EXTERN HANDLE AtGlobalEvent; +EXTERN HANDLE AtGlobalLogFile; +EXTERN DWORD AtGlobalSeed; +EXTERN AT_TIME AtGlobalSleepTime; +EXTERN AT_TIME AtGlobalGetupTime; +EXTERN HANDLE AtGlobalEvent; +EXTERN DWORD AtGlobalSeed; +EXTERN HKEY AtGlobalKey; +EXTERN BOOL AtGlobalHaveWindowsKey; +EXTERN BOOL AtGlobalPermitServerOperators; +EXTERN HKEY AtGlobalWindowsKey; +EXTERN STARTUPINFO AtGlobalStartupInfo; +EXTERN CHAR AtGlobalDebugBuffer[ 1024]; // arbitrary + + +#ifdef AT_DEBUG + +EXTERN DWORD AtGlobalDebug; + +//////////////////////////////////////////////////////////////////////// +// +// Debug Definititions +// +//////////////////////////////////////////////////////////////////////// + +#define AT_DEBUG_MAIN 0x00000001 +#define AT_DEBUG_UTIL 0x00000002 +#define AT_DEBUG_CRITICAL 0x00000004 + +// +// Control bits. +// + +#define AT_TIMESTAMP 0x10000000 // TimeStamp each output line + +#define IF_DEBUG(Function) \ + if (AtGlobalDebug & AT_ ## Function) + +VOID AtDebugCreate( VOID); +VOID AtDebugDelete( VOID); + +VOID AtAssertFailed( + IN PVOID FailedAssertion, + IN PVOID FileName, + IN ULONG LineNumber, + IN PCHAR Message OPTIONAL + ); + +#define AtAssert( Predicate) \ + { \ + if (!(Predicate)) \ + AtAssertFailed( #Predicate, __FILE__, __LINE__, NULL ); \ + } + +VOID AtLogRoutine( + IN DWORD DebugFlag, + IN LPSTR Format, // PRINTF()-STYLE FORMAT STRING. + ... // OTHER ARGUMENTS ARE POSSIBLE. + ); +VOID AtLogRuntimeList( IN PCHAR Comment); +VOID AtLogTimeout( IN DWORD timeout); + +#define AtLog( _x_) AtLogRoutine _x_ + +#else // AT_DEBUG + +#define AtAssert( condition) + +#define AtLog( _x_) +#define AtLogRuntimeList( _x_) +#define AtLogTimeout( _x_) + +#endif // AT_DEBUG + + + +// +// SCHEDULE_EVENTLOG_NAME is the name of registry key (under EventLog service) +// used to interpret events for schedule service. +// + +#define SCHEDULE_EVENTLOG_NAME TEXT( "Schedule") diff --git a/private/net/svcdlls/at/server/atapi.c b/private/net/svcdlls/at/server/atapi.c new file mode 100644 index 000000000..0217221b2 --- /dev/null +++ b/private/net/svcdlls/at/server/atapi.c @@ -0,0 +1,636 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + atapi.c + +Abstract: + + This module contains the worker routines for all APIs implemented + in the Schedule service. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + +#include "at.h" + + +NET_API_STATUS NET_API_FUNCTION +NetrJobAdd( + IN LPCWSTR ServerName OPTIONAL, + IN LPAT_INFO pAtInfo, + OUT LPDWORD pJobId + ) +/*++ + +Routine Description: + + This function is the NetJobAdd entry point in the Schedule service. + Given the info about a new job, its creates a new job and returns the + id of the new job. + +Arguments: + + ServerName - IGNORED + pAtInfo - pointer to information about the job to be added + pJobId - pointer to id of the newly added job + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NET_API_STATUS status; + PAT_RECORD pRecord; + DWORD CommandLength; + DWORD CommandSize; + AT_TIME time; + + + UNREFERENCED_PARAMETER( ServerName); + + status = AtCheckSecurity( AT_JOB_ADD); + if ( status != NERR_Success) { + return( ERROR_ACCESS_DENIED); + } + + // + // Is it safe to calculate string length below? + // Should RPC supply string length parameter? + // Note that wcslen() returns length of UNICODE string in WCHAR-s. + // Thus storage needed for Command is (CommandLength+1)*sizeof(WCHAR) + // + CommandLength = wcslen( pAtInfo->Command); + + if ( ( CommandLength > MAXIMUM_COMMAND_LENGTH) || + ( pAtInfo->JobTime > MAXIMUM_JOB_TIME) || + ( pAtInfo->DaysOfWeek & ~DAYS_OF_WEEK) != 0 || + ( pAtInfo->DaysOfMonth & ~DAYS_OF_MONTH) != 0 || + ( pAtInfo->Flags & ~JOB_INPUT_FLAGS) != 0 ) { + return( ERROR_INVALID_PARAMETER); + } + + CommandSize = ( CommandLength + 1) * sizeof( WCHAR); + + pRecord = (PAT_RECORD)LocalAlloc( + LMEM_FIXED, + sizeof( AT_RECORD) + AT_KEY_NAME_SIZE + CommandSize + ); + if ( pRecord == NULL) { + return( ERROR_NOT_ENOUGH_MEMORY); + } + + pRecord->CommandSize = (WORD)CommandSize; + pRecord->NameSize = AT_KEY_NAME_SIZE; // max possible + + pRecord->JobTime = pAtInfo->JobTime; + pRecord->JobDay = JOB_INVALID_DAY; // the default +#ifdef AT_DEBUG + pRecord->Debug = 0; +#endif // AT_DEBUG + + pRecord->Name = (PWCHAR)( (PBYTE)pRecord + sizeof( AT_RECORD)); + memset( pRecord->Name, 0, AT_KEY_NAME_SIZE); + + pRecord->Command = (PWCHAR)( (PBYTE)&pRecord->Name[0] + AT_KEY_NAME_SIZE); + memcpy( pRecord->Command, pAtInfo->Command, CommandSize); + + EnterCriticalSection( &AtGlobalCriticalSection); + + AtLog(( AT_DEBUG_MAIN, "++JobAdd: Command=%ws\n", pRecord->Command)); + + AtTimeGet( &time); // needed in what follows + + if ( pAtInfo->Flags & JOB_ADD_CURRENT_DATE) { + pAtInfo->Flags &= ~JOB_ADD_CURRENT_DATE; + pAtInfo->DaysOfMonth |= 1 << ( time.CurrentDay - 1); + } + + pRecord->Flags = pAtInfo->Flags; + pRecord->DaysOfMonth = pAtInfo->DaysOfMonth; + pRecord->DaysOfWeek = pAtInfo->DaysOfWeek; + + // + // BUGBUG Should we have a more stringent test that makes sure + // BUGBUG that service state is SERVICE_RUNNING ? + // + if ( AtGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED) { + // + // BUGBUG Error ERROR_SERVICE_PAUSED is not defined properly for now. + // + status = ERROR_SERVICE_PAUSED; + goto error_exit; + } + ASSERT( AtGlobalServiceStatus.dwCurrentState == SERVICE_RUNNING); + + + *pJobId = pRecord->JobId = AtGlobalJobId++; + + status = AtCreateKey( pRecord); + if ( status != NERR_Success) { + LocalFree( pRecord); + goto error_exit; + } + + AtCalculateRuntime( pRecord, &time); + AtInsertRecord( pRecord, BOTH_QUEUES); + +error_exit: + AtLog(( AT_DEBUG_MAIN, "--JobAdd: Command=%ws status=%d JobId=%d\n", + pRecord->Command, status, pRecord->JobId)); + LeaveCriticalSection( &AtGlobalCriticalSection); + SetEvent( AtGlobalEvent); // to calculate new timeout + return( status); + +} // NetrJobAdd + + + + +NET_API_STATUS NET_API_FUNCTION +NetrJobDel( + IN LPCWSTR ServerName OPTIONAL, + IN DWORD MinJobId, + IN DWORD MaxJobId + ) +/*++ + +Routine Description: + + This function is the NetJobDel entry point in the Schedule service. + Given the minimum and maximum job id, this routines deletes all jobs + whose job id is greater than or equal to the minimum job id and + less than or equal to the maximum job id. + +Arguments: + + ServerName - IGNORED + MinJobId - minimum job id + MaxJobId - maximum job id + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NET_API_STATUS status; + PLIST_ENTRY pListEntry; + PAT_RECORD pRecord; + BOOL JobDeleted; + + UNREFERENCED_PARAMETER(ServerName); + + status = AtCheckSecurity( AT_JOB_DEL); + if ( status != NERR_Success) { + return( status); + } + + if ( MinJobId > MaxJobId) { + return( ERROR_INVALID_PARAMETER); + } + + EnterCriticalSection( &AtGlobalCriticalSection); + + AtLog(( AT_DEBUG_MAIN, "++JobDel: MinJobId=%d MaxJobId=%d\n", + MinJobId, MaxJobId)); + + for ( JobDeleted = FALSE, pListEntry = AtGlobalJobIdListHead.Flink; + pListEntry != &AtGlobalJobIdListHead; + NOTHING) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + JobIdList + ); + + if ( pRecord->JobId > MaxJobId) { + break; // JobId is too larger, we are done + } + + pListEntry = pListEntry->Flink; // actual iteration statement + + if ( pRecord->JobId < MinJobId) { + continue; // JobId is too small, look further + } + + JobDeleted = TRUE; + + AtDeleteKey( pRecord); + AtRemoveRecord( pRecord, BOTH_QUEUES); + (VOID)LocalFree( pRecord); + } + + status = JobDeleted == TRUE ? NERR_Success : APE_AT_ID_NOT_FOUND; + + AtLog(( AT_DEBUG_MAIN, "--JobDel: MinJobId=%d MaxJobId=%d status=%d\n", + MinJobId, MaxJobId, status)); + + LeaveCriticalSection( &AtGlobalCriticalSection); + SetEvent( AtGlobalEvent); // to calculate new timeout + return( status); + +} // NetrJobDelete + + + +NET_API_STATUS NET_API_FUNCTION +NetrJobEnum( + IN LPCWSTR ServerName OPTIONAL, + IN OUT LPAT_ENUM_CONTAINER pEnumContainer, + IN DWORD PreferredMaximumLength, + OUT LPDWORD TotalEntries, + IN OUT LPDWORD ResumeHandle OPTIONAL + ) +/*++ + +Routine Description: + + This function is the NetJobEnum entry point in the Schedule service. + It returns information about jobs starting with a job id given by + resume handle, or if resume handle is absent, starting with a job + with the lowest job id. + +Arguments: + + ServerName - IGNORED + + pEnumContainer - pointer to enumeration container which contains the + array of job information structures and size of that array. + + PreferedMaximumLength - Supplies the number of bytes of information + to return in the buffer. If this value is MAXULONG, all available + information will be returned. + + TotalEntries - Returns the total number of entries available. This value + is only valid if the return code is NERR_Success or ERROR_MORE_DATA. + + ResumeHandle - Supplies a handle to resume the enumeration from where it + left off the last time through. Returns the resume handle if return + code is ERROR_MORE_DATA. + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NET_API_STATUS status; + DWORD JobId; // resume job id + DWORD JobCount; + DWORD BufferSize; + PLIST_ENTRY pListEntry; + PAT_RECORD pRecord; + LPBYTE Buffer; + LPAT_ENUM pAtEnum; + LPBYTE StringBuffer; + DWORD EntriesRead; + BOOL success; + AT_TIME time; + + UNREFERENCED_PARAMETER(ServerName); + + status = AtCheckSecurity( AT_JOB_ENUM); + if ( status != NERR_Success) { + return( ERROR_ACCESS_DENIED); + } + + JobId = (ARGUMENT_PRESENT( ResumeHandle)) ? *ResumeHandle : 0; + Buffer = NULL; + EntriesRead = 0; + + + EnterCriticalSection( &AtGlobalCriticalSection); + + AtLog(( AT_DEBUG_MAIN, "++JobEnum: JobId=%d\n", JobId)); + + AtTimeGet( &time); // needed in what follows + + for ( JobCount = 0, BufferSize = 0, + pListEntry = AtGlobalJobIdListHead.Blink; + pListEntry != &AtGlobalJobIdListHead; + JobCount++, BufferSize += pRecord->CommandSize, + pListEntry = pListEntry->Blink) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + JobIdList + ); + + if ( pRecord->JobId < JobId) { + break; // reached first stale record + } + } + + + *TotalEntries = JobCount; + + if ( JobCount == 0) { + goto error_exit; + } + + if ( PreferredMaximumLength == -1) { + // + // If the caller has not specified a size, calculate a size + // that will hold the entire enumeration. + // + BufferSize += JobCount * sizeof( AT_ENUM); + + } else { + BufferSize = PreferredMaximumLength; + } + + Buffer = (LPBYTE)MIDL_user_allocate( BufferSize); + if ( Buffer == NULL) { + status = ERROR_NOT_ENOUGH_MEMORY; + goto error_exit; + } + + // + // When we arrive here "pListEntry" points either to the head of the + // list (if we enumerate from the beginning) or to the first stale + // record. + // + for ( pListEntry = pListEntry->Flink, pAtEnum = (PAT_ENUM)Buffer, + StringBuffer = Buffer + BufferSize; + pListEntry != &AtGlobalJobIdListHead; + pListEntry = pListEntry->Flink, pAtEnum++, + EntriesRead++) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + JobIdList + ); + + if ( StringBuffer <= (LPBYTE)pAtEnum + sizeof( AT_ENUM)) { + status = ERROR_MORE_DATA; + break; // the buffer is full + } + + pAtEnum->JobId = pRecord->JobId; + pAtEnum->JobTime = pRecord->JobTime; + pAtEnum->DaysOfMonth = pRecord->DaysOfMonth; + pAtEnum->DaysOfWeek = pRecord->DaysOfWeek; + pAtEnum->Flags = pRecord->Flags; + + if ( time.CurrentTime < pRecord->JobTime) { + pAtEnum->Flags |= JOB_RUNS_TODAY; + } + + success = NetpCopyStringToBuffer( + pRecord->Command, + pRecord->CommandSize / sizeof( WCHAR) - 1, + (LPBYTE)(pAtEnum+1), + (LPWSTR *)&StringBuffer, + &pAtEnum->Command + ); + + if ( success == FALSE) { + status = ERROR_MORE_DATA; + KdPrint(( "[Job] NetrJobEnum: Not enough room\n")); + break; + } + } + + if ( status == ERROR_MORE_DATA) { + JobId = pRecord->JobId; // JobId of first one we have not read + } else { + JobId = 0; // we have read everything, reset resume handle + } + +error_exit: + + AtLog(( AT_DEBUG_MAIN, "--JobEnum: JobId=%d\n", JobId)); + LeaveCriticalSection( &AtGlobalCriticalSection); + SetEvent( AtGlobalEvent); // to calculate new timeout + + pEnumContainer->EntriesRead = EntriesRead; + + if ( EntriesRead == 0 && Buffer != NULL) { + + MIDL_user_free( Buffer); + Buffer = NULL; + + } + + pEnumContainer->Buffer = (LPAT_ENUM)Buffer; + + if ( ARGUMENT_PRESENT( ResumeHandle)) { + *ResumeHandle = JobId; + } + + return( status); + +} // NetrJobEnum + + + +NET_API_STATUS NET_API_FUNCTION +NetrJobGetInfo( + IN LPCWSTR ServerName OPTIONAL, + IN DWORD JobId, + OUT LPAT_INFO * ppAtInfo + ) +/*++ + +Routine Description: + + This function is the NetJobGetInfo entry point in the Schedule service. + It returns information about a job corresponding to the supplied job id. + +Arguments: + + ServerName - IGNORED + JobId - job id of a job we are interested in + ppAtInfo - pointer to pointer to data about the job in question + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NET_API_STATUS status; + PAT_RECORD pRecord; + PLIST_ENTRY pListEntry; + LPAT_INFO pAtInfo; + AT_TIME time; + + UNREFERENCED_PARAMETER(ServerName); + +#ifdef AT_DEBUG + if ( JobId == 70503010) { + DbgUserBreakPoint(); + } +#endif // AT_DEBUG + + status = AtCheckSecurity( AT_JOB_GET_INFO); + if ( status != NERR_Success) { + return( ERROR_ACCESS_DENIED); + } + + pAtInfo = NULL; + + EnterCriticalSection( &AtGlobalCriticalSection); + AtLog(( AT_DEBUG_MAIN, "++JobGetInfo: JobId=%d\n", JobId)); + + AtTimeGet( &time); // needed in what follows + + for ( pListEntry = AtGlobalJobIdListHead.Flink; + pListEntry != &AtGlobalJobIdListHead; + pListEntry = pListEntry->Flink) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + JobIdList + ); + + if ( pRecord->JobId == JobId) { + break; + } + } + + if ( pListEntry == &AtGlobalJobIdListHead) { + status = APE_AT_ID_NOT_FOUND; + goto error_exit; + } + + pAtInfo = (PAT_INFO)MIDL_user_allocate( + sizeof( AT_INFO) + pRecord->CommandSize + ); + if ( pAtInfo == NULL) { + status = ERROR_NOT_ENOUGH_MEMORY; + goto error_exit; + } + + pAtInfo->JobTime = pRecord->JobTime; + pAtInfo->DaysOfMonth = pRecord->DaysOfMonth; + pAtInfo->DaysOfWeek = pRecord->DaysOfWeek; + pAtInfo->Flags = pRecord->Flags; + + if ( time.CurrentTime < pRecord->JobTime) { + pAtInfo->Flags |= JOB_RUNS_TODAY; + } + + pAtInfo->Command = (LPWSTR)( pAtInfo + 1); + + memcpy( pAtInfo->Command, pRecord->Command, pRecord->CommandSize); + +error_exit: + + AtLog(( AT_DEBUG_MAIN, "--JobGetInfo: JobId=%d\n", JobId)); + LeaveCriticalSection( &AtGlobalCriticalSection); + SetEvent( AtGlobalEvent); // to calculate new timeout + + *ppAtInfo = pAtInfo; + + return( status); + +} // NetrJobGetInfo + + +#ifdef NOT_YET + + +NET_API_STATUS NET_API_FUNCTION +NetrJobControl( + IN LPCWSTR ServerName OPTIONAL, + IN OUT LPJOB_CONTROL_INFO ControlInfo, + IN DWORD Opcode + ) +/*++ + +Routine Description: + + This function is the NetJobControl entry point in the Schedule service. + +Arguments: + + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +Comments: + + BUGBUG JobControl contains old stuff - need to clean it up & make it work. + +--*/ +{ + NET_API_STATUS status; + + UNREFERENCED_PARAMETER(ServerName); + + status = AtCheckSecurity( AT_JOB_CONTROL); + if ( status != NERR_Success) { + return( ERROR_ACCESS_DENIED); + } + + EnterCriticalSection( &AtGlobalCriticalSection); + + switch ( opcode) { + case ENABLE: + case DISABLE: + break; + default: + status = ERROR_INVALID_REQUEST; + goto error_exit; + } + + pJob = FindExactJob( pDelRequest); + if ( pJob == NULL) { + status = ERROR_JOB_NOT_FOUND; + goto error_exit; + } + + if ( opcode == pJob->State) { // case of stupid request + status = STATUS_SUCCESS; + goto error_exit; + } + + pJob->State = opcode; + DeleteJob( pJob); + + switch( opcode) { + case ENABLED: + // Evaluate the next run time and queue the job near the front + // of the list where all enabled jobs reside. + break; + case DISABLED: + // Optionally set next run time to infinity, then queue the job + // near the back of the list where all the disabled jobs reside. + break; + } + + status = STATUS_SUCCESS; + + + // Queue a job delete request to a registry queue. + + +error_exit: + + LeaveCriticalSection( &AtGlobalCriticalSection); + SetEvent( AtGlobalEvent); // to calculate new timeout + return( status); + +} // NetrJobControl + +#endif // NOT_YET + + diff --git a/private/net/svcdlls/at/server/atdebug.c b/private/net/svcdlls/at/server/atdebug.c new file mode 100644 index 000000000..e3d88fd6a --- /dev/null +++ b/private/net/svcdlls/at/server/atdebug.c @@ -0,0 +1,295 @@ +/*++ + +Copyright (c) 1991-1993 Microsoft Corporation + +Module Name: + + atdebug.c + +Abstract: + + Debugging module. + +Author: + + Vladimir Z. Vulovic 27 - July - 1993 + +Environment: + + User mode + +Revision History : + +--*/ + +#include "at.h" +#include <stdio.h> // vsprintf + +#ifdef AT_DEBUG + +#define ATSVC_LOG_FILE L"%SystemRoot%\\atsvc.log" + + +VOID AtDebugDelete( VOID) +{ + DeleteCriticalSection( &AtGlobalProtectLogFile); +} + + +VOID AtDebugCreate( VOID) +{ + WCHAR Buffer[ MAX_PATH]; + DWORD Length; + + AtGlobalLogFile = INVALID_HANDLE_VALUE; + AtGlobalDebug = (DWORD)(-1); // max debug by default + InitializeCriticalSection( &AtGlobalProtectLogFile ); + + // + // Length returned by ExpandEnvironmentalStrings includes terminating + // NULL byte. + // + Length = ExpandEnvironmentStrings( ATSVC_LOG_FILE, Buffer, sizeof( Buffer)); + if ( Length == 0) { + AtLog(( AT_DEBUG_CRITICAL, "Error=%d", GetLastError())); + return; + } + if ( Length > sizeof( Buffer) || Length != wcslen(Buffer) + 1) { + AtLog(( AT_DEBUG_CRITICAL, "Buffer=%x, Length = %d", Buffer, Length)); + return; + } + + AtGlobalLogFile = CreateFileW( Buffer, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if ( AtGlobalLogFile == INVALID_HANDLE_VALUE ) { + AtLog(( AT_DEBUG_CRITICAL, "Cannot open %ws", Buffer )); + return; + } + + // + // Position the log file at the end + // + (VOID) SetFilePointer( AtGlobalLogFile, + 0, + NULL, + FILE_END ); + +} + + +VOID AtLogRoutine( + IN DWORD DebugFlag, + IN LPSTR Format, + ... + ) + +{ + va_list arglist; + ULONG length; + DWORD BytesWritten; + + // + // If we aren't debugging this functionality, just return. + // + if ( DebugFlag != 0 && (AtGlobalDebug & DebugFlag) == 0 ) { + return; + } + + // + // vsprintf isn't multithreaded + we don't want to intermingle output + // from different threads. Therefore we can use just a single output + // debug buffer. + // + + EnterCriticalSection( &AtGlobalProtectLogFile ); + length = 0; + + // + // Handle the beginning of a new line. + // + // + + // + // Put the timestamp at the begining of the line. + // + IF_DEBUG( TIMESTAMP ) { + SYSTEMTIME SystemTime; + GetLocalTime( &SystemTime ); + length += (ULONG) sprintf( &AtGlobalDebugBuffer[length], + "%02u/%02u %02u:%02u:%02u ", + SystemTime.wMonth, + SystemTime.wDay, + SystemTime.wHour, + SystemTime.wMinute, + SystemTime.wSecond ); + } + + // + // Indicate the type of message on the line + // + { + char *Text; + + switch (DebugFlag) { + case AT_DEBUG_MAIN: + Text = "MAIN"; + break; + case AT_DEBUG_UTIL: + Text = "UTIL"; + break; + default: + Text = NULL; + break; + } + if ( Text != NULL ) { + length += (ULONG) sprintf( &AtGlobalDebugBuffer[length], "[%s] ", Text ); + } + } + + // + // Put a the information requested by the caller onto the line + // + + va_start( arglist, Format); + + length += (ULONG) vsprintf( &AtGlobalDebugBuffer[length], Format, arglist); + + va_end(arglist); + + AtAssert(length <= sizeof(AtGlobalDebugBuffer)); + + + // + // If the log file isn't open, just output to the debug terminal. + // + + if ( AtGlobalLogFile == INVALID_HANDLE_VALUE ) { + (void) DbgPrint( (PCH) AtGlobalDebugBuffer); + + // + // Write the debug info to the log file. + // + + } else { + if ( !WriteFile( AtGlobalLogFile, + AtGlobalDebugBuffer, + length, + &BytesWritten, + NULL ) ) { + (void) DbgPrint( (PCH) AtGlobalDebugBuffer); + } + + } + + LeaveCriticalSection( &AtGlobalProtectLogFile ); +} + +VOID AtAssertFailed( + IN PVOID FailedAssertion, + IN PVOID FileName, + IN ULONG LineNumber, + IN PCHAR Message OPTIONAL + ) +/*++ + + Have my own version of RtlAssert so debug versions of netlogon really assert on + free builds. + +--*/ +{ + char Response[ 2 ]; + + for ( ; ; ) { + DbgPrint( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", + Message ? Message : "", + FailedAssertion, + FileName, + LineNumber + ); + + DbgPrompt( "Break, Ignore, terminate Process, Sleep 30 seconds, or terminate Thread (bipst)? ", + Response, sizeof( Response)); + switch ( toupper(Response[0])) { + case 'B': + DbgBreakPoint(); + break; + case 'I': + return; + break; + case 'P': + NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL ); + break; + case 'S': + Sleep( 30000L); + break; + case 'T': + NtTerminateThread( NtCurrentThread(), STATUS_UNSUCCESSFUL ); + break; + } + } + + DbgBreakPoint(); + NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL ); +} + + +VOID AtLogRuntimeList( IN PCHAR Comment) +{ + PLIST_ENTRY pListEntry; + PAT_RECORD pRecord; + TIME_FIELDS TimeFields; + + AtLog(( AT_DEBUG_MAIN, "%s\n", Comment)); + + for ( pListEntry = AtGlobalRuntimeListHead.Flink; + pListEntry != &AtGlobalRuntimeListHead; + pListEntry = pListEntry->Flink) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + RuntimeList + ); + RtlTimeToTimeFields( &pRecord->Runtime, &TimeFields); + AtLog(( AT_DEBUG_MAIN, + "LogRecord: JobId=%d Command=%ws Runtime=%02u/%02u %02u:%02u:%02u\n", + pRecord->JobId, + pRecord->Command, + TimeFields.Month, + TimeFields.Day, + TimeFields.Hour, + TimeFields.Minute, + TimeFields.Second + )); + } +} + +VOID AtLogTimeout( IN DWORD timeout) +{ + int Second, Minute, Hour, Day; + Second = timeout / 1000; + Minute = Second / 60; + Second -= Minute * 60; + Hour = Minute / 60; + Minute -= Hour * 60; + Day = Hour / 24; + Hour -= Day * 24; + AtLog(( AT_DEBUG_MAIN, + "Sleep for: 00/%02u %02u:%02u:%02u\n", + Day, + Hour, + Minute, + Second + )); +} + +#endif // AT_DEBUG + + + diff --git a/private/net/svcdlls/at/server/atenv.c b/private/net/svcdlls/at/server/atenv.c new file mode 100644 index 000000000..c82980279 --- /dev/null +++ b/private/net/svcdlls/at/server/atenv.c @@ -0,0 +1,176 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + atenv.c + +Abstract: + + Initializes all system and some user environment variables. Code is + borrowed from "shell\library\regenv.c" & "user\winlogon\usrenv.c". + + Unlike shell & user code here we do not attempt to set env variables + related to user profile. It is harder to obtain user profile for + Schedule service than for the interactively logged on user. And, amount + of work is too big one month away from Daytona ship date. + +Author: + + Vladimir Z. Vulovic (vladimv) 23 - June - 1994 + +Environment: + + User Mode - Win32 + +Revision History: + + 23-Jun-1994 vladimv + Created + +--*/ + +#include "at.h" + +#define COMPUTERNAME_VARIABLE TEXT("COMPUTERNAME") +#define USERNAME_VARIABLE TEXT("USERNAME") +#define USERDOMAIN_VARIABLE TEXT("USERDOMAIN") +#define OS_VARIABLE TEXT("OS") +#define PROCESSOR_VARIABLE TEXT("PROCESSOR_ARCHITECTURE") +#define PROCESSOR_LEVEL_VARIABLE TEXT("PROCESSOR_LEVEL") + +DBGSTATIC BOOL +GetUserNameAndDomain( + OUT LPTSTR * UserName, + OUT LPTSTR * UserDomain + ) +{ + HANDLE hToken; + DWORD cbTokenBuffer = 0; + PTOKEN_USER pUserToken; + LPTSTR lpUserName = NULL; + LPTSTR lpUserDomain = NULL; + DWORD cbAccountName = 0; + DWORD cbUserDomain = 0; + SID_NAME_USE SidNameUse; + BOOL bRet = FALSE; + + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_QUERY, + &hToken) ){ + return(FALSE); + } + + // + // Get space needed for token information + // + if (!GetTokenInformation(hToken, + TokenUser, + NULL, + 0, + &cbTokenBuffer) ) { + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + CloseHandle(hToken); + return(FALSE); + } + } + + // + // Get the actual token information + // + pUserToken = (PTOKEN_USER)LocalAlloc(LPTR, cbTokenBuffer*sizeof(WCHAR)); + if (pUserToken == NULL) { + CloseHandle(hToken); + return(FALSE); + } + + if (!GetTokenInformation(hToken, + TokenUser, + pUserToken, + cbTokenBuffer, + &cbTokenBuffer) ) { + goto Error; + } + + // + // Get the space needed for the User name and the Domain name + // + if (!LookupAccountSid(NULL, + pUserToken->User.Sid, + NULL, &cbAccountName, + NULL, &cbUserDomain, + &SidNameUse + ) ) { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + goto Error; + } + } + lpUserName = (LPTSTR)LocalAlloc(LPTR, cbAccountName*sizeof(WCHAR)); + if (!lpUserName) { + goto Error; + } + + lpUserDomain = (LPTSTR)LocalAlloc(LPTR, cbUserDomain*sizeof(WCHAR)); + if (!lpUserDomain) { + LocalFree(lpUserName); + goto Error; + } + + // + // Now get the user name and domain name + // + if (!LookupAccountSid(NULL, + pUserToken->User.Sid, + lpUserName, &cbAccountName, + lpUserDomain, &cbUserDomain, + &SidNameUse + ) ) { + + LocalFree(lpUserName); + LocalFree(lpUserDomain); + goto Error; + } + + *UserName = lpUserName; + *UserDomain = lpUserDomain; + bRet = TRUE; + +Error: + LocalFree(pUserToken); + CloseHandle(hToken); + + return(bRet); +} + + + +VOID AtSetEnvironment( LPSTARTUPINFO pStartupInfo) +/*++ + Get startup info & set the environment for us & our children. +--*/ +{ + LPTSTR UserName = NULL; + LPTSTR UserDomain = NULL; + TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1]; + DWORD dwComputerNameSize = MAX_COMPUTERNAME_LENGTH+1; + + GetStartupInfo( pStartupInfo); + pStartupInfo->lpTitle = NULL; + + // + // Changes to the initial environment made in user\winlogon\usrenv.c + // should be reflected below. In particular, when processor types + // are added in usrenv.c, they also need to be added here. + // + + if (GetComputerName (szComputerName, &dwComputerNameSize)) { + SetEnvironmentVariable(COMPUTERNAME_VARIABLE, (LPTSTR) szComputerName); + } + GetUserNameAndDomain(&UserName, &UserDomain); + SetEnvironmentVariable( USERNAME_VARIABLE, UserName); + SetEnvironmentVariable( USERDOMAIN_VARIABLE, UserDomain); + LocalFree( UserName); + LocalFree( UserDomain); +} diff --git a/private/net/svcdlls/at/server/atmain.c b/private/net/svcdlls/at/server/atmain.c new file mode 100644 index 000000000..5f326a12e --- /dev/null +++ b/private/net/svcdlls/at/server/atmain.c @@ -0,0 +1,1060 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + atmain.c + +Abstract: + + This is the main routine for the NT LAN Manager Schedule service. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Environment: + + User Mode - Win32 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + +#define ATDATA_ALLOCATE +#include "at.h" +#undef ATDATA_ALLOCATE +#include <atnames.h> // AT_INTERFACE_NAME +#include <winuser.h> // for winuserp.h +#include <wingdi.h> +#include <..\..\..\..\windows\inc\winuserp.h> // for STARTF_DESKTOPINHERIT + + +typedef enum _AT_ERROR_CONDITION { + AtErrorRegisterControlHandler = 0, + AtErrorCreateEvent, + AtErrorNotifyServiceController, + AtErrorStartRpcServer, + AtErrorCreateSecurityObject, + AtErrorMakeDataFromRegistry +} AT_ERROR_CONDITION, *PAT_ERROR_CONDITION; + + + + +DBGSTATIC NET_API_STATUS AtUpdateStatus( VOID) +/*++ + +Routine Description: + + This function updates the Schedule service status with the Service + Controller. + +Arguments: + + None. + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NET_API_STATUS status = NERR_Success; + + if (AtGlobalServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) { + AtLog(( AT_DEBUG_MAIN, "Cannot call SetServiceStatus, no status handle.\n")); + return( ERROR_INVALID_HANDLE); + } + + if ( ! SetServiceStatus( AtGlobalServiceStatusHandle, &AtGlobalServiceStatus)) { + status = GetLastError(); + AtLog(( AT_DEBUG_MAIN, "SetServiceStatus error %lu\n", status)); + } + + return( status); +} + + + +DBGSTATIC VOID AtShutdown( + IN NET_API_STATUS ErrorCode, + IN DWORD initState + ) +/*++ + +Routine Description: + + This function shuts down the Schedule service. + +Arguments: + + ErrorCode - Supplies the error code of the failure + + initState - Supplies a flag to indicate how far we got with initializing + the Schedule service before an error occured, thus the amount of + clean up needed. + +Return Value: + + None. + +Note: + + BUGBUG There is a possible race condition between main thread shutting + BUGBUG down & some of the rpc-api thread trying to complete their work. + +--*/ +{ + // + // Service stop still pending. Update checkpoint counter and the + // status with the Service Controller. + // + (AtGlobalServiceStatus.dwCheckPoint)++; + (VOID) AtUpdateStatus(); + + + if ( initState & AT_RPC_SERVER_STARTED) { + // + // Stop the RPC server + // + NetpStopRpcServer( atsvc_ServerIfHandle); + } + + if ( initState & AT_SECURITY_OBJECT_CREATED) { + // + // Destroy schedule security object + // + AtDeleteSecurityObject(); + } + + // + // Service stop still pending. Update checkpoint counter and the + // status with the Service Controller. + // + (AtGlobalServiceStatus.dwCheckPoint)++; + (VOID) AtUpdateStatus(); + + if ( initState & AT_EVENT_CREATED) { + // + // Close handle to the event. Note that we do not need to signal + // the event. We assume AtShutdown() will be called from the main + // schedule service thread and only when it is safe - i.e. when + // main service thread is outside its main loop. This avoids some + // ugly competition between main thread & shutdown. + // + CloseHandle( AtGlobalEvent); + } + + // + // We are done with cleaning up. Tell Service Controller that we are + // stopped. + // + AtGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED; + AtGlobalServiceStatus.dwControlsAccepted = 0; + + + SET_SERVICE_EXITCODE( + ErrorCode, + AtGlobalServiceStatus.dwWin32ExitCode, + AtGlobalServiceStatus.dwServiceSpecificExitCode + ); + + AtGlobalServiceStatus.dwCheckPoint = 0; + AtGlobalServiceStatus.dwWaitHint = 0; + + (VOID) AtUpdateStatus(); +} + + + +DBGSTATIC VOID AtHandleError( + IN AT_ERROR_CONDITION failingCondition, + IN NET_API_STATUS status, + IN DWORD initState + ) +/*++ + +Routine Description: + + This function handles a Schedule service error condition. If the error + condition is fatal, it shuts down the Schedule service. + +Arguments: + + failingCondition - Supplies a value which indicates what the failure is. + + status - Supplies the status code for the failure. + + initState - Supplies a flag to indicate how far we got with initializing + the Schedule service before an error occured, thus the amount of + clean up needed. + +Return Value: + + None. + +--*/ +{ + +#ifdef AT_DEBUG + + PCHAR string; + + switch (failingCondition) { + + case AtErrorRegisterControlHandler: + string = "Cannot register control handler"; + break; + + case AtErrorCreateEvent: + string = "Cannot create event"; + break; + + case AtErrorNotifyServiceController: + string = "SetServiceStatus error"; + break; + + case AtErrorStartRpcServer: + string = "Cannot start RPC server"; + break; + + case AtErrorCreateSecurityObject: + string = "Error in creating security object"; + break; + + case AtErrorMakeDataFromRegistry: + string = "Severe error while initializing from registry"; + break; + + default: + string = "Unknown error condition %lu\n"; + NetpAssert(FALSE); + } + + NetpKdPrint(( + "[Job] %s " FORMAT_API_STATUS "\n", + string, + status + )); +#endif // AT_DEBUG + + AtShutdown( status, initState); +} + + +VOID AtControlHandler( IN DWORD Opcode) +/*++ + +Routine Description: + + This is the service control handler of the Schedule service. + +Arguments: + + Opcode - Supplies a value which specifies the action for the Schedule + service to perform. + +Return Value: + + None. + +Comments: + + Note that AtGlobalCriticalSection is used to protect changes both to + AtGlobalTasks & AtGlobalServiceStatus.dwCurrentState. + +--*/ +{ + AtLog(( AT_DEBUG_MAIN, "In Control Handler\n")); + + switch (Opcode) { + + case SERVICE_CONTROL_PAUSE: + + // Take critical section and set status so that no new requests + // can be submitted. Current requests will still be honored. + // Then release critical seciton. + + EnterCriticalSection( &AtGlobalCriticalSection); + AtGlobalServiceStatus.dwCurrentState = SERVICE_PAUSED; + LeaveCriticalSection( &AtGlobalCriticalSection); + + break; + + case SERVICE_CONTROL_CONTINUE: + + // Take critical section and set status so that new requests + // can be submitted. Then release critical seciton. + + EnterCriticalSection( &AtGlobalCriticalSection); + AtGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING; + LeaveCriticalSection( &AtGlobalCriticalSection); + + break; + + case SERVICE_CONTROL_STOP: + + if (AtGlobalServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) { + + AtLog(( AT_DEBUG_MAIN, "Stopping schedule service...\n")); + + AtGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + AtGlobalServiceStatus.dwCheckPoint = 1; + AtGlobalServiceStatus.dwWaitHint = AT_WAIT_HINT_TIME; + + // + // Send the status response. + // + (VOID) AtUpdateStatus(); + + EnterCriticalSection( &AtGlobalCriticalSection); + AtGlobalTasks |= AT_SERVICE_SHUTDOWN; + LeaveCriticalSection( &AtGlobalCriticalSection); + + if (! SetEvent( AtGlobalEvent)) { + AtLog(( AT_DEBUG_CRITICAL, "Error setting the event 0x%x\n", GetLastError())); + AtAssert( FALSE); + } + + return; + } + break; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + AtLog(( AT_DEBUG_CRITICAL, "Unknown schedule service opcode 0x%x\n", Opcode)); + break; + } + + // + // Send the status response. + // + (VOID) AtUpdateStatus(); +} + + + +DBGSTATIC NET_API_STATUS AtInitialize( + IN PAT_TIME pTime, + OUT LPDWORD pInitState + ) +/*++ + +Routine Description: + + This function initializes the Schedule service. + +Arguments: + + pInitState - Returns a flag to indicate how far we got with initializing + the Schedule service before an error occured. + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NET_API_STATUS status; + + + // + // Initialize all the status fields so that subsequent calls to + // SetServiceStatus need to only update fields that changed. + // + + AtGlobalServiceStatus.dwServiceType = SERVICE_WIN32; + AtGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING; + AtGlobalServiceStatus.dwControlsAccepted = 0; + AtGlobalServiceStatus.dwCheckPoint = 1; + AtGlobalServiceStatus.dwWaitHint = AT_WAIT_HINT_TIME; + + SET_SERVICE_EXITCODE( + NO_ERROR, + AtGlobalServiceStatus.dwWin32ExitCode, + AtGlobalServiceStatus.dwServiceSpecificExitCode + ); + + // + // Initialize schedule service to receive service requests by registering + // the control handler. + // + if ((AtGlobalServiceStatusHandle = RegisterServiceCtrlHandler( + SERVICE_SCHEDULE, + AtControlHandler + )) == (SERVICE_STATUS_HANDLE) NULL) { + + status = GetLastError(); + AtHandleError( AtErrorRegisterControlHandler, status, *pInitState); + return( status); + } + + // + // Read the registry path & build queue of things to do. + // + status = AtMakeDataFromRegistry( pTime); + if ( status != NERR_Success) { + AtHandleError( AtErrorMakeDataFromRegistry, status, *pInitState); + return( status); + } + (*pInitState) |= AT_QUEUES_CREATED; + + AtGlobalPermitServerOperators = AtPermitServerOperators(); + + // + // Create the only event used by schedule service. + // + if ((AtGlobalEvent = + CreateEvent( + NULL, // Event attributes + FALSE, // Event will be automaticallly reset + FALSE, + NULL // Initial state not signalled + )) == NULL) { + + status = GetLastError(); + AtHandleError( AtErrorCreateEvent, status, *pInitState); + return( status); + } + (*pInitState) |= AT_EVENT_CREATED; + + + // + // Notify the Service Controller for the first time that we are alive + // and we are start pending + // + if ((status = AtUpdateStatus()) != NERR_Success) { + + AtHandleError( AtErrorNotifyServiceController, status, *pInitState); + return( status); + } + + AtSetEnvironment( &AtGlobalStartupInfo); + + // + // Create Schedule service security object + // + if ((status = AtCreateSecurityObject()) != NERR_Success) { + + AtHandleError( AtErrorCreateSecurityObject, status, *pInitState); + return( status); + } + (*pInitState) |= AT_SECURITY_OBJECT_CREATED; + + + // + // Initialize the schedule service to receive RPC requests. + // Note that interface name is defined in atsvc.idl file. + // + if ((status = NetpStartRpcServer( + AT_INTERFACE_NAME, + atsvc_ServerIfHandle + )) != NERR_Success) { + + AtHandleError( AtErrorStartRpcServer, status, *pInitState); + return( status); + } + (*pInitState) |= AT_RPC_SERVER_STARTED; + + + // + // We are done with starting the Schedule service. Tell Service + // Controller our new status. + // + + AtGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING; + AtGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE; + AtGlobalServiceStatus.dwCheckPoint = 0; + AtGlobalServiceStatus.dwWaitHint = 0; + + if ((status = AtUpdateStatus()) != NERR_Success) { + + AtHandleError( AtErrorNotifyServiceController, status, *pInitState); + return( status); + } + + AtLog(( AT_DEBUG_MAIN, "Successful Initialization\n")); + + return( NERR_Success); +} + + + +DBGSTATIC DWORD AtCalculateTimeout( + LARGE_INTEGER Runtime, + LARGE_INTEGER TimeLargeInteger + ) +/*++ + +Routine Description: + + Given the current time and the next time to run, this routine returns + the number of miliseconds to sleep before the next runttime. + +Arguments: + + Runtime - next time to run a job + TimeLargeInteger - current time + +Return Value: + + Number of miliseconds to sleep before the next runtime. + +--*/ +{ + DWORD Remainder; + + Runtime.QuadPart = Runtime.QuadPart - TimeLargeInteger.QuadPart; + Runtime = RtlExtendedLargeIntegerDivide( + Runtime, + NT_TICKS_IN_WINDOWS_TICK, + &Remainder + ); + if ( Runtime.HighPart != 0 || Runtime.LowPart > MAX_BUSY_TIMEOUT) { + return( MAX_BUSY_TIMEOUT); + } else { + return( Runtime.LowPart); + } +} + + +DBGSTATIC BOOL AtBatOrCmd( IN OUT PWCHAR Command) +/*++ + + Returns true if we have .BAT or .CMD file. + +--*/ +{ + WCHAR NameBuffer[ MAX_PATH]; + PWCHAR Temp; + DWORD Length; + DWORD Index; + PWCHAR BatOrCmd[] = { L".bat", L".cmd"}; + + for ( Temp = Command; *Temp != 0; Temp++) { + if ( *Temp == L' ' || *Temp == L'\t') { + *Temp = 0; + break; + } + } + + for ( Index = 0; Index < sizeof(BatOrCmd)/sizeof(BatOrCmd[0]); Index++) { + Temp = NULL; + Length = SearchPathW( + NULL, + Command, + BatOrCmd[ Index], + sizeof( NameBuffer) / sizeof( WCHAR), + NameBuffer, + &Temp + ); + if ( Length == 0 || Length >= MAX_PATH || Temp == NULL) { + continue; + } +#ifdef AT_DEBUG + Temp = wcschr( Temp, L'.'); + if ( Temp == NULL || _wcsicmp( Temp, BatOrCmd[ Index])) { + AtLog(( AT_DEBUG_CRITICAL, "BatOrCmd: Command=%ws Extension=%ws\n", + Command, BatOrCmd[ Index])); + } +#endif + return( TRUE); + } + return( FALSE); +} + + +VOID AtReportEvent( + IN WORD EventType, + IN DWORD MessageId, + IN WORD StringCount, + IN LPWSTR * StringArray, + IN DWORD RawDataBufferLength OPTIONAL, + IN LPVOID RawDataBuffer + ) +/*++ + +Routine Description: + + Writes an error message and ascii string to the error log. Also, + writes the data in the data buf if there are any. + +Arguments: + + EventType - warning, error, ... + + MessageId - Message ID + + StringCount - number of strings to use in the string array + + StringArray - array of insertion strings + + RawDataBufferLength - size of data to be printed from the auxiliary data + buffer. May be zero, in which case the actual value depends on + "RawDataBuffer". It is 0 if "RawDataBuffer" is NULL, else it is + "sizeof( wchar) * wcslen( RawDataBuffer)". + + RawDataBuffer - data buffer containing secondary error code & some + other useful info. Must be NULL terminated string or NULL if + "RawDataBufferLength" is zero. + +Return Value: + + None. + +--*/ +{ + HANDLE logHandle; + + logHandle = RegisterEventSource( NULL, SCHEDULE_EVENTLOG_NAME); + + // If the event log cannot be opened, just return. + + if ( logHandle == NULL) { +#ifdef AT_DEBUG + AtLog(( AT_DEBUG_CRITICAL, "ReportEvent: RegisterEventSource() failed with error %d", + GetLastError())); +#endif + return; + } + + // + // Use default for RawDataBufferLength if caller requested us so. + // + if ( RawDataBufferLength == 0 && RawDataBuffer != NULL) { + RawDataBufferLength = sizeof( TCHAR) * wcslen( (LPWSTR)RawDataBuffer); + } + + if ( !ReportEvent( + logHandle, + EventType, + 0, // event category + MessageId, // event id + NULL, // user SID. We're local system - uninteresting + StringCount, // number of strings + RawDataBufferLength, // raw data size + StringArray, // string array + RawDataBuffer // raw data buffer + )) { +#ifdef AT_DEBUG + AtLog(( AT_DEBUG_CRITICAL, "ReportEvent: ReportEvent() failed with error %d", + GetLastError())); +#endif // NOT_YET + } + + DeregisterEventSource( logHandle); +} + + + +DBGSTATIC VOID AtCreateProcess( PAT_RECORD pRecord) +/*++ + +Routine Description: + + Creates a process for a given record. + +Arguments: + + pRecord - pointer to AT_RECORD + +--*/ +#define CMD_EXE L"cmd /c " +{ + PROCESS_INFORMATION ProcessInformation; + BOOL success; + BOOL JobInteractive; + LPWSTR StringArray[ 2]; + WCHAR ErrorCodeString[ 25]; + WCHAR Command[ sizeof( CMD_EXE) + MAXIMUM_COMMAND_LENGTH]; + + wcscpy( Command, pRecord->Command); + + if ( AtBatOrCmd( Command)) { + wcscpy( Command, CMD_EXE); + wcscat( Command, pRecord->Command); + } else { + wcscpy( Command, pRecord->Command); + } + + if ( pRecord->Flags & JOB_NONINTERACTIVE) { + JobInteractive = FALSE; + } else { + // + // Job is to be executed interactively only if the global system flag + // allows that. + // + JobInteractive = AtSystemInteractive(); + if ( !JobInteractive) { + StringArray[ 0] = pRecord->Command; + AtReportEvent( EVENTLOG_WARNING_TYPE, EVENT_COMMAND_NOT_INTERACTIVE, + 1, StringArray, 0, NULL); + } + } + + // + // If the process is to be interactive, set the appropriate flags. + // + if ( JobInteractive) { + AtGlobalStartupInfo.dwFlags |= STARTF_DESKTOPINHERIT; + AtGlobalStartupInfo.lpDesktop = INTERACTIVE_DESKTOP; + } + else { + AtGlobalStartupInfo.dwFlags &= (~STARTF_DESKTOPINHERIT); + AtGlobalStartupInfo.lpDesktop = NULL; + } + + success = CreateProcess( + NULL, // image name is imbedded in the command line + Command, // command line + NULL, // pSecAttrProcess + NULL, // pSecAttrThread + FALSE, // this process will not inherit our handles + CREATE_NEW_CONSOLE | CREATE_SEPARATE_WOW_VDM, +// CREATE_NO_WINDOW, // the only choice that works + NULL, // pEnvironment + NULL, // pCurrentDirectory + &AtGlobalStartupInfo, + &ProcessInformation + ); + + if ( success == FALSE) { + + DWORD winError; + + winError = GetLastError(); + + AtLog(( AT_DEBUG_CRITICAL, "CreateProcess( %ws) fails with winError = %d\n", + pRecord->Command, winError)); + + pRecord->Flags |= JOB_EXEC_ERROR; + + StringArray[ 0] = pRecord->Command; + wcscpy( ErrorCodeString, L"%%"); // tell event viewer to expand this error + ultow( winError, ErrorCodeString + 2, 10); + StringArray[ 1] = ErrorCodeString; + AtReportEvent( EVENTLOG_ERROR_TYPE, EVENT_COMMAND_START_FAILED, + 2, StringArray, 0, NULL); + + } else { + AtLog(( AT_DEBUG_MAIN, + "CreateProcess( %ws) succeeded\t" + "ProcessInformation=%x %x %x %x JobId=%x JobDay=%x Runtime=%x:%x\n", + pRecord->Command, + ProcessInformation.hProcess, + ProcessInformation.hThread, + ProcessInformation.dwProcessId, + ProcessInformation.dwThreadId, + pRecord->JobId, + pRecord->JobDay, + pRecord->Runtime.HighPart, + pRecord->Runtime.LowPart + )); + CloseHandle( ProcessInformation.hThread); + CloseHandle( ProcessInformation.hProcess); + pRecord->Flags &= ~JOB_EXEC_ERROR; + } + + ASSERT( pRecord->Debug++ < 20); + + if ( pRecord->JobDay != JOB_INVALID_DAY) { + + ASSERT( (pRecord->Flags & JOB_RUN_PERIODICALLY) == 0 && + ( pRecord->DaysOfWeek != 0 || pRecord->DaysOfMonth != 0)); + + if ( pRecord->Flags & JOB_CLEAR_WEEKDAY) { + pRecord->DaysOfWeek &= ~( 1<< pRecord->JobDay); + } else { + pRecord->DaysOfMonth &= ~( 1<< ( pRecord->JobDay - 1)); + } + pRecord->JobDay = JOB_INVALID_DAY; + + } else { + + ASSERT( (pRecord->Flags & JOB_RUN_PERIODICALLY) != 0 || + ( pRecord->DaysOfWeek == 0 && pRecord->DaysOfMonth == 0)); + + } +} + + + +DBGSTATIC VOID AtUpdateRecords( IN PAT_TIME pTime) +/*++ + +Routine Description: + + Reshufles runtime queue to reflect the time change. + +Arguments: + + pTime - pointer to the new system time + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY pListEntry; + PAT_RECORD pRecord; + + AtLog(( AT_DEBUG_MAIN, "Update runtime queue.\n")); + + // + // If we want to simulate OS/2 behavior, here we can insert a block + // which runs all records whose Runtimes are earlier than the new + // Jan.01,70 system time. + // + + if ( IsListEmpty( &AtGlobalJobIdListHead) == TRUE) { + return; // do not bother waking up the master if queue is empty + } + + for ( pListEntry = AtGlobalJobIdListHead.Flink; + pListEntry != &AtGlobalJobIdListHead; + pListEntry = pListEntry->Flink) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + JobIdList + ); + + AtCalculateRuntime( pRecord, pTime); + AtRemoveRecord( pRecord, RUNTIME_QUEUE); + AtInsertRecord( pRecord, RUNTIME_QUEUE); + } +} + + + +DBGSTATIC BOOL AtTimeChanged( + IN PAT_TIME pGetupTime, + IN OUT PAT_TIME pSleepTime + ) +/*++ + +Routine Description: + + Detects if there was a change in system time since we went to sleep. + If there was a sustem time it revises SleepTime to match GetupTime. + +Arguments: + + pGetupTime - points to AT_TIME structure for the Getup moment + pSleepTime - points to AT_TIME structure for the Sleep moment + + +Return Value: + + TRUE - if there was a time change + FALSE - otherwise + +--*/ + +{ + // + // DateDelta says how long we slept in units that CAN be adjusted + // by user. + // + LARGE_INTEGER DateDelta; + // + // BootDelta says how long we slept in units that CANNOT be adjusted + // by user. + // + LARGE_INTEGER BootDelta; +#ifdef AT_DEBUG + PCHAR Format; +#endif + + DateDelta.QuadPart = pGetupTime->LargeInteger.QuadPart - + pSleepTime->LargeInteger.QuadPart; + BootDelta = RtlEnlargedUnsignedMultiply( + pGetupTime->TickCount - pSleepTime->TickCount, + NT_TICKS_IN_WINDOWS_TICK + ); + + if ( DateDelta.QuadPart < 0) { +#ifdef AT_DEBUG + Format = "TimeChanged: negative DateDelta: Getup=%x:%x|%x Sleep=%x:%x|%x\n"; +#endif + goto time_changed; + } + + if ( DateDelta.QuadPart >= BootDelta.QuadPart ) { + DateDelta.QuadPart = DateDelta.QuadPart - BootDelta.QuadPart; + } else { + DateDelta.QuadPart = BootDelta.QuadPart - DateDelta.QuadPart; + } + + if ( DateDelta.HighPart != 0 || + DateDelta.LowPart > ONE_MINUTE_IN_NT_TICKS) { +#ifdef AT_DEBUG + Format = "TimeChanged: large DateDelta: Getup=%x:%x!%x Sleep=%x:%x!%x\n"; +#endif + goto time_changed; + } + return( FALSE); + +time_changed: + + AtLog(( AT_DEBUG_MAIN, Format, + pGetupTime->LargeInteger.HighPart, pGetupTime->LargeInteger.LowPart, pGetupTime->TickCount, + pSleepTime->LargeInteger.HighPart, pSleepTime->LargeInteger.LowPart, pSleepTime->TickCount)); + pSleepTime->LargeInteger.QuadPart = pGetupTime->LargeInteger.QuadPart - BootDelta.QuadPart; + AtTimeGetCurrents( pSleepTime); + AtLog(( AT_DEBUG_MAIN, "TimeChanged: revised Sleep=%x:%x|%x\n", + pSleepTime->LargeInteger.HighPart, pSleepTime->LargeInteger.LowPart, pSleepTime->TickCount)); + return( TRUE); +} + + +VOID SCHEDULE_main( + DWORD argc, + LPWSTR * argv + ) +/*++ + +Routine Description: + + This is the main routine of the Schedule Service which registers + itself as an RPC server and notifies the Service Controller of the + Schedule service control entry point. + + After the Schedule Service is started, this thread is used to spawn + processes corresponding to records waiting in the queue. + +Arguments: + + argc - Supplies the number of strings specified in argv + + argv - Supplies string arguments that are specified in the + StartService API call. + +Return Value: + + None. + +--*/ +{ + DWORD initState = 0; + + UNREFERENCED_PARAMETER( argc); + UNREFERENCED_PARAMETER( argv); + +#ifdef AT_DEBUG + AtDebugCreate(); +#endif + +#ifdef NOT_YET + if ( !AllocConsole()) { + AT_DEBUG_PRINT( + AT_DEBUG_MAIN,( + "[Job] AllocConsole() returns WinError = %d\n", + GetLastError() + )); + } +#endif // NOT_YET + + // + // Perhaps one should use something that builds in current day & time + // to be even more random. + // + AtGlobalSeed = GetTickCount(); + + InitializeCriticalSection( &AtGlobalCriticalSection); + + // + // Initialize the scheduler. + // + AtTimeGet( &AtGlobalGetupTime); // initialize AtGlobalGetupTime + if ( AtInitialize( &AtGlobalGetupTime, &initState) != NERR_Success) { + return; + } + + // + // We enter and leave loop while holding the critical section. + // + + EnterCriticalSection( &AtGlobalCriticalSection); + + for ( ; ; ) { + + DWORD timeout; // in milliseconds + PAT_RECORD pRecord; + + if ( AtGlobalTasks & AT_SERVICE_SHUTDOWN) { + AtLog(( AT_DEBUG_MAIN, "Service shutdown.\n")); + break; + } + + AtLogRuntimeList( "Before executions"); + + timeout = MAX_LAZY_TIMEOUT; + + while ( IsListEmpty( &AtGlobalRuntimeListHead) == FALSE) { + + pRecord = CONTAINING_RECORD( + AtGlobalRuntimeListHead.Flink, + AT_RECORD, + RuntimeList + ); + + if (pRecord->Runtime.QuadPart > AtGlobalGetupTime.LargeInteger.QuadPart) { + timeout = AtCalculateTimeout( pRecord->Runtime, AtGlobalGetupTime.LargeInteger); + break; // nothing to do for now + } + + AtCreateProcess( pRecord); + + if ( pRecord->DaysOfWeek == 0 && pRecord->DaysOfMonth == 0) { + AtDeleteKey( pRecord); + AtRemoveRecord( pRecord, BOTH_QUEUES); + (VOID)LocalFree( pRecord); + } else { + AtCalculateRuntime( pRecord, &AtGlobalGetupTime); + AtRemoveRecord( pRecord, RUNTIME_QUEUE); + AtInsertRecord( pRecord, RUNTIME_QUEUE); + } + } + + AtGlobalSleepTime = AtGlobalGetupTime; + AtLogRuntimeList( "After executions"); + + LeaveCriticalSection( &AtGlobalCriticalSection); + + AtLogTimeout( timeout); + (VOID)WaitForSingleObject( AtGlobalEvent, timeout); + + EnterCriticalSection( &AtGlobalCriticalSection); + + AtTimeGet( &AtGlobalGetupTime); + if ( AtTimeChanged( &AtGlobalGetupTime, &AtGlobalSleepTime) == TRUE) { + // + // SleepTime has been revised and is now expressed in GetupTime + // units. Reorder the queue with respect to revised SleepTime + // and run whatever needs to be run. + // Above has the same effect as if a time change occured + // JUST BEFORE the last time main thread ordered the runtime + // queue and went to sleep. + // + AtUpdateRecords( &AtGlobalSleepTime); + } + } + + LeaveCriticalSection( &AtGlobalCriticalSection); + + AtShutdown( NERR_Success, initState); +#ifdef AT_DEBUG + AtDebugDelete(); +#endif +} diff --git a/private/net/svcdlls/at/server/atreg.c b/private/net/svcdlls/at/server/atreg.c new file mode 100644 index 000000000..4e04a61f3 --- /dev/null +++ b/private/net/svcdlls/at/server/atreg.c @@ -0,0 +1,603 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + atreg.c + +Abstract: + + Routines that deal with the registry & queues. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Environment: + + User Mode - Win32 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + +#include "at.h" + + +DWORD AtCreateKey( PAT_RECORD pRecord) +/*++ + +Routine Description: + + This routine first loops trying to find an unused name in AT part + of the registry. Then it saves the data from the in memory record + into the new registry key. Finally, it saves the name of the + registry key in the in memory record. + +Arguments: + + pRecord - pointer to record for which we need to create registry key + +Return Value: + + TRUE if success + FALSE otherwise + +Note: + + +--*/ +{ + DWORD random; + DWORD status; + HKEY Key; + DWORD disposition; + DWORD Length; + PWCHAR Name; + WCHAR Buffer[ AT_KEY_NAME_MAX_LEN + 1]; + AT_SCHEDULE Schedule; + + + for ( ; ;) { + + random = RtlRandom( &AtGlobalSeed); + + Name = &Buffer[ AT_KEY_NAME_MAX_LEN]; + *Name = 0; // null terminate the string + + while ( random != 0) { + + *--Name = L"0123456789ABCDEF"[ random & 0xF]; + random >>= 4; + } + + status = RegCreateKeyEx( + AtGlobalKey, + Name, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &Key, + &disposition + ); + if (status != ERROR_SUCCESS){ + KdPrint(( + "[Job] Cannot create Name = %ws, status = %d\n", + Name, + status + )); + return( status ); + } + if ( disposition == REG_CREATED_NEW_KEY) { + break; + } + + KdPrint(( + "[Job] Key name collision Name = %ws\n", + Name + )); + } + + wcscpy( pRecord->Name, Name); + Schedule.JobTime = pRecord->JobTime; + Schedule.DaysOfMonth = pRecord->DaysOfMonth; + Schedule.DaysOfWeek = pRecord->DaysOfWeek; + Schedule.Flags = pRecord->Flags; + Schedule.Reserved = 0; + + status = RegSetValueEx( + Key, + AT_SCHEDULE_NAME, + 0, + REG_BINARY, + (LPBYTE)&Schedule, + sizeof( Schedule) + ); + if ( status != STATUS_SUCCESS) { + return( status ); + } + + status = RegSetValueEx( + Key, + AT_COMMAND_NAME, + 0, + REG_SZ, + (LPBYTE)&pRecord->Command[0], + pRecord->CommandSize + ); + if ( status != STATUS_SUCCESS) { + return( status ); + } + +#ifdef AT_DEBUG + { + WCHAR Command[ MAXIMUM_COMMAND_LENGTH + 1]; + DWORD type; + + memset( Command, 0, sizeof( Command)); + Length = sizeof( Command); + status = RegQueryValueEx( + Key, + AT_COMMAND_NAME, + NULL, + &type, + (LPBYTE)Command, + &Length + ); + if ( status != ERROR_SUCCESS || + type != REG_SZ || + wcscmp( pRecord->Command, Command) != 0 || + (DWORD)pRecord->CommandSize != Length ) { + ASSERT( FALSE); + KdPrint(( + "[Job] Registry bug: status=%d, type=%x, " + " pRecord->Command=%ws, Command=%ws, " + " pRecord->CommandSize=%d, Length=%d\n", + status, + type, + pRecord->Command, + Command, + pRecord->CommandSize, + Length + )); + } + } +#endif // AT_DEBUG + + (VOID)RegCloseKey( Key); + + return( NERR_Success ); +} + + + +BOOL AtDeleteKey( PAT_RECORD pRecord) +/*++ + +Routine Description: + + Given an in memory record this routine deletes the registry key + with corresponding name. + +Arguments: + + pRecord - pointer to record for which we need to delete registry key + +Return Value: + + TRUE if success + FALSE otherwise + +--*/ +{ + DWORD status; + + status = RegDeleteKey( + AtGlobalKey, + pRecord->Name + ); + if (status != ERROR_SUCCESS) { + KdPrint(( + "[Job] Cannot delete pRecord->Name = %ws, status = %d\n", + pRecord->Name, + status + )); + return( FALSE); + } + return( TRUE); +} + + + +VOID AtInsertRecord( + PAT_RECORD pNewRecord, + DWORD QueueMask + ) +/*++ + +Routine Description: + + Depending on the value of a QueueMask argument, this function does + one or both of the following: + + - inserts record in a doubly linked list sorted by Runtime + - inserts record in a doubly linked list sorted by JobId + + Since newly added jobs have ever increasing JobId-s, JobId queue + insertion is done by inserting job at the end of the JobId queue. + + +Arguments: + + pNewRecord - pointer to record to be inserted + QueueMask - mask of queues where this record should be inserted to. + +Return Value: + + None. + +--*/ +{ + PAT_RECORD pRecord; + PLIST_ENTRY pListEntry; + + if ( QueueMask & RUNTIME_QUEUE) { + + for ( pListEntry = AtGlobalRuntimeListHead.Flink; + pListEntry != &AtGlobalRuntimeListHead; + pListEntry = pListEntry->Flink) { + + pRecord = CONTAINING_RECORD( + pListEntry, + AT_RECORD, + RuntimeList + ); + + if ( pRecord->Runtime.QuadPart >= pNewRecord->Runtime.QuadPart) { + InsertTailList( &pRecord->RuntimeList, &pNewRecord->RuntimeList); + break; + } + } + + if ( pListEntry == &AtGlobalRuntimeListHead) { + InsertTailList( &AtGlobalRuntimeListHead, &pNewRecord->RuntimeList); + } + } + + if ( QueueMask & JOBID_QUEUE) { + InsertTailList( &AtGlobalJobIdListHead, &pNewRecord->JobIdList); + } + +} + + + +NET_API_STATUS AtMakeDataFromRegistry( IN PAT_TIME pTime) +{ + NET_API_STATUS status; + HKEY Key; + DWORD index; + WCHAR NameBuffer[ 20]; // some arbitrary value + FILETIME lastWriteTime; + PAT_RECORD pRecord; + WCHAR Command[ MAXIMUM_COMMAND_LENGTH + 1]; + AT_SCHEDULE Schedule; + DWORD Length; + DWORD type; + DWORD NameSize; + DWORD CommandSize; + + InitializeListHead( &AtGlobalRuntimeListHead); + InitializeListHead( &AtGlobalJobIdListHead); + + + // First open the AT service registry tree. + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + AT_REGISTRY_PATH, + 0, + KEY_READ, + &AtGlobalKey + ); + if (status != ERROR_SUCCESS){ + KdPrint(("[Job] Cannot open AtGlobalKey, status = %d\n", status)); + return( status); + } + + for ( index = 0; ; index++) { + + // + // Regedit can sometimes display other keys in addition to keys + // found here. Also, it often fails to display last character in + // the Command and after a refresh it may not display some of the + // spurious keys. + // + Length = sizeof( NameBuffer); + status = RegEnumKeyEx( + AtGlobalKey, + index, + NameBuffer, // lpName + &Length, // lpcbName + 0, // lpReserved + NULL, // lpClass + NULL, // lpcbClass + &lastWriteTime + ); + if ( status != ERROR_SUCCESS) { + if ( status == ERROR_NO_MORE_ITEMS) { + status = ERROR_SUCCESS; // no more items to enumerate + } + break; // the only exit point from this loop + } + // + // Length returned is the number of characters in a UNICODE string + // representing the key name (not counting the terminating NULL + // character which is also supplied). + // + NameSize = ( Length + 1) * sizeof( WCHAR); + + status = RegOpenKeyEx( + AtGlobalKey, + NameBuffer, + 0L, + KEY_READ, + &Key + ); + if ( status != ERROR_SUCCESS) { + KdPrint(( + "[Job] RegOpenKeyEx( %ws) returns %d\n", + NameBuffer, + status + )); + (VOID)RegDeleteKey( AtGlobalKey, NameBuffer); + continue; + } + + Length = sizeof( Schedule); + status = RegQueryValueEx( + Key, + AT_SCHEDULE_NAME, + NULL, + &type, + (LPBYTE)&Schedule, + &Length + ); + if ( status != ERROR_SUCCESS) { + KdPrint(( + "[Job] RegQueryValueEx( AT_SCHEDULE_NAME, %ws) returns %d\n", + NameBuffer, + status + )); + (VOID)RegCloseKey( Key); + (VOID)RegDeleteKey( AtGlobalKey, NameBuffer); + continue; + } + if ( type != REG_BINARY || + Length != sizeof( AT_SCHEDULE) || + (Schedule.DaysOfWeek & ~DAYS_OF_WEEK) != 0 || + (Schedule.DaysOfMonth & ~DAYS_OF_MONTH) != 0 || + Schedule.JobTime >= MAXIMUM_JOB_TIME ) { + KdPrint(( + "[Job] RegQueryValueEx( %ws) returns invalid schedule info\n" + )); + (VOID)RegCloseKey( Key); + (VOID)RegDeleteKey( AtGlobalKey, NameBuffer); + continue; + } + + Length = sizeof( Command); + status = RegQueryValueEx( + Key, + AT_COMMAND_NAME, + NULL, + &type, + (LPBYTE)Command, + &Length + ); + if ( status != ERROR_SUCCESS) { + KdPrint(( + "[Job] RegQueryValueEx( AT_COMMAND_NAME, %ws) returns %d\n", + NameBuffer, + status + )); + (VOID)RegCloseKey( Key); + (VOID)RegDeleteKey( AtGlobalKey, NameBuffer); + continue; + } + if ( type != REG_SZ) { + KdPrint(( + "[Job] RegQueryValueEx( %ws): Command is not of REG_SZ type\n" + )); + (VOID)RegCloseKey( Key); + (VOID)RegDeleteKey( AtGlobalKey, NameBuffer); + continue; + } + // + // Above call should already return the actual length of the null + // terminated string. However, because of bugs in reg apis it may + // return larger length than the actual length. Thus this: + // + Length = wcslen( Command); + CommandSize = (Length + 1) * sizeof( WCHAR); + + pRecord = (PAT_RECORD)LocalAlloc( + LMEM_FIXED, + sizeof( AT_RECORD) + NameSize + CommandSize + ); + if ( pRecord == NULL) { + KdPrint(( + "[Job] LocalAlloc fails\n" + )); + (VOID)RegCloseKey( Key); + (VOID)RegDeleteKey( AtGlobalKey, NameBuffer); + continue; + } + + pRecord->JobId = AtGlobalJobId++; + + pRecord->JobTime = Schedule.JobTime; + pRecord->DaysOfMonth = Schedule.DaysOfMonth; + pRecord->DaysOfWeek = Schedule.DaysOfWeek; + pRecord->Flags = Schedule.Flags; +#ifdef AT_DEBUG + pRecord->Debug = 0; +#endif // AT_DEBUG + pRecord->CommandSize = (WORD)CommandSize; + pRecord->NameSize = (WORD)NameSize; + + pRecord->JobDay = JOB_INVALID_DAY; + + pRecord->Name = (PWCHAR)( pRecord + 1); + memcpy( pRecord->Name, NameBuffer, NameSize); + + pRecord->Command = (PWCHAR)( (LPBYTE)&pRecord->Name[0] + NameSize); + memcpy( pRecord->Command, Command, CommandSize); + + AtCalculateRuntime( pRecord, pTime); + + AtInsertRecord( pRecord, BOTH_QUEUES); + + (VOID)RegCloseKey( Key); + } + return( status); +} + + + +BOOL AtPermitServerOperators( VOID) +/*++ + We permit server operators to manage schedule service only if the key + exists the proper flag is set. In all other case we do not permit server + operators to manage schedule service. +--*/ +{ + DWORD SubmitControl; + DWORD status; + DWORD type; + DWORD Length; + HKEY LsaKey; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + LSA_REGISTRY_PATH, + 0L, + KEY_READ, + &LsaKey + ); + if ( status != ERROR_SUCCESS) { + AtLog(( AT_DEBUG_CRITICAL, "RegOpenKeyEx( LsaKey) returns %d\n", status)); + return( FALSE); + } + + Length = sizeof( SubmitControl); + status = RegQueryValueEx( + LsaKey, + LSA_SUBMIT_CONTROL, + NULL, + &type, + (LPBYTE)&SubmitControl, + &Length + ); + (VOID)RegCloseKey( LsaKey); + + if ( status != ERROR_SUCCESS || type != REG_DWORD + || Length != sizeof( SubmitControl)) { + AtLog(( AT_DEBUG_MAIN, "LsaKey query: status=%d type=0x%x Length=%d value=0x%x\n", + status, type, Length, SubmitControl)); + return( FALSE); + } + + return( (SubmitControl & LSA_SERVER_OPERATORS) != 0); +} + + + +VOID AtRemoveRecord( + PAT_RECORD pRecord, + DWORD QueueMask + ) +/*++ + +Routine Description: + + Depending on the value of a QueueMask argument, this function does + one or both of the following: + + - removes record from a doubly linked list sorted by Runtime + - removes record from a doubly linked list sorted by JobId + +Arguments: + + pRecord - pointer to record to be removed + QueueMask - mask of queues where this record should be removed from + +Return Value: + + None. + +Note: + + This routine should probably be a macro. + +--*/ +{ + if ( QueueMask & RUNTIME_QUEUE) { + RemoveEntryList( &pRecord->RuntimeList); + } + + if ( QueueMask & JOBID_QUEUE) { + RemoveEntryList( &pRecord->JobIdList); + } +} + + + +BOOL AtSystemInteractive( VOID) +{ + DWORD NoInteractiveServices; + DWORD status; + DWORD type; + DWORD Length; + + if ( AtGlobalHaveWindowsKey == FALSE) { + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + WINDOWS_REGISTRY_PATH, + 0L, + KEY_READ, + &AtGlobalWindowsKey + ); + if ( status != ERROR_SUCCESS) { + AtLog(( AT_DEBUG_CRITICAL, "RegOpenKeyEx( WindowsKey) returns %d\n", status)); + return( TRUE); + } + AtGlobalHaveWindowsKey = TRUE; + } + + Length = sizeof( NoInteractiveServices); + status = RegQueryValueEx( + AtGlobalWindowsKey, + WINDOWS_VALUE_NAME, + NULL, + &type, + (LPBYTE)&NoInteractiveServices, + &Length + ); + if ( status == ERROR_SUCCESS && type == REG_DWORD + && Length == sizeof( NoInteractiveServices)) { + return( NoInteractiveServices == 0); + } + + AtLog(( AT_DEBUG_MAIN, "WindowsKey query: status=%d type=0x%x Length=%d value=0x%x\n", + status, type, Length, NoInteractiveServices)); + return( TRUE); +} + + diff --git a/private/net/svcdlls/at/server/atsec.c b/private/net/svcdlls/at/server/atsec.c new file mode 100644 index 000000000..5b62a14b3 --- /dev/null +++ b/private/net/svcdlls/at/server/atsec.c @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + atsec.c + +Abstract: + + This module contains the schedule service support routines + which create security objects and enforce security _access checking. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + +#include "at.h" +#include <secobj.h> // ACE_DATA + + + + + +#define AT_JOB_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \ + AT_JOB_ADD | \ + AT_JOB_DEL | \ + AT_JOB_ENUM | \ + AT_JOB_GET_INFO ) + +// +// Security descriptors of schedule service to control user accesses +// to the schedule service configuration information. +// +PSECURITY_DESCRIPTOR AtGlobalSecurityDescriptor; + +// +// Structure that describes the mapping of Generic access rights to +// object specific access rights for the schedule service security object. +// +GENERIC_MAPPING AtGlobalInformationMapping = { + + STANDARD_RIGHTS_READ | // Generic read + AT_JOB_ENUM | + AT_JOB_GET_INFO, + STANDARD_RIGHTS_WRITE | // Generic write + AT_JOB_ADD | + AT_JOB_DEL, + STANDARD_RIGHTS_EXECUTE, // Generic execute + AT_JOB_ALL_ACCESS // Generic all + }; + + +NET_API_STATUS +AtCheckSecurity( + ACCESS_MASK DesiredAccess + ) +/*++ + +Routine Description: + + This routine checks if an rpc caller is allowed to perform a given + operation on AT service. Currently, members of groups LocalAdmin + and LocalBackupOperators are allowed to do all operations and everybody + else is not allowed to do anything. + + This routine calls NtAccessCheck() and not NtAccessCheckAndAuditAlarm() + because: + + - we do not need the audit part anyway + + - NtAccessCheckAndAuditAlarm() cannot be issued if a service is running + in an account which is NOT a LocalSystem account + + The second reason prohibits a use of NetpAccessCheckAndAudit() netlib + function. + +Arguments: + + ACCESS_MASK - Desired Access + +Return Value: + + NET_API_STATUS - NERR_Success or reason for failure. + +--*/ +{ + NTSTATUS NtStatus; + NET_API_STATUS Status; + HANDLE ClientToken; + LPWSTR StringArray[ 2]; + WCHAR ErrorCodeString[ 25]; + + if ( ( Status = RpcImpersonateClient(NULL)) != NERR_Success) { + AtLog(( AT_DEBUG_CRITICAL, "RpcImpersonateClient() returns WinError = %d\n", Status)); + return( Status); + } + + NtStatus = NtOpenThreadToken( + NtCurrentThread(), + TOKEN_QUERY, + (BOOLEAN)TRUE, + &ClientToken + ); + + if ( NtStatus != STATUS_SUCCESS) { + AtLog(( AT_DEBUG_CRITICAL, "NtOpenThreadToken() returns NtStatus = 0x%x\n", NtStatus)); + } else { + + PRIVILEGE_SET PrivilegeSet; + DWORD PrivilegeSetLength; + ACCESS_MASK GrantedAccess; + NTSTATUS AccessStatus; + + PrivilegeSetLength = sizeof( PrivilegeSet); + + // NtAccessCheck() returns STATUS_SUCCESS if parameters + // are correct. Whether or not access is allowed is + // governed by the returned value of AccessStatus. + + NtStatus = NtAccessCheck( + AtGlobalSecurityDescriptor, // SecurityDescriptor + ClientToken, // ClientToken + DesiredAccess, // DesiredAccess + &AtGlobalInformationMapping, // GenericMapping + &PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, // GrantedAccess + &AccessStatus // AccessStatus + ); + if ( NtStatus != STATUS_SUCCESS) { + AtLog(( AT_DEBUG_CRITICAL, "NtAccessCheck() returns NtStatus = 0x%x\n", NtStatus)); + } else { + NtStatus = AccessStatus; + } + (VOID)NtClose( ClientToken); + } + + if ( ( Status = RpcRevertToSelf()) != NERR_Success) { + StringArray[ 0] = L"RpcRevertToSelf"; + wcscpy( ErrorCodeString, L"%%"); // tell event viewer to expand this error + ultow( Status, ErrorCodeString + 2, 10); + StringArray[ 1] = ErrorCodeString; + AtReportEvent( EVENTLOG_ERROR_TYPE, EVENT_CALL_TO_FUNCTION_FAILED, + 2, StringArray, 0, NULL); + EnterCriticalSection( &AtGlobalCriticalSection); + AtGlobalTasks |= AT_SERVICE_SHUTDOWN; + LeaveCriticalSection( &AtGlobalCriticalSection); + SetEvent( AtGlobalEvent); + return( Status); + } + + return( NetpNtStatusToApiStatus( NtStatus)); +} + + +NET_API_STATUS +AtCreateSecurityObject( + VOID + ) +/*++ + +Routine Description: + + This function creates the scheduler user-mode configuration + information object which is represented by a security descriptors. + +Arguments: + + None. + +Return Value: + + NET_API_STATUS code + +--*/ +{ + NTSTATUS status; + + // + // Order matters! These ACEs are inserted into the DACL in the + // following order. Security access is granted or denied based on + // the order of the ACEs in the DACL. + // + // In win3.1 both LocalGroupAdmins and LocalGroupSystemOps were + // allowed to perform all Schedule Service operations. In win3.5 + // LocalGroupSystemOps may be disallowed (this is the default case). + // + + ACE_DATA aceData[] = { + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &AliasAdminsSid}, + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &AliasSystemOpsSid} + }; + + + status = NetpCreateSecurityObject( + aceData, // pAceData + AtGlobalPermitServerOperators ? 2 : 1, // countAceData + NULL, // OwnerSid + NULL, // PrimaryGroupSid + &AtGlobalInformationMapping, // GenericToSpecificMapping + &AtGlobalSecurityDescriptor // ppNewDescriptor + ); + + if ( ! NT_SUCCESS (status)) { + AtLog(( AT_DEBUG_CRITICAL, "Failure to create security object\n")); + return NetpNtStatusToApiStatus( status); + } + + return NERR_Success; +} + + + +NET_API_STATUS +AtDeleteSecurityObject( + VOID + ) +/*++ + +Routine Description: + + This function destroys the schedule service user-mode configuration + information object which is represented by a security descriptors. + +Arguments: + + None. + +Return Value: + + NET_API_STATUS code + +--*/ +{ + return( NetpDeleteSecurityObject( &AtGlobalSecurityDescriptor)); +} + + diff --git a/private/net/svcdlls/at/server/attime.c b/private/net/svcdlls/at/server/attime.c new file mode 100644 index 000000000..c9004ab38 --- /dev/null +++ b/private/net/svcdlls/at/server/attime.c @@ -0,0 +1,355 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + attime.c + +Abstract: + + This module contains routines for calculating times & runtimes. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + + +#include "at.h" + +#define DAYS_IN_WEEK 7 +#define SECONDS_PER_DAY 60L*60L*24L + + +DBGSTATIC INT DaysInMonth( + INT month, + INT year + ) +/*++ + +Routine Description: + + Returns number of days in a given month - the only exceptional case is + a leap year February. + +Arguments: + + month - integer index, must be between 0 and 11 + year - integer index, any value allowed + +Return Value: + + Number of days in a particular month. + +--*/ +{ + // JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC + static int DaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + return( month == 1 && ( (year%4 == 0 && year%100 != 0) || year%400 == 0) + ? 29 : DaysInMonth[ month]); +} + + + +VOID AtCalculateRuntime( + IN OUT PAT_RECORD pRecord, + IN PAT_TIME pTime + ) +/*++ + +Routine Description: + + Fills in the next runtime for a given record. + +Arguments: + + pRecord pointer to AT schedule record + pTime pointer to AT time structure + +Return Value: + + None. + +--*/ +{ + DWORD BestDelta; // number of days before the best runtime day + DWORD Delta; // number of days before a possible runtime day + + DWORD BestJobDay; // the JobDay when to run + DWORD JobDay; // index of day in week or day in month + DWORD CurrentDay; // index of the current day (week or month) + + DWORD DayMask; // mask for days in week or days in month + BOOL Weekday; // TRUE if next runtime is for a week day + + DWORD CurrentTime;// day time in miliseconds for the current day + DWORD JobTime; // day time in miliseconds for this job + + LARGE_INTEGER Runtime; + +#ifdef NOT_YET + if ( (pRecord->Flags & JOB_IS_ENABLED) == 0) { + // + // This job is disabled, so this will have the effect of putting + // it at the end of the queue. + // + pRecord->Runtime.LowPart = MAXULONG; + pRecord->Runtime.HighPart = MAXLONG; + return; + } +#endif // NOT_YET + + + CurrentTime = pTime->CurrentTime; + JobTime = pRecord->JobTime; + + + if ( (pRecord->DaysOfWeek == 0) && (pRecord->DaysOfMonth == 0)) { + + BestDelta = (CurrentTime > JobTime) ? 1 : 0; // case of single runtime + + } else { + + BestDelta = MAXULONG; // initialize to an invalid value + + if ( (DayMask = pRecord->DaysOfWeek) != 0) { + + CurrentDay = pTime->CurrentDayOfWeek; // current day of week + JobDay = 0; // running day of week, starting from Monday + + // Because of a bug caused by wrong compiler optimization + // (compiler obtained in mid January 93), the test for DayMask + // equal to 0 and loop increments have to burried within a body + // of the for loop. (see bug #7414). An alternative was to + // disable global optimization for this routine. + + for ( ; ;) { + + if ( (DayMask & 1) != 0) { + + if ( CurrentDay > JobDay || + CurrentDay == JobDay && CurrentTime >= JobTime) { + + Delta = DAYS_IN_WEEK - CurrentDay + JobDay; + + } else { + + Delta = JobDay - CurrentDay; + + } + + if ( Delta < BestDelta) { + BestDelta = Delta; + BestJobDay = JobDay; + Weekday = TRUE; + } + } + + DayMask >>= 1; + + if ( DayMask == 0) { + break; + } + + JobDay++; + } + } + + if ( (DayMask = pRecord->DaysOfMonth) != 0) { + + DWORD ThisMonthLength; + DWORD NextMonthLength; + + ThisMonthLength = DaysInMonth( + pTime->CurrentMonth - 1, + pTime->CurrentYear + ); + NextMonthLength = DaysInMonth( + (pTime->CurrentMonth) % 12, + pTime->CurrentYear + ); + + CurrentDay = pTime->CurrentDay; // current day of month + JobDay = 1; // running day of month, starting from 1-st day + + for ( ; ;) { + + if ( (DayMask & 1) != 0) { + + if ( CurrentDay > JobDay || + CurrentDay == JobDay && CurrentTime >= JobTime) { + + // + // Must look in the next two months. + // + + if ( NextMonthLength >= JobDay) { + + Delta = ThisMonthLength - CurrentDay + JobDay; + + } else { + + Delta = ThisMonthLength - CurrentDay + + NextMonthLength + JobDay; + + } + + } else { + + // + // Must look in the current month and the next month. + // + + if ( ThisMonthLength >= JobDay) { + + Delta = JobDay - CurrentDay; + + } else { + + Delta = ThisMonthLength - CurrentDay + JobDay; + + } + } + + if ( Delta < BestDelta) { + BestDelta = Delta; + BestJobDay = JobDay; + Weekday = FALSE; + } + } + + DayMask >>= 1; + + if ( DayMask == 0) { + break; + } + + JobDay++; + } + } + + // + // Make sure we found a valid solution. + // + ASSERT( BestDelta != MAXULONG && BestJobDay != JOB_INVALID_DAY); + + // + // If this is a NEXT type of job, remember the day to clear when + // we create the process for this job. + // + if ( (pRecord->Flags & JOB_RUN_PERIODICALLY) == 0) { + if ( Weekday == TRUE) { + pRecord->Flags |= JOB_CLEAR_WEEKDAY; + } else { + pRecord->Flags &= ~JOB_CLEAR_WEEKDAY; + } + pRecord->JobDay = (UCHAR)BestJobDay; + } + } + + // + // The first operation requires large integer when BestDelta > 49. + // + Runtime.QuadPart = UInt32x32To64(BestDelta, 24L * 3600L * 1000L); + // count of miliseconds in a day + Runtime.QuadPart += JobTime; + Runtime.QuadPart -= CurrentTime; + Runtime.QuadPart = Runtime.QuadPart * NT_TICKS_IN_WINDOWS_TICK; + pRecord->Runtime.QuadPart = pTime->LargeInteger.QuadPart + + Runtime.QuadPart; + +// AtLog(( AT_DEBUG_MAIN, "CalculateRuntime: JobId=%d Command=%ws Runtime=%x:%x\n", +// pRecord->JobId, pRecord->Command, pRecord->Runtime.HighPart, pRecord->Runtime.LowPart)); + // + // Make sure we return a runtime that is after the current time! + // + ASSERT( pRecord->Runtime.QuadPart > pTime->LargeInteger.QuadPart); +} + +VOID AtTimeGetCurrents( IN OUT PAT_TIME pTime) +/*++ + +Routine Description: + + LargeInteger & TickCount fields in input data structure + are known at input. Here we just fill in Current fields + using the input value of LargeInteger field. + +Arguments: + + pTime - points to AT_TIME structure. + + Fields in SYSTEMTIME Structure: +WORD wYear - the current year. +WORD wMonth - the current month with January equal to 1. +WORD wDayOfWeek - the current day of the week where 0=Sunday, 1=Monday... +WORD wDay - the current day of the month. +WORD wHour - the current hour. +WORD wMinute - the current minute within the hour. +WORD wSecond - the current second within the minute. +WORD wMilliseconds - the current millisecond within the second. + +Return Value: + + None. + +--*/ +{ + TIME_FIELDS TimeFields; + + RtlTimeToTimeFields( &pTime->LargeInteger, &TimeFields); + + pTime->CurrentYear = TimeFields.Year; + pTime->CurrentMonth = TimeFields.Month; + // + // Note that Windows structure has Sunday=0, Monday=1, ... + // while AT command uses: Monday=0, Tuesday=1, ... Here + // we convert from windows into AT command DayOfWeek units. + // + if ( TimeFields.Weekday == 0) { + pTime->CurrentDayOfWeek = 6; // Sunday + } else { + pTime->CurrentDayOfWeek = (WORD)(TimeFields.Weekday - 1); + } + pTime->CurrentDay = TimeFields.Day; + pTime->CurrentTime = (DWORD)TimeFields.Milliseconds + + 1000 * ( TimeFields.Second + + 60 * ( TimeFields.Minute + 60 * TimeFields.Hour)); +} + + +VOID AtTimeGet( OUT PAT_TIME pTime) +/*++ + +Routine Description: + + Returns all time information needed by AT service. + +Arguments: + + pTime - points to AT_TIME structure + +Return Value: + + None. + +--*/ +{ + LARGE_INTEGER UniversalTime; + + NtQuerySystemTime( &UniversalTime); + pTime->TickCount = GetTickCount(); + RtlSystemTimeToLocalTime( &UniversalTime, &pTime->LargeInteger); + AtTimeGetCurrents( pTime); +} + + + diff --git a/private/net/svcdlls/at/server/makefile b/private/net/svcdlls/at/server/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/net/svcdlls/at/server/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/net/svcdlls/at/server/run.c b/private/net/svcdlls/at/server/run.c new file mode 100644 index 000000000..3386485bb --- /dev/null +++ b/private/net/svcdlls/at/server/run.c @@ -0,0 +1,242 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + run.c + +Abstract: + + Example of a test code for command execution. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Revision History: + + 06-Nov-1992 vladimv + Created + +--*/ + +#include "at.h" +#include <stdio.h> // printf + +DBGSTATIC WCHAR ** +ArgvToUnicode( + IN int argc, + IN CHAR ** charArgv + ); +DBGSTATIC WCHAR * +AsciizToUnicode( + IN CHAR * Asciiz + ); +DBGSTATIC BOOL +FillCommand( + IN int argc, + IN WCHAR ** argv + ); + +#define MAX_COMMAND_LEN 128 + +WCHAR Command[ MAX_COMMAND_LEN + 1]; + + +VOID _CRTAPI1 +main( + int argc, + CHAR ** charArgv + ) +{ + PROCESS_INFORMATION ProcessInformation; + BOOL success; + WCHAR ** argv; + STARTUPINFO StartupInfo; + + + + if ( ( argv = ArgvToUnicode( argc, charArgv)) == NULL) { + printf( "Failed to map input strings to unicode.\n"); + exit( -1); + } + + if ( FillCommand( argc, argv) == FALSE) { + exit( -1); + } + + GetStartupInfo( &StartupInfo); + +// StartupInfo.lpTitle = "just a test"; + StartupInfo.lpTitle = NULL; + + success = CreateProcess( + NULL, // image name is imbedded in the command line + Command, // command line + NULL, // pSecAttrProcess + NULL, // pSecAttrThread + FALSE, // this process will not inherit our handles + DETACHED_PROCESS, // instead of DETACHED_PROCESS + NULL, // pEnvironment + NULL, // pCurrentDirectory + &StartupInfo, // instead of NULL + &ProcessInformation + ); + + if ( success == FALSE) { + + DWORD winError; + + winError = GetLastError(); + + printf( + "CreateProcess( %ws) fails with winError = %d\n", + Command, + winError + ); + + } else { + + printf( + "CreateProcess( %ws) succeeds\n" + " ProcessInformation = %x %x %x %x\n", + Command, + ProcessInformation.hProcess, + ProcessInformation.hThread, + ProcessInformation.dwProcessId, + ProcessInformation.dwThreadId + ); + } +} + + +DBGSTATIC WCHAR ** +ArgvToUnicode( + IN int argc, + IN CHAR ** charArgv + ) +/*++ + No attempt is made to clean up if we fail half way along. Cleanup + will be done at the exit process time. +--*/ +{ + WCHAR ** argv; + int index; + + argv = (WCHAR **)LocalAlloc( + LMEM_FIXED, + argc * sizeof( WCHAR *) + ); + if ( argv == NULL) { + return( NULL); + } + + for ( index = 0; index < argc; index++ ) { + + if ( ( argv[ index] = AsciizToUnicode( charArgv[ index])) == NULL) { + return( NULL); + } + } + + return( argv); +} + + + +DBGSTATIC WCHAR * +AsciizToUnicode( + IN CHAR * Asciiz + ) +/*++ + No attempt is made to clean up if we fail half way along. Cleanup + will be done at the exit process time. +--*/ +{ + UNICODE_STRING UnicodeString; + BOOL success; + + RtlInitUnicodeString( + &UnicodeString, + NULL + ); + + success = RtlCreateUnicodeStringFromAsciiz( + &UnicodeString, + Asciiz + ); + + if ( success != TRUE) { + printf( "Failed to make unicode string out of %s\n", Asciiz); + return( NULL); + } + + return( UnicodeString.Buffer); +} + + +#define DEFAULT_CMD_EXE L"cmd" +#define SLASH_C_APPEND L" /c " +#define SLASH_C_APPEND_LENGTH ( sizeof( SLASH_C_APPEND) / sizeof( WCHAR) - 1) +DBGSTATIC BOOL +FillCommand( + IN int argc, + IN WCHAR ** argv + ) +{ + WCHAR * recdatap; // ptr used to build atr_command + DWORD recdata_len; // len of arg to put in atr_command + int i; + WCHAR cmdexe[ MAX_PATH + SLASH_C_APPEND_LENGTH]; + DWORD length; + + // + // This is really bogus behavior, but "length" returned is count of + // BYTES, not count of UNICODE characters, and it does not include the + // terminating NULL UNICODE character. + // + length = GetEnvironmentVariable( L"ComSpec", cmdexe, MAX_PATH); + if ( length == 0) { + // + // We arrive here if somebody undefined ComSpec environment + // variable. Then, DEFAULT_CMD_EXE helps provided there is + // no bogus file with cmd.exe name on the search path before + // the real cmd.exe (e.g. a bogus file in the current directory). + // + wcscpy( cmdexe, DEFAULT_CMD_EXE); + } + wcscat( cmdexe, SLASH_C_APPEND); + + wcscpy( Command, cmdexe); + recdatap = Command + wcslen( Command); + recdata_len = 0; + + for ( i = 1; i < argc; i++) { + + DWORD temp; + + temp = wcslen( argv[i]) + 1; + + recdata_len += temp; + + if ( recdata_len > MAX_COMMAND_LEN) { + printf( "Command too long\n"); + return( FALSE); + } + + wcscpy( recdatap, argv[i]); + recdatap += temp; + + // To construct lpszCommandLine argument to CreateProcess call + // we replace nuls with spaces. + + *(recdatap - 1) = ' '; + } + + // Reset space back to null on last argument in string. + + *(recdatap - 1) = '\0'; + + return( TRUE); +} + diff --git a/private/net/svcdlls/at/server/sources b/private/net/svcdlls/at/server/sources new file mode 100644 index 000000000..ffeeb36c0 --- /dev/null +++ b/private/net/svcdlls/at/server/sources @@ -0,0 +1,38 @@ +MAJORCOMP=net +MINORCOMP=atserver + +TARGETPATH=obj +TARGETNAME=atsvc +TARGETTYPE=LIBRARY + +TARGETLIBS= \ + $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib + +INCLUDES=..;..\..\..\inc;..\..\..\..\inc;..\..\..\api + +!IFNDEF DISABLE_NET_UNICODE +UNICODE=1 +NET_C_DEFINES=-DUNICODE +!ENDIF + +SOURCES= \ + atdebug.c \ + atenv.c \ + atmain.c \ + atapi.c \ + atreg.c \ + atsec.c \ + attime.c \ + atsvc_s.c + +C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H + +#386_WARNING_LEVEL=-W4 + +UMTYPE=console +UMTEST=run +UMLIBS= |