mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
603 lines
16 KiB
603 lines
16 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
|
|
|