File: System\Diagnostics\Reader\EventLogSession.cs
Web Access
Project: src\src\runtime\src\libraries\System.Diagnostics.EventLog\src\System.Diagnostics.EventLog.csproj (System.Diagnostics.EventLog)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Globalization;
using System.Security;
using Microsoft.Win32;

namespace System.Diagnostics.Eventing.Reader
{
    /// <summary>
    /// Session Login Type
    /// </summary>
    public enum SessionAuthentication
    {
        Default = 0,
        Negotiate = 1,
        Kerberos = 2,
        Ntlm = 3
    }

    /// <summary>
    /// The type: log / external log file to query
    /// </summary>
    public enum PathType
    {
        LogName = 1,
        FilePath = 2
    }

    /// <summary>
    /// Defines a session for Event Log operations. The session can
    /// be configured for a remote machine and can use specific
    /// user credentials.
    /// </summary>
    public class EventLogSession : IDisposable
    {
        //
        // the two context handles for rendering (for EventLogRecord).
        // the system and user context handles. They are both common for all the event instances and can be created only once.
        // access to the data member references is safe, while
        // invoking methods on it is marked SecurityCritical as appropriate.
        //
        internal EventLogHandle renderContextHandleSystem = EventLogHandle.Zero;
        internal EventLogHandle renderContextHandleUser = EventLogHandle.Zero;

        // The dummy sync object for the two contexts.
        private readonly object _syncObject;

        private readonly string? _server;
        private readonly string? _user;
        private readonly string? _domain;
        private readonly SessionAuthentication _logOnType;

        // Setup the System Context, once for all the EventRecords.
        internal void SetupSystemContext()
        {
            if (!this.renderContextHandleSystem.IsInvalid)
                return;
            lock (_syncObject)
            {
                if (this.renderContextHandleSystem.IsInvalid)
                {
                    // Create the SYSTEM render context
                    // Call the EvtCreateRenderContext to get the renderContextHandleSystem, so that we can get the system/values/user properties.
                    this.renderContextHandleSystem = NativeWrapper.EvtCreateRenderContext(0, null, UnsafeNativeMethods.EvtRenderContextFlags.EvtRenderContextSystem);
                }
            }
        }

        internal void SetupUserContext()
        {
            lock (_syncObject)
            {
                if (this.renderContextHandleUser.IsInvalid)
                {
                    // Create the USER render context
                    this.renderContextHandleUser = NativeWrapper.EvtCreateRenderContext(0, null, UnsafeNativeMethods.EvtRenderContextFlags.EvtRenderContextUser);
                }
            }
        }

        public EventLogSession()
        {
            // handle = EventLogHandle.Zero;
            _syncObject = new object();
        }

        public EventLogSession(string server)
            : this(server, null, null, (SecureString?)null, SessionAuthentication.Default)
        {
        }

        public EventLogSession(string server, string? domain, string? user, SecureString? password, SessionAuthentication logOnType)
        {
            server ??= "localhost";

            _syncObject = new object();

            _server = server;
            _domain = domain;
            _user = user;
            _logOnType = logOnType;

            UnsafeNativeMethods.EvtRpcLogin erLogin = new UnsafeNativeMethods.EvtRpcLogin(
                server: _server,
                user: _user,
                domain: _domain,
                flags: (int)_logOnType,
                password: CoTaskMemUnicodeSafeHandle.Zero);
            try
            {
                if (password != null)
                    erLogin.Password.SetMemory(SecureStringMarshal.SecureStringToCoTaskMemUnicode(password));
                // Open a session using the erLogin structure.
                Handle = NativeWrapper.EvtOpenSession(UnsafeNativeMethods.EvtLoginClass.EvtRpcLogin, ref erLogin, 0, 0);
            }
            finally
            {
                erLogin.Password.Dispose();
            }
        }

        internal EventLogHandle Handle { get; } = EventLogHandle.Zero;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this == s_globalSession)
                    throw new InvalidOperationException();
            }

            if (this.renderContextHandleSystem != null &&
                !this.renderContextHandleSystem.IsInvalid)
                this.renderContextHandleSystem.Dispose();

            if (this.renderContextHandleUser != null &&
                !this.renderContextHandleUser.IsInvalid)
                this.renderContextHandleUser.Dispose();

            if (Handle != null && !Handle.IsInvalid)
                Handle.Dispose();
        }

        public void CancelCurrentOperations()
        {
            NativeWrapper.EvtCancel(Handle);
        }

        private static readonly EventLogSession s_globalSession = new EventLogSession();
        public static EventLogSession GlobalSession
        {
            get { return s_globalSession; }
        }

        public IEnumerable<string> GetProviderNames()
        {
            List<string> namesList = new List<string>(100);

            using (EventLogHandle ProviderEnum = NativeWrapper.EvtOpenProviderEnum(this.Handle, 0))
            {
                bool finish = false;

                do
                {
                    string? s = NativeWrapper.EvtNextPublisherId(ProviderEnum, ref finish);
                    if (!finish)
                    {
                        Debug.Assert(s != null, "value should only be null when there are no more items");
                        namesList.Add(s);
                    }
                }
                while (!finish);

                return namesList;
            }
        }

        public IEnumerable<string> GetLogNames()
        {
            List<string> namesList = new List<string>(100);

            using (EventLogHandle channelEnum = NativeWrapper.EvtOpenChannelEnum(this.Handle, 0))
            {
                bool finish = false;

                do
                {
                    string? s = NativeWrapper.EvtNextChannelPath(channelEnum, ref finish);
                    if (!finish)
                    {
                        Debug.Assert(s != null, "value should only be null when there are no more items");
                        namesList.Add(s);
                    }
                }
                while (!finish);

                return namesList;
            }
        }

        public EventLogInformation GetLogInformation(string logName, PathType pathType)
        {
            ArgumentNullException.ThrowIfNull(logName);

            return new EventLogInformation(this, logName, pathType);
        }

        public void ExportLog(string path, PathType pathType, string? query, string targetFilePath)
        {
            this.ExportLog(path, pathType, query, targetFilePath, false);
        }

        public void ExportLog(string path, PathType pathType, string? query, string targetFilePath, bool tolerateQueryErrors)
        {
            ArgumentNullException.ThrowIfNull(path);
            ArgumentNullException.ThrowIfNull(targetFilePath);

            UnsafeNativeMethods.EvtExportLogFlags flag = pathType switch
            {
                PathType.LogName => UnsafeNativeMethods.EvtExportLogFlags.EvtExportLogChannelPath,
                PathType.FilePath => UnsafeNativeMethods.EvtExportLogFlags.EvtExportLogFilePath,
                _ => throw new ArgumentOutOfRangeException(nameof(pathType)),
            };
            if (!tolerateQueryErrors)
                NativeWrapper.EvtExportLog(this.Handle, path, query, targetFilePath, (int)flag);
            else
                NativeWrapper.EvtExportLog(this.Handle, path, query, targetFilePath, (int)flag | (int)UnsafeNativeMethods.EvtExportLogFlags.EvtExportLogTolerateQueryErrors);
        }

        public void ExportLogAndMessages(string path, PathType pathType, string? query, string targetFilePath)
        {
            this.ExportLogAndMessages(path, pathType, query, targetFilePath, false, CultureInfo.CurrentCulture);
        }

        public void ExportLogAndMessages(string path, PathType pathType, string? query, string targetFilePath, bool tolerateQueryErrors, CultureInfo? targetCultureInfo)
        {
            ExportLog(path, pathType, query, targetFilePath, tolerateQueryErrors);
            // Ignore the CultureInfo, pass 0 to use the calling thread's locale
            NativeWrapper.EvtArchiveExportedLog(this.Handle, targetFilePath, 0, 0);
        }

        public void ClearLog(string logName)
        {
            this.ClearLog(logName, null);
        }

        public void ClearLog(string logName, string? backupPath)
        {
            ArgumentNullException.ThrowIfNull(logName);

            NativeWrapper.EvtClearLog(this.Handle, logName, backupPath, 0);
        }
    }
}