NT4/private/ntos/ex/sysevent.c
2020-09-30 17:12:29 +02:00

612 lines
14 KiB
C

/*++
Copyright (c) 1989-1993 Microsoft Corporation
Copyright (c) 1994 International Business Machines Corporation
Module Name:
sysevent.c
Abstract:
This module implements the system event related code.
Author:
Environment:
Kernel mode
Revision History:
--*/
#include "exp.h"
//
// Define the type for entries placed on the system events queue.
//
typedef struct _SYSTEMEVENT_PACKET {
LIST_ENTRY ListEntry;
ULONG EventID;
ULONG EventDataLength;
UCHAR EventData[1];
} SYSTEMEVENT_PACKET , *PSYSTEMEVENT_PACKET;
//
// Define the type for entries placed on the system events Dpc.
//
typedef struct _EXP_SYSTEM_EVENT_CONTEXT {
KDPC SysEventDpc;
KTIMER SysEventTimer;
} EXP_SYSTEM_EVENT_CONTEXT , *PEXP_SYSTEM_EVENT_CONTEXT;
//
// Define a global varibles used by the error logging code.
//
#ifdef _PNP_POWER_
KSPIN_LOCK ExpSysEventDataLock;
LIST_ENTRY ExpSysEventQueueHead;
WORK_QUEUE_ITEM ExpSysEventWorkItem;
#endif
VOID
ExpSysEventDpc(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
VOID
ExpQueueSysEventRequest(
VOID
);
VOID
ExpSysEventThread(
IN PVOID StartContext
);
NTSTATUS
ExpSysEventPipeConnect(
PHANDLE Handle
);
#ifdef _PNP_POWER_
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ExpSysEventInitialization)
#pragma alloc_text(PAGE, ExpQueueSysEventRequest)
#pragma alloc_text(PAGE, ExpSysEventThread)
#pragma alloc_text(PAGE, ExpSysEventPipeConnect)
#endif
BOOLEAN
ExpSysEventInitialization(
VOID
)
/*++
Routine Description:
This function initalize the SysEvent related code.
Arguments:
NONE.
Return Value:
A value of TRUE is returned if the system event object is
successfully initialized. Otherwise a value of FALSE is returned.
--*/
{
//
// Initialize SysEvent spin lock.
//
KeInitializeSpinLock( &ExpSysEventDataLock );
// Initialize SysEvent queue.
InitializeListHead( &ExpSysEventQueueHead );
// Initialize work item entry
ExInitializeWorkItem( &ExpSysEventWorkItem, ExpSysEventThread, NULL );
return TRUE;
}
#endif
VOID
ExPostSystemEvent(
IN SYSTEM_EVENT_ID EventID,
IN PVOID EventData OPTIONAL,
IN ULONG EventDataLength
)
/*++
Routine Description:
This API queues a free form data structure to the session manager . It is
used to notify the session manager of various SysEvents.
Actually rePipe the SysEvent by signalling events, enqueing
APCs, and so forth.
Arguments:
EventID - Supplies the ID of the event.
EventDataLength - Supplies the length of the record that is to
send the event information.
EventData - Supplies a pointer to a record that is to send the
event information.
Return Value:
None.
--*/
{
PSYSTEMEVENT_PACKET systemevent;
PLIST_ENTRY listEntry;
#ifndef _PNP_POWER
return ;
#else
//
// Begin by attempting to allocate storage for the SysEvent packet. If
// one cannot be allocated, ignore this and simply return .
//
// NOTE: Even if no SysEvent object created , any system event should
// have been remembered . When boot up , some device drivers or HAL would
// rePipe LID state and/or card insertion event.
systemevent = ExAllocatePoolWithTag( NonPagedPool,
FIELD_OFFSET(SYSTEMEVENT_PACKET, EventData ) +
EventDataLength ,
'EsyS' );
if( !systemevent ) {
// No system resource is found . Ignore this System Event
return ;
}
//
// Initialize the systemevent packet and insert it onto the tail of the list.
// Note that this is done because some drivers have dependencies on LIFO
// notification ordering.
//
systemevent->EventID = EventID;
systemevent->EventDataLength = EventDataLength;
if (ARGUMENT_PRESENT(EventData)) {
// copy the event data from EventData to buffer .
RtlCopyMemory( systemevent->EventData,
EventData,
EventDataLength );
}
//
// Insert this event into the system event queue
//
listEntry = ExInterlockedInsertTailList (&ExpSysEventQueueHead,
&systemevent->ListEntry,
&ExpSysEventDataLock );
//
// If this was the first item added, then request work item
//
if (!listEntry) {
ExQueueWorkItem( &ExpSysEventWorkItem, DelayedWorkQueue );
}
#endif
}
#ifdef _PNP_POWER_
VOID
ExpSysEventThread (
IN PVOID StartContext
)
/*++
Routine Description:
This is the main loop for the system event rePipe thread which executes in the
system process context. This routine is started when the system is
initialized.
Arguments:
StartContext - Startup context; not used.
Return Value:
None.
--*/
{
NTSTATUS status;
IO_STATUS_BLOCK iosb;
PSYSTEMEVENT_PACKET systemevent;
PLIST_ENTRY listEntry, listEntry2;
ULONG writeLength;
HANDLE Handle;
UNREFERENCED_PARAMETER( StartContext );
//
// Check to see whether a connection has been made to the error log
// Pipe. If the Pipe is not connected return.
//
if (!NT_SUCCESS(ExpSysEventPipeConnect(&Handle))) {
KdPrint(( "EX: ExpSysEventPipeConnect is Failed\n" ));
//
// The Pipe could not be connected.
// Set a timer up for another attempt later.
//
ExpQueueSysEventRequest();
return;
}
for (;;) {
// Loop dequeueing packets from the queue head and attempt to
// write the named pipe file.
//
// If the write works, continue looping until there are no more packets.
// Otherwise, indicate that the connection has been broken, cleanup,
// place the packet back onto the head of the queue, and start from the
// top of the loop again.
//
listEntry = ExpSysEventQueueHead.Flink;
if (listEntry == &ExpSysEventQueueHead) {
//
// Indicate that no more work will be done.
//
break;
}
systemevent = CONTAINING_RECORD( listEntry,
SYSTEMEVENT_PACKET,
ListEntry );
writeLength = FIELD_OFFSET(SYSTEMEVENT_PACKET, EventData ) -
FIELD_OFFSET(SYSTEMEVENT_PACKET, EventID ) +
systemevent->EventDataLength;
status = NtWriteFile( Handle,
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb,
&(systemevent->EventID),
writeLength,
NULL, // ByteOffset
NULL // Key
) ;
if (!NT_SUCCESS( status )) {
//
// The send failed. Set a timer up for another attempt later.
//
ExpQueueSysEventRequest();
break;
}
//
// Event sent. Remove it from the queue.
//
listEntry2 = ExInterlockedRemoveHeadList (&ExpSysEventQueueHead,
&ExpSysEventDataLock );
ASSERT (listEntry2 == listEntry);
ExFreePool( systemevent );
}
NtClose (Handle);
}
NTSTATUS
ExpSysEventPipeConnect (
PHANDLE Handle
)
/*++
Routine Description:
This routine attempts to connect to the SysEvents pipe . If the connection
was made successfully and the pipe allows suficiently large messages, then
the SysEvents pipe to the namedpipe handle, SysEventPipeConnected is set to
TRUE and TRUE is retuned. Otherwise a timer is started to queue a
worker thread at a later time, unless there is a pending connection.
Arguments:
None.
Return Value:
Returns TRUE if the Pipe was connected.
--*/
{
UNICODE_STRING sysEventPipeName;
HANDLE npfsHandle;
UNICODE_STRING unicodeSysEventPipeName;
UNICODE_STRING unicodeNpfsName;
NTSTATUS status;
SECURITY_QUALITY_OF_SERVICE dynamicQos;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
PFILE_PIPE_WAIT_FOR_BUFFER waitPipe;
ULONG waitPipeLength;
FILE_PIPE_INFORMATION pipeInfoBuffer;
//
// If the ErrorLogPipe is connected then return true.
//
RtlInitUnicodeString( &unicodeNpfsName, L"\\Device\\NamedPipe\\" );
RtlInitUnicodeString( &sysEventPipeName, L"SysEvents" );
RtlInitUnicodeString( &unicodeSysEventPipeName,
L"\\Device\\NamedPipe\\SysEvents" );
//
// Wait for the server's pipe to reach a listen state...
//
InitializeObjectAttributes(
&objectAttributes,
&unicodeNpfsName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtOpenFile( &npfsHandle,
GENERIC_READ | SYNCHRONIZE,
&objectAttributes,
&iosb,
FILE_SHARE_READ,
0 );
if (NT_SUCCESS( status ) ) {
waitPipeLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) +
sysEventPipeName.Length;
waitPipe = ExAllocatePoolWithTag( NonPagedPool,
waitPipeLength ,
'SpxE' );
waitPipe->TimeoutSpecified = FALSE;
waitPipe->NameLength = sysEventPipeName.Length;
RtlMoveMemory( waitPipe->Name,
sysEventPipeName.Buffer,
sysEventPipeName.Length );
status = NtFsControlFile( npfsHandle,
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb,
FSCTL_PIPE_WAIT,
waitPipe,
waitPipeLength,
NULL,
0 );
if (status == STATUS_PENDING) {
status = NtWaitForSingleObject( npfsHandle, TRUE, NULL ) ;
}
//
// Close NemdPipe Handle
//
NtClose( npfsHandle );
ExFreePool( waitPipe );
}
if (NT_SUCCESS( status ) ) {
//
// Initialize the attributes
//
InitializeObjectAttributes(
&objectAttributes,
&unicodeSysEventPipeName,
0,
NULL,
NULL
);
//
// Set up the security quality of service parameters to use over the
// Pipe. Use the most efficient (least overhead) - which is dynamic
// rather than static tracking.
//
dynamicQos.ImpersonationLevel = SecurityImpersonation;
dynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
dynamicQos.EffectiveOnly = TRUE;
objectAttributes.SecurityQualityOfService = (PVOID)(&dynamicQos);
//
// And now open it...
//
status = NtOpenFile( Handle,
GENERIC_WRITE | SYNCHRONIZE, // NEED CHK
&objectAttributes,
&iosb,
FILE_SHARE_WRITE,
0 ) ;
if (NT_SUCCESS( status ) ) {
//
// Set ReadMode and CompletionMode
//
pipeInfoBuffer.ReadMode = FILE_PIPE_MESSAGE_MODE;
pipeInfoBuffer.CompletionMode = FILE_PIPE_COMPLETE_OPERATION;
NtSetInformationFile( Handle,
&iosb,
&pipeInfoBuffer,
sizeof(FILE_PIPE_INFORMATION),
FilePipeInformation );
}
}
return status;
}
VOID
ExpSysEventDpc(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine queues a work request to the worker thread to process system
events. It is called by a timer DPC when the system event pipe cannot be
connected. The DPC is freed after this routine completes.
Arguments:
Dpc - Supplies a pointer to the DPC structure. This structre is freed by
this routine.
DeferredContext - Unused.
SystemArgument1 - Unused.
SystemArgument2 - Unused.
Return Value:
None
--*/
{
//
// Free memory for this context
//
ExFreePool(DeferredContext);
//
// Queue work item
//
ExQueueWorkItem( &ExpSysEventWorkItem, DelayedWorkQueue );
}
VOID
ExpQueueSysEventRequest(
VOID
)
/*++
Routine Description:
This routine sets a timer to fire after 30 seconds. The timer queues a
DPC which then queues a worker thread request to run the Systen Event thread
routine.
Arguments:
None.
Return Value:
None.
--*/
{
LARGE_INTEGER interval;
PEXP_SYSTEM_EVENT_CONTEXT context;
//
// Allocate a context block which will contain the timer and the DPC.
//
context = ExAllocatePoolWithTag( NonPagedPool,
sizeof(EXP_SYSTEM_EVENT_CONTEXT),
'QpxE');
if (context == NULL) {
// no memory - requeue workitem
ExQueueWorkItem( &ExpSysEventWorkItem, DelayedWorkQueue );
return;
}
KeInitializeTimer( &context->SysEventTimer );
KeInitializeDpc( &context->SysEventDpc, ExpSysEventDpc, context );
//
// Delay for 20 seconds and try to connect to the pipe again.
//
interval.QuadPart = -200000000;
KeSetTimer( &context->SysEventTimer, interval, &context->SysEventDpc );
}
#endif // _PNP_POWER_