3837 lines · 181.0 KB · C#
RawDownload
//******************************************************************************************************
//  ServiceHelper.cs - Gbtc
//
//  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
//  not use this file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://www.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  08/29/2006 - Pinal C. Patel
//       Original version of source code generated.
//  11/30/2007 - Pinal C. Patel
//       Modified the "design time" check in EndInit() method to use LicenseManager. UsageMode property
//       instead of DesignMode property as the former is more accurate than the latter.
//  12/14/2007 - Pinal C. Patel
//       Made monitoring of service health optional via the MonitorServiceHealth property.
//  09/30/2008 - J. Ritchie Carroll
//       Converted to C#.
//  03/06/2009 - Pinal C. Patel
//       Edited code comments.
//  06/19/2009 - Pinal C. Patel
//       Modified Initialize() method to just load settings from the config file.
//  06/30/2009 - Pinal C. Patel
//       Changed ServiceComponents to be a collection of object instead of ISupportLifecycle so it can
//       host a wide range of object implementing various interfaces used by the ServiceHelper commands.
//  07/15/2009 - Pinal C. Patel
//       Added AllowedRemoteUsers and ImpersonateRemoteUser properties as part of security.
//  07/16/2009 - Pinal C. Patel
//       Added SupportTelnetSessions property so that telnet support can be optionally turned-on.
//  07/17/2009 - Pinal C. Patel
//       Modified MonitorServiceHealth to default to false as it is not always needed.
//  07/30/2009 - Pinal C. Patel
//       Modified to set the principal of the thread where client request are handled to that of the the 
//       process or remote client (if transmitted by the remote client upon connection).
//  08/07/2009 - Pinal C. Patel
//       Subscribed to ErrorLogger.LoggingException event.
//  08/10/2009 - Josh L. Patterson
//       Edited Comments
//  09/08/2009 - Pinal C. Patel
//       Modified GetProcess(), GetConnectedClient() and GetClientRequestHandler() to use lambda
//       functions instead of manual iteration of collection items.
//       Renamed GetProcess(), GetConnectedClient() and GetClientRequestHandler() to FindProcess(), 
//       FindConnectedClient() and FindClientRequestHandler() respectively.
//  09/14/2009 - Stephen C. Wills
//       Added new header and license agreement.
//  10/23/2009 - Pinal C. Patel
//       Modified UpdateStatus() method to allow the type of update to be specified.
//  12/18/2009 - Pinal C. Patel
//       Added message flooding control to UpdateStatus().
//  06/16/2010 - Pinal C. Patel
//       Made changes necessary to implement role-based security.
//       Added thread synchronization to list-based member variable.
//  10/14/2010 - Pinal C. Patel
//       Updated security implementation to include the entire client request text including command 
//       and arguments instead of just the command.
//  12/16/2010 - Pinal C. Patel
//       Added SupportSystemCommands property that can be used to enable or disable system-level access 
//       via the build-in commands.
//  01/04/2011 - J. Ritchie Carroll
//       Changed default performance sampling interval on services to every 5 seconds instead of every
//       second to reduce overhead and increase performance. Added a "-lifetime" option to the Health
//       command to show lifetime performance statistics. Made sampling interval a user config option.
//  01/17/2011 - J. Ritchie Carroll
//       Added default "version" and "time" service commands.
//  02/11/2011 - Pinal C. Patel
//       Updated VerifySecurity() to work correctly with TCP integrated windows authentication. 
//  03/09/2011 - Pinal C. Patel
//       Moved UpdateType enumeration to GSF namespace in GSF.Core.dll for broader usage.
//  04/14/2011 - Pinal C. Patel
//       Updated to use new serialization methods in GSF.Serialization class.
//  09/22/2011 - J. Ritchie Carroll
//       Added Mono implementation exception regions.
//  02/17/2012 - Stephen C. Wills
//       Added ReloadCryptoCache service command.
//  04/12/2012 - Pinal C. Patel
//       Added new UnscheduleProcess() and RemoveScheduledProcess methods.
//       Changed AddProcess(), AddScheduledProcess() and ScheduleProcess() method to return a boolean.
//  12/20/2012 - Starlynn Danyelle Gilliam
//       Modified Header.
//
//******************************************************************************************************

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Principal;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using GSF.Collections;
using GSF.Communication;
using GSF.Configuration;
using GSF.Diagnostics;
using GSF.ErrorManagement;
using GSF.IO;
using GSF.Reflection;
using GSF.Scheduling;
using GSF.Security;
using GSF.Security.Cryptography;
using GSF.Threading;
using GSF.Units;

namespace GSF.ServiceProcess
{
    #region [ Enumerations ]

    /// <summary>
    /// Indicates the state of a Windows Service.
    /// </summary>
    public enum ServiceState
    {
        /// <summary>
        /// Service has started.
        /// </summary>
        Started,
        /// <summary>
        /// Service has stopped.
        /// </summary>
        Stopped,
        /// <summary>
        /// Service has paused.
        /// </summary>
        Paused,
        /// <summary>
        /// Service has resumed.
        /// </summary>
        Resumed,
        /// <summary>
        /// Service has shutdown.
        /// </summary>
        Shutdown
    }

    #endregion

    /// <summary>
    /// Component that provides added functionality to a Windows Service.
    /// </summary>
    /// <seealso cref="ServiceProcess"/>
    /// <seealso cref="ServiceResponse"/>
    /// <seealso cref="ClientInfo"/>
    /// <seealso cref="ClientRequest"/>
    /// <seealso cref="ClientRequestHandler"/>
    [ToolboxBitmap(typeof(ServiceHelper))]
    public class ServiceHelper : Component, ISupportLifecycle, ISupportInitialize, IProvideStatus, IPersistSettings
    {
        #region [ Members ]

        // Nested Types
        private class StatusUpdate
        {
            public StatusUpdate(Guid client, UpdateType type, string message)
            {
                this.Client = client;
                this.Type = type;
                this.Message = message;
            }

            public readonly Guid Client;
            public readonly UpdateType Type;
            public readonly string Message;
        }

        // Delegates
        private delegate bool TryGetClientPrincipalFunctionSignature(Guid clientID, out WindowsPrincipal clientPrincipal);

        // Constants

        /// <summary>
        /// Specifies the default value for the <see cref="LogStatusUpdates"/> property.
        /// </summary>
        public const bool DefaultLogStatusUpdates = true;

        /// <summary>
        /// Specifies the default value for the <see cref="MaxStatusUpdatesLength"/> property.
        /// </summary>
        public const int DefaultMaxStatusUpdatesLength = 8192;

        /// <summary>
        /// Specifies the default value for the <see cref="MaxStatusUpdatesFrequency"/> property.
        /// </summary>
        public const int DefaultMaxStatusUpdatesFrequency = 30;

        /// <summary>
        /// Specifies the default value for the <see cref="MonitorServiceHealth"/> property.
        /// </summary>
        public const bool DefaultMonitorServiceHealth = false;

        /// <summary>
        /// Specifies the default value for the <see cref="HealthMonitorInterval"/> property.
        /// </summary>
        public const double DefaultHealthMonitorInterval = 5.0D;

        /// <summary>
        /// Specifies the default value for the <see cref="RequestHistoryLimit"/> property.
        /// </summary>
        public const int DefaultRequestHistoryLimit = 50;

        /// <summary>
        /// Specifies the default value for the <see cref="SupportTelnetSessions"/> property.
        /// </summary>
        public const bool DefaultSupportTelnetSessions = false;

        /// <summary>
        /// Specifies the default value for the <see cref="SupportSystemCommands"/> property.
        /// </summary>
        public const bool DefaultSupportSystemCommands = false;

        /// <summary>
        /// Specifies the default value for the <see cref="SecureRemoteInteractions"/> property.
        /// </summary>
        public const bool DefaultSecureRemoteInteractions = false;

        /// <summary>
        /// Specifies the default value for the <see cref="PersistSettings"/> property.
        /// </summary>
        public const bool DefaultPersistSettings = false;

        /// <summary>
        /// Specifies the default value for the <see cref="SettingsCategory"/> property.
        /// </summary>
        public const string DefaultSettingsCategory = "ServiceHelper";

        // Events

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> is starting.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService is starting.")]
        public event EventHandler<EventArgs<string[]>> ServiceStarting;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> has started.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService has started.")]
        public event EventHandler ServiceStarted;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> is stopping.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService is stopping.")]
        public event EventHandler ServiceStopping;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> has stopped.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService has stopped.")]
        public event EventHandler ServiceStopped;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> is pausing.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService is pausing.")]
        public event EventHandler ServicePausing;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> has paused.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService has paused.")]
        public event EventHandler ServicePaused;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> is resuming.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService is resuming.")]
        public event EventHandler ServiceResuming;

        /// <summary>
        /// Occurs when the <see cref="ParentService"/> has resumed.
        /// </summary>
        [Category("Service"),
        Description("Occurs when the ParentService has resumed.")]
        public event EventHandler ServiceResumed;

        /// <summary>
        /// Occurs when the system is being shutdown.
        /// </summary>
        [Category("System"),
        Description("Occurs when the system is being shutdown.")]
        public event EventHandler SystemShutdown;

        /// <summary>
        /// Occurs when a request is received from one of the <see cref="RemoteClients"/>.
        /// </summary>
        /// <remarks>
        /// <see cref="EventArgs{T1,T2}.Argument1"/> is the ID of the remote client that sent the request.<br/>
        /// <see cref="EventArgs{T1,T2}.Argument2"/> is the <see cref="ClientRequest"/> sent by the remote client.
        /// </remarks>
        [Category("Client"),
        Description("Occurs when a request is received from one of the RemoteClients.")]
        public event EventHandler<EventArgs<Guid, ClientRequest>> ReceivedClientRequest;

        /// <summary>
        /// Occurs when the state of a defined <see cref="ServiceProcess"/> changes.
        /// </summary>
        /// <remarks>
        /// <see cref="EventArgs{T1,T2}.Argument1"/> is the <see cref="ServiceProcess.Name"/>.<br/>
        /// <see cref="EventArgs{T1,T2}.Argument2"/> is the <see cref="ServiceProcess.CurrentState"/>
        /// </remarks>
        [Category("Process"),
        Description("Occurs when the state of a defined ServiceProcess changes.")]
        public event EventHandler<EventArgs<string, ServiceProcessState>> ProcessStateChanged;

        // Fields
        private bool m_logStatusUpdates;
        private int m_maxStatusUpdatesLength;
        private int m_maxStatusUpdatesFrequency;
        private bool m_monitorServiceHealth;
        private double m_healthMonitorInterval;
        private int m_requestHistoryLimit;
        private bool m_supportTelnetSessions;
        private bool m_supportSystemCommands;
        private bool m_secureRemoteInteractions;
        private bool m_persistSettings;
        private string m_settingsCategory;
        private string m_telnetSessionPassword;
        private ServiceBase m_parentService;
        private ServerBase m_remotingServer;
        private readonly LogFile m_statusLog;
        private readonly ScheduleManager m_processScheduler;
        private readonly ErrorLogger m_errorLogger;
        private PerformanceMonitor m_performanceMonitor;
        private readonly List<ServiceProcess> m_processes;
        private readonly List<object> m_serviceComponents;
        private readonly List<ClientInfo> m_remoteClients;
        private readonly List<ClientRequestInfo> m_clientRequestHistory;
        private readonly List<ClientRequestHandler> m_clientRequestHandlers;
        private readonly Dictionary<ISupportLifecycle, bool> m_componentEnabledStates;
        private TryGetClientPrincipalFunctionSignature m_tryGetClientPrincipalFunction;
        private readonly ProcessQueue<StatusUpdate> m_statusUpdateQueue;
        private bool m_enabled;
        private bool m_disposed;
        private bool m_initialized;
        private bool m_suppressUpdates;
        private Guid m_remoteCommandClientID;
        private Process m_remoteCommandProcess;
        private Ticks m_lastStatusUpdateTime;
        private long m_statusUpdateCount;
        private bool m_supressStatusUpdates;

        #endregion

        #region [ Constructors ]

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceHelper"/> class.
        /// </summary>
        public ServiceHelper()
        {
            m_telnetSessionPassword = "s3cur3";
            m_logStatusUpdates = DefaultLogStatusUpdates;
            m_maxStatusUpdatesLength = DefaultMaxStatusUpdatesLength;
            m_maxStatusUpdatesFrequency = DefaultMaxStatusUpdatesFrequency;
            m_monitorServiceHealth = DefaultMonitorServiceHealth;
            m_healthMonitorInterval = DefaultHealthMonitorInterval;
            m_requestHistoryLimit = DefaultRequestHistoryLimit;
            m_supportTelnetSessions = DefaultSupportTelnetSessions;
            m_supportSystemCommands = DefaultSupportSystemCommands;
            m_secureRemoteInteractions = DefaultSecureRemoteInteractions;
            m_persistSettings = DefaultPersistSettings;
            m_settingsCategory = DefaultSettingsCategory;
            m_processes = new List<ServiceProcess>();
            m_remoteClients = new List<ClientInfo>();
            m_clientRequestHistory = new List<ClientRequestInfo>();
            m_serviceComponents = new List<object>();
            m_clientRequestHandlers = new List<ClientRequestHandler>();
            m_componentEnabledStates = new Dictionary<ISupportLifecycle, bool>();

            // Components
            m_statusUpdateQueue = ProcessQueue<StatusUpdate>.CreateRealTimeQueue(ProcessStatusUpdates);
            m_statusUpdateQueue.SynchronizedOperationType = SynchronizedOperationType.LongBackground;
            m_statusUpdateQueue.Name = "StatusUpdateQueue";
            m_statusUpdateQueue.ProcessException += StatusUpdateQueue_ProcessException;

            m_statusLog = new LogFile();
            m_statusLog.FileName = "StatusLog.txt";
            m_statusLog.SettingsCategory = "StatusLog";
            m_statusLog.LogException += StatusLog_LogException;

            m_processScheduler = new ScheduleManager();
            m_processScheduler.SettingsCategory = "ProcessScheduler";
            m_processScheduler.ScheduleDue += Scheduler_ScheduleDue;

            m_errorLogger = new ErrorLogger();
            m_errorLogger.ExitOnUnhandledException = false;
            m_errorLogger.SettingsCategory = "ErrorLogger";
            m_errorLogger.ErrorLog.SettingsCategory = "ErrorLog";
            m_errorLogger.LoggingException += ErrorLogger_LoggingException;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceHelper"/> class.
        /// </summary>
        /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="ServiceHelper"/>.</param>
        public ServiceHelper(IContainer container)
            : this()
        {
            if ((object)container != null)
                container.Add(this);
        }

        #endregion

        #region [ Properties ]

        /// <summary>
        /// Gets or sets a boolean value that indicates whether messages sent using <see cref="UpdateStatus(UpdateType,string,object[])"/> 
        /// or <see cref="UpdateStatus(Guid,UpdateType,string,object[])"/> are to be logged to the <see cref="StatusLog"/>.
        /// </summary>
        [Category("Updates"),
        DefaultValue(DefaultLogStatusUpdates),
        Description("Indicates whether messages sent using UpdateStatus() method overloads are to be logged to the StatusLog.")]
        public bool LogStatusUpdates
        {
            get
            {
                return m_logStatusUpdates;
            }
            set
            {
                m_logStatusUpdates = value;
            }
        }

        /// <summary>
        /// Gets or sets the maximum numbers of characters allowed in update status messages without getting suppressed from being displayed.
        /// </summary>
        /// <exception cref="ArgumentException">The value being assigned is negative or zero.</exception>
        [Category("Updates"),
        DefaultValue(DefaultMaxStatusUpdatesLength),
        Description("Maximum numbers of characters allowed in update status messages without getting suppressed from being displayed.")]
        public int MaxStatusUpdatesLength
        {
            get
            {
                return m_maxStatusUpdatesLength;
            }
            set
            {
                if (value <= 0)
                    throw new ArgumentException("Value must be positive");

                m_maxStatusUpdatesLength = value;
            }
        }

        /// <summary>
        /// Gets or sets the maximum number of status update messages that can be issued in a second without getting suppressed from being displayed.
        /// </summary>
        /// <exception cref="ArgumentException">The value being assigned is negative or zero.</exception>
        [Category("Updates"),
        DefaultValue(DefaultMaxStatusUpdatesFrequency),
        Description("Maximum number of status update messages that can be issued in a second without getting suppressed from being displayed.")]
        public int MaxStatusUpdatesFrequency
        {
            get
            {
                return m_maxStatusUpdatesFrequency;
            }
            set
            {
                if (value <= 0)
                    throw new ArgumentException("Value must be positive");

                m_maxStatusUpdatesFrequency = value;
            }
        }

        /// <summary>
        /// Gets or sets a boolean value that indicates whether the health of the <see cref="ParentService"/> is to be monitored.
        /// </summary>
        [Category("Settings"),
        DefaultValue(DefaultMonitorServiceHealth),
        Description("Indicates whether the health of the ParentService is to be monitored.")]
        public bool MonitorServiceHealth
        {
            get
            {

Lines 1–500 of 3837.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t