/*++ Copyright (c) 1991 Microsoft Corporation Module Name: eventlog.c Abstract: This module provides support routines for eventlogging. Author: Madan Appiah (madana) 27-Jul-1992 Environment: Contains NT specific code. Revision History: --*/ #include #include #include #include // DWORD. #include // event log apis #include // NO_ERROR #include // NET_API_STATUS. #include // Alert defines #include // These routines #include // needed by logonp.h #include // NetpLogon routines #include // ultow() // // Structure describing the entire list of logged events. // typedef struct _NL_EVENT_LIST { CRITICAL_SECTION EventListCritSect; LIST_ENTRY EventList; // Number of milli-seconds to keep EventList entry for. ULONG DuplicateEventlogTimeout; // Event source LPWSTR Source; } NL_EVENT_LIST, *PNL_EVENT_LIST; // // Structure describing an event that has already been logged. // typedef struct _NL_EVENT_ENTRY { LIST_ENTRY Next; LARGE_INTEGER FirstLogTime; DWORD EventId; DWORD EventType; DWORD EventCategory; LPBYTE RawDataBuffer; DWORD RawDataSize; LPWSTR *StringArray; DWORD StringCount; DWORD EventsLogged; // total times event encountered. } NL_EVENT_ENTRY, *PNL_EVENT_ENTRY; DWORD NetpWriteEventlogEx( LPWSTR Source, DWORD EventID, DWORD EventType, DWORD EventCategory, DWORD NumStrings, LPWSTR *Strings, DWORD DataLength, LPVOID Data ) /*++ Routine Description: This function writes the specified (EventID) log at the end of the eventlog. Arguments: Source - Points to a null-terminated string that specifies the name of the module referenced. The node must exist in the registration database, and the module name has the following format: \EventLog\System\Lanmanworkstation EventID - The specific event identifier. This identifies the message that goes with this event. EventType - Specifies the type of event being logged. This parameter can have one of the following values: Value Meaning EVENTLOG_ERROR_TYPE Error event EVENTLOG_WARNING_TYPE Warning event EVENTLOG_INFORMATION_TYPE Information event NumStrings - Specifies the number of strings that are in the array at 'Strings'. A value of zero indicates no strings are present. Strings - Points to a buffer containing an array of null-terminated strings that are merged into the message before displaying to the user. This parameter must be a valid pointer (or NULL), even if cStrings is zero. DataLength - Specifies the number of bytes of event-specific raw (binary) data to write to the log. If cbData is zero, no event-specific data is present. Data - Buffer containing the raw data. This parameter must be a valid pointer (or NULL), even if cbData is zero. Return Value: Returns the WIN32 extended error obtained by GetLastError(). NOTE : This function works slow since it calls the open and close eventlog source everytime. --*/ { HANDLE EventlogHandle; DWORD ReturnCode; // // open eventlog section. // EventlogHandle = RegisterEventSourceW( NULL, Source ); if (EventlogHandle == NULL) { ReturnCode = GetLastError(); goto Cleanup; } // // Log the error code specified // if( !ReportEventW( EventlogHandle, (WORD)EventType, (WORD)EventCategory, // event category EventID, NULL, (WORD)NumStrings, DataLength, Strings, Data ) ) { ReturnCode = GetLastError(); goto Cleanup; } ReturnCode = NO_ERROR; Cleanup: if( EventlogHandle != NULL ) { DeregisterEventSource(EventlogHandle); } return ReturnCode; } DWORD NetpWriteEventlog( LPWSTR Source, DWORD EventID, DWORD EventType, DWORD NumStrings, LPWSTR *Strings, DWORD DataLength, LPVOID Data ) { return NetpWriteEventlogEx( Source, EventID, EventType, 0, NumStrings, Strings, DataLength, Data ); } DWORD NetpRaiseAlert( IN LPWSTR ServiceName, IN DWORD alert_no, IN LPWSTR *string_array ) /*++ Routine Description: Raise NETLOGON specific Admin alerts. Arguments: alert_no - The alert to be raised, text in alertmsg.h string_array - array of strings terminated by NULL string. Return Value: None. --*/ { NET_API_STATUS NetStatus; LPWSTR *SArray; PCHAR Next; PCHAR End; char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)]; PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message; // // Build the variable data // admin->alrtad_errcode = alert_no; admin->alrtad_numstrings = 0; Next = (PCHAR) ALERT_VAR_DATA(admin); End = Next + ALERTSZ; // // now take care of (optional) char strings // for( SArray = string_array; *SArray != NULL; SArray++ ) { DWORD StringLen; StringLen = (wcslen(*SArray) + 1) * sizeof(WCHAR); if( Next + StringLen < End ) { // // copy next string. // RtlCopyMemory(Next, *SArray, StringLen); Next += StringLen; admin->alrtad_numstrings++; } else { return ERROR_BUFFER_OVERFLOW; } } // // Call alerter. // NetStatus = NetAlertRaiseEx( ALERT_ADMIN_EVENT, message, (DWORD)((PCHAR)Next - (PCHAR)message), ServiceName ); return NetStatus; } HANDLE NetpEventlogOpen ( IN LPWSTR Source, IN ULONG DuplicateEventlogTimeout ) /*++ Routine Description: This routine open a context that keeps track of events that have been logged in the recent past. Arguments: Source - Name of the service opening the eventlog DuplicateEventlogTimeout - Number of milli-seconds to keep EventList entry for. Return Value: Handle to be passed to related routines. NULL: if memory could not be allocated. --*/ { PNL_EVENT_LIST EventList; LPBYTE Where; // // Allocate a buffer to keep the context in. // EventList = LocalAlloc( 0, sizeof(NL_EVENT_LIST) + wcslen(Source) * sizeof(WCHAR) + sizeof(WCHAR) ); if ( EventList == NULL ) { return NULL; } // // Initialize the critical section // try { InitializeCriticalSection( &EventList->EventListCritSect ); } except( EXCEPTION_EXECUTE_HANDLER ) { LocalFree( EventList ); return NULL; } // // Initialize the buffer // InitializeListHead( &EventList->EventList ); EventList->DuplicateEventlogTimeout = DuplicateEventlogTimeout; // // Copy the service name into the buffer // Where = (LPBYTE)(EventList + 1); wcscpy( (LPWSTR)Where, Source ); EventList->Source = (LPWSTR) Where; return EventList; } DWORD NetpEventlogWriteEx2 ( IN HANDLE NetpEventHandle, IN DWORD EventType, IN DWORD EventCategory, IN DWORD EventId, IN DWORD StringCount, IN DWORD StatusMessageIndex, IN DWORD RawDataSize, IN LPWSTR *StringArray, IN LPVOID pvRawDataBuffer OPTIONAL ) /*++ Routine Description: Stub routine for calling writing Event Log and skipping duplicates Arguments: NetpEventHandle - Handle from NetpEventlogOpen EventId - event log ID. EventType - Type of event. RawDataBuffer - Data to be logged with the error. numbyte - Size in bytes of "RawDataBuffer" StringArray - array of null-terminated strings. StringCount - number of zero terminated strings in "StringArray". The following flags can be OR'd in to the count: NETP_LAST_MESSAGE_IS_NTSTATUS NETP_LAST_MESSAGE_IS_NETSTATUS NETP_ALLOW_DUPLICATE_EVENTS NETP_RAISE_ALERT_TOO StatusMessageIndex - Specifies the index of the message that is a Net or NT status in the StringArray. Used only if NETP_LAST_MESSAGE_IS_NETSTATUS or NETP_LAST_MESSAGE_IS_NTSTATUS are set in StringCount. If this parameter is MAXULONG and either of these flags is set, the default is assumed which is the last message in the list. Return Value: Win 32 status of the operation. ERROR_ALREAY_EXISTS: Success status indicating the message was already logged --*/ { DWORD ErrorCode; DWORD AlertErrorCode = NO_ERROR; WCHAR ErrorNumberBuffer[25]; PLIST_ENTRY ListEntry; ULONG StringIndex; DWORD LocalStatusMessageIndex = StatusMessageIndex; BOOLEAN AllowDuplicateEvents; BOOLEAN RaiseAlertToo; PNL_EVENT_ENTRY EventEntry; PNL_EVENT_LIST EventList = (PNL_EVENT_LIST)NetpEventHandle; LPBYTE RawDataBuffer = (LPBYTE)pvRawDataBuffer; // // Remove sundry flags // EnterCriticalSection( &EventList->EventListCritSect ); AllowDuplicateEvents = (StringCount & NETP_ALLOW_DUPLICATE_EVENTS) != 0; StringCount &= ~NETP_ALLOW_DUPLICATE_EVENTS; RaiseAlertToo = (StringCount & NETP_RAISE_ALERT_TOO) != 0; StringCount &= ~NETP_RAISE_ALERT_TOO; // // Check if the status message index in the list // should be assigned the default value // if ( (StringCount & NETP_LAST_MESSAGE_IS_NETSTATUS) != 0 || (StringCount & NETP_LAST_MESSAGE_IS_NTSTATUS) != 0 ) { if ( LocalStatusMessageIndex == MAXULONG ) { LocalStatusMessageIndex = (StringCount & NETP_STRING_COUNT_MASK) - 1; } } // // If an NT status code was passed in, // convert it to a net status code. // if ( StringCount & NETP_LAST_MESSAGE_IS_NTSTATUS ) { StringCount &= ~NETP_LAST_MESSAGE_IS_NTSTATUS; // // Do the "better" error mapping when eventviewer ParameterMessageFile // can be a list of files. Then, add netmsg.dll to the list. // // StringArray[((StringCount&NETP_STRING_COUNT_MASK)-1] = (LPWSTR) NetpNtStatusToApiStatus( (NTSTATUS) StringArray[(StringCount&NETP_STRING_COUNT_MASK)-1] ); StringArray[LocalStatusMessageIndex] = (LPWSTR) (ULONG_PTR) RtlNtStatusToDosError( (NTSTATUS) ((ULONG_PTR)StringArray[LocalStatusMessageIndex]) ); StringCount |= NETP_LAST_MESSAGE_IS_NETSTATUS; } // // If a net/windows status code was passed in, // convert to the the %%N format the eventviewer knows. // if ( StringCount & NETP_LAST_MESSAGE_IS_NETSTATUS ) { StringCount &= ~NETP_LAST_MESSAGE_IS_NETSTATUS; wcscpy( ErrorNumberBuffer, L"%%" ); ultow( (ULONG) ((ULONG_PTR)StringArray[LocalStatusMessageIndex]), ErrorNumberBuffer+2, 10 ); StringArray[LocalStatusMessageIndex] = ErrorNumberBuffer; } // // Check to see if this problem has already been reported. // if ( !AllowDuplicateEvents ) { for ( ListEntry = EventList->EventList.Flink ; ListEntry != &EventList->EventList ; ) { EventEntry = CONTAINING_RECORD( ListEntry, NL_EVENT_ENTRY, Next ); // Entry might be freed (or moved) below ListEntry = ListEntry->Flink; // // If the entry is too old, // ditch it. // if ( NetpLogonTimeHasElapsed( EventEntry->FirstLogTime, EventList->DuplicateEventlogTimeout ) ) { // NlPrint((NL_MISC, "Ditched a duplicate event. %ld\n", EventEntry->EventId )); RemoveEntryList( &EventEntry->Next ); LocalFree( EventEntry ); continue; } // // Compare this event to the one being logged. // if ( EventEntry->EventId == EventId && EventEntry->EventType == EventType && EventEntry->EventCategory == EventCategory && EventEntry->RawDataSize == RawDataSize && EventEntry->StringCount == StringCount ) { if ( RawDataSize != 0 && !RtlEqualMemory( EventEntry->RawDataBuffer, RawDataBuffer, RawDataSize ) ) { continue; } for ( StringIndex=0; StringIndex < StringCount; StringIndex ++ ) { if ( EventEntry->StringArray[StringIndex] == NULL) { if ( StringArray[StringIndex] != NULL ) { break; } } else { if ( StringArray[StringIndex] == NULL ) { break; } if ( wcscmp( EventEntry->StringArray[StringIndex], StringArray[StringIndex] ) != 0 ) { break; } } } // // If the event has already been logged, // skip this one. // if ( StringIndex == StringCount ) { RemoveEntryList( &EventEntry->Next ); InsertHeadList( &EventList->EventList, &EventEntry->Next ); ErrorCode = ERROR_ALREADY_EXISTS; // // update count of events logged. // EventEntry->EventsLogged ++; goto Cleanup; } } } } // // Raise an alert if one is needed. // if ( RaiseAlertToo ) { ASSERT( StringArray[StringCount] == NULL ); if ( StringArray[StringCount] == NULL ) { AlertErrorCode = NetpRaiseAlert( EventList->Source, EventId, StringArray ); } } // // write event // ErrorCode = NetpWriteEventlogEx( EventList->Source, EventId, EventType, EventCategory, StringCount, StringArray, RawDataSize, RawDataBuffer); if( ErrorCode != NO_ERROR ) { goto Cleanup; } // // Save the event for later // (Only cache events while the service is starting or running.) // if ( !AllowDuplicateEvents ) { ULONG EventEntrySize; // // Compute the size of the allocated block. // EventEntrySize = sizeof(NL_EVENT_ENTRY) + RawDataSize; for ( StringIndex=0; StringIndex < StringCount; StringIndex ++ ) { EventEntrySize += sizeof(LPWSTR); if ( StringArray[StringIndex] != NULL ) { EventEntrySize += wcslen(StringArray[StringIndex]) * sizeof(WCHAR) + sizeof(WCHAR); } } // // Allocate a block for the entry // EventEntry = LocalAlloc( 0, EventEntrySize ); // // Copy the description of this event into the allocated block. // if ( EventEntry != NULL ) { LPBYTE Where; EventEntry->EventId = EventId; EventEntry->EventType = EventType; EventEntry->EventCategory = EventCategory; EventEntry->RawDataSize = RawDataSize; EventEntry->StringCount = StringCount; EventEntry->EventsLogged = 1; GetSystemTimeAsFileTime( (PFILETIME)&EventEntry->FirstLogTime ); Where = (LPBYTE)(EventEntry+1); EventEntry->StringArray = (LPWSTR *)Where; Where += StringCount * sizeof(LPWSTR); for ( StringIndex=0; StringIndex < StringCount; StringIndex ++ ) { if ( StringArray[StringIndex] == NULL ) { EventEntry->StringArray[StringIndex] = NULL; } else { EventEntry->StringArray[StringIndex] = (LPWSTR) Where; wcscpy( (LPWSTR)Where, StringArray[StringIndex] ); Where += wcslen( StringArray[StringIndex] ) * sizeof(WCHAR) + sizeof(WCHAR); } } if ( RawDataSize != 0 ) { EventEntry->RawDataBuffer = Where; RtlCopyMemory( Where, RawDataBuffer, RawDataSize ); } InsertHeadList( &EventList->EventList, &EventEntry->Next ); } } Cleanup: LeaveCriticalSection( &EventList->EventListCritSect ); return (ErrorCode == NO_ERROR) ? AlertErrorCode : ErrorCode; } DWORD NetpEventlogWriteEx ( IN HANDLE NetpEventHandle, IN DWORD EventType, IN DWORD EventCategory, IN DWORD EventId, IN DWORD StringCount, IN DWORD RawDataSize, IN LPWSTR *StringArray, IN LPVOID pvRawDataBuffer OPTIONAL ) { return NetpEventlogWriteEx2 ( NetpEventHandle, EventType, // wType EventCategory, EventId, // dwEventID StringCount, MAXULONG, // default status message index RawDataSize, StringArray, pvRawDataBuffer ); } DWORD NetpEventlogWrite ( IN HANDLE NetpEventHandle, IN DWORD EventId, IN DWORD EventType, IN LPBYTE RawDataBuffer OPTIONAL, IN DWORD RawDataSize, IN LPWSTR *StringArray, IN DWORD StringCount ) { return NetpEventlogWriteEx2 ( NetpEventHandle, EventType, // wType 0, // wCategory EventId, // dwEventID StringCount, MAXULONG, // default status message index RawDataSize, StringArray, RawDataBuffer ); } VOID NetpEventlogClearList ( IN HANDLE NetpEventHandle ) /*++ Routine Description: This routine clears the list of events that have already been logged. Arguments: NetpEventHandle - Handle from NetpEventlogOpen Return Value: None. --*/ { PNL_EVENT_LIST EventList = (PNL_EVENT_LIST)NetpEventHandle; EnterCriticalSection(&EventList->EventListCritSect); while (!IsListEmpty(&EventList->EventList)) { PNL_EVENT_ENTRY EventEntry = CONTAINING_RECORD(EventList->EventList.Flink, NL_EVENT_ENTRY, Next); RemoveEntryList( &EventEntry->Next ); LocalFree( EventEntry ); } LeaveCriticalSection(&EventList->EventListCritSect); } VOID NetpEventlogSetTimeout ( IN HANDLE NetpEventHandle, IN ULONG DuplicateEventlogTimeout ) /*++ Routine Description: This routine sets a new timeout for logged events Arguments: NetpEventHandle - Handle from NetpEventlogOpen DuplicateEventlogTimeout - Number of milli-seconds to keep EventList entry for. Return Value: None. --*/ { PNL_EVENT_LIST EventList = (PNL_EVENT_LIST)NetpEventHandle; EventList->DuplicateEventlogTimeout = DuplicateEventlogTimeout; } VOID NetpEventlogClose ( IN HANDLE NetpEventHandle ) /*++ Routine Description: This routine closes the handle returned from NetpEventlogOpen Arguments: NetpEventHandle - Handle from NetpEventlogOpen Return Value: None. --*/ { PNL_EVENT_LIST EventList = (PNL_EVENT_LIST)NetpEventHandle; // // Clear the list of logged events. // NetpEventlogClearList( NetpEventHandle ); // // Delete the critsect // DeleteCriticalSection( &EventList->EventListCritSect ); // // Free the allocated buffer. // LocalFree( EventList ); }